| 
									
										
										
										
											2015-09-26 10:38:01 +02:00
										 |  |  | import os | 
					
						
							| 
									
										
										
										
											2015-09-29 22:48:52 +02:00
										 |  |  | import random | 
					
						
							| 
									
										
										
										
											2015-09-26 10:38:01 +02:00
										 |  |  | import re | 
					
						
							| 
									
										
										
										
											2023-09-26 20:46:52 +02:00
										 |  |  | import shlex | 
					
						
							| 
									
										
										
										
											2015-09-26 10:38:01 +02:00
										 |  |  | import sys | 
					
						
							| 
									
										
										
										
											2023-10-01 00:37:23 +02:00
										 |  |  | import sysconfig | 
					
						
							| 
									
										
										
										
											2016-03-22 15:14:09 +01:00
										 |  |  | import time | 
					
						
							| 
									
										
										
										
											2023-11-10 18:17:45 +01:00
										 |  |  | import trace | 
					
						
							| 
									
										
										
										
											2023-09-11 01:11:22 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | from test import support | 
					
						
							| 
									
										
										
										
											2023-10-06 02:37:28 +02:00
										 |  |  | from test.support import os_helper, MS_WINDOWS | 
					
						
							| 
									
										
										
										
											2023-09-11 01:11:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-11 09:02:35 +02:00
										 |  |  | from .cmdline import _parse_args, Namespace | 
					
						
							| 
									
										
										
										
											2023-09-11 10:52:03 +02:00
										 |  |  | from .findtests import findtests, split_test_packages, list_cases | 
					
						
							| 
									
										
										
										
											2023-09-11 09:02:35 +02:00
										 |  |  | from .logger import Logger | 
					
						
							| 
									
										
										
										
											2023-09-29 02:51:22 +02:00
										 |  |  | from .pgo import setup_pgo_tests | 
					
						
							| 
									
										
										
										
											2023-11-10 18:17:45 +01:00
										 |  |  | from .result import State, TestResult | 
					
						
							| 
									
										
										
										
											2023-09-29 02:51:22 +02:00
										 |  |  | from .results import TestResults, EXITCODE_INTERRUPTED | 
					
						
							| 
									
										
										
										
											2023-09-11 09:02:35 +02:00
										 |  |  | from .runtests import RunTests, HuntRefleak | 
					
						
							|  |  |  | from .setup import setup_process, setup_test_dir | 
					
						
							|  |  |  | from .single import run_single_test, PROGRESS_MIN_TIME | 
					
						
							|  |  |  | from .utils import ( | 
					
						
							| 
									
										
										
										
											2023-10-21 17:44:46 +03:00
										 |  |  |     StrPath, StrJSON, TestName, TestList, TestTuple, TestFilter, | 
					
						
							| 
									
										
										
										
											2023-09-11 02:07:18 +02:00
										 |  |  |     strip_py_suffix, count, format_duration, | 
					
						
							| 
									
										
										
										
											2023-09-11 10:52:03 +02:00
										 |  |  |     printlist, get_temp_dir, get_work_dir, exit_timeout, | 
					
						
							| 
									
										
										
										
											2023-09-26 20:46:52 +02:00
										 |  |  |     display_header, cleanup_temp_dir, print_warning, | 
					
						
							| 
									
										
										
										
											2023-10-01 00:37:23 +02:00
										 |  |  |     is_cross_compiled, get_host_runner, | 
					
						
							| 
									
										
										
										
											2023-10-06 02:37:28 +02:00
										 |  |  |     EXIT_TIMEOUT) | 
					
						
							| 
									
										
										
										
											2019-09-18 08:29:25 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-29 22:48:52 +02:00
										 |  |  | class Regrtest: | 
					
						
							| 
									
										
										
										
											2015-09-26 10:38:01 +02:00
										 |  |  |     """Execute a test suite.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     This also parses command-line options and modifies its behavior | 
					
						
							|  |  |  |     accordingly. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     tests -- a list of strings containing test names (optional) | 
					
						
							|  |  |  |     testdir -- the directory in which to look for tests (optional) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Users other than the Python test suite will certainly want to | 
					
						
							|  |  |  |     specify testdir; if it's omitted, the directory containing the | 
					
						
							|  |  |  |     Python test suite is searched for. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     If the tests argument is omitted, the tests listed on the | 
					
						
							|  |  |  |     command-line will be used.  If that's empty, too, then all *.py | 
					
						
							|  |  |  |     files beginning with test_ will be used. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     The other default arguments (verbose, quiet, exclude, | 
					
						
							| 
									
										
										
										
											2021-11-12 16:19:09 +01:00
										 |  |  |     single, randomize, use_resources, trace, coverdir, | 
					
						
							| 
									
										
										
										
											2015-09-26 10:38:01 +02:00
										 |  |  |     print_slow, and random_seed) allow programmers calling main() | 
					
						
							|  |  |  |     directly to set the values that would normally be set by flags | 
					
						
							|  |  |  |     on the command line. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2023-09-26 23:59:11 +02:00
										 |  |  |     def __init__(self, ns: Namespace, _add_python_opts: bool = False): | 
					
						
							| 
									
										
										
										
											2023-09-11 03:46:26 +02:00
										 |  |  |         # Log verbosity | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |         self.verbose: int = int(ns.verbose) | 
					
						
							| 
									
										
										
										
											2023-09-11 03:46:26 +02:00
										 |  |  |         self.quiet: bool = ns.quiet | 
					
						
							|  |  |  |         self.pgo: bool = ns.pgo | 
					
						
							|  |  |  |         self.pgo_extended: bool = ns.pgo_extended | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Test results | 
					
						
							|  |  |  |         self.results: TestResults = TestResults() | 
					
						
							|  |  |  |         self.first_state: str | None = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Logger | 
					
						
							|  |  |  |         self.logger = Logger(self.results, self.quiet, self.pgo) | 
					
						
							| 
									
										
										
										
											2023-09-10 05:04:26 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-09 01:48:54 +02:00
										 |  |  |         # Actions | 
					
						
							| 
									
										
										
										
											2023-09-09 03:37:48 +02:00
										 |  |  |         self.want_header: bool = ns.header | 
					
						
							|  |  |  |         self.want_list_tests: bool = ns.list_tests | 
					
						
							|  |  |  |         self.want_list_cases: bool = ns.list_cases | 
					
						
							|  |  |  |         self.want_wait: bool = ns.wait | 
					
						
							|  |  |  |         self.want_cleanup: bool = ns.cleanup | 
					
						
							| 
									
										
										
										
											2023-09-10 03:07:05 +02:00
										 |  |  |         self.want_rerun: bool = ns.rerun | 
					
						
							|  |  |  |         self.want_run_leaks: bool = ns.runleaks | 
					
						
							| 
									
										
										
										
											2023-09-26 23:59:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-01 00:37:23 +02:00
										 |  |  |         self.ci_mode: bool = (ns.fast_ci or ns.slow_ci) | 
					
						
							| 
									
										
										
										
											2023-09-26 23:59:11 +02:00
										 |  |  |         self.want_add_python_opts: bool = (_add_python_opts | 
					
						
							| 
									
										
										
										
											2023-10-01 00:37:23 +02:00
										 |  |  |                                            and ns._add_python_opts) | 
					
						
							| 
									
										
										
										
											2023-09-09 01:48:54 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Select tests | 
					
						
							| 
									
										
										
										
											2023-10-21 17:44:46 +03:00
										 |  |  |         self.match_tests: TestFilter = ns.match_tests | 
					
						
							| 
									
										
										
										
											2023-09-09 03:37:48 +02:00
										 |  |  |         self.exclude: bool = ns.exclude | 
					
						
							| 
									
										
										
										
											2023-09-10 03:07:05 +02:00
										 |  |  |         self.fromfile: StrPath | None = ns.fromfile | 
					
						
							|  |  |  |         self.starting_test: TestName | None = ns.start | 
					
						
							|  |  |  |         self.cmdline_args: TestList = ns.args | 
					
						
							| 
									
										
										
										
											2023-09-09 01:48:54 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-10 03:07:05 +02:00
										 |  |  |         # Workers | 
					
						
							| 
									
										
										
										
											2023-09-10 02:24:38 +02:00
										 |  |  |         if ns.use_mp is None: | 
					
						
							|  |  |  |             num_workers = 0  # run sequentially | 
					
						
							|  |  |  |         elif ns.use_mp <= 0: | 
					
						
							|  |  |  |             num_workers = -1  # use the number of CPUs | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             num_workers = ns.use_mp | 
					
						
							|  |  |  |         self.num_workers: int = num_workers | 
					
						
							| 
									
										
										
										
											2023-09-10 03:07:05 +02:00
										 |  |  |         self.worker_json: StrJSON | None = ns.worker_json | 
					
						
							| 
									
										
										
										
											2023-09-10 02:24:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-09 01:48:54 +02:00
										 |  |  |         # Options to run tests | 
					
						
							| 
									
										
										
										
											2023-09-09 03:37:48 +02:00
										 |  |  |         self.fail_fast: bool = ns.failfast | 
					
						
							| 
									
										
										
										
											2023-09-10 02:24:38 +02:00
										 |  |  |         self.fail_env_changed: bool = ns.fail_env_changed | 
					
						
							| 
									
										
										
										
											2023-09-10 03:07:05 +02:00
										 |  |  |         self.fail_rerun: bool = ns.fail_rerun | 
					
						
							| 
									
										
										
										
											2023-09-09 03:37:48 +02:00
										 |  |  |         self.forever: bool = ns.forever | 
					
						
							|  |  |  |         self.output_on_failure: bool = ns.verbose3 | 
					
						
							|  |  |  |         self.timeout: float | None = ns.timeout | 
					
						
							| 
									
										
										
										
											2023-09-09 11:18:14 +02:00
										 |  |  |         if ns.huntrleaks: | 
					
						
							| 
									
										
										
										
											2023-09-11 01:11:22 +02:00
										 |  |  |             warmups, runs, filename = ns.huntrleaks | 
					
						
							|  |  |  |             filename = os.path.abspath(filename) | 
					
						
							| 
									
										
										
										
											2023-09-14 19:33:18 +01:00
										 |  |  |             self.hunt_refleak: HuntRefleak | None = HuntRefleak(warmups, runs, filename) | 
					
						
							| 
									
										
										
										
											2023-09-09 11:18:14 +02:00
										 |  |  |         else: | 
					
						
							|  |  |  |             self.hunt_refleak = None | 
					
						
							| 
									
										
										
										
											2023-09-10 03:07:05 +02:00
										 |  |  |         self.test_dir: StrPath | None = ns.testdir | 
					
						
							|  |  |  |         self.junit_filename: StrPath | None = ns.xmlpath | 
					
						
							| 
									
										
										
										
											2023-09-10 01:41:21 +02:00
										 |  |  |         self.memory_limit: str | None = ns.memlimit | 
					
						
							|  |  |  |         self.gc_threshold: int | None = ns.threshold | 
					
						
							| 
									
										
										
										
											2023-09-14 19:33:18 +01:00
										 |  |  |         self.use_resources: tuple[str, ...] = tuple(ns.use_resources) | 
					
						
							| 
									
										
										
										
											2023-09-12 05:35:08 +02:00
										 |  |  |         if ns.python: | 
					
						
							| 
									
										
										
										
											2023-09-14 19:33:18 +01:00
										 |  |  |             self.python_cmd: tuple[str, ...] | None = tuple(ns.python) | 
					
						
							| 
									
										
										
										
											2023-09-12 05:35:08 +02:00
										 |  |  |         else: | 
					
						
							|  |  |  |             self.python_cmd = None | 
					
						
							| 
									
										
										
										
											2023-09-10 03:07:05 +02:00
										 |  |  |         self.coverage: bool = ns.trace | 
					
						
							|  |  |  |         self.coverage_dir: StrPath | None = ns.coverdir | 
					
						
							|  |  |  |         self.tmp_dir: StrPath | None = ns.tempdir | 
					
						
							| 
									
										
										
										
											2023-09-09 01:48:54 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-01 22:41:03 +02:00
										 |  |  |         # Randomize | 
					
						
							|  |  |  |         self.randomize: bool = ns.randomize | 
					
						
							| 
									
										
										
										
											2023-10-21 10:37:48 +02:00
										 |  |  |         if ('SOURCE_DATE_EPOCH' in os.environ | 
					
						
							|  |  |  |             # don't use the variable if empty | 
					
						
							|  |  |  |             and os.environ['SOURCE_DATE_EPOCH'] | 
					
						
							|  |  |  |         ): | 
					
						
							| 
									
										
										
										
											2023-10-01 22:41:03 +02:00
										 |  |  |             self.randomize = False | 
					
						
							| 
									
										
										
										
											2023-10-21 10:37:48 +02:00
										 |  |  |             # SOURCE_DATE_EPOCH should be an integer, but use a string to not | 
					
						
							|  |  |  |             # fail if it's not integer. random.seed() accepts a string. | 
					
						
							|  |  |  |             # https://reproducible-builds.org/docs/source-date-epoch/ | 
					
						
							|  |  |  |             self.random_seed: int | str = os.environ['SOURCE_DATE_EPOCH'] | 
					
						
							|  |  |  |         elif ns.random_seed is None: | 
					
						
							|  |  |  |             self.random_seed = random.getrandbits(32) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.random_seed = ns.random_seed | 
					
						
							| 
									
										
										
										
											2023-10-01 22:41:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-29 22:48:52 +02:00
										 |  |  |         # tests | 
					
						
							| 
									
										
										
										
											2023-09-09 01:48:54 +02:00
										 |  |  |         self.first_runtests: RunTests | None = None | 
					
						
							| 
									
										
										
										
											2015-09-29 22:48:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-10 03:07:05 +02:00
										 |  |  |         # used by --slowest | 
					
						
							|  |  |  |         self.print_slowest: bool = ns.print_slow | 
					
						
							| 
									
										
										
										
											2015-09-26 10:38:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-29 22:48:52 +02:00
										 |  |  |         # used to display the progress bar "[ 3/100]" | 
					
						
							| 
									
										
										
										
											2023-09-02 18:09:36 +02:00
										 |  |  |         self.start_time = time.perf_counter() | 
					
						
							| 
									
										
										
										
											2015-09-26 10:38:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-29 22:48:52 +02:00
										 |  |  |         # used by --single | 
					
						
							| 
									
										
										
										
											2023-09-10 03:07:05 +02:00
										 |  |  |         self.single_test_run: bool = ns.single | 
					
						
							|  |  |  |         self.next_single_test: TestName | None = None | 
					
						
							|  |  |  |         self.next_single_filename: StrPath | None = None | 
					
						
							| 
									
										
										
										
											2015-09-26 10:38:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-03 16:15:16 +02:00
										 |  |  |     def log(self, line=''): | 
					
						
							| 
									
										
										
										
											2023-09-10 05:04:26 +02:00
										 |  |  |         self.logger.log(line) | 
					
						
							| 
									
										
										
										
											2015-09-29 22:48:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |     def find_tests(self, tests: TestList | None = None) -> tuple[TestTuple, TestList | None]: | 
					
						
							| 
									
										
										
										
											2023-09-10 03:07:05 +02:00
										 |  |  |         if self.single_test_run: | 
					
						
							| 
									
										
										
										
											2019-05-14 15:49:16 +02:00
										 |  |  |             self.next_single_filename = os.path.join(self.tmp_dir, 'pynexttest') | 
					
						
							| 
									
										
										
										
											2015-09-29 22:48:52 +02:00
										 |  |  |             try: | 
					
						
							|  |  |  |                 with open(self.next_single_filename, 'r') as fp: | 
					
						
							|  |  |  |                     next_test = fp.read().strip() | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |                     tests = [next_test] | 
					
						
							| 
									
										
										
										
											2015-09-29 22:48:52 +02:00
										 |  |  |             except OSError: | 
					
						
							|  |  |  |                 pass | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-09 01:48:54 +02:00
										 |  |  |         if self.fromfile: | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |             tests = [] | 
					
						
							| 
									
										
										
										
											2016-03-24 09:43:00 +01:00
										 |  |  |             # regex to match 'test_builtin' in line: | 
					
						
							|  |  |  |             # '0:00:00 [  4/400] test_builtin -- test_dict took 1 sec' | 
					
						
							| 
									
										
										
										
											2017-01-03 01:38:58 +01:00
										 |  |  |             regex = re.compile(r'\btest_[a-zA-Z0-9_]+\b') | 
					
						
							| 
									
										
										
										
											2023-09-09 01:48:54 +02:00
										 |  |  |             with open(os.path.join(os_helper.SAVEDCWD, self.fromfile)) as fp: | 
					
						
							| 
									
										
										
										
											2015-09-29 22:48:52 +02:00
										 |  |  |                 for line in fp: | 
					
						
							| 
									
										
										
										
											2016-12-09 16:05:51 +01:00
										 |  |  |                     line = line.split('#', 1)[0] | 
					
						
							| 
									
										
										
										
											2016-03-24 09:43:00 +01:00
										 |  |  |                     line = line.strip() | 
					
						
							| 
									
										
										
										
											2016-12-09 16:05:51 +01:00
										 |  |  |                     match = regex.search(line) | 
					
						
							|  |  |  |                     if match is not None: | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |                         tests.append(match.group()) | 
					
						
							| 
									
										
										
										
											2015-09-29 22:48:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |         strip_py_suffix(tests) | 
					
						
							| 
									
										
										
										
											2015-09-29 22:48:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-09 03:37:48 +02:00
										 |  |  |         if self.pgo: | 
					
						
							| 
									
										
										
										
											2019-07-23 21:33:48 -07:00
										 |  |  |             # add default PGO tests if no tests are specified | 
					
						
							| 
									
										
										
										
											2023-09-10 03:07:05 +02:00
										 |  |  |             setup_pgo_tests(self.cmdline_args, self.pgo_extended) | 
					
						
							| 
									
										
										
										
											2019-07-22 12:54:25 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-03 23:37:15 +02:00
										 |  |  |         exclude_tests = set() | 
					
						
							| 
									
										
										
										
											2023-09-09 01:48:54 +02:00
										 |  |  |         if self.exclude: | 
					
						
							| 
									
										
										
										
											2023-09-10 03:07:05 +02:00
										 |  |  |             for arg in self.cmdline_args: | 
					
						
							| 
									
										
										
										
											2023-09-03 23:37:15 +02:00
										 |  |  |                 exclude_tests.add(arg) | 
					
						
							| 
									
										
										
										
											2023-09-10 03:07:05 +02:00
										 |  |  |             self.cmdline_args = [] | 
					
						
							| 
									
										
										
										
											2015-09-29 22:48:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-09 11:18:14 +02:00
										 |  |  |         alltests = findtests(testdir=self.test_dir, | 
					
						
							|  |  |  |                              exclude=exclude_tests) | 
					
						
							| 
									
										
										
										
											2015-09-29 22:48:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-09 01:48:54 +02:00
										 |  |  |         if not self.fromfile: | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |             selected = tests or self.cmdline_args | 
					
						
							|  |  |  |             if selected: | 
					
						
							|  |  |  |                 selected = split_test_packages(selected) | 
					
						
							| 
									
										
										
										
											2023-08-24 04:44:58 +02:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |                 selected = alltests | 
					
						
							| 
									
										
										
										
											2016-03-24 09:43:00 +01:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |             selected = tests | 
					
						
							| 
									
										
										
										
											2023-08-24 04:44:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-10 03:07:05 +02:00
										 |  |  |         if self.single_test_run: | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |             selected = selected[:1] | 
					
						
							| 
									
										
										
										
											2015-09-29 22:48:52 +02:00
										 |  |  |             try: | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |                 pos = alltests.index(selected[0]) | 
					
						
							| 
									
										
										
										
											2015-09-29 22:48:52 +02:00
										 |  |  |                 self.next_single_test = alltests[pos + 1] | 
					
						
							|  |  |  |             except IndexError: | 
					
						
							|  |  |  |                 pass | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-30 00:59:35 +02:00
										 |  |  |         # Remove all the selected tests that precede start if it's set. | 
					
						
							| 
									
										
										
										
											2023-09-09 01:48:54 +02:00
										 |  |  |         if self.starting_test: | 
					
						
							| 
									
										
										
										
											2015-09-29 22:48:52 +02:00
										 |  |  |             try: | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |                 del selected[:selected.index(self.starting_test)] | 
					
						
							| 
									
										
										
										
											2015-09-29 22:48:52 +02:00
										 |  |  |             except ValueError: | 
					
						
							| 
									
										
										
										
											2023-09-09 01:48:54 +02:00
										 |  |  |                 print(f"Cannot find starting test: {self.starting_test}") | 
					
						
							| 
									
										
										
										
											2023-09-03 23:37:15 +02:00
										 |  |  |                 sys.exit(1) | 
					
						
							| 
									
										
										
										
											2015-09-29 22:48:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-04 09:42:12 +03:00
										 |  |  |         random.seed(self.random_seed) | 
					
						
							| 
									
										
										
										
											2023-09-09 01:48:54 +02:00
										 |  |  |         if self.randomize: | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |             random.shuffle(selected) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return (tuple(selected), tests) | 
					
						
							| 
									
										
										
										
											2015-09-29 22:48:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-10 03:07:05 +02:00
										 |  |  |     @staticmethod | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |     def list_tests(tests: TestTuple): | 
					
						
							| 
									
										
										
										
											2023-09-10 03:07:05 +02:00
										 |  |  |         for name in tests: | 
					
						
							| 
									
										
										
										
											2015-10-03 00:21:12 +02:00
										 |  |  |             print(name) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-10 04:30:43 +02:00
										 |  |  |     def _rerun_failed_tests(self, runtests: RunTests): | 
					
						
							| 
									
										
										
										
											2023-09-03 23:37:15 +02:00
										 |  |  |         # Configure the runner to re-run tests | 
					
						
							| 
									
										
										
										
											2023-09-10 02:24:38 +02:00
										 |  |  |         if self.num_workers == 0: | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |             # Always run tests in fresh processes to have more deterministic | 
					
						
							|  |  |  |             # initial state. Don't re-run tests in parallel but limit to a | 
					
						
							|  |  |  |             # single worker process to have side effects (on the system load | 
					
						
							|  |  |  |             # and timings) between tests. | 
					
						
							| 
									
										
										
										
											2023-09-10 02:24:38 +02:00
										 |  |  |             self.num_workers = 1 | 
					
						
							| 
									
										
										
										
											2023-09-03 23:37:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-10 04:30:43 +02:00
										 |  |  |         tests, match_tests_dict = self.results.prepare_rerun() | 
					
						
							| 
									
										
										
										
											2023-09-03 23:37:15 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Re-run failed tests | 
					
						
							|  |  |  |         self.log(f"Re-running {len(tests)} failed tests in verbose mode in subprocesses") | 
					
						
							| 
									
										
										
										
											2023-09-09 03:37:48 +02:00
										 |  |  |         runtests = runtests.copy( | 
					
						
							| 
									
										
										
										
											2023-09-10 04:30:43 +02:00
										 |  |  |             tests=tests, | 
					
						
							| 
									
										
										
										
											2023-09-09 03:37:48 +02:00
										 |  |  |             rerun=True, | 
					
						
							| 
									
										
										
										
											2023-09-09 11:18:14 +02:00
										 |  |  |             verbose=True, | 
					
						
							| 
									
										
										
										
											2023-09-09 03:37:48 +02:00
										 |  |  |             forever=False, | 
					
						
							|  |  |  |             fail_fast=False, | 
					
						
							|  |  |  |             match_tests_dict=match_tests_dict, | 
					
						
							|  |  |  |             output_on_failure=False) | 
					
						
							| 
									
										
										
										
											2023-09-10 05:04:26 +02:00
										 |  |  |         self.logger.set_tests(runtests) | 
					
						
							| 
									
										
										
										
											2023-09-10 02:24:38 +02:00
										 |  |  |         self._run_tests_mp(runtests, self.num_workers) | 
					
						
							| 
									
										
										
										
											2023-09-09 03:37:48 +02:00
										 |  |  |         return runtests | 
					
						
							| 
									
										
										
										
											2022-06-21 14:42:32 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-10 04:30:43 +02:00
										 |  |  |     def rerun_failed_tests(self, runtests: RunTests): | 
					
						
							| 
									
										
										
										
											2023-09-10 01:41:21 +02:00
										 |  |  |         if self.python_cmd: | 
					
						
							| 
									
										
										
										
											2022-06-21 14:42:32 +02:00
										 |  |  |             # Temp patch for https://github.com/python/cpython/issues/94052 | 
					
						
							|  |  |  |             self.log( | 
					
						
							|  |  |  |                 "Re-running failed tests is not supported with --python " | 
					
						
							|  |  |  |                 "host runner option." | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-10 04:30:43 +02:00
										 |  |  |         self.first_state = self.get_state() | 
					
						
							| 
									
										
										
										
											2021-09-07 18:21:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-03 23:37:15 +02:00
										 |  |  |         print() | 
					
						
							| 
									
										
										
										
											2023-09-10 04:30:43 +02:00
										 |  |  |         rerun_runtests = self._rerun_failed_tests(runtests) | 
					
						
							| 
									
										
										
										
											2019-04-26 09:28:53 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-10 04:30:43 +02:00
										 |  |  |         if self.results.bad: | 
					
						
							|  |  |  |             print(count(len(self.results.bad), 'test'), "failed again:") | 
					
						
							|  |  |  |             printlist(self.results.bad) | 
					
						
							| 
									
										
										
										
											2015-09-30 02:32:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-09 03:37:48 +02:00
										 |  |  |         self.display_result(rerun_runtests) | 
					
						
							| 
									
										
										
										
											2018-05-28 21:03:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-09 03:37:48 +02:00
										 |  |  |     def display_result(self, runtests): | 
					
						
							| 
									
										
										
										
											2018-05-28 21:03:43 +02:00
										 |  |  |         # If running the test suite for PGO then no one cares about results. | 
					
						
							| 
									
										
										
										
											2023-09-10 04:30:43 +02:00
										 |  |  |         if runtests.pgo: | 
					
						
							| 
									
										
										
										
											2018-05-28 21:03:43 +02:00
										 |  |  |             return | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-10 04:30:43 +02:00
										 |  |  |         state = self.get_state() | 
					
						
							| 
									
										
										
										
											2018-05-28 21:03:43 +02:00
										 |  |  |         print() | 
					
						
							| 
									
										
										
										
											2023-09-10 04:30:43 +02:00
										 |  |  |         print(f"== Tests result: {state} ==") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |         self.results.display_result(runtests.tests, | 
					
						
							|  |  |  |                                     self.quiet, self.print_slowest) | 
					
						
							| 
									
										
										
										
											2018-11-29 17:17:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-10 18:17:45 +01:00
										 |  |  |     def run_test( | 
					
						
							|  |  |  |         self, test_name: TestName, runtests: RunTests, tracer: trace.Trace | None | 
					
						
							|  |  |  |     ) -> TestResult: | 
					
						
							| 
									
										
										
										
											2023-09-09 02:30:28 +02:00
										 |  |  |         if tracer is not None: | 
					
						
							| 
									
										
										
										
											2023-09-03 23:37:15 +02:00
										 |  |  |             # If we're tracing code coverage, then we don't exit with status | 
					
						
							|  |  |  |             # if on a false return value from main. | 
					
						
							| 
									
										
										
										
											2023-09-10 01:41:21 +02:00
										 |  |  |             cmd = ('result = run_single_test(test_name, runtests)') | 
					
						
							| 
									
										
										
										
											2023-09-10 03:07:05 +02:00
										 |  |  |             namespace = dict(locals()) | 
					
						
							|  |  |  |             tracer.runctx(cmd, globals=globals(), locals=namespace) | 
					
						
							|  |  |  |             result = namespace['result'] | 
					
						
							| 
									
										
										
										
											2023-11-30 23:00:14 +00:00
										 |  |  |             # Mypy doesn't know about this attribute yet, | 
					
						
							|  |  |  |             # but it will do soon: https://github.com/python/typeshed/pull/11091 | 
					
						
							|  |  |  |             result.covered_lines = list(tracer.counts)  # type: ignore[attr-defined] | 
					
						
							| 
									
										
										
										
											2023-09-03 23:37:15 +02:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2023-09-10 01:41:21 +02:00
										 |  |  |             result = run_single_test(test_name, runtests) | 
					
						
							| 
									
										
										
										
											2023-09-03 23:37:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-10 04:30:43 +02:00
										 |  |  |         self.results.accumulate_result(result, runtests) | 
					
						
							| 
									
										
										
										
											2023-09-03 23:37:15 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return result | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-10 18:17:45 +01:00
										 |  |  |     def run_tests_sequentially(self, runtests) -> None: | 
					
						
							| 
									
										
										
										
											2023-09-10 03:07:05 +02:00
										 |  |  |         if self.coverage: | 
					
						
							| 
									
										
										
										
											2023-09-09 02:30:28 +02:00
										 |  |  |             tracer = trace.Trace(trace=False, count=True) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             tracer = None | 
					
						
							| 
									
										
										
										
											2015-09-30 00:59:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-04 17:43:27 +02:00
										 |  |  |         save_modules = set(sys.modules) | 
					
						
							| 
									
										
										
										
											2015-09-29 22:48:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-12 05:47:04 +02:00
										 |  |  |         jobs = runtests.get_jobs() | 
					
						
							|  |  |  |         if jobs is not None: | 
					
						
							| 
									
										
										
										
											2023-09-12 15:13:29 +02:00
										 |  |  |             tests = count(jobs, 'test') | 
					
						
							| 
									
										
										
										
											2023-09-12 05:47:04 +02:00
										 |  |  |         else: | 
					
						
							|  |  |  |             tests = 'tests' | 
					
						
							|  |  |  |         msg = f"Run {tests} sequentially" | 
					
						
							| 
									
										
										
										
											2023-09-09 03:37:48 +02:00
										 |  |  |         if runtests.timeout: | 
					
						
							|  |  |  |             msg += " (timeout: %s)" % format_duration(runtests.timeout) | 
					
						
							| 
									
										
										
										
											2020-04-14 18:29:44 +02:00
										 |  |  |         self.log(msg) | 
					
						
							| 
									
										
										
										
											2016-03-24 11:55:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-23 12:14:10 +01:00
										 |  |  |         previous_test = None | 
					
						
							| 
									
										
										
										
											2023-09-03 23:37:15 +02:00
										 |  |  |         tests_iter = runtests.iter_tests() | 
					
						
							|  |  |  |         for test_index, test_name in enumerate(tests_iter, 1): | 
					
						
							| 
									
										
										
										
											2023-09-02 18:09:36 +02:00
										 |  |  |             start_time = time.perf_counter() | 
					
						
							| 
									
										
										
										
											2016-03-23 12:14:10 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-09 02:30:28 +02:00
										 |  |  |             text = test_name | 
					
						
							|  |  |  |             if previous_test: | 
					
						
							|  |  |  |                 text = '%s -- %s' % (text, previous_test) | 
					
						
							| 
									
										
										
										
											2023-09-11 03:46:26 +02:00
										 |  |  |             self.logger.display_progress(test_index, text) | 
					
						
							| 
									
										
										
										
											2023-09-09 02:30:28 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             result = self.run_test(test_name, runtests, tracer) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-04 17:43:27 +02:00
										 |  |  |             # Unload the newly imported test modules (best effort finalization) | 
					
						
							|  |  |  |             new_modules = [module for module in sys.modules | 
					
						
							|  |  |  |                            if module not in save_modules and | 
					
						
							|  |  |  |                                 module.startswith(("test.", "test_"))] | 
					
						
							|  |  |  |             for module in new_modules: | 
					
						
							|  |  |  |                 sys.modules.pop(module, None) | 
					
						
							|  |  |  |                 # Remove the attribute of the parent module. | 
					
						
							|  |  |  |                 parent, _, name = module.rpartition('.') | 
					
						
							|  |  |  |                 try: | 
					
						
							|  |  |  |                     delattr(sys.modules[parent], name) | 
					
						
							|  |  |  |                 except (KeyError, AttributeError): | 
					
						
							|  |  |  |                     pass | 
					
						
							| 
									
										
										
										
											2019-04-26 04:08:53 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-10 02:24:38 +02:00
										 |  |  |             if result.must_stop(self.fail_fast, self.fail_env_changed): | 
					
						
							| 
									
										
										
										
											2019-04-26 04:08:53 +02:00
										 |  |  |                 break | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-22 20:25:58 +02:00
										 |  |  |             previous_test = str(result) | 
					
						
							| 
									
										
										
										
											2023-09-02 18:09:36 +02:00
										 |  |  |             test_time = time.perf_counter() - start_time | 
					
						
							| 
									
										
										
										
											2016-03-23 12:14:10 +01:00
										 |  |  |             if test_time >= PROGRESS_MIN_TIME: | 
					
						
							| 
									
										
										
										
											2016-08-17 12:22:52 +02:00
										 |  |  |                 previous_test = "%s in %s" % (previous_test, format_duration(test_time)) | 
					
						
							| 
									
										
										
										
											2023-09-02 18:09:36 +02:00
										 |  |  |             elif result.state == State.PASSED: | 
					
						
							| 
									
										
										
										
											2016-05-20 13:37:40 +02:00
										 |  |  |                 # be quiet: say nothing if the test passed shortly | 
					
						
							| 
									
										
										
										
											2016-03-23 12:14:10 +01:00
										 |  |  |                 previous_test = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if previous_test: | 
					
						
							|  |  |  |             print(previous_test) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-10 04:30:43 +02:00
										 |  |  |     def get_state(self): | 
					
						
							|  |  |  |         state = self.results.get_state(self.fail_env_changed) | 
					
						
							| 
									
										
										
										
											2023-09-03 23:37:15 +02:00
										 |  |  |         if self.first_state: | 
					
						
							| 
									
										
										
										
											2023-09-10 04:30:43 +02:00
										 |  |  |             state = f'{self.first_state} then {state}' | 
					
						
							|  |  |  |         return state | 
					
						
							| 
									
										
										
										
											2018-06-01 00:48:57 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-10 02:24:38 +02:00
										 |  |  |     def _run_tests_mp(self, runtests: RunTests, num_workers: int) -> None: | 
					
						
							| 
									
										
										
										
											2023-09-11 09:02:35 +02:00
										 |  |  |         from .run_workers import RunWorkers | 
					
						
							| 
									
										
										
										
											2023-09-11 03:46:26 +02:00
										 |  |  |         RunWorkers(num_workers, runtests, self.logger, self.results).run() | 
					
						
							| 
									
										
										
										
											2015-09-26 10:38:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-10 18:17:45 +01:00
										 |  |  |     def finalize_tests(self, coverage: trace.CoverageResults | None) -> None: | 
					
						
							| 
									
										
										
										
											2015-09-29 22:48:52 +02:00
										 |  |  |         if self.next_single_filename: | 
					
						
							|  |  |  |             if self.next_single_test: | 
					
						
							|  |  |  |                 with open(self.next_single_filename, 'w') as fp: | 
					
						
							|  |  |  |                     fp.write(self.next_single_test + '\n') | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 os.unlink(self.next_single_filename) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-10 18:17:45 +01:00
										 |  |  |         if coverage is not None: | 
					
						
							| 
									
										
										
										
											2023-11-30 23:00:14 +00:00
										 |  |  |             # uses a new-in-Python 3.13 keyword argument that mypy doesn't know about yet: | 
					
						
							|  |  |  |             coverage.write_results(show_missing=True, summary=True,  # type: ignore[call-arg] | 
					
						
							| 
									
										
										
										
											2023-11-10 18:17:45 +01:00
										 |  |  |                                    coverdir=self.coverage_dir, | 
					
						
							|  |  |  |                                    ignore_missing_files=True) | 
					
						
							| 
									
										
										
										
											2015-09-29 22:48:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-10 03:07:05 +02:00
										 |  |  |         if self.want_run_leaks: | 
					
						
							| 
									
										
										
										
											2015-09-29 22:48:52 +02:00
										 |  |  |             os.system("leaks %d" % os.getpid()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-10 04:30:43 +02:00
										 |  |  |         if self.junit_filename: | 
					
						
							|  |  |  |             self.results.write_junit(self.junit_filename) | 
					
						
							| 
									
										
										
										
											2023-09-03 23:37:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-02 18:09:36 +02:00
										 |  |  |     def display_summary(self): | 
					
						
							| 
									
										
										
										
											2023-09-10 05:04:26 +02:00
										 |  |  |         duration = time.perf_counter() - self.logger.start_time | 
					
						
							| 
									
										
										
										
											2023-10-21 17:44:46 +03:00
										 |  |  |         filtered = bool(self.match_tests) | 
					
						
							| 
									
										
										
										
											2023-09-02 18:09:36 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Total duration | 
					
						
							| 
									
										
										
										
											2023-09-03 23:37:15 +02:00
										 |  |  |         print() | 
					
						
							| 
									
										
										
										
											2023-09-02 18:09:36 +02:00
										 |  |  |         print("Total duration: %s" % format_duration(duration)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-10 04:30:43 +02:00
										 |  |  |         self.results.display_summary(self.first_runtests, filtered) | 
					
						
							| 
									
										
										
										
											2023-09-02 18:09:36 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Result | 
					
						
							| 
									
										
										
										
											2023-09-10 04:30:43 +02:00
										 |  |  |         state = self.get_state() | 
					
						
							|  |  |  |         print(f"Result: {state}") | 
					
						
							| 
									
										
										
										
											2018-09-18 09:10:26 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |     def create_run_tests(self, tests: TestTuple): | 
					
						
							| 
									
										
										
										
											2023-09-10 05:04:26 +02:00
										 |  |  |         return RunTests( | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |             tests, | 
					
						
							| 
									
										
										
										
											2023-09-09 03:37:48 +02:00
										 |  |  |             fail_fast=self.fail_fast, | 
					
						
							| 
									
										
										
										
											2023-09-12 05:35:08 +02:00
										 |  |  |             fail_env_changed=self.fail_env_changed, | 
					
						
							| 
									
										
										
										
											2023-09-09 03:37:48 +02:00
										 |  |  |             match_tests=self.match_tests, | 
					
						
							| 
									
										
										
										
											2023-09-12 05:35:08 +02:00
										 |  |  |             match_tests_dict=None, | 
					
						
							| 
									
										
										
										
											2023-09-14 19:33:18 +01:00
										 |  |  |             rerun=False, | 
					
						
							| 
									
										
										
										
											2023-09-09 03:37:48 +02:00
										 |  |  |             forever=self.forever, | 
					
						
							|  |  |  |             pgo=self.pgo, | 
					
						
							|  |  |  |             pgo_extended=self.pgo_extended, | 
					
						
							|  |  |  |             output_on_failure=self.output_on_failure, | 
					
						
							| 
									
										
										
										
											2023-09-09 11:18:14 +02:00
										 |  |  |             timeout=self.timeout, | 
					
						
							|  |  |  |             verbose=self.verbose, | 
					
						
							|  |  |  |             quiet=self.quiet, | 
					
						
							|  |  |  |             hunt_refleak=self.hunt_refleak, | 
					
						
							|  |  |  |             test_dir=self.test_dir, | 
					
						
							| 
									
										
										
										
											2023-09-11 01:11:22 +02:00
										 |  |  |             use_junit=(self.junit_filename is not None), | 
					
						
							| 
									
										
										
										
											2023-11-10 18:17:45 +01:00
										 |  |  |             coverage=self.coverage, | 
					
						
							| 
									
										
										
										
											2023-09-10 01:41:21 +02:00
										 |  |  |             memory_limit=self.memory_limit, | 
					
						
							|  |  |  |             gc_threshold=self.gc_threshold, | 
					
						
							|  |  |  |             use_resources=self.use_resources, | 
					
						
							|  |  |  |             python_cmd=self.python_cmd, | 
					
						
							| 
									
										
										
										
											2023-09-12 05:35:08 +02:00
										 |  |  |             randomize=self.randomize, | 
					
						
							|  |  |  |             random_seed=self.random_seed, | 
					
						
							| 
									
										
										
										
											2023-09-10 01:41:21 +02:00
										 |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |     def _run_tests(self, selected: TestTuple, tests: TestList | None) -> int: | 
					
						
							| 
									
										
										
										
											2023-09-10 05:04:26 +02:00
										 |  |  |         if self.hunt_refleak and self.hunt_refleak.warmups < 3: | 
					
						
							|  |  |  |             msg = ("WARNING: Running tests with --huntrleaks/-R and " | 
					
						
							|  |  |  |                    "less than 3 warmup repetitions can give false positives!") | 
					
						
							|  |  |  |             print(msg, file=sys.stdout, flush=True) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if self.num_workers < 0: | 
					
						
							|  |  |  |             # Use all CPUs + 2 extra worker processes for tests | 
					
						
							|  |  |  |             # that like to sleep | 
					
						
							| 
									
										
										
										
											2023-11-30 23:00:14 +00:00
										 |  |  |             # | 
					
						
							|  |  |  |             # os.process.cpu_count() is new in Python 3.13; | 
					
						
							|  |  |  |             # mypy doesn't know about it yet | 
					
						
							|  |  |  |             self.num_workers = (os.process_cpu_count() or 1) + 2  # type: ignore[attr-defined] | 
					
						
							| 
									
										
										
										
											2023-09-10 05:04:26 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # For a partial run, we do not need to clutter the output. | 
					
						
							|  |  |  |         if (self.want_header | 
					
						
							|  |  |  |             or not(self.pgo or self.quiet or self.single_test_run | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |                    or tests or self.cmdline_args)): | 
					
						
							| 
									
										
										
										
											2023-10-01 00:37:23 +02:00
										 |  |  |             display_header(self.use_resources, self.python_cmd) | 
					
						
							| 
									
										
										
										
											2023-09-10 05:04:26 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-21 10:37:48 +02:00
										 |  |  |         print("Using random seed:", self.random_seed) | 
					
						
							| 
									
										
										
										
											2023-09-10 05:04:26 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |         runtests = self.create_run_tests(selected) | 
					
						
							| 
									
										
										
										
											2023-09-10 05:04:26 +02:00
										 |  |  |         self.first_runtests = runtests | 
					
						
							|  |  |  |         self.logger.set_tests(runtests) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |         setup_process() | 
					
						
							| 
									
										
										
										
											2023-09-09 03:37:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-26 03:05:07 +02:00
										 |  |  |         if self.hunt_refleak and not self.num_workers: | 
					
						
							|  |  |  |             # gh-109739: WindowsLoadTracker thread interfers with refleak check | 
					
						
							|  |  |  |             use_load_tracker = False | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             # WindowsLoadTracker is only needed on Windows | 
					
						
							|  |  |  |             use_load_tracker = MS_WINDOWS | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if use_load_tracker: | 
					
						
							|  |  |  |             self.logger.start_load_tracker() | 
					
						
							| 
									
										
										
										
											2023-09-10 05:04:26 +02:00
										 |  |  |         try: | 
					
						
							|  |  |  |             if self.num_workers: | 
					
						
							|  |  |  |                 self._run_tests_mp(runtests, self.num_workers) | 
					
						
							|  |  |  |             else: | 
					
						
							| 
									
										
										
										
											2023-11-10 18:17:45 +01:00
										 |  |  |                 self.run_tests_sequentially(runtests) | 
					
						
							| 
									
										
										
										
											2023-09-03 23:37:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-10 18:17:45 +01:00
										 |  |  |             coverage = self.results.get_coverage_results() | 
					
						
							| 
									
										
										
										
											2023-09-10 05:04:26 +02:00
										 |  |  |             self.display_result(runtests) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if self.want_rerun and self.results.need_rerun(): | 
					
						
							|  |  |  |                 self.rerun_failed_tests(runtests) | 
					
						
							|  |  |  |         finally: | 
					
						
							| 
									
										
										
										
											2023-09-26 03:05:07 +02:00
										 |  |  |             if use_load_tracker: | 
					
						
							|  |  |  |                 self.logger.stop_load_tracker() | 
					
						
							| 
									
										
										
										
											2023-09-03 23:37:15 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         self.display_summary() | 
					
						
							| 
									
										
										
										
											2023-11-10 18:17:45 +01:00
										 |  |  |         self.finalize_tests(coverage) | 
					
						
							| 
									
										
										
										
											2023-09-03 23:37:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-10 05:04:26 +02:00
										 |  |  |         return self.results.get_exitcode(self.fail_env_changed, | 
					
						
							|  |  |  |                                          self.fail_rerun) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |     def run_tests(self, selected: TestTuple, tests: TestList | None) -> int: | 
					
						
							|  |  |  |         os.makedirs(self.tmp_dir, exist_ok=True) | 
					
						
							| 
									
										
										
										
											2023-09-12 15:13:29 +02:00
										 |  |  |         work_dir = get_work_dir(self.tmp_dir) | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Put a timeout on Python exit | 
					
						
							|  |  |  |         with exit_timeout(): | 
					
						
							|  |  |  |             # Run the tests in a context manager that temporarily changes the | 
					
						
							|  |  |  |             # CWD to a temporary and writable directory. If it's not possible | 
					
						
							|  |  |  |             # to create or change the CWD, the original CWD will be used. | 
					
						
							|  |  |  |             # The original CWD is available from os_helper.SAVEDCWD. | 
					
						
							|  |  |  |             with os_helper.temp_cwd(work_dir, quiet=True): | 
					
						
							|  |  |  |                 # When using multiprocessing, worker processes will use | 
					
						
							|  |  |  |                 # work_dir as their parent temporary directory. So when the | 
					
						
							|  |  |  |                 # main process exit, it removes also subdirectories of worker | 
					
						
							|  |  |  |                 # processes. | 
					
						
							|  |  |  |                 return self._run_tests(selected, tests) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-01 00:37:23 +02:00
										 |  |  |     def _add_cross_compile_opts(self, regrtest_opts): | 
					
						
							|  |  |  |         # WASM/WASI buildbot builders pass multiple PYTHON environment | 
					
						
							|  |  |  |         # variables such as PYTHONPATH and _PYTHON_HOSTRUNNER. | 
					
						
							|  |  |  |         keep_environ = bool(self.python_cmd) | 
					
						
							|  |  |  |         environ = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Are we using cross-compilation? | 
					
						
							|  |  |  |         cross_compile = is_cross_compiled() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Get HOSTRUNNER | 
					
						
							|  |  |  |         hostrunner = get_host_runner() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if cross_compile: | 
					
						
							|  |  |  |             # emulate -E, but keep PYTHONPATH + cross compile env vars, | 
					
						
							|  |  |  |             # so test executable can load correct sysconfigdata file. | 
					
						
							|  |  |  |             keep = { | 
					
						
							|  |  |  |                 '_PYTHON_PROJECT_BASE', | 
					
						
							|  |  |  |                 '_PYTHON_HOST_PLATFORM', | 
					
						
							|  |  |  |                 '_PYTHON_SYSCONFIGDATA_NAME', | 
					
						
							|  |  |  |                 'PYTHONPATH' | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             old_environ = os.environ | 
					
						
							|  |  |  |             new_environ = { | 
					
						
							|  |  |  |                 name: value for name, value in os.environ.items() | 
					
						
							|  |  |  |                 if not name.startswith(('PYTHON', '_PYTHON')) or name in keep | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             # Only set environ if at least one variable was removed | 
					
						
							|  |  |  |             if new_environ != old_environ: | 
					
						
							|  |  |  |                 environ = new_environ | 
					
						
							|  |  |  |             keep_environ = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if cross_compile and hostrunner: | 
					
						
							|  |  |  |             if self.num_workers == 0: | 
					
						
							|  |  |  |                 # For now use only two cores for cross-compiled builds; | 
					
						
							|  |  |  |                 # hostrunner can be expensive. | 
					
						
							|  |  |  |                 regrtest_opts.extend(['-j', '2']) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # If HOSTRUNNER is set and -p/--python option is not given, then | 
					
						
							|  |  |  |             # use hostrunner to execute python binary for tests. | 
					
						
							|  |  |  |             if not self.python_cmd: | 
					
						
							|  |  |  |                 buildpython = sysconfig.get_config_var("BUILDPYTHON") | 
					
						
							|  |  |  |                 python_cmd = f"{hostrunner} {buildpython}" | 
					
						
							|  |  |  |                 regrtest_opts.extend(["--python", python_cmd]) | 
					
						
							|  |  |  |                 keep_environ = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return (environ, keep_environ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _add_ci_python_opts(self, python_opts, keep_environ): | 
					
						
							|  |  |  |         # --fast-ci and --slow-ci add options to Python: | 
					
						
							|  |  |  |         # "-u -W default -bb -E" | 
					
						
							| 
									
										
										
										
											2023-09-26 23:59:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Unbuffered stdout and stderr | 
					
						
							|  |  |  |         if not sys.stdout.write_through: | 
					
						
							|  |  |  |             python_opts.append('-u') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Add warnings filter 'default' | 
					
						
							|  |  |  |         if 'default' not in sys.warnoptions: | 
					
						
							|  |  |  |             python_opts.extend(('-W', 'default')) | 
					
						
							| 
									
										
										
										
											2023-09-26 20:46:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-26 23:59:11 +02:00
										 |  |  |         # Error on bytes/str comparison | 
					
						
							|  |  |  |         if sys.flags.bytes_warning < 2: | 
					
						
							|  |  |  |             python_opts.append('-bb') | 
					
						
							| 
									
										
										
										
											2023-09-26 20:46:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-01 00:37:23 +02:00
										 |  |  |         if not keep_environ: | 
					
						
							| 
									
										
										
										
											2023-09-27 12:01:16 +02:00
										 |  |  |             # Ignore PYTHON* environment variables | 
					
						
							|  |  |  |             if not sys.flags.ignore_environment: | 
					
						
							|  |  |  |                 python_opts.append('-E') | 
					
						
							| 
									
										
										
										
											2023-09-26 23:59:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-01 00:37:23 +02:00
										 |  |  |     def _execute_python(self, cmd, environ): | 
					
						
							| 
									
										
										
										
											2023-09-26 20:46:52 +02:00
										 |  |  |         # Make sure that messages before execv() are logged | 
					
						
							|  |  |  |         sys.stdout.flush() | 
					
						
							|  |  |  |         sys.stderr.flush() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-26 23:59:11 +02:00
										 |  |  |         cmd_text = shlex.join(cmd) | 
					
						
							| 
									
										
										
										
											2023-09-26 20:46:52 +02:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2023-10-01 00:37:23 +02:00
										 |  |  |             print(f"+ {cmd_text}", flush=True) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-26 23:59:11 +02:00
										 |  |  |             if hasattr(os, 'execv') and not MS_WINDOWS: | 
					
						
							|  |  |  |                 os.execv(cmd[0], cmd) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:51:22 +02:00
										 |  |  |                 # On success, execv() do no return. | 
					
						
							|  |  |  |                 # On error, it raises an OSError. | 
					
						
							| 
									
										
										
										
											2023-09-26 23:59:11 +02:00
										 |  |  |             else: | 
					
						
							|  |  |  |                 import subprocess | 
					
						
							| 
									
										
										
										
											2023-10-01 00:37:23 +02:00
										 |  |  |                 with subprocess.Popen(cmd, env=environ) as proc: | 
					
						
							| 
									
										
										
										
											2023-09-29 02:51:22 +02:00
										 |  |  |                     try: | 
					
						
							|  |  |  |                         proc.wait() | 
					
						
							|  |  |  |                     except KeyboardInterrupt: | 
					
						
							|  |  |  |                         # There is no need to call proc.terminate(): on CTRL+C, | 
					
						
							|  |  |  |                         # SIGTERM is also sent to the child process. | 
					
						
							|  |  |  |                         try: | 
					
						
							|  |  |  |                             proc.wait(timeout=EXIT_TIMEOUT) | 
					
						
							|  |  |  |                         except subprocess.TimeoutExpired: | 
					
						
							|  |  |  |                             proc.kill() | 
					
						
							|  |  |  |                             proc.wait() | 
					
						
							|  |  |  |                             sys.exit(EXITCODE_INTERRUPTED) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-26 23:59:11 +02:00
										 |  |  |                 sys.exit(proc.returncode) | 
					
						
							|  |  |  |         except Exception as exc: | 
					
						
							|  |  |  |             print_warning(f"Failed to change Python options: {exc!r}\n" | 
					
						
							| 
									
										
										
										
											2023-09-26 20:46:52 +02:00
										 |  |  |                           f"Command: {cmd_text}") | 
					
						
							| 
									
										
										
										
											2023-09-26 23:59:11 +02:00
										 |  |  |             # continue executing main() | 
					
						
							| 
									
										
										
										
											2023-09-26 20:46:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-01 00:37:23 +02:00
										 |  |  |     def _add_python_opts(self): | 
					
						
							|  |  |  |         python_opts = [] | 
					
						
							|  |  |  |         regrtest_opts = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         environ, keep_environ = self._add_cross_compile_opts(regrtest_opts) | 
					
						
							|  |  |  |         if self.ci_mode: | 
					
						
							|  |  |  |             self._add_ci_python_opts(python_opts, keep_environ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (not python_opts) and (not regrtest_opts) and (environ is None): | 
					
						
							|  |  |  |             # Nothing changed: nothing to do | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Create new command line | 
					
						
							|  |  |  |         cmd = list(sys.orig_argv) | 
					
						
							|  |  |  |         if python_opts: | 
					
						
							|  |  |  |             cmd[1:1] = python_opts | 
					
						
							|  |  |  |         if regrtest_opts: | 
					
						
							|  |  |  |             cmd.extend(regrtest_opts) | 
					
						
							|  |  |  |         cmd.append("--dont-add-python-opts") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self._execute_python(cmd, environ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-26 21:34:50 +02:00
										 |  |  |     def _init(self): | 
					
						
							|  |  |  |         # Set sys.stdout encoder error handler to backslashreplace, | 
					
						
							|  |  |  |         # similar to sys.stderr error handler, to avoid UnicodeEncodeError | 
					
						
							|  |  |  |         # when printing a traceback or any other non-encodable character. | 
					
						
							|  |  |  |         sys.stdout.reconfigure(errors="backslashreplace") | 
					
						
							| 
									
										
										
										
											2023-09-26 20:46:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |         if self.junit_filename and not os.path.isabs(self.junit_filename): | 
					
						
							|  |  |  |             self.junit_filename = os.path.abspath(self.junit_filename) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         strip_py_suffix(self.cmdline_args) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.tmp_dir = get_temp_dir(self.tmp_dir) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-26 21:34:50 +02:00
										 |  |  |     def main(self, tests: TestList | None = None): | 
					
						
							| 
									
										
										
										
											2023-09-26 23:59:11 +02:00
										 |  |  |         if self.want_add_python_opts: | 
					
						
							|  |  |  |             self._add_python_opts() | 
					
						
							| 
									
										
										
										
											2023-09-26 21:34:50 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         self._init() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |         if self.want_cleanup: | 
					
						
							| 
									
										
										
										
											2023-09-11 10:52:03 +02:00
										 |  |  |             cleanup_temp_dir(self.tmp_dir) | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |             sys.exit(0) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-09 01:48:54 +02:00
										 |  |  |         if self.want_wait: | 
					
						
							| 
									
										
										
										
											2015-09-30 00:59:35 +02:00
										 |  |  |             input("Press any key to continue...") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-09 11:18:14 +02:00
										 |  |  |         setup_test_dir(self.test_dir) | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |         selected, tests = self.find_tests(tests) | 
					
						
							| 
									
										
										
										
											2015-09-30 00:59:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-03 23:37:15 +02:00
										 |  |  |         exitcode = 0 | 
					
						
							| 
									
										
										
										
											2023-09-09 01:48:54 +02:00
										 |  |  |         if self.want_list_tests: | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |             self.list_tests(selected) | 
					
						
							| 
									
										
										
										
											2023-09-09 01:48:54 +02:00
										 |  |  |         elif self.want_list_cases: | 
					
						
							| 
									
										
										
										
											2023-09-11 10:52:03 +02:00
										 |  |  |             list_cases(selected, | 
					
						
							|  |  |  |                        match_tests=self.match_tests, | 
					
						
							|  |  |  |                        test_dir=self.test_dir) | 
					
						
							| 
									
										
										
										
											2023-09-03 23:37:15 +02:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2023-09-11 05:27:37 +02:00
										 |  |  |             exitcode = self.run_tests(selected, tests) | 
					
						
							| 
									
										
										
										
											2018-09-18 09:10:26 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-03 23:37:15 +02:00
										 |  |  |         sys.exit(exitcode) | 
					
						
							| 
									
										
										
										
											2015-09-26 10:38:01 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-26 23:59:11 +02:00
										 |  |  | def main(tests=None, _add_python_opts=False, **kwargs): | 
					
						
							| 
									
										
										
										
											2016-03-24 17:53:20 +01:00
										 |  |  |     """Run the Python suite.""" | 
					
						
							| 
									
										
										
										
											2023-09-09 00:41:26 +02:00
										 |  |  |     ns = _parse_args(sys.argv[1:], **kwargs) | 
					
						
							| 
									
										
										
										
											2023-09-26 23:59:11 +02:00
										 |  |  |     Regrtest(ns, _add_python_opts=_add_python_opts).main(tests=tests) |