test_log_utils.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  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 typing import Optional
  26. from dulwich.log_utils import (
  27. _DULWICH_LOGGER,
  28. _NULL_HANDLER,
  29. _get_trace_target,
  30. _NullHandler,
  31. _should_trace,
  32. default_logging_config,
  33. getLogger,
  34. remove_null_handler,
  35. )
  36. from . import TestCase
  37. class LogUtilsTests(TestCase):
  38. """Tests for log_utils."""
  39. def setUp(self) -> None:
  40. super().setUp()
  41. # Save original handler configuration
  42. self.original_handlers = list(_DULWICH_LOGGER.handlers)
  43. # Save original GIT_TRACE value
  44. self.original_git_trace = os.environ.get("GIT_TRACE")
  45. def tearDown(self) -> None:
  46. # Restore original handler configuration
  47. _DULWICH_LOGGER.handlers = self.original_handlers
  48. # Restore original GIT_TRACE value
  49. if self.original_git_trace is None:
  50. os.environ.pop("GIT_TRACE", None)
  51. else:
  52. os.environ["GIT_TRACE"] = self.original_git_trace
  53. super().tearDown()
  54. def _set_git_trace(self, value: Optional[str]) -> None:
  55. """Helper to set GIT_TRACE environment variable."""
  56. if value is None:
  57. os.environ.pop("GIT_TRACE", None)
  58. else:
  59. os.environ["GIT_TRACE"] = value
  60. def test_null_handler(self) -> None:
  61. """Test the _NullHandler class."""
  62. handler = _NullHandler()
  63. # Create a test record
  64. record = logging.LogRecord(
  65. name="test",
  66. level=logging.INFO,
  67. pathname="test_log_utils.py",
  68. lineno=1,
  69. msg="Test message",
  70. args=(),
  71. exc_info=None,
  72. )
  73. # Should not raise any exceptions
  74. handler.emit(record)
  75. def test_get_logger(self) -> None:
  76. """Test the getLogger function."""
  77. # Should return a logger instance
  78. logger = getLogger("dulwich.test")
  79. self.assertIsInstance(logger, logging.Logger)
  80. self.assertEqual(logger.name, "dulwich.test")
  81. def test_remove_null_handler(self) -> None:
  82. """Test removing the null handler."""
  83. # Make sure _NULL_HANDLER is in the handlers
  84. if _NULL_HANDLER not in _DULWICH_LOGGER.handlers:
  85. _DULWICH_LOGGER.addHandler(_NULL_HANDLER)
  86. # Remove the null handler
  87. remove_null_handler()
  88. # Check that it was removed
  89. self.assertNotIn(_NULL_HANDLER, _DULWICH_LOGGER.handlers)
  90. def test_default_logging_config(self) -> None:
  91. """Test the default logging configuration."""
  92. # Apply default config
  93. default_logging_config()
  94. # Check that the null handler was removed
  95. self.assertNotIn(_NULL_HANDLER, _DULWICH_LOGGER.handlers)
  96. # Check that the root logger has a handler
  97. root_logger = logging.getLogger()
  98. self.assertTrue(root_logger.handlers)
  99. # Reset the root logger to not affect other tests
  100. root_logger.handlers = []
  101. def test_should_trace_disabled(self) -> None:
  102. """Test _should_trace with tracing disabled."""
  103. # Test with unset environment variable
  104. self._set_git_trace(None)
  105. self.assertFalse(_should_trace())
  106. # Test with empty string
  107. self._set_git_trace("")
  108. self.assertFalse(_should_trace())
  109. # Test with "0"
  110. self._set_git_trace("0")
  111. self.assertFalse(_should_trace())
  112. # Test with "false" (case insensitive)
  113. self._set_git_trace("false")
  114. self.assertFalse(_should_trace())
  115. self._set_git_trace("FALSE")
  116. self.assertFalse(_should_trace())
  117. def test_should_trace_enabled(self) -> None:
  118. """Test _should_trace with tracing enabled."""
  119. self._set_git_trace("1")
  120. self.assertTrue(_should_trace())
  121. self._set_git_trace("/tmp/trace.log")
  122. self.assertTrue(_should_trace())
  123. def test_get_trace_target_disabled(self) -> None:
  124. """Test _get_trace_target with tracing disabled."""
  125. # Test with unset environment variable
  126. self._set_git_trace(None)
  127. self.assertIsNone(_get_trace_target())
  128. # Test with empty string
  129. self._set_git_trace("")
  130. self.assertIsNone(_get_trace_target())
  131. # Test with "0"
  132. self._set_git_trace("0")
  133. self.assertIsNone(_get_trace_target())
  134. # Test with "false"
  135. self._set_git_trace("false")
  136. self.assertIsNone(_get_trace_target())
  137. def test_get_trace_target_stderr(self) -> None:
  138. """Test _get_trace_target with stderr output."""
  139. # Test with "1"
  140. self._set_git_trace("1")
  141. self.assertEqual(_get_trace_target(), 2)
  142. # Test with "2"
  143. self._set_git_trace("2")
  144. self.assertEqual(_get_trace_target(), 2)
  145. # Test with "true" (case insensitive)
  146. self._set_git_trace("true")
  147. self.assertEqual(_get_trace_target(), 2)
  148. self._set_git_trace("TRUE")
  149. self.assertEqual(_get_trace_target(), 2)
  150. def test_get_trace_target_file_descriptor(self) -> None:
  151. """Test _get_trace_target with file descriptor."""
  152. for fd in range(3, 10):
  153. self._set_git_trace(str(fd))
  154. self.assertEqual(_get_trace_target(), fd)
  155. # Test out of range values
  156. self._set_git_trace("10")
  157. self.assertIsNone(_get_trace_target())
  158. self._set_git_trace("2")
  159. self.assertEqual(_get_trace_target(), 2) # Special case: stderr
  160. def test_get_trace_target_file(self) -> None:
  161. """Test _get_trace_target with file path."""
  162. with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
  163. trace_file = f.name
  164. try:
  165. self._set_git_trace(trace_file)
  166. self.assertEqual(_get_trace_target(), trace_file)
  167. finally:
  168. if os.path.exists(trace_file):
  169. os.unlink(trace_file)
  170. def test_get_trace_target_directory(self) -> None:
  171. """Test _get_trace_target with directory path."""
  172. with tempfile.TemporaryDirectory() as tmpdir:
  173. self._set_git_trace(tmpdir)
  174. self.assertEqual(_get_trace_target(), tmpdir)
  175. def test_get_trace_target_other_values(self) -> None:
  176. """Test _get_trace_target with other values."""
  177. # Any non-absolute path should be treated as disabled
  178. self._set_git_trace("relative/path")
  179. self.assertIsNone(_get_trace_target())
  180. def test_default_logging_config_with_trace(self) -> None:
  181. """Test default_logging_config with GIT_TRACE enabled."""
  182. # Save current root logger state
  183. root_logger = logging.getLogger()
  184. original_level = root_logger.level
  185. original_handlers = list(root_logger.handlers)
  186. # Clean up after test
  187. def cleanup() -> None:
  188. root_logger.handlers = original_handlers
  189. root_logger.level = original_level
  190. self.addCleanup(cleanup)
  191. # Reset root logger to ensure clean state
  192. root_logger.handlers = []
  193. root_logger.level = logging.WARNING
  194. self._set_git_trace("1")
  195. default_logging_config()
  196. # Check that the null handler was removed
  197. self.assertNotIn(_NULL_HANDLER, _DULWICH_LOGGER.handlers)
  198. # Check that the root logger has a handler
  199. self.assertTrue(root_logger.handlers)
  200. # Check that the level is DEBUG when tracing is enabled
  201. self.assertEqual(root_logger.level, logging.DEBUG)