| 
									
										
										
										
											2023-09-11 02:07:18 +02:00
										 |  |  | import faulthandler | 
					
						
							|  |  |  | import gc | 
					
						
							|  |  |  | import importlib | 
					
						
							|  |  |  | import io | 
					
						
							|  |  |  | import sys | 
					
						
							|  |  |  | import time | 
					
						
							|  |  |  | import traceback | 
					
						
							|  |  |  | import unittest | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from test import support | 
					
						
							|  |  |  | from test.support import threading_helper | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-25 12:41:21 +03:00
										 |  |  | from .filter import match_test | 
					
						
							|  |  |  | from .result import State, TestResult, TestStats | 
					
						
							| 
									
										
										
										
											2023-09-11 09:02:35 +02:00
										 |  |  | from .runtests import RunTests | 
					
						
							|  |  |  | from .save_env import saved_test_environment | 
					
						
							|  |  |  | from .setup import setup_tests | 
					
						
							| 
									
										
										
										
											2023-10-25 12:41:21 +03:00
										 |  |  | from .testresult import get_test_runner | 
					
						
							| 
									
										
										
										
											2023-09-11 09:02:35 +02:00
										 |  |  | from .utils import ( | 
					
						
							| 
									
										
										
										
											2023-09-11 02:07:18 +02:00
										 |  |  |     TestName, | 
					
						
							|  |  |  |     clear_caches, remove_testfn, abs_module_name, print_warning) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Minimum duration of a test to display its duration or to mention that | 
					
						
							|  |  |  | # the test is running in background | 
					
						
							|  |  |  | PROGRESS_MIN_TIME = 30.0   # seconds | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def run_unittest(test_mod): | 
					
						
							|  |  |  |     loader = unittest.TestLoader() | 
					
						
							|  |  |  |     tests = loader.loadTestsFromModule(test_mod) | 
					
						
							|  |  |  |     for error in loader.errors: | 
					
						
							|  |  |  |         print(error, file=sys.stderr) | 
					
						
							|  |  |  |     if loader.errors: | 
					
						
							|  |  |  |         raise Exception("errors while loading tests") | 
					
						
							| 
									
										
										
										
											2023-10-25 12:41:21 +03:00
										 |  |  |     _filter_suite(tests, match_test) | 
					
						
							|  |  |  |     return _run_suite(tests) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _filter_suite(suite, pred): | 
					
						
							|  |  |  |     """Recursively filter test cases in a suite based on a predicate.""" | 
					
						
							|  |  |  |     newtests = [] | 
					
						
							|  |  |  |     for test in suite._tests: | 
					
						
							|  |  |  |         if isinstance(test, unittest.TestSuite): | 
					
						
							|  |  |  |             _filter_suite(test, pred) | 
					
						
							|  |  |  |             newtests.append(test) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             if pred(test): | 
					
						
							|  |  |  |                 newtests.append(test) | 
					
						
							|  |  |  |     suite._tests = newtests | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _run_suite(suite): | 
					
						
							|  |  |  |     """Run tests from a unittest.TestSuite-derived class.""" | 
					
						
							|  |  |  |     runner = get_test_runner(sys.stdout, | 
					
						
							|  |  |  |                              verbosity=support.verbose, | 
					
						
							|  |  |  |                              capture_output=(support.junit_xml_list is not None)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     result = runner.run(suite) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if support.junit_xml_list is not None: | 
					
						
							| 
									
										
										
										
											2024-05-20 17:05:39 -04:00
										 |  |  |         import xml.etree.ElementTree as ET | 
					
						
							|  |  |  |         xml_elem = result.get_xml_element() | 
					
						
							|  |  |  |         xml_str = ET.tostring(xml_elem).decode('ascii') | 
					
						
							|  |  |  |         support.junit_xml_list.append(xml_str) | 
					
						
							| 
									
										
										
										
											2023-10-25 12:41:21 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if not result.testsRun and not result.skipped and not result.errors: | 
					
						
							|  |  |  |         raise support.TestDidNotRun | 
					
						
							|  |  |  |     if not result.wasSuccessful(): | 
					
						
							|  |  |  |         stats = TestStats.from_unittest(result) | 
					
						
							|  |  |  |         if len(result.errors) == 1 and not result.failures: | 
					
						
							|  |  |  |             err = result.errors[0][1] | 
					
						
							|  |  |  |         elif len(result.failures) == 1 and not result.errors: | 
					
						
							|  |  |  |             err = result.failures[0][1] | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             err = "multiple errors occurred" | 
					
						
							| 
									
										
										
										
											2023-10-25 17:50:25 +03:00
										 |  |  |             if not support.verbose: err += "; run in verbose mode for details" | 
					
						
							| 
									
										
										
										
											2023-10-25 12:41:21 +03:00
										 |  |  |         errors = [(str(tc), exc_str) for tc, exc_str in result.errors] | 
					
						
							|  |  |  |         failures = [(str(tc), exc_str) for tc, exc_str in result.failures] | 
					
						
							|  |  |  |         raise support.TestFailedWithDetails(err, errors, failures, stats=stats) | 
					
						
							|  |  |  |     return result | 
					
						
							| 
									
										
										
										
											2023-09-11 02:07:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def regrtest_runner(result: TestResult, test_func, runtests: RunTests) -> None: | 
					
						
							|  |  |  |     # Run test_func(), collect statistics, and detect reference and memory | 
					
						
							|  |  |  |     # leaks. | 
					
						
							|  |  |  |     if runtests.hunt_refleak: | 
					
						
							| 
									
										
										
										
											2023-09-11 09:02:35 +02:00
										 |  |  |         from .refleak import runtest_refleak | 
					
						
							| 
									
										
										
										
											2023-09-11 02:07:18 +02:00
										 |  |  |         refleak, test_result = runtest_refleak(result.test_name, test_func, | 
					
						
							|  |  |  |                                                runtests.hunt_refleak, | 
					
						
							|  |  |  |                                                runtests.quiet) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         test_result = test_func() | 
					
						
							|  |  |  |         refleak = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if refleak: | 
					
						
							|  |  |  |         result.state = State.REFLEAK | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-14 19:33:18 +01:00
										 |  |  |     stats: TestStats | None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-11 02:07:18 +02:00
										 |  |  |     match test_result: | 
					
						
							|  |  |  |         case TestStats(): | 
					
						
							|  |  |  |             stats = test_result | 
					
						
							|  |  |  |         case unittest.TestResult(): | 
					
						
							|  |  |  |             stats = TestStats.from_unittest(test_result) | 
					
						
							|  |  |  |         case None: | 
					
						
							|  |  |  |             print_warning(f"{result.test_name} test runner returned None: {test_func}") | 
					
						
							|  |  |  |             stats = None | 
					
						
							|  |  |  |         case _: | 
					
						
							| 
									
										
										
										
											2023-11-09 16:00:10 +01:00
										 |  |  |             # Don't import doctest at top level since only few tests return | 
					
						
							|  |  |  |             # a doctest.TestResult instance. | 
					
						
							|  |  |  |             import doctest | 
					
						
							|  |  |  |             if isinstance(test_result, doctest.TestResults): | 
					
						
							|  |  |  |                 stats = TestStats.from_doctest(test_result) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 print_warning(f"Unknown test result type: {type(test_result)}") | 
					
						
							|  |  |  |                 stats = None | 
					
						
							| 
									
										
										
										
											2023-09-11 02:07:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     result.stats = stats | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Storage of uncollectable GC objects (gc.garbage) | 
					
						
							|  |  |  | GC_GARBAGE = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _load_run_test(result: TestResult, runtests: RunTests) -> None: | 
					
						
							| 
									
										
										
										
											2023-09-12 05:01:33 +02:00
										 |  |  |     # Load the test module and run the tests. | 
					
						
							|  |  |  |     test_name = result.test_name | 
					
						
							|  |  |  |     module_name = abs_module_name(test_name, runtests.test_dir) | 
					
						
							| 
									
										
										
										
											2023-09-11 02:07:18 +02:00
										 |  |  |     test_mod = importlib.import_module(module_name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if hasattr(test_mod, "test_main"): | 
					
						
							|  |  |  |         # https://github.com/python/cpython/issues/89392 | 
					
						
							| 
									
										
										
										
											2023-09-12 05:01:33 +02:00
										 |  |  |         raise Exception(f"Module {test_name} defines test_main() which " | 
					
						
							|  |  |  |                         f"is no longer supported by regrtest") | 
					
						
							| 
									
										
										
										
											2023-09-11 02:07:18 +02:00
										 |  |  |     def test_func(): | 
					
						
							|  |  |  |         return run_unittest(test_mod) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try: | 
					
						
							| 
									
										
										
										
											2023-09-12 05:01:33 +02:00
										 |  |  |         regrtest_runner(result, test_func, runtests) | 
					
						
							| 
									
										
										
										
											2023-09-11 02:07:18 +02:00
										 |  |  |     finally: | 
					
						
							|  |  |  |         # First kill any dangling references to open files etc. | 
					
						
							|  |  |  |         # This can also issue some ResourceWarnings which would otherwise get | 
					
						
							|  |  |  |         # triggered during the following test run, and possibly produce | 
					
						
							|  |  |  |         # failures. | 
					
						
							|  |  |  |         support.gc_collect() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-12 05:01:33 +02:00
										 |  |  |         remove_testfn(test_name, runtests.verbose) | 
					
						
							| 
									
										
										
										
											2023-09-11 02:07:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if gc.garbage: | 
					
						
							|  |  |  |         support.environment_altered = True | 
					
						
							| 
									
										
										
										
											2023-09-12 05:01:33 +02:00
										 |  |  |         print_warning(f"{test_name} created {len(gc.garbage)} " | 
					
						
							| 
									
										
										
										
											2023-09-11 02:07:18 +02:00
										 |  |  |                       f"uncollectable object(s)") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # move the uncollectable objects somewhere, | 
					
						
							|  |  |  |         # so we don't see them again | 
					
						
							|  |  |  |         GC_GARBAGE.extend(gc.garbage) | 
					
						
							|  |  |  |         gc.garbage.clear() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     support.reap_children() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _runtest_env_changed_exc(result: TestResult, runtests: RunTests, | 
					
						
							|  |  |  |                              display_failure: bool = True) -> None: | 
					
						
							| 
									
										
										
										
											2023-09-12 05:01:33 +02:00
										 |  |  |     # Handle exceptions, detect environment changes. | 
					
						
							| 
									
										
										
										
											2023-09-11 02:07:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Reset the environment_altered flag to detect if a test altered | 
					
						
							|  |  |  |     # the environment | 
					
						
							|  |  |  |     support.environment_altered = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pgo = runtests.pgo | 
					
						
							|  |  |  |     if pgo: | 
					
						
							|  |  |  |         display_failure = False | 
					
						
							|  |  |  |     quiet = runtests.quiet | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     test_name = result.test_name | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         clear_caches() | 
					
						
							|  |  |  |         support.gc_collect() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-12 05:01:33 +02:00
										 |  |  |         with saved_test_environment(test_name, | 
					
						
							|  |  |  |                                     runtests.verbose, quiet, pgo=pgo): | 
					
						
							| 
									
										
										
										
											2023-09-11 02:07:18 +02:00
										 |  |  |             _load_run_test(result, runtests) | 
					
						
							| 
									
										
										
										
											2023-09-15 18:01:28 +01:00
										 |  |  |     except support.ResourceDenied as exc: | 
					
						
							| 
									
										
										
										
											2023-09-11 02:07:18 +02:00
										 |  |  |         if not quiet and not pgo: | 
					
						
							| 
									
										
										
										
											2023-09-15 18:01:28 +01:00
										 |  |  |             print(f"{test_name} skipped -- {exc}", flush=True) | 
					
						
							| 
									
										
										
										
											2023-09-11 02:07:18 +02:00
										 |  |  |         result.state = State.RESOURCE_DENIED | 
					
						
							|  |  |  |         return | 
					
						
							| 
									
										
										
										
											2023-09-15 18:01:28 +01:00
										 |  |  |     except unittest.SkipTest as exc: | 
					
						
							| 
									
										
										
										
											2023-09-11 02:07:18 +02:00
										 |  |  |         if not quiet and not pgo: | 
					
						
							| 
									
										
										
										
											2023-09-15 18:01:28 +01:00
										 |  |  |             print(f"{test_name} skipped -- {exc}", flush=True) | 
					
						
							| 
									
										
										
										
											2023-09-11 02:07:18 +02:00
										 |  |  |         result.state = State.SKIPPED | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     except support.TestFailedWithDetails as exc: | 
					
						
							|  |  |  |         msg = f"test {test_name} failed" | 
					
						
							|  |  |  |         if display_failure: | 
					
						
							|  |  |  |             msg = f"{msg} -- {exc}" | 
					
						
							|  |  |  |         print(msg, file=sys.stderr, flush=True) | 
					
						
							|  |  |  |         result.state = State.FAILED | 
					
						
							|  |  |  |         result.errors = exc.errors | 
					
						
							|  |  |  |         result.failures = exc.failures | 
					
						
							|  |  |  |         result.stats = exc.stats | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     except support.TestFailed as exc: | 
					
						
							|  |  |  |         msg = f"test {test_name} failed" | 
					
						
							|  |  |  |         if display_failure: | 
					
						
							|  |  |  |             msg = f"{msg} -- {exc}" | 
					
						
							|  |  |  |         print(msg, file=sys.stderr, flush=True) | 
					
						
							|  |  |  |         result.state = State.FAILED | 
					
						
							|  |  |  |         result.stats = exc.stats | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     except support.TestDidNotRun: | 
					
						
							|  |  |  |         result.state = State.DID_NOT_RUN | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     except KeyboardInterrupt: | 
					
						
							|  |  |  |         print() | 
					
						
							|  |  |  |         result.state = State.INTERRUPTED | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     except: | 
					
						
							|  |  |  |         if not pgo: | 
					
						
							|  |  |  |             msg = traceback.format_exc() | 
					
						
							|  |  |  |             print(f"test {test_name} crashed -- {msg}", | 
					
						
							|  |  |  |                   file=sys.stderr, flush=True) | 
					
						
							|  |  |  |         result.state = State.UNCAUGHT_EXC | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if support.environment_altered: | 
					
						
							|  |  |  |         result.set_env_changed() | 
					
						
							|  |  |  |     # Don't override the state if it was already set (REFLEAK or ENV_CHANGED) | 
					
						
							|  |  |  |     if result.state is None: | 
					
						
							|  |  |  |         result.state = State.PASSED | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _runtest(result: TestResult, runtests: RunTests) -> None: | 
					
						
							|  |  |  |     # Capture stdout and stderr, set faulthandler timeout, | 
					
						
							|  |  |  |     # and create JUnit XML report. | 
					
						
							|  |  |  |     verbose = runtests.verbose | 
					
						
							|  |  |  |     output_on_failure = runtests.output_on_failure | 
					
						
							|  |  |  |     timeout = runtests.timeout | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-01 14:54:33 +00:00
										 |  |  |     if timeout is not None and threading_helper.can_start_thread: | 
					
						
							|  |  |  |         use_timeout = True | 
					
						
							| 
									
										
										
										
											2023-09-11 02:07:18 +02:00
										 |  |  |         faulthandler.dump_traceback_later(timeout, exit=True) | 
					
						
							| 
									
										
										
										
											2023-12-01 14:54:33 +00:00
										 |  |  |     else: | 
					
						
							|  |  |  |         use_timeout = False | 
					
						
							| 
									
										
										
										
											2023-09-11 02:07:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     try: | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |         setup_tests(runtests) | 
					
						
							| 
									
										
										
										
											2023-09-11 02:07:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if output_on_failure: | 
					
						
							|  |  |  |             support.verbose = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             stream = io.StringIO() | 
					
						
							|  |  |  |             orig_stdout = sys.stdout | 
					
						
							|  |  |  |             orig_stderr = sys.stderr | 
					
						
							|  |  |  |             print_warning = support.print_warning | 
					
						
							|  |  |  |             orig_print_warnings_stderr = print_warning.orig_stderr | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             output = None | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 sys.stdout = stream | 
					
						
							|  |  |  |                 sys.stderr = stream | 
					
						
							|  |  |  |                 # print_warning() writes into the temporary stream to preserve | 
					
						
							|  |  |  |                 # messages order. If support.environment_altered becomes true, | 
					
						
							|  |  |  |                 # warnings will be written to sys.stderr below. | 
					
						
							|  |  |  |                 print_warning.orig_stderr = stream | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 _runtest_env_changed_exc(result, runtests, display_failure=False) | 
					
						
							|  |  |  |                 # Ignore output if the test passed successfully | 
					
						
							|  |  |  |                 if result.state != State.PASSED: | 
					
						
							|  |  |  |                     output = stream.getvalue() | 
					
						
							|  |  |  |             finally: | 
					
						
							|  |  |  |                 sys.stdout = orig_stdout | 
					
						
							|  |  |  |                 sys.stderr = orig_stderr | 
					
						
							|  |  |  |                 print_warning.orig_stderr = orig_print_warnings_stderr | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if output is not None: | 
					
						
							|  |  |  |                 sys.stderr.write(output) | 
					
						
							|  |  |  |                 sys.stderr.flush() | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             # Tell tests to be moderately quiet | 
					
						
							|  |  |  |             support.verbose = verbose | 
					
						
							|  |  |  |             _runtest_env_changed_exc(result, runtests, | 
					
						
							|  |  |  |                                      display_failure=not verbose) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         xml_list = support.junit_xml_list | 
					
						
							|  |  |  |         if xml_list: | 
					
						
							| 
									
										
										
										
											2024-05-20 17:05:39 -04:00
										 |  |  |             result.xml_data = xml_list | 
					
						
							| 
									
										
										
										
											2023-09-11 02:07:18 +02:00
										 |  |  |     finally: | 
					
						
							|  |  |  |         if use_timeout: | 
					
						
							|  |  |  |             faulthandler.cancel_dump_traceback_later() | 
					
						
							|  |  |  |         support.junit_xml_list = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def run_single_test(test_name: TestName, runtests: RunTests) -> TestResult: | 
					
						
							|  |  |  |     """Run a single test.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     test_name -- the name of the test | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Returns a TestResult. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     If runtests.use_junit, xml_data is a list containing each generated | 
					
						
							|  |  |  |     testsuite element. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     start_time = time.perf_counter() | 
					
						
							|  |  |  |     result = TestResult(test_name) | 
					
						
							|  |  |  |     pgo = runtests.pgo | 
					
						
							|  |  |  |     try: | 
					
						
							| 
									
										
										
										
											2024-04-29 14:36:02 -04:00
										 |  |  |         # gh-117783: don't immortalize deferred objects when tracking | 
					
						
							| 
									
										
										
										
											2024-08-12 12:16:41 +08:00
										 |  |  |         # refleaks. Only relevant for the free-threaded build. | 
					
						
							| 
									
										
										
										
											2024-04-29 14:36:02 -04:00
										 |  |  |         with support.suppress_immortalization(runtests.hunt_refleak): | 
					
						
							|  |  |  |             _runtest(result, runtests) | 
					
						
							| 
									
										
										
										
											2023-09-11 02:07:18 +02:00
										 |  |  |     except: | 
					
						
							|  |  |  |         if not pgo: | 
					
						
							|  |  |  |             msg = traceback.format_exc() | 
					
						
							|  |  |  |             print(f"test {test_name} crashed -- {msg}", | 
					
						
							|  |  |  |                   file=sys.stderr, flush=True) | 
					
						
							|  |  |  |         result.state = State.UNCAUGHT_EXC | 
					
						
							| 
									
										
										
										
											2023-09-11 19:33:42 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     sys.stdout.flush() | 
					
						
							|  |  |  |     sys.stderr.flush() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-11 02:07:18 +02:00
										 |  |  |     result.duration = time.perf_counter() - start_time | 
					
						
							|  |  |  |     return result |