test_log_utils.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. # test_log_utils.py -- Tests for log_utils.py
  2. # Copyright (C) 2025 Jelmer Vernooij <jelmer@jelmer.uk>
  3. #
  4. # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  5. # Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU
  6. # General Public License as published by the Free Software Foundation; version 2.0
  7. # or (at your option) any later version. You can redistribute it and/or
  8. # modify it under the terms of either of these two licenses.
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. #
  16. # You should have received a copy of the licenses; if not, see
  17. # <http://www.gnu.org/licenses/> for a copy of the GNU General Public License
  18. # and <http://www.apache.org/licenses/LICENSE-2.0> for a copy of the Apache
  19. # License, Version 2.0.
  20. #
  21. """Tests for dulwich.log_utils."""
  22. import logging
  23. import os
  24. import tempfile
  25. from dulwich.log_utils import (
  26. _DULWICH_LOGGER,
  27. _NULL_HANDLER,
  28. _get_trace_target,
  29. _NullHandler,
  30. _should_trace,
  31. default_logging_config,
  32. getLogger,
  33. remove_null_handler,
  34. )
  35. from . import TestCase
  36. class LogUtilsTests(TestCase):
  37. """Tests for log_utils."""
  38. def setUp(self) -> None:
  39. super().setUp()
  40. # Save original handler configuration
  41. self.original_handlers = list(_DULWICH_LOGGER.handlers)
  42. # Save original GIT_TRACE value
  43. self.original_git_trace = os.environ.get("GIT_TRACE")
  44. def tearDown(self) -> None:
  45. # Restore original handler configuration
  46. _DULWICH_LOGGER.handlers = self.original_handlers
  47. # Restore original GIT_TRACE value
  48. if self.original_git_trace is None:
  49. os.environ.pop("GIT_TRACE", None)
  50. else:
  51. os.environ["GIT_TRACE"] = self.original_git_trace
  52. super().tearDown()
  53. def _set_git_trace(self, value: str | None) -> None:
  54. """Helper to set GIT_TRACE environment variable."""
  55. if value is None:
  56. os.environ.pop("GIT_TRACE", None)
  57. else:
  58. os.environ["GIT_TRACE"] = value
  59. def test_null_handler(self) -> None:
  60. """Test the _NullHandler class."""
  61. handler = _NullHandler()
  62. # Create a test record
  63. record = logging.LogRecord(
  64. name="test",
  65. level=logging.INFO,
  66. pathname="test_log_utils.py",
  67. lineno=1,
  68. msg="Test message",
  69. args=(),
  70. exc_info=None,
  71. )
  72. # Should not raise any exceptions
  73. handler.emit(record)
  74. def test_get_logger(self) -> None:
  75. """Test the getLogger function."""
  76. # Should return a logger instance
  77. logger = getLogger("dulwich.test")
  78. self.assertIsInstance(logger, logging.Logger)
  79. self.assertEqual(logger.name, "dulwich.test")
  80. def test_remove_null_handler(self) -> None:
  81. """Test removing the null handler."""
  82. # Make sure _NULL_HANDLER is in the handlers
  83. if _NULL_HANDLER not in _DULWICH_LOGGER.handlers:
  84. _DULWICH_LOGGER.addHandler(_NULL_HANDLER)
  85. # Remove the null handler
  86. remove_null_handler()
  87. # Check that it was removed
  88. self.assertNotIn(_NULL_HANDLER, _DULWICH_LOGGER.handlers)
  89. def test_default_logging_config(self) -> None:
  90. """Test the default logging configuration."""
  91. # Apply default config
  92. default_logging_config()
  93. # Check that the null handler was removed
  94. self.assertNotIn(_NULL_HANDLER, _DULWICH_LOGGER.handlers)
  95. # Check that the root logger has a handler
  96. root_logger = logging.getLogger()
  97. self.assertTrue(root_logger.handlers)
  98. # Reset the root logger to not affect other tests
  99. root_logger.handlers = []
  100. def test_should_trace_disabled(self) -> None:
  101. """Test _should_trace with tracing disabled."""
  102. # Test with unset environment variable
  103. self._set_git_trace(None)
  104. self.assertFalse(_should_trace())
  105. # Test with empty string
  106. self._set_git_trace("")
  107. self.assertFalse(_should_trace())
  108. # Test with "0"
  109. self._set_git_trace("0")
  110. self.assertFalse(_should_trace())
  111. # Test with "false" (case insensitive)
  112. self._set_git_trace("false")
  113. self.assertFalse(_should_trace())
  114. self._set_git_trace("FALSE")
  115. self.assertFalse(_should_trace())
  116. def test_should_trace_enabled(self) -> None:
  117. """Test _should_trace with tracing enabled."""
  118. self._set_git_trace("1")
  119. self.assertTrue(_should_trace())
  120. self._set_git_trace("/tmp/trace.log")
  121. self.assertTrue(_should_trace())
  122. def test_get_trace_target_disabled(self) -> None:
  123. """Test _get_trace_target with tracing disabled."""
  124. # Test with unset environment variable
  125. self._set_git_trace(None)
  126. self.assertIsNone(_get_trace_target())
  127. # Test with empty string
  128. self._set_git_trace("")
  129. self.assertIsNone(_get_trace_target())
  130. # Test with "0"
  131. self._set_git_trace("0")
  132. self.assertIsNone(_get_trace_target())
  133. # Test with "false"
  134. self._set_git_trace("false")
  135. self.assertIsNone(_get_trace_target())
  136. def test_get_trace_target_stderr(self) -> None:
  137. """Test _get_trace_target with stderr output."""
  138. # Test with "1"
  139. self._set_git_trace("1")
  140. self.assertEqual(_get_trace_target(), 2)
  141. # Test with "2"
  142. self._set_git_trace("2")
  143. self.assertEqual(_get_trace_target(), 2)
  144. # Test with "true" (case insensitive)
  145. self._set_git_trace("true")
  146. self.assertEqual(_get_trace_target(), 2)
  147. self._set_git_trace("TRUE")
  148. self.assertEqual(_get_trace_target(), 2)
  149. def test_get_trace_target_file_descriptor(self) -> None:
  150. """Test _get_trace_target with file descriptor."""
  151. for fd in range(3, 10):
  152. self._set_git_trace(str(fd))
  153. self.assertEqual(_get_trace_target(), fd)
  154. # Test out of range values
  155. self._set_git_trace("10")
  156. self.assertIsNone(_get_trace_target())
  157. self._set_git_trace("2")
  158. self.assertEqual(_get_trace_target(), 2) # Special case: stderr
  159. def test_get_trace_target_file(self) -> None:
  160. """Test _get_trace_target with file path."""
  161. with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
  162. trace_file = f.name
  163. try:
  164. self._set_git_trace(trace_file)
  165. self.assertEqual(_get_trace_target(), trace_file)
  166. finally:
  167. if os.path.exists(trace_file):
  168. os.unlink(trace_file)
  169. def test_get_trace_target_directory(self) -> None:
  170. """Test _get_trace_target with directory path."""
  171. with tempfile.TemporaryDirectory() as tmpdir:
  172. self._set_git_trace(tmpdir)
  173. self.assertEqual(_get_trace_target(), tmpdir)
  174. def test_get_trace_target_other_values(self) -> None:
  175. """Test _get_trace_target with other values."""
  176. # Any non-absolute path should be treated as disabled
  177. self._set_git_trace("relative/path")
  178. self.assertIsNone(_get_trace_target())
  179. def test_default_logging_config_with_trace(self) -> None:
  180. """Test default_logging_config with GIT_TRACE enabled."""
  181. # Save current root logger state
  182. root_logger = logging.getLogger()
  183. original_level = root_logger.level
  184. original_handlers = list(root_logger.handlers)
  185. # Clean up after test
  186. def cleanup() -> None:
  187. root_logger.handlers = original_handlers
  188. root_logger.level = original_level
  189. self.addCleanup(cleanup)
  190. # Reset root logger to ensure clean state
  191. root_logger.handlers = []
  192. root_logger.level = logging.WARNING
  193. self._set_git_trace("1")
  194. default_logging_config()
  195. # Check that the null handler was removed
  196. self.assertNotIn(_NULL_HANDLER, _DULWICH_LOGGER.handlers)
  197. # Check that the root logger has a handler
  198. self.assertTrue(root_logger.handlers)
  199. # Check that the level is DEBUG when tracing is enabled
  200. self.assertEqual(root_logger.level, logging.DEBUG)