runtest.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. import faulthandler
  2. import functools
  3. import gc
  4. import importlib
  5. import io
  6. import os
  7. import sys
  8. import time
  9. import traceback
  10. import unittest
  11. from test import support
  12. from test.support import os_helper
  13. from test.support import threading_helper
  14. from test.libregrtest.cmdline import Namespace
  15. from test.libregrtest.save_env import saved_test_environment
  16. from test.libregrtest.utils import clear_caches, format_duration, print_warning
  17. class TestResult:
  18. def __init__(
  19. self,
  20. name: str,
  21. duration_sec: float = 0.0,
  22. xml_data: list[str] | None = None,
  23. ) -> None:
  24. self.name = name
  25. self.duration_sec = duration_sec
  26. self.xml_data = xml_data
  27. def __str__(self) -> str:
  28. return f"{self.name} finished"
  29. class Passed(TestResult):
  30. def __str__(self) -> str:
  31. return f"{self.name} passed"
  32. class Failed(TestResult):
  33. def __init__(
  34. self,
  35. name: str,
  36. duration_sec: float = 0.0,
  37. xml_data: list[str] | None = None,
  38. errors: list[tuple[str, str]] | None = None,
  39. failures: list[tuple[str, str]] | None = None,
  40. ) -> None:
  41. super().__init__(name, duration_sec=duration_sec, xml_data=xml_data)
  42. self.errors = errors
  43. self.failures = failures
  44. def __str__(self) -> str:
  45. if self.errors and self.failures:
  46. le = len(self.errors)
  47. lf = len(self.failures)
  48. error_s = "error" + ("s" if le > 1 else "")
  49. failure_s = "failure" + ("s" if lf > 1 else "")
  50. return f"{self.name} failed ({le} {error_s}, {lf} {failure_s})"
  51. if self.errors:
  52. le = len(self.errors)
  53. error_s = "error" + ("s" if le > 1 else "")
  54. return f"{self.name} failed ({le} {error_s})"
  55. if self.failures:
  56. lf = len(self.failures)
  57. failure_s = "failure" + ("s" if lf > 1 else "")
  58. return f"{self.name} failed ({lf} {failure_s})"
  59. return f"{self.name} failed"
  60. class UncaughtException(Failed):
  61. def __str__(self) -> str:
  62. return f"{self.name} failed (uncaught exception)"
  63. class EnvChanged(Failed):
  64. def __str__(self) -> str:
  65. return f"{self.name} failed (env changed)"
  66. class RefLeak(Failed):
  67. def __str__(self) -> str:
  68. return f"{self.name} failed (reference leak)"
  69. class Skipped(TestResult):
  70. def __str__(self) -> str:
  71. return f"{self.name} skipped"
  72. class ResourceDenied(Skipped):
  73. def __str__(self) -> str:
  74. return f"{self.name} skipped (resource denied)"
  75. class Interrupted(TestResult):
  76. def __str__(self) -> str:
  77. return f"{self.name} interrupted"
  78. class ChildError(Failed):
  79. def __str__(self) -> str:
  80. return f"{self.name} crashed"
  81. class DidNotRun(TestResult):
  82. def __str__(self) -> str:
  83. return f"{self.name} ran no tests"
  84. class Timeout(Failed):
  85. def __str__(self) -> str:
  86. return f"{self.name} timed out ({format_duration(self.duration_sec)})"
  87. # Minimum duration of a test to display its duration or to mention that
  88. # the test is running in background
  89. PROGRESS_MIN_TIME = 30.0 # seconds
  90. # small set of tests to determine if we have a basically functioning interpreter
  91. # (i.e. if any of these fail, then anything else is likely to follow)
  92. STDTESTS = [
  93. 'test_grammar',
  94. 'test_opcodes',
  95. 'test_dict',
  96. 'test_builtin',
  97. 'test_exceptions',
  98. 'test_types',
  99. 'test_unittest',
  100. 'test_doctest',
  101. 'test_doctest2',
  102. 'test_support'
  103. ]
  104. # set of tests that we don't want to be executed when using regrtest
  105. NOTTESTS = set()
  106. # Storage of uncollectable objects
  107. FOUND_GARBAGE = []
  108. def is_failed(result: TestResult, ns: Namespace) -> bool:
  109. if isinstance(result, EnvChanged):
  110. return ns.fail_env_changed
  111. return isinstance(result, Failed)
  112. def findtestdir(path=None):
  113. return path or os.path.dirname(os.path.dirname(__file__)) or os.curdir
  114. def findtests(testdir=None, stdtests=STDTESTS, nottests=NOTTESTS):
  115. """Return a list of all applicable test modules."""
  116. testdir = findtestdir(testdir)
  117. names = os.listdir(testdir)
  118. tests = []
  119. others = set(stdtests) | nottests
  120. for name in names:
  121. mod, ext = os.path.splitext(name)
  122. if mod[:5] == "test_" and ext in (".py", "") and mod not in others:
  123. tests.append(mod)
  124. return stdtests + sorted(tests)
  125. def get_abs_module(ns: Namespace, test_name: str) -> str:
  126. if test_name.startswith('test.') or ns.testdir:
  127. return test_name
  128. else:
  129. # Import it from the test package
  130. return 'test.' + test_name
  131. def _runtest(ns: Namespace, test_name: str) -> TestResult:
  132. # Handle faulthandler timeout, capture stdout+stderr, XML serialization
  133. # and measure time.
  134. output_on_failure = ns.verbose3
  135. use_timeout = (
  136. ns.timeout is not None and threading_helper.can_start_thread
  137. )
  138. if use_timeout:
  139. faulthandler.dump_traceback_later(ns.timeout, exit=True)
  140. start_time = time.perf_counter()
  141. try:
  142. support.set_match_tests(ns.match_tests, ns.ignore_tests)
  143. support.junit_xml_list = xml_list = [] if ns.xmlpath else None
  144. if ns.failfast:
  145. support.failfast = True
  146. if output_on_failure:
  147. support.verbose = True
  148. stream = io.StringIO()
  149. orig_stdout = sys.stdout
  150. orig_stderr = sys.stderr
  151. print_warning = support.print_warning
  152. orig_print_warnings_stderr = print_warning.orig_stderr
  153. output = None
  154. try:
  155. sys.stdout = stream
  156. sys.stderr = stream
  157. # print_warning() writes into the temporary stream to preserve
  158. # messages order. If support.environment_altered becomes true,
  159. # warnings will be written to sys.stderr below.
  160. print_warning.orig_stderr = stream
  161. result = _runtest_inner(ns, test_name,
  162. display_failure=False)
  163. if not isinstance(result, Passed):
  164. output = stream.getvalue()
  165. finally:
  166. sys.stdout = orig_stdout
  167. sys.stderr = orig_stderr
  168. print_warning.orig_stderr = orig_print_warnings_stderr
  169. if output is not None:
  170. sys.stderr.write(output)
  171. sys.stderr.flush()
  172. else:
  173. # Tell tests to be moderately quiet
  174. support.verbose = ns.verbose
  175. result = _runtest_inner(ns, test_name,
  176. display_failure=not ns.verbose)
  177. if xml_list:
  178. import xml.etree.ElementTree as ET
  179. result.xml_data = [
  180. ET.tostring(x).decode('us-ascii')
  181. for x in xml_list
  182. ]
  183. result.duration_sec = time.perf_counter() - start_time
  184. return result
  185. finally:
  186. if use_timeout:
  187. faulthandler.cancel_dump_traceback_later()
  188. support.junit_xml_list = None
  189. def runtest(ns: Namespace, test_name: str) -> TestResult:
  190. """Run a single test.
  191. ns -- regrtest namespace of options
  192. test_name -- the name of the test
  193. Returns a TestResult sub-class depending on the kind of result received.
  194. If ns.xmlpath is not None, xml_data is a list containing each
  195. generated testsuite element.
  196. """
  197. try:
  198. return _runtest(ns, test_name)
  199. except:
  200. if not ns.pgo:
  201. msg = traceback.format_exc()
  202. print(f"test {test_name} crashed -- {msg}",
  203. file=sys.stderr, flush=True)
  204. return Failed(test_name)
  205. def _test_module(the_module):
  206. loader = unittest.TestLoader()
  207. tests = loader.loadTestsFromModule(the_module)
  208. for error in loader.errors:
  209. print(error, file=sys.stderr)
  210. if loader.errors:
  211. raise Exception("errors while loading tests")
  212. support.run_unittest(tests)
  213. def save_env(ns: Namespace, test_name: str):
  214. return saved_test_environment(test_name, ns.verbose, ns.quiet, pgo=ns.pgo)
  215. def _runtest_inner2(ns: Namespace, test_name: str) -> bool:
  216. # Load the test function, run the test function, handle huntrleaks
  217. # to detect leaks.
  218. abstest = get_abs_module(ns, test_name)
  219. # remove the module from sys.module to reload it if it was already imported
  220. try:
  221. del sys.modules[abstest]
  222. except KeyError:
  223. pass
  224. the_module = importlib.import_module(abstest)
  225. if ns.huntrleaks:
  226. from test.libregrtest.refleak import dash_R
  227. # If the test has a test_main, that will run the appropriate
  228. # tests. If not, use normal unittest test loading.
  229. test_runner = getattr(the_module, "test_main", None)
  230. if test_runner is None:
  231. test_runner = functools.partial(_test_module, the_module)
  232. try:
  233. with save_env(ns, test_name):
  234. if ns.huntrleaks:
  235. # Return True if the test leaked references
  236. refleak = dash_R(ns, test_name, test_runner)
  237. else:
  238. test_runner()
  239. refleak = False
  240. finally:
  241. # First kill any dangling references to open files etc.
  242. # This can also issue some ResourceWarnings which would otherwise get
  243. # triggered during the following test run, and possibly produce
  244. # failures.
  245. support.gc_collect()
  246. cleanup_test_droppings(test_name, ns.verbose)
  247. if gc.garbage:
  248. support.environment_altered = True
  249. print_warning(f"{test_name} created {len(gc.garbage)} "
  250. f"uncollectable object(s).")
  251. # move the uncollectable objects somewhere,
  252. # so we don't see them again
  253. FOUND_GARBAGE.extend(gc.garbage)
  254. gc.garbage.clear()
  255. support.reap_children()
  256. return refleak
  257. def _runtest_inner(
  258. ns: Namespace, test_name: str, display_failure: bool = True
  259. ) -> TestResult:
  260. # Detect environment changes, handle exceptions.
  261. # Reset the environment_altered flag to detect if a test altered
  262. # the environment
  263. support.environment_altered = False
  264. if ns.pgo:
  265. display_failure = False
  266. try:
  267. clear_caches()
  268. support.gc_collect()
  269. with save_env(ns, test_name):
  270. refleak = _runtest_inner2(ns, test_name)
  271. except support.ResourceDenied as msg:
  272. if not ns.quiet and not ns.pgo:
  273. print(f"{test_name} skipped -- {msg}", flush=True)
  274. return ResourceDenied(test_name)
  275. except unittest.SkipTest as msg:
  276. if not ns.quiet and not ns.pgo:
  277. print(f"{test_name} skipped -- {msg}", flush=True)
  278. return Skipped(test_name)
  279. except support.TestFailedWithDetails as exc:
  280. msg = f"test {test_name} failed"
  281. if display_failure:
  282. msg = f"{msg} -- {exc}"
  283. print(msg, file=sys.stderr, flush=True)
  284. return Failed(test_name, errors=exc.errors, failures=exc.failures)
  285. except support.TestFailed as exc:
  286. msg = f"test {test_name} failed"
  287. if display_failure:
  288. msg = f"{msg} -- {exc}"
  289. print(msg, file=sys.stderr, flush=True)
  290. return Failed(test_name)
  291. except support.TestDidNotRun:
  292. return DidNotRun(test_name)
  293. except KeyboardInterrupt:
  294. print()
  295. return Interrupted(test_name)
  296. except:
  297. if not ns.pgo:
  298. msg = traceback.format_exc()
  299. print(f"test {test_name} crashed -- {msg}",
  300. file=sys.stderr, flush=True)
  301. return UncaughtException(test_name)
  302. if refleak:
  303. return RefLeak(test_name)
  304. if support.environment_altered:
  305. return EnvChanged(test_name)
  306. return Passed(test_name)
  307. def cleanup_test_droppings(test_name: str, verbose: int) -> None:
  308. # Try to clean up junk commonly left behind. While tests shouldn't leave
  309. # any files or directories behind, when a test fails that can be tedious
  310. # for it to arrange. The consequences can be especially nasty on Windows,
  311. # since if a test leaves a file open, it cannot be deleted by name (while
  312. # there's nothing we can do about that here either, we can display the
  313. # name of the offending test, which is a real help).
  314. for name in (os_helper.TESTFN,):
  315. if not os.path.exists(name):
  316. continue
  317. if os.path.isdir(name):
  318. import shutil
  319. kind, nuker = "directory", shutil.rmtree
  320. elif os.path.isfile(name):
  321. kind, nuker = "file", os.unlink
  322. else:
  323. raise RuntimeError(f"os.path says {name!r} exists but is neither "
  324. f"directory nor file")
  325. if verbose:
  326. print_warning(f"{test_name} left behind {kind} {name!r}")
  327. support.environment_altered = True
  328. try:
  329. import stat
  330. # fix possible permissions problems that might prevent cleanup
  331. os.chmod(name, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
  332. nuker(name)
  333. except Exception as exc:
  334. print_warning(f"{test_name} left behind {kind} {name!r} "
  335. f"and it couldn't be removed: {exc}")