blob: c0cfa5312f705798edfdbb5134771b56db6a3e9b [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 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
11from test import support
Serhiy Storchaka83910262016-11-11 11:46:44 +020012from test.libregrtest.refleak import dash_R, clear_caches
Victor Stinner3844fe52015-09-26 10:38:01 +020013from test.libregrtest.save_env import saved_test_environment
Victor Stinner4d299832019-04-26 04:08:53 +020014from test.libregrtest.utils import print_warning
Victor Stinner3844fe52015-09-26 10:38:01 +020015
16
17# Test result constants.
18PASSED = 1
19FAILED = 0
20ENV_CHANGED = -1
21SKIPPED = -2
22RESOURCE_DENIED = -3
23INTERRUPTED = -4
24CHILD_ERROR = -5 # error in a child process
Pablo Galindo97243482018-11-29 17:17:44 +000025TEST_DID_NOT_RUN = -6 # error in a child process
Victor Stinner3844fe52015-09-26 10:38:01 +020026
Victor Stinner1b8b4232016-05-20 13:37:40 +020027_FORMAT_TEST_RESULT = {
28 PASSED: '%s passed',
29 FAILED: '%s failed',
30 ENV_CHANGED: '%s failed (env changed)',
31 SKIPPED: '%s skipped',
32 RESOURCE_DENIED: '%s skipped (resource denied)',
33 INTERRUPTED: '%s interrupted',
34 CHILD_ERROR: '%s crashed',
Pablo Galindo97243482018-11-29 17:17:44 +000035 TEST_DID_NOT_RUN: '%s run no tests',
Victor Stinner1b8b4232016-05-20 13:37:40 +020036}
37
Victor Stinner69649f22016-03-23 12:14:10 +010038# Minimum duration of a test to display its duration or to mention that
39# the test is running in background
40PROGRESS_MIN_TIME = 30.0 # seconds
41
Victor Stinner3844fe52015-09-26 10:38:01 +020042# small set of tests to determine if we have a basically functioning interpreter
43# (i.e. if any of these fail, then anything else is likely to follow)
44STDTESTS = [
45 'test_grammar',
46 'test_opcodes',
47 'test_dict',
48 'test_builtin',
49 'test_exceptions',
50 'test_types',
51 'test_unittest',
52 'test_doctest',
53 'test_doctest2',
54 'test_support'
55]
56
57# set of tests that we don't want to be executed when using regrtest
58NOTTESTS = set()
59
60
Victor Stinner4d299832019-04-26 04:08:53 +020061# used by --findleaks, store for gc.garbage
62found_garbage = []
63
64
65def format_test_result(result):
66 fmt = _FORMAT_TEST_RESULT.get(result.result, "%s")
67 return fmt % result.test_name
68
69
70def findtestdir(path=None):
71 return path or os.path.dirname(os.path.dirname(__file__)) or os.curdir
Victor Stinner1b8b4232016-05-20 13:37:40 +020072
73
Victor Stinner3844fe52015-09-26 10:38:01 +020074def findtests(testdir=None, stdtests=STDTESTS, nottests=NOTTESTS):
75 """Return a list of all applicable test modules."""
76 testdir = findtestdir(testdir)
77 names = os.listdir(testdir)
78 tests = []
79 others = set(stdtests) | nottests
80 for name in names:
81 mod, ext = os.path.splitext(name)
82 if mod[:5] == "test_" and ext in (".py", "") and mod not in others:
83 tests.append(mod)
84 return stdtests + sorted(tests)
85
86
Victor Stinner4d299832019-04-26 04:08:53 +020087def get_abs_module(ns, test_name):
88 if test_name.startswith('test.') or ns.testdir:
89 return test_name
mlouielua49c9352017-06-16 17:36:19 +080090 else:
Victor Stinner4d299832019-04-26 04:08:53 +020091 # Import it from the test package
92 return 'test.' + test_name
mlouielua49c9352017-06-16 17:36:19 +080093
94
Victor Stinner4d299832019-04-26 04:08:53 +020095TestResult = collections.namedtuple('TestResult',
96 'test_name result test_time xml_data')
97
98def _runtest(ns, test_name):
99 # Handle faulthandler timeout, capture stdout+stderr, XML serialization
100 # and measure time.
101
102 output_on_failure = ns.verbose3
103
104 use_timeout = (ns.timeout is not None)
105 if use_timeout:
106 faulthandler.dump_traceback_later(ns.timeout, exit=True)
107
108 start_time = time.perf_counter()
109 try:
110 support.set_match_tests(ns.match_tests)
111 support.junit_xml_list = xml_list = [] if ns.xmlpath else None
112 if ns.failfast:
113 support.failfast = True
114
115 if output_on_failure:
116 support.verbose = True
117
118 stream = io.StringIO()
119 orig_stdout = sys.stdout
120 orig_stderr = sys.stderr
121 try:
122 sys.stdout = stream
123 sys.stderr = stream
124 result = _runtest_inner(ns, test_name,
125 display_failure=False)
126 if result != PASSED:
127 output = stream.getvalue()
128 orig_stderr.write(output)
129 orig_stderr.flush()
130 finally:
131 sys.stdout = orig_stdout
132 sys.stderr = orig_stderr
133 else:
134 # Tell tests to be moderately quiet
135 support.verbose = ns.verbose
136
137 result = _runtest_inner(ns, test_name,
138 display_failure=not ns.verbose)
139
140 if xml_list:
141 import xml.etree.ElementTree as ET
142 xml_data = [ET.tostring(x).decode('us-ascii') for x in xml_list]
143 else:
144 xml_data = None
145
146 test_time = time.perf_counter() - start_time
147
148 return TestResult(test_name, result, test_time, xml_data)
149 finally:
150 if use_timeout:
151 faulthandler.cancel_dump_traceback_later()
152 support.junit_xml_list = None
153
154
155def runtest(ns, test_name):
Victor Stinner3844fe52015-09-26 10:38:01 +0200156 """Run a single test.
157
Victor Stinnerab983672016-08-22 14:28:52 +0200158 ns -- regrtest namespace of options
Victor Stinner4d299832019-04-26 04:08:53 +0200159 test_name -- the name of the test
Victor Stinner3844fe52015-09-26 10:38:01 +0200160
Steve Dowerd0f49d22018-09-18 09:10:26 -0700161 Returns the tuple (result, test_time, xml_data), where result is one
162 of the constants:
Victor Stinnerab983672016-08-22 14:28:52 +0200163
Victor Stinner4d299832019-04-26 04:08:53 +0200164 INTERRUPTED KeyboardInterrupt
Victor Stinner3844fe52015-09-26 10:38:01 +0200165 RESOURCE_DENIED test skipped because resource denied
166 SKIPPED test skipped for some other reason
167 ENV_CHANGED test failed because it changed the execution environment
168 FAILED test failed
169 PASSED test passed
Pablo Galindo97243482018-11-29 17:17:44 +0000170 EMPTY_TEST_SUITE test ran no subtests.
Steve Dowerd0f49d22018-09-18 09:10:26 -0700171
172 If ns.xmlpath is not None, xml_data is a list containing each
173 generated testsuite element.
Victor Stinner3844fe52015-09-26 10:38:01 +0200174 """
Victor Stinner3844fe52015-09-26 10:38:01 +0200175 try:
Victor Stinner4d299832019-04-26 04:08:53 +0200176 return _runtest(ns, test_name)
177 except:
178 if not ns.pgo:
179 msg = traceback.format_exc()
180 print(f"test {test_name} crashed -- {msg}",
181 file=sys.stderr, flush=True)
182 return TestResult(test_name, FAILED, 0.0, None)
Victor Stinner3844fe52015-09-26 10:38:01 +0200183
184
Victor Stinnere3510d72017-08-09 17:44:33 +0200185def post_test_cleanup():
Victor Stinner4d299832019-04-26 04:08:53 +0200186 support.gc_collect()
Victor Stinnere3510d72017-08-09 17:44:33 +0200187 support.reap_children()
188
189
Victor Stinner4d299832019-04-26 04:08:53 +0200190def _test_module(the_module):
191 loader = unittest.TestLoader()
192 tests = loader.loadTestsFromModule(the_module)
193 for error in loader.errors:
194 print(error, file=sys.stderr)
195 if loader.errors:
196 raise Exception("errors while loading tests")
197 support.run_unittest(tests)
Victor Stinner3844fe52015-09-26 10:38:01 +0200198
Victor Stinner4d299832019-04-26 04:08:53 +0200199
200def _runtest_inner2(ns, test_name):
201 # Load the test function, run the test function, handle huntrleaks
202 # and findleaks to detect leaks
203
204 abstest = get_abs_module(ns, test_name)
205
206 # remove the module from sys.module to reload it if it was already imported
207 support.unload(abstest)
208
209 the_module = importlib.import_module(abstest)
210
211 # If the test has a test_main, that will run the appropriate
212 # tests. If not, use normal unittest test loading.
213 test_runner = getattr(the_module, "test_main", None)
214 if test_runner is None:
215 test_runner = functools.partial(_test_module, the_module)
216
Victor Stinner3844fe52015-09-26 10:38:01 +0200217 try:
Victor Stinner4d299832019-04-26 04:08:53 +0200218 if ns.huntrleaks:
219 # Return True if the test leaked references
220 refleak = dash_R(ns, test_name, test_runner)
221 else:
222 test_runner()
223 refleak = False
224 finally:
225 cleanup_test_droppings(test_name, ns.verbose)
226
227 if ns.findleaks:
228 import gc
229 support.gc_collect()
230 if gc.garbage:
231 import gc
232 gc.garbage = [1]
233 print_warning(f"{test_name} created {len(gc.garbage)} "
234 f"uncollectable object(s).")
235 # move the uncollectable objects somewhere,
236 # so we don't see them again
237 found_garbage.extend(gc.garbage)
238 gc.garbage.clear()
239 support.environment_altered = True
240
241 post_test_cleanup()
242
243 return refleak
244
245
246def _runtest_inner(ns, test_name, display_failure=True):
247 # Detect environment changes, handle exceptions.
248
249 # Reset the environment_altered flag to detect if a test altered
250 # the environment
251 support.environment_altered = False
252
253 if ns.pgo:
254 display_failure = False
255
256 try:
Serhiy Storchaka83910262016-11-11 11:46:44 +0200257 clear_caches()
Victor Stinner4d299832019-04-26 04:08:53 +0200258
259 with saved_test_environment(test_name, ns.verbose, ns.quiet, pgo=ns.pgo) as environment:
260 refleak = _runtest_inner2(ns, test_name)
Victor Stinner3844fe52015-09-26 10:38:01 +0200261 except support.ResourceDenied as msg:
Victor Stinnerab983672016-08-22 14:28:52 +0200262 if not ns.quiet and not ns.pgo:
Victor Stinner4d299832019-04-26 04:08:53 +0200263 print(f"{test_name} skipped -- {msg}", flush=True)
264 return RESOURCE_DENIED
Victor Stinner3844fe52015-09-26 10:38:01 +0200265 except unittest.SkipTest 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 SKIPPED
269 except support.TestFailed as exc:
270 msg = f"test {test_name} failed"
271 if display_failure:
272 msg = f"{msg} -- {exc}"
273 print(msg, file=sys.stderr, flush=True)
274 return FAILED
Pablo Galindo97243482018-11-29 17:17:44 +0000275 except support.TestDidNotRun:
Victor Stinner4d299832019-04-26 04:08:53 +0200276 return TEST_DID_NOT_RUN
277 except KeyboardInterrupt:
Victor Stinner3cde4402019-04-26 08:40:25 +0200278 print()
Victor Stinner4d299832019-04-26 04:08:53 +0200279 return INTERRUPTED
Victor Stinner3844fe52015-09-26 10:38:01 +0200280 except:
Victor Stinnerab983672016-08-22 14:28:52 +0200281 if not ns.pgo:
Victor Stinner4d299832019-04-26 04:08:53 +0200282 msg = traceback.format_exc()
283 print(f"test {test_name} crashed -- {msg}",
284 file=sys.stderr, flush=True)
285 return FAILED
286
287 if refleak:
288 return FAILED
289 if environment.changed:
290 return ENV_CHANGED
291 return PASSED
Victor Stinner3844fe52015-09-26 10:38:01 +0200292
293
Victor Stinner4d299832019-04-26 04:08:53 +0200294def cleanup_test_droppings(test_name, verbose):
Victor Stinner3844fe52015-09-26 10:38:01 +0200295 # First kill any dangling references to open files etc.
296 # This can also issue some ResourceWarnings which would otherwise get
297 # triggered during the following test run, and possibly produce failures.
Victor Stinner4d299832019-04-26 04:08:53 +0200298 support.gc_collect()
Victor Stinner3844fe52015-09-26 10:38:01 +0200299
300 # Try to clean up junk commonly left behind. While tests shouldn't leave
301 # any files or directories behind, when a test fails that can be tedious
302 # for it to arrange. The consequences can be especially nasty on Windows,
303 # since if a test leaves a file open, it cannot be deleted by name (while
304 # there's nothing we can do about that here either, we can display the
305 # name of the offending test, which is a real help).
306 for name in (support.TESTFN,
307 "db_home",
308 ):
309 if not os.path.exists(name):
310 continue
311
312 if os.path.isdir(name):
Victor Stinner4d299832019-04-26 04:08:53 +0200313 import shutil
Victor Stinner3844fe52015-09-26 10:38:01 +0200314 kind, nuker = "directory", shutil.rmtree
315 elif os.path.isfile(name):
316 kind, nuker = "file", os.unlink
317 else:
Victor Stinner4d299832019-04-26 04:08:53 +0200318 raise RuntimeError(f"os.path says {name!r} exists but is neither "
319 f"directory nor file")
Victor Stinner3844fe52015-09-26 10:38:01 +0200320
321 if verbose:
Victor Stinner4d299832019-04-26 04:08:53 +0200322 print_warning("%r left behind %s %r" % (test_name, kind, name))
323 support.environment_altered = True
324
Victor Stinner3844fe52015-09-26 10:38:01 +0200325 try:
Victor Stinner4d299832019-04-26 04:08:53 +0200326 import stat
Anthony Sottile8377cd42019-02-25 14:32:27 -0800327 # fix possible permissions problems that might prevent cleanup
328 os.chmod(name, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
Victor Stinner3844fe52015-09-26 10:38:01 +0200329 nuker(name)
Victor Stinner4d299832019-04-26 04:08:53 +0200330 except Exception as exc:
331 print_warning(f"{test_name} left behind {kind} {name!r} "
332 f"and it couldn't be removed: {exc}")