blob: a43b7666cd1e35707ad048e161553064dd3ef1dd [file] [log] [blame]
Victor Stinner4d299832019-04-26 04:08:53 +02001import collections
Victor Stinner3844fe52015-09-26 10:38:01 +02002import faulthandler
Victor Stinner4d299832019-04-26 04:08:53 +02003import functools
Victor Stinner75120d22019-04-26 09:28:53 +02004import gc
Victor Stinner3844fe52015-09-26 10:38:01 +02005import importlib
6import io
Victor Stinner3844fe52015-09-26 10:38:01 +02007import os
8import sys
9import time
10import traceback
11import unittest
Victor Stinner75120d22019-04-26 09:28:53 +020012
Victor Stinner3844fe52015-09-26 10:38:01 +020013from test import support
Serhiy Storchaka83910262016-11-11 11:46:44 +020014from test.libregrtest.refleak import dash_R, clear_caches
Victor Stinner3844fe52015-09-26 10:38:01 +020015from test.libregrtest.save_env import saved_test_environment
Victor Stinner4d299832019-04-26 04:08:53 +020016from test.libregrtest.utils import print_warning
Victor Stinner3844fe52015-09-26 10:38:01 +020017
18
19# Test result constants.
20PASSED = 1
21FAILED = 0
22ENV_CHANGED = -1
23SKIPPED = -2
24RESOURCE_DENIED = -3
25INTERRUPTED = -4
26CHILD_ERROR = -5 # error in a child process
Victor Stinnerb0917df2019-05-13 19:17:54 +020027TEST_DID_NOT_RUN = -6
Victor Stinner3844fe52015-09-26 10:38:01 +020028
Victor Stinner1b8b4232016-05-20 13:37:40 +020029_FORMAT_TEST_RESULT = {
30 PASSED: '%s passed',
31 FAILED: '%s failed',
32 ENV_CHANGED: '%s failed (env changed)',
33 SKIPPED: '%s skipped',
34 RESOURCE_DENIED: '%s skipped (resource denied)',
35 INTERRUPTED: '%s interrupted',
36 CHILD_ERROR: '%s crashed',
Pablo Galindo97243482018-11-29 17:17:44 +000037 TEST_DID_NOT_RUN: '%s run no tests',
Victor Stinner1b8b4232016-05-20 13:37:40 +020038}
39
Victor Stinner69649f22016-03-23 12:14:10 +010040# Minimum duration of a test to display its duration or to mention that
41# the test is running in background
42PROGRESS_MIN_TIME = 30.0 # seconds
43
Victor Stinner3844fe52015-09-26 10:38:01 +020044# small set of tests to determine if we have a basically functioning interpreter
45# (i.e. if any of these fail, then anything else is likely to follow)
46STDTESTS = [
47 'test_grammar',
48 'test_opcodes',
49 'test_dict',
50 'test_builtin',
51 'test_exceptions',
52 'test_types',
53 'test_unittest',
54 'test_doctest',
55 'test_doctest2',
56 'test_support'
57]
58
59# set of tests that we don't want to be executed when using regrtest
60NOTTESTS = set()
61
62
Victor Stinner4d299832019-04-26 04:08:53 +020063# used by --findleaks, store for gc.garbage
Victor Stinner75120d22019-04-26 09:28:53 +020064FOUND_GARBAGE = []
Victor Stinner4d299832019-04-26 04:08:53 +020065
66
Victor Stinnerb0917df2019-05-13 19:17:54 +020067def is_failed(result, ns):
68 ok = result.result
69 if ok in (PASSED, RESOURCE_DENIED, SKIPPED, TEST_DID_NOT_RUN):
70 return False
71 if ok == ENV_CHANGED:
72 return ns.fail_env_changed
73 return True
74
75
Victor Stinner4d299832019-04-26 04:08:53 +020076def format_test_result(result):
77 fmt = _FORMAT_TEST_RESULT.get(result.result, "%s")
78 return fmt % result.test_name
79
80
81def findtestdir(path=None):
82 return path or os.path.dirname(os.path.dirname(__file__)) or os.curdir
Victor Stinner1b8b4232016-05-20 13:37:40 +020083
84
Victor Stinner3844fe52015-09-26 10:38:01 +020085def findtests(testdir=None, stdtests=STDTESTS, nottests=NOTTESTS):
86 """Return a list of all applicable test modules."""
87 testdir = findtestdir(testdir)
88 names = os.listdir(testdir)
89 tests = []
90 others = set(stdtests) | nottests
91 for name in names:
92 mod, ext = os.path.splitext(name)
93 if mod[:5] == "test_" and ext in (".py", "") and mod not in others:
94 tests.append(mod)
95 return stdtests + sorted(tests)
96
97
Victor Stinner4d299832019-04-26 04:08:53 +020098def get_abs_module(ns, test_name):
99 if test_name.startswith('test.') or ns.testdir:
100 return test_name
mlouielua49c9352017-06-16 17:36:19 +0800101 else:
Victor Stinner4d299832019-04-26 04:08:53 +0200102 # Import it from the test package
103 return 'test.' + test_name
mlouielua49c9352017-06-16 17:36:19 +0800104
105
Victor Stinner4d299832019-04-26 04:08:53 +0200106TestResult = collections.namedtuple('TestResult',
107 'test_name result test_time xml_data')
108
109def _runtest(ns, test_name):
110 # Handle faulthandler timeout, capture stdout+stderr, XML serialization
111 # and measure time.
112
113 output_on_failure = ns.verbose3
114
115 use_timeout = (ns.timeout is not None)
116 if use_timeout:
117 faulthandler.dump_traceback_later(ns.timeout, exit=True)
118
119 start_time = time.perf_counter()
120 try:
121 support.set_match_tests(ns.match_tests)
122 support.junit_xml_list = xml_list = [] if ns.xmlpath else None
123 if ns.failfast:
124 support.failfast = True
125
126 if output_on_failure:
127 support.verbose = True
128
129 stream = io.StringIO()
130 orig_stdout = sys.stdout
131 orig_stderr = sys.stderr
132 try:
133 sys.stdout = stream
134 sys.stderr = stream
135 result = _runtest_inner(ns, test_name,
136 display_failure=False)
137 if result != PASSED:
138 output = stream.getvalue()
139 orig_stderr.write(output)
140 orig_stderr.flush()
141 finally:
142 sys.stdout = orig_stdout
143 sys.stderr = orig_stderr
144 else:
145 # Tell tests to be moderately quiet
146 support.verbose = ns.verbose
147
148 result = _runtest_inner(ns, test_name,
149 display_failure=not ns.verbose)
150
151 if xml_list:
152 import xml.etree.ElementTree as ET
153 xml_data = [ET.tostring(x).decode('us-ascii') for x in xml_list]
154 else:
155 xml_data = None
156
157 test_time = time.perf_counter() - start_time
158
159 return TestResult(test_name, result, test_time, xml_data)
160 finally:
161 if use_timeout:
162 faulthandler.cancel_dump_traceback_later()
163 support.junit_xml_list = None
164
165
166def runtest(ns, test_name):
Victor Stinner3844fe52015-09-26 10:38:01 +0200167 """Run a single test.
168
Victor Stinnerab983672016-08-22 14:28:52 +0200169 ns -- regrtest namespace of options
Victor Stinner4d299832019-04-26 04:08:53 +0200170 test_name -- the name of the test
Victor Stinner3844fe52015-09-26 10:38:01 +0200171
Steve Dowerd0f49d22018-09-18 09:10:26 -0700172 Returns the tuple (result, test_time, xml_data), where result is one
173 of the constants:
Victor Stinnerab983672016-08-22 14:28:52 +0200174
Victor Stinner4d299832019-04-26 04:08:53 +0200175 INTERRUPTED KeyboardInterrupt
Victor Stinner3844fe52015-09-26 10:38:01 +0200176 RESOURCE_DENIED test skipped because resource denied
177 SKIPPED test skipped for some other reason
178 ENV_CHANGED test failed because it changed the execution environment
179 FAILED test failed
180 PASSED test passed
Pablo Galindo97243482018-11-29 17:17:44 +0000181 EMPTY_TEST_SUITE test ran no subtests.
Steve Dowerd0f49d22018-09-18 09:10:26 -0700182
183 If ns.xmlpath is not None, xml_data is a list containing each
184 generated testsuite element.
Victor Stinner3844fe52015-09-26 10:38:01 +0200185 """
Victor Stinner3844fe52015-09-26 10:38:01 +0200186 try:
Victor Stinner4d299832019-04-26 04:08:53 +0200187 return _runtest(ns, test_name)
188 except:
189 if not ns.pgo:
190 msg = traceback.format_exc()
191 print(f"test {test_name} crashed -- {msg}",
192 file=sys.stderr, flush=True)
193 return TestResult(test_name, FAILED, 0.0, None)
Victor Stinner3844fe52015-09-26 10:38:01 +0200194
195
Victor Stinner4d299832019-04-26 04:08:53 +0200196def _test_module(the_module):
197 loader = unittest.TestLoader()
198 tests = loader.loadTestsFromModule(the_module)
199 for error in loader.errors:
200 print(error, file=sys.stderr)
201 if loader.errors:
202 raise Exception("errors while loading tests")
203 support.run_unittest(tests)
Victor Stinner3844fe52015-09-26 10:38:01 +0200204
Victor Stinner4d299832019-04-26 04:08:53 +0200205
206def _runtest_inner2(ns, test_name):
207 # Load the test function, run the test function, handle huntrleaks
208 # and findleaks to detect leaks
209
210 abstest = get_abs_module(ns, test_name)
211
212 # remove the module from sys.module to reload it if it was already imported
213 support.unload(abstest)
214
215 the_module = importlib.import_module(abstest)
216
217 # If the test has a test_main, that will run the appropriate
218 # tests. If not, use normal unittest test loading.
219 test_runner = getattr(the_module, "test_main", None)
220 if test_runner is None:
221 test_runner = functools.partial(_test_module, the_module)
222
Victor Stinner3844fe52015-09-26 10:38:01 +0200223 try:
Victor Stinner4d299832019-04-26 04:08:53 +0200224 if ns.huntrleaks:
225 # Return True if the test leaked references
226 refleak = dash_R(ns, test_name, test_runner)
227 else:
228 test_runner()
229 refleak = False
230 finally:
231 cleanup_test_droppings(test_name, ns.verbose)
232
Victor Stinner75120d22019-04-26 09:28:53 +0200233 support.gc_collect()
Victor Stinner4d299832019-04-26 04:08:53 +0200234
Victor Stinner75120d22019-04-26 09:28:53 +0200235 if gc.garbage:
236 support.environment_altered = True
237 print_warning(f"{test_name} created {len(gc.garbage)} "
238 f"uncollectable object(s).")
239
240 # move the uncollectable objects somewhere,
241 # so we don't see them again
242 FOUND_GARBAGE.extend(gc.garbage)
243 gc.garbage.clear()
244
245 support.reap_children()
Victor Stinner4d299832019-04-26 04:08:53 +0200246
247 return refleak
248
249
250def _runtest_inner(ns, test_name, display_failure=True):
251 # Detect environment changes, handle exceptions.
252
253 # Reset the environment_altered flag to detect if a test altered
254 # the environment
255 support.environment_altered = False
256
257 if ns.pgo:
258 display_failure = False
259
260 try:
Serhiy Storchaka83910262016-11-11 11:46:44 +0200261 clear_caches()
Victor Stinner4d299832019-04-26 04:08:53 +0200262
263 with saved_test_environment(test_name, ns.verbose, ns.quiet, pgo=ns.pgo) as environment:
264 refleak = _runtest_inner2(ns, test_name)
Victor Stinner3844fe52015-09-26 10:38:01 +0200265 except support.ResourceDenied as msg:
Victor Stinnerab983672016-08-22 14:28:52 +0200266 if not ns.quiet and not ns.pgo:
Victor Stinner4d299832019-04-26 04:08:53 +0200267 print(f"{test_name} skipped -- {msg}", flush=True)
268 return RESOURCE_DENIED
Victor Stinner3844fe52015-09-26 10:38:01 +0200269 except unittest.SkipTest as msg:
Victor Stinnerab983672016-08-22 14:28:52 +0200270 if not ns.quiet and not ns.pgo:
Victor Stinner4d299832019-04-26 04:08:53 +0200271 print(f"{test_name} skipped -- {msg}", flush=True)
272 return SKIPPED
273 except support.TestFailed as exc:
274 msg = f"test {test_name} failed"
275 if display_failure:
276 msg = f"{msg} -- {exc}"
277 print(msg, file=sys.stderr, flush=True)
278 return FAILED
Pablo Galindo97243482018-11-29 17:17:44 +0000279 except support.TestDidNotRun:
Victor Stinner4d299832019-04-26 04:08:53 +0200280 return TEST_DID_NOT_RUN
281 except KeyboardInterrupt:
Victor Stinner3cde4402019-04-26 08:40:25 +0200282 print()
Victor Stinner4d299832019-04-26 04:08:53 +0200283 return INTERRUPTED
Victor Stinner3844fe52015-09-26 10:38:01 +0200284 except:
Victor Stinnerab983672016-08-22 14:28:52 +0200285 if not ns.pgo:
Victor Stinner4d299832019-04-26 04:08:53 +0200286 msg = traceback.format_exc()
287 print(f"test {test_name} crashed -- {msg}",
288 file=sys.stderr, flush=True)
289 return FAILED
290
291 if refleak:
292 return FAILED
293 if environment.changed:
294 return ENV_CHANGED
295 return PASSED
Victor Stinner3844fe52015-09-26 10:38:01 +0200296
297
Victor Stinner4d299832019-04-26 04:08:53 +0200298def cleanup_test_droppings(test_name, verbose):
Victor Stinner3844fe52015-09-26 10:38:01 +0200299 # First kill any dangling references to open files etc.
300 # This can also issue some ResourceWarnings which would otherwise get
301 # triggered during the following test run, and possibly produce failures.
Victor Stinner4d299832019-04-26 04:08:53 +0200302 support.gc_collect()
Victor Stinner3844fe52015-09-26 10:38:01 +0200303
304 # Try to clean up junk commonly left behind. While tests shouldn't leave
305 # any files or directories behind, when a test fails that can be tedious
306 # for it to arrange. The consequences can be especially nasty on Windows,
307 # since if a test leaves a file open, it cannot be deleted by name (while
308 # there's nothing we can do about that here either, we can display the
309 # name of the offending test, which is a real help).
310 for name in (support.TESTFN,
311 "db_home",
312 ):
313 if not os.path.exists(name):
314 continue
315
316 if os.path.isdir(name):
Victor Stinner4d299832019-04-26 04:08:53 +0200317 import shutil
Victor Stinner3844fe52015-09-26 10:38:01 +0200318 kind, nuker = "directory", shutil.rmtree
319 elif os.path.isfile(name):
320 kind, nuker = "file", os.unlink
321 else:
Victor Stinner4d299832019-04-26 04:08:53 +0200322 raise RuntimeError(f"os.path says {name!r} exists but is neither "
323 f"directory nor file")
Victor Stinner3844fe52015-09-26 10:38:01 +0200324
325 if verbose:
Victor Stinner4d299832019-04-26 04:08:53 +0200326 print_warning("%r left behind %s %r" % (test_name, kind, name))
327 support.environment_altered = True
328
Victor Stinner3844fe52015-09-26 10:38:01 +0200329 try:
Victor Stinner4d299832019-04-26 04:08:53 +0200330 import stat
Anthony Sottile8377cd42019-02-25 14:32:27 -0800331 # fix possible permissions problems that might prevent cleanup
332 os.chmod(name, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
Victor Stinner3844fe52015-09-26 10:38:01 +0200333 nuker(name)
Victor Stinner4d299832019-04-26 04:08:53 +0200334 except Exception as exc:
335 print_warning(f"{test_name} left behind {kind} {name!r} "
336 f"and it couldn't be removed: {exc}")