__init__.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. # __init__.py -- The tests for dulwich
  2. # Copyright (C) 2007 James Westby <jw+debian@jameswestby.net>
  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."""
  22. __all__ = [
  23. "BlackboxTestCase",
  24. "SkipTest",
  25. "TestCase",
  26. "expectedFailure",
  27. "skipIf",
  28. ]
  29. import doctest
  30. import os
  31. import shutil
  32. import subprocess
  33. import sys
  34. import tempfile
  35. # If Python itself provides an exception, use that
  36. import unittest
  37. from collections.abc import Sequence
  38. from typing import ClassVar, Optional
  39. from unittest import SkipTest, expectedFailure, skipIf
  40. from unittest import TestCase as _TestCase
  41. class TestCase(_TestCase):
  42. def setUp(self) -> None:
  43. super().setUp()
  44. self.overrideEnv("HOME", "/nonexistent")
  45. self.overrideEnv("GIT_CONFIG_NOSYSTEM", "1")
  46. def overrideEnv(self, name: str, value: Optional[str]) -> None:
  47. def restore() -> None:
  48. if oldval is not None:
  49. os.environ[name] = oldval
  50. elif name in os.environ:
  51. del os.environ[name]
  52. oldval = os.environ.get(name)
  53. if value is not None:
  54. os.environ[name] = value
  55. elif name in os.environ:
  56. del os.environ[name]
  57. self.addCleanup(restore)
  58. class BlackboxTestCase(TestCase):
  59. """Blackbox testing."""
  60. # TODO(jelmer): Include more possible binary paths.
  61. bin_directories: ClassVar[list[str]] = [
  62. os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "bin")),
  63. "/usr/bin",
  64. "/usr/local/bin",
  65. ]
  66. def bin_path(self, name: str) -> str:
  67. """Determine the full path of a binary.
  68. Args:
  69. name: Name of the script
  70. Returns: Full path
  71. """
  72. for d in self.bin_directories:
  73. p = os.path.join(d, name)
  74. if os.path.isfile(p):
  75. return p
  76. else:
  77. raise SkipTest(f"Unable to find binary {name}")
  78. def run_command(self, name: str, args: Sequence[str]) -> subprocess.Popen[bytes]:
  79. """Run a Dulwich command.
  80. Args:
  81. name: Name of the command, as it exists in bin/
  82. args: Arguments to the command
  83. """
  84. env = dict(os.environ)
  85. env["PYTHONPATH"] = os.pathsep.join(sys.path)
  86. # Since they don't have any extensions, Windows can't recognize
  87. # executablility of the Python files in /bin. Even then, we'd have to
  88. # expect the user to set up file associations for .py files.
  89. #
  90. # Save us from all that headache and call python with the bin script.
  91. argv = [sys.executable, self.bin_path(name), *args]
  92. return subprocess.Popen(
  93. argv,
  94. stdout=subprocess.PIPE,
  95. stdin=subprocess.PIPE,
  96. stderr=subprocess.PIPE,
  97. env=env,
  98. )
  99. def self_test_suite() -> unittest.TestSuite:
  100. names = [
  101. "annotate",
  102. "archive",
  103. "attrs",
  104. "bisect",
  105. "blackbox",
  106. "bundle",
  107. "cli",
  108. "cli_cherry_pick",
  109. "cli_merge",
  110. "client",
  111. "cloud_gcs",
  112. "commit_graph",
  113. "config",
  114. "credentials",
  115. "diff",
  116. "diff_tree",
  117. "dumb",
  118. "fastexport",
  119. "file",
  120. "filter_branch",
  121. "filters",
  122. "gc",
  123. "grafts",
  124. "graph",
  125. "greenthreads",
  126. "hooks",
  127. "ignore",
  128. "index",
  129. "lfs",
  130. "lfs_integration",
  131. "line_ending",
  132. "log_utils",
  133. "lru_cache",
  134. "mailmap",
  135. "merge",
  136. "merge_drivers",
  137. "missing_obj_finder",
  138. "notes",
  139. "objects",
  140. "objectspec",
  141. "object_store",
  142. "pack",
  143. "patch",
  144. "porcelain",
  145. "porcelain_cherry_pick",
  146. "porcelain_filters",
  147. "porcelain_lfs",
  148. "porcelain_merge",
  149. "porcelain_notes",
  150. "protocol",
  151. "rebase",
  152. "reflog",
  153. "refs",
  154. "reftable",
  155. "repository",
  156. "server",
  157. "sparse_patterns",
  158. "stash",
  159. "submodule",
  160. "utils",
  161. "walk",
  162. "web",
  163. "whitespace",
  164. "worktree",
  165. ]
  166. module_names = ["tests.test_" + name for name in names]
  167. loader = unittest.TestLoader()
  168. return loader.loadTestsFromNames(module_names)
  169. def tutorial_test_suite() -> unittest.TestSuite:
  170. tutorial = [
  171. "introduction",
  172. "file-format",
  173. "repo",
  174. "object-store",
  175. "remote",
  176. "conclusion",
  177. ]
  178. tutorial_files = [f"../docs/tutorial/{name}.txt" for name in tutorial]
  179. to_restore = []
  180. def overrideEnv(name: str, value: Optional[str]) -> None:
  181. oldval = os.environ.get(name)
  182. if value is not None:
  183. os.environ[name] = value
  184. else:
  185. del os.environ[name]
  186. to_restore.append((name, oldval))
  187. def setup(test: doctest.DocTest) -> None:
  188. test.__old_cwd = os.getcwd() # type: ignore[attr-defined]
  189. test.tempdir = tempfile.mkdtemp() # type: ignore[attr-defined]
  190. test.globs.update({"tempdir": test.tempdir}) # type: ignore[attr-defined]
  191. os.chdir(test.tempdir) # type: ignore[attr-defined]
  192. overrideEnv("HOME", "/nonexistent")
  193. overrideEnv("GIT_CONFIG_NOSYSTEM", "1")
  194. def teardown(test: doctest.DocTest) -> None:
  195. os.chdir(test.__old_cwd) # type: ignore[attr-defined]
  196. shutil.rmtree(test.tempdir) # type: ignore[attr-defined]
  197. for name, oldval in to_restore:
  198. if oldval is not None:
  199. os.environ[name] = oldval
  200. else:
  201. del os.environ[name]
  202. to_restore.clear()
  203. return doctest.DocFileSuite(
  204. module_relative=True,
  205. package="tests",
  206. setUp=setup,
  207. tearDown=teardown,
  208. *tutorial_files,
  209. )
  210. def nocompat_test_suite() -> unittest.TestSuite:
  211. result = unittest.TestSuite()
  212. result.addTests(self_test_suite())
  213. result.addTests(tutorial_test_suite())
  214. from .contrib import test_suite as contrib_test_suite
  215. result.addTests(contrib_test_suite())
  216. return result
  217. def compat_test_suite() -> unittest.TestSuite:
  218. result = unittest.TestSuite()
  219. from .compat import test_suite as compat_test_suite
  220. result.addTests(compat_test_suite())
  221. return result
  222. def test_suite() -> unittest.TestSuite:
  223. result = unittest.TestSuite()
  224. result.addTests(self_test_suite())
  225. if sys.platform != "win32":
  226. result.addTests(tutorial_test_suite())
  227. from .compat import test_suite as compat_test_suite
  228. result.addTests(compat_test_suite())
  229. from .contrib import test_suite as contrib_test_suite
  230. result.addTests(contrib_test_suite())
  231. return result