blob: 489ab986cb4e5e4d1ddc68263580eb1349c1a9b1 [file] [log] [blame]
Victor Stinner3844fe52015-09-26 10:38:01 +02001import faulthandler
Victor Stinner4d299832019-04-26 04:08:53 +02002import functools
Victor Stinner75120d22019-04-26 09:28:53 +02003import gc
Victor Stinner3844fe52015-09-26 10:38:01 +02004import importlib
5import io
Victor Stinner3844fe52015-09-26 10:38:01 +02006import os
7import sys
8import time
9import traceback
10import unittest
Victor Stinner75120d22019-04-26 09:28:53 +020011
Victor Stinner3844fe52015-09-26 10:38:01 +020012from test import support
Hai Shif7ba40b2020-06-25 18:38:51 +080013from test.support import os_helper
Miss Islington (bot)96087192021-07-22 13:30:44 -070014from test.libregrtest.cmdline import Namespace
Victor Stinner3844fe52015-09-26 10:38:01 +020015from test.libregrtest.save_env import saved_test_environment
Miss Islington (bot)96087192021-07-22 13:30:44 -070016from test.libregrtest.utils import clear_caches, format_duration, print_warning
Victor Stinner3844fe52015-09-26 10:38:01 +020017
18
Miss Islington (bot)96087192021-07-22 13:30:44 -070019class TestResult:
20 def __init__(
21 self,
22 name: str,
23 duration_sec: float = 0.0,
24 xml_data: list[str] | None = None,
25 ) -> None:
26 self.name = name
27 self.duration_sec = duration_sec
28 self.xml_data = xml_data
Victor Stinner3844fe52015-09-26 10:38:01 +020029
Miss Islington (bot)96087192021-07-22 13:30:44 -070030 def __str__(self) -> str:
31 return f"{self.name} finished"
32
33
34class Passed(TestResult):
35 def __str__(self) -> str:
36 return f"{self.name} passed"
37
38
39class Failed(TestResult):
40 def __init__(
41 self,
42 name: str,
43 duration_sec: float = 0.0,
44 xml_data: list[str] | None = None,
45 errors: list[tuple[str, str]] | None = None,
46 failures: list[tuple[str, str]] | None = None,
47 ) -> None:
48 super().__init__(name, duration_sec=duration_sec, xml_data=xml_data)
49 self.errors = errors
50 self.failures = failures
51
52 def __str__(self) -> str:
53 if self.errors and self.failures:
54 le = len(self.errors)
55 lf = len(self.failures)
56 error_s = "error" + ("s" if le > 1 else "")
57 failure_s = "failure" + ("s" if lf > 1 else "")
58 return f"{self.name} failed ({le} {error_s}, {lf} {failure_s})"
59
60 if self.errors:
61 le = len(self.errors)
62 error_s = "error" + ("s" if le > 1 else "")
63 return f"{self.name} failed ({le} {error_s})"
64
65 if self.failures:
66 lf = len(self.failures)
67 failure_s = "failure" + ("s" if lf > 1 else "")
68 return f"{self.name} failed ({lf} {failure_s})"
69
70 return f"{self.name} failed"
71
72
73class UncaughtException(Failed):
74 def __str__(self) -> str:
75 return f"{self.name} failed (uncaught exception)"
76
77
78class EnvChanged(Failed):
79 def __str__(self) -> str:
80 return f"{self.name} failed (env changed)"
81
82
83class RefLeak(Failed):
84 def __str__(self) -> str:
85 return f"{self.name} failed (reference leak)"
86
87
88class Skipped(TestResult):
89 def __str__(self) -> str:
90 return f"{self.name} skipped"
91
92
93class ResourceDenied(Skipped):
94 def __str__(self) -> str:
95 return f"{self.name} skipped (resource denied)"
96
97
98class Interrupted(TestResult):
99 def __str__(self) -> str:
100 return f"{self.name} interrupted"
101
102
103class ChildError(Failed):
104 def __str__(self) -> str:
105 return f"{self.name} crashed"
106
107
108class DidNotRun(TestResult):
109 def __str__(self) -> str:
110 return f"{self.name} ran no tests"
111
112
113class Timeout(Failed):
114 def __str__(self) -> str:
115 return f"{self.name} timed out ({format_duration(self.duration_sec)})"
116
Victor Stinner1b8b4232016-05-20 13:37:40 +0200117
Victor Stinner69649f22016-03-23 12:14:10 +0100118# Minimum duration of a test to display its duration or to mention that
119# the test is running in background
120PROGRESS_MIN_TIME = 30.0 # seconds
121
Victor Stinner3844fe52015-09-26 10:38:01 +0200122# small set of tests to determine if we have a basically functioning interpreter
123# (i.e. if any of these fail, then anything else is likely to follow)
124STDTESTS = [
125 'test_grammar',
126 'test_opcodes',
127 'test_dict',
128 'test_builtin',
129 'test_exceptions',
130 'test_types',
131 'test_unittest',
132 'test_doctest',
133 'test_doctest2',
134 'test_support'
135]
136
137# set of tests that we don't want to be executed when using regrtest
138NOTTESTS = set()
139
140
Victor Stinner4d299832019-04-26 04:08:53 +0200141# used by --findleaks, store for gc.garbage
Victor Stinner75120d22019-04-26 09:28:53 +0200142FOUND_GARBAGE = []
Victor Stinner4d299832019-04-26 04:08:53 +0200143
144
Miss Islington (bot)96087192021-07-22 13:30:44 -0700145def is_failed(result: TestResult, ns: Namespace) -> bool:
146 if isinstance(result, EnvChanged):
Victor Stinnerb0917df2019-05-13 19:17:54 +0200147 return ns.fail_env_changed
Miss Islington (bot)96087192021-07-22 13:30:44 -0700148 return isinstance(result, Failed)
Victor Stinner4d299832019-04-26 04:08:53 +0200149
150
151def findtestdir(path=None):
152 return path or os.path.dirname(os.path.dirname(__file__)) or os.curdir
Victor Stinner1b8b4232016-05-20 13:37:40 +0200153
154
Victor Stinner3844fe52015-09-26 10:38:01 +0200155def findtests(testdir=None, stdtests=STDTESTS, nottests=NOTTESTS):
156 """Return a list of all applicable test modules."""
157 testdir = findtestdir(testdir)
158 names = os.listdir(testdir)
159 tests = []
160 others = set(stdtests) | nottests
161 for name in names:
162 mod, ext = os.path.splitext(name)
163 if mod[:5] == "test_" and ext in (".py", "") and mod not in others:
164 tests.append(mod)
165 return stdtests + sorted(tests)
166
167
Miss Islington (bot)96087192021-07-22 13:30:44 -0700168def get_abs_module(ns: Namespace, test_name: str) -> str:
Victor Stinner4d299832019-04-26 04:08:53 +0200169 if test_name.startswith('test.') or ns.testdir:
170 return test_name
mlouielua49c9352017-06-16 17:36:19 +0800171 else:
Victor Stinner4d299832019-04-26 04:08:53 +0200172 # Import it from the test package
173 return 'test.' + test_name
mlouielua49c9352017-06-16 17:36:19 +0800174
175
Miss Islington (bot)96087192021-07-22 13:30:44 -0700176def _runtest(ns: Namespace, test_name: str) -> TestResult:
Victor Stinner4d299832019-04-26 04:08:53 +0200177 # Handle faulthandler timeout, capture stdout+stderr, XML serialization
178 # and measure time.
179
180 output_on_failure = ns.verbose3
181
182 use_timeout = (ns.timeout is not None)
183 if use_timeout:
184 faulthandler.dump_traceback_later(ns.timeout, exit=True)
185
186 start_time = time.perf_counter()
187 try:
Pablo Galindoe0cd8aa2019-11-19 23:46:49 +0000188 support.set_match_tests(ns.match_tests, ns.ignore_tests)
Victor Stinner4d299832019-04-26 04:08:53 +0200189 support.junit_xml_list = xml_list = [] if ns.xmlpath else None
190 if ns.failfast:
191 support.failfast = True
192
193 if output_on_failure:
194 support.verbose = True
195
196 stream = io.StringIO()
197 orig_stdout = sys.stdout
198 orig_stderr = sys.stderr
199 try:
200 sys.stdout = stream
201 sys.stderr = stream
202 result = _runtest_inner(ns, test_name,
203 display_failure=False)
Miss Islington (bot)96087192021-07-22 13:30:44 -0700204 if not isinstance(result, Passed):
Victor Stinner4d299832019-04-26 04:08:53 +0200205 output = stream.getvalue()
206 orig_stderr.write(output)
207 orig_stderr.flush()
208 finally:
209 sys.stdout = orig_stdout
210 sys.stderr = orig_stderr
211 else:
212 # Tell tests to be moderately quiet
213 support.verbose = ns.verbose
214
215 result = _runtest_inner(ns, test_name,
216 display_failure=not ns.verbose)
217
218 if xml_list:
219 import xml.etree.ElementTree as ET
Miss Islington (bot)96087192021-07-22 13:30:44 -0700220 result.xml_data = [
221 ET.tostring(x).decode('us-ascii')
222 for x in xml_list
223 ]
Victor Stinner4d299832019-04-26 04:08:53 +0200224
Miss Islington (bot)96087192021-07-22 13:30:44 -0700225 result.duration_sec = time.perf_counter() - start_time
226 return result
Victor Stinner4d299832019-04-26 04:08:53 +0200227 finally:
228 if use_timeout:
229 faulthandler.cancel_dump_traceback_later()
230 support.junit_xml_list = None
231
232
Miss Islington (bot)96087192021-07-22 13:30:44 -0700233def runtest(ns: Namespace, test_name: str) -> TestResult:
Victor Stinner3844fe52015-09-26 10:38:01 +0200234 """Run a single test.
235
Victor Stinnerab983672016-08-22 14:28:52 +0200236 ns -- regrtest namespace of options
Victor Stinner4d299832019-04-26 04:08:53 +0200237 test_name -- the name of the test
Victor Stinner3844fe52015-09-26 10:38:01 +0200238
Miss Islington (bot)96087192021-07-22 13:30:44 -0700239 Returns a TestResult sub-class depending on the kind of result received.
Steve Dowerd0f49d22018-09-18 09:10:26 -0700240
241 If ns.xmlpath is not None, xml_data is a list containing each
242 generated testsuite element.
Victor Stinner3844fe52015-09-26 10:38:01 +0200243 """
Victor Stinner3844fe52015-09-26 10:38:01 +0200244 try:
Victor Stinner4d299832019-04-26 04:08:53 +0200245 return _runtest(ns, test_name)
246 except:
247 if not ns.pgo:
248 msg = traceback.format_exc()
249 print(f"test {test_name} crashed -- {msg}",
250 file=sys.stderr, flush=True)
Miss Islington (bot)96087192021-07-22 13:30:44 -0700251 return Failed(test_name)
Victor Stinner3844fe52015-09-26 10:38:01 +0200252
253
Victor Stinner4d299832019-04-26 04:08:53 +0200254def _test_module(the_module):
255 loader = unittest.TestLoader()
256 tests = loader.loadTestsFromModule(the_module)
257 for error in loader.errors:
258 print(error, file=sys.stderr)
259 if loader.errors:
260 raise Exception("errors while loading tests")
261 support.run_unittest(tests)
Victor Stinner3844fe52015-09-26 10:38:01 +0200262
Victor Stinner4d299832019-04-26 04:08:53 +0200263
Miss Islington (bot)96087192021-07-22 13:30:44 -0700264def save_env(ns: Namespace, test_name: str):
Victor Stinner532e0632021-03-22 23:52:13 +0100265 return saved_test_environment(test_name, ns.verbose, ns.quiet, pgo=ns.pgo)
266
267
Miss Islington (bot)96087192021-07-22 13:30:44 -0700268def _runtest_inner2(ns: Namespace, test_name: str) -> bool:
Victor Stinner4d299832019-04-26 04:08:53 +0200269 # Load the test function, run the test function, handle huntrleaks
270 # and findleaks to detect leaks
271
272 abstest = get_abs_module(ns, test_name)
273
274 # remove the module from sys.module to reload it if it was already imported
Victor Stinner0473fb22021-03-23 01:08:49 +0100275 try:
276 del sys.modules[abstest]
277 except KeyError:
278 pass
Victor Stinner4d299832019-04-26 04:08:53 +0200279
280 the_module = importlib.import_module(abstest)
281
Victor Stinner10417dd2021-03-23 00:17:05 +0100282 if ns.huntrleaks:
283 from test.libregrtest.refleak import dash_R
284
Victor Stinner4d299832019-04-26 04:08:53 +0200285 # If the test has a test_main, that will run the appropriate
286 # tests. If not, use normal unittest test loading.
287 test_runner = getattr(the_module, "test_main", None)
288 if test_runner is None:
289 test_runner = functools.partial(_test_module, the_module)
290
Victor Stinner3844fe52015-09-26 10:38:01 +0200291 try:
Victor Stinner532e0632021-03-22 23:52:13 +0100292 with save_env(ns, test_name):
293 if ns.huntrleaks:
294 # Return True if the test leaked references
295 refleak = dash_R(ns, test_name, test_runner)
296 else:
297 test_runner()
298 refleak = False
Victor Stinner4d299832019-04-26 04:08:53 +0200299 finally:
300 cleanup_test_droppings(test_name, ns.verbose)
301
Victor Stinner75120d22019-04-26 09:28:53 +0200302 support.gc_collect()
Victor Stinner4d299832019-04-26 04:08:53 +0200303
Victor Stinner75120d22019-04-26 09:28:53 +0200304 if gc.garbage:
305 support.environment_altered = True
306 print_warning(f"{test_name} created {len(gc.garbage)} "
307 f"uncollectable object(s).")
308
309 # move the uncollectable objects somewhere,
310 # so we don't see them again
311 FOUND_GARBAGE.extend(gc.garbage)
312 gc.garbage.clear()
313
314 support.reap_children()
Victor Stinner4d299832019-04-26 04:08:53 +0200315
316 return refleak
317
318
Miss Islington (bot)96087192021-07-22 13:30:44 -0700319def _runtest_inner(
320 ns: Namespace, test_name: str, display_failure: bool = True
321) -> TestResult:
Victor Stinner4d299832019-04-26 04:08:53 +0200322 # Detect environment changes, handle exceptions.
323
324 # Reset the environment_altered flag to detect if a test altered
325 # the environment
326 support.environment_altered = False
327
328 if ns.pgo:
329 display_failure = False
330
331 try:
Serhiy Storchaka83910262016-11-11 11:46:44 +0200332 clear_caches()
Victor Stinner4d299832019-04-26 04:08:53 +0200333
Victor Stinner532e0632021-03-22 23:52:13 +0100334 with save_env(ns, test_name):
Victor Stinner4d299832019-04-26 04:08:53 +0200335 refleak = _runtest_inner2(ns, test_name)
Victor Stinner3844fe52015-09-26 10:38:01 +0200336 except support.ResourceDenied as msg:
Victor Stinnerab983672016-08-22 14:28:52 +0200337 if not ns.quiet and not ns.pgo:
Victor Stinner4d299832019-04-26 04:08:53 +0200338 print(f"{test_name} skipped -- {msg}", flush=True)
Miss Islington (bot)96087192021-07-22 13:30:44 -0700339 return ResourceDenied(test_name)
Victor Stinner3844fe52015-09-26 10:38:01 +0200340 except unittest.SkipTest as msg:
Victor Stinnerab983672016-08-22 14:28:52 +0200341 if not ns.quiet and not ns.pgo:
Victor Stinner4d299832019-04-26 04:08:53 +0200342 print(f"{test_name} skipped -- {msg}", flush=True)
Miss Islington (bot)96087192021-07-22 13:30:44 -0700343 return Skipped(test_name)
344 except support.TestFailedWithDetails as exc:
345 msg = f"test {test_name} failed"
346 if display_failure:
347 msg = f"{msg} -- {exc}"
348 print(msg, file=sys.stderr, flush=True)
349 return Failed(test_name, errors=exc.errors, failures=exc.failures)
Victor Stinner4d299832019-04-26 04:08:53 +0200350 except support.TestFailed as exc:
351 msg = f"test {test_name} failed"
352 if display_failure:
353 msg = f"{msg} -- {exc}"
354 print(msg, file=sys.stderr, flush=True)
Miss Islington (bot)96087192021-07-22 13:30:44 -0700355 return Failed(test_name)
Pablo Galindo97243482018-11-29 17:17:44 +0000356 except support.TestDidNotRun:
Miss Islington (bot)96087192021-07-22 13:30:44 -0700357 return DidNotRun(test_name)
Victor Stinner4d299832019-04-26 04:08:53 +0200358 except KeyboardInterrupt:
Victor Stinner3cde4402019-04-26 08:40:25 +0200359 print()
Miss Islington (bot)96087192021-07-22 13:30:44 -0700360 return Interrupted(test_name)
Victor Stinner3844fe52015-09-26 10:38:01 +0200361 except:
Victor Stinnerab983672016-08-22 14:28:52 +0200362 if not ns.pgo:
Victor Stinner4d299832019-04-26 04:08:53 +0200363 msg = traceback.format_exc()
364 print(f"test {test_name} crashed -- {msg}",
365 file=sys.stderr, flush=True)
Miss Islington (bot)96087192021-07-22 13:30:44 -0700366 return UncaughtException(test_name)
Victor Stinner4d299832019-04-26 04:08:53 +0200367
368 if refleak:
Miss Islington (bot)96087192021-07-22 13:30:44 -0700369 return RefLeak(test_name)
Victor Stinner532e0632021-03-22 23:52:13 +0100370 if support.environment_altered:
Miss Islington (bot)96087192021-07-22 13:30:44 -0700371 return EnvChanged(test_name)
372 return Passed(test_name)
Victor Stinner3844fe52015-09-26 10:38:01 +0200373
374
Miss Islington (bot)96087192021-07-22 13:30:44 -0700375def cleanup_test_droppings(test_name: str, verbose: int) -> None:
Victor Stinner3844fe52015-09-26 10:38:01 +0200376 # First kill any dangling references to open files etc.
377 # This can also issue some ResourceWarnings which would otherwise get
378 # triggered during the following test run, and possibly produce failures.
Victor Stinner4d299832019-04-26 04:08:53 +0200379 support.gc_collect()
Victor Stinner3844fe52015-09-26 10:38:01 +0200380
381 # Try to clean up junk commonly left behind. While tests shouldn't leave
382 # any files or directories behind, when a test fails that can be tedious
383 # for it to arrange. The consequences can be especially nasty on Windows,
384 # since if a test leaves a file open, it cannot be deleted by name (while
385 # there's nothing we can do about that here either, we can display the
386 # name of the offending test, which is a real help).
Hai Shif7ba40b2020-06-25 18:38:51 +0800387 for name in (os_helper.TESTFN,):
Victor Stinner3844fe52015-09-26 10:38:01 +0200388 if not os.path.exists(name):
389 continue
390
391 if os.path.isdir(name):
Victor Stinner4d299832019-04-26 04:08:53 +0200392 import shutil
Victor Stinner3844fe52015-09-26 10:38:01 +0200393 kind, nuker = "directory", shutil.rmtree
394 elif os.path.isfile(name):
395 kind, nuker = "file", os.unlink
396 else:
Victor Stinner4d299832019-04-26 04:08:53 +0200397 raise RuntimeError(f"os.path says {name!r} exists but is neither "
398 f"directory nor file")
Victor Stinner3844fe52015-09-26 10:38:01 +0200399
400 if verbose:
Victor Stinnerd663d342020-04-23 19:03:52 +0200401 print_warning(f"{test_name} left behind {kind} {name!r}")
Victor Stinner4d299832019-04-26 04:08:53 +0200402 support.environment_altered = True
403
Victor Stinner3844fe52015-09-26 10:38:01 +0200404 try:
Victor Stinner4d299832019-04-26 04:08:53 +0200405 import stat
Anthony Sottile8377cd42019-02-25 14:32:27 -0800406 # fix possible permissions problems that might prevent cleanup
407 os.chmod(name, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
Victor Stinner3844fe52015-09-26 10:38:01 +0200408 nuker(name)
Victor Stinner4d299832019-04-26 04:08:53 +0200409 except Exception as exc:
410 print_warning(f"{test_name} left behind {kind} {name!r} "
411 f"and it couldn't be removed: {exc}")