mirror of
https://github.com/python/cpython.git
synced 2025-10-17 20:28:43 +00:00
gh-109162: Regrtest copies 'ns' attributes (#109168)
* Regrtest.__init__() now copies 'ns' namespace attributes to Regrtest attributes. Regrtest match_tests and ignore_tests attributes have type FilterTuple (tuple), instead of a list. * Add RunTests.copy(). Regrtest._rerun_failed_tests() now uses RunTests.copy(). * Replace Regrtest.all_tests (list) with Regrtest.first_runtests (RunTests). * Make random_seed maximum 10x larger (9 digits, instead of 8).
This commit is contained in:
parent
5b7303e265
commit
ac8409b38b
3 changed files with 73 additions and 48 deletions
|
@ -12,7 +12,8 @@ import unittest
|
||||||
from test.libregrtest.cmdline import _parse_args, Namespace
|
from test.libregrtest.cmdline import _parse_args, Namespace
|
||||||
from test.libregrtest.runtest import (
|
from test.libregrtest.runtest import (
|
||||||
findtests, split_test_packages, runtest, abs_module_name,
|
findtests, split_test_packages, runtest, abs_module_name,
|
||||||
PROGRESS_MIN_TIME, State, FilterDict, RunTests, TestResult, TestList)
|
PROGRESS_MIN_TIME, State, RunTests, TestResult,
|
||||||
|
FilterTuple, FilterDict, TestList)
|
||||||
from test.libregrtest.setup import setup_tests
|
from test.libregrtest.setup import setup_tests
|
||||||
from test.libregrtest.pgo import setup_pgo_tests
|
from test.libregrtest.pgo import setup_pgo_tests
|
||||||
from test.libregrtest.utils import (strip_py_suffix, count, format_duration,
|
from test.libregrtest.utils import (strip_py_suffix, count, format_duration,
|
||||||
|
@ -62,10 +63,35 @@ class Regrtest:
|
||||||
# Namespace of command line options
|
# Namespace of command line options
|
||||||
self.ns: Namespace = ns
|
self.ns: Namespace = ns
|
||||||
|
|
||||||
|
# Actions
|
||||||
|
self.want_header = ns.header
|
||||||
|
self.want_list_tests = ns.list_tests
|
||||||
|
self.want_list_cases = ns.list_cases
|
||||||
|
self.want_wait = ns.wait
|
||||||
|
self.want_cleanup = ns.cleanup
|
||||||
|
|
||||||
|
# Select tests
|
||||||
|
if ns.match_tests:
|
||||||
|
self.match_tests: FilterTuple = tuple(ns.match_tests)
|
||||||
|
else:
|
||||||
|
self.match_tests = None
|
||||||
|
if ns.ignore_tests:
|
||||||
|
self.ignore_tests: FilterTuple = tuple(ns.ignore_tests)
|
||||||
|
else:
|
||||||
|
self.ignore_tests = None
|
||||||
|
self.exclude = ns.exclude
|
||||||
|
self.fromfile = ns.fromfile
|
||||||
|
self.starting_test = ns.start
|
||||||
|
|
||||||
|
# Options to run tests
|
||||||
|
self.forever = ns.forever
|
||||||
|
self.randomize = ns.randomize
|
||||||
|
self.random_seed = ns.random_seed
|
||||||
|
|
||||||
# tests
|
# tests
|
||||||
self.tests = []
|
self.tests = []
|
||||||
self.selected = []
|
self.selected = []
|
||||||
self.all_runtests: list[RunTests] = []
|
self.first_runtests: RunTests | None = None
|
||||||
|
|
||||||
# test results
|
# test results
|
||||||
self.good: TestList = []
|
self.good: TestList = []
|
||||||
|
@ -187,12 +213,8 @@ class Regrtest:
|
||||||
def find_tests(self):
|
def find_tests(self):
|
||||||
ns = self.ns
|
ns = self.ns
|
||||||
single = ns.single
|
single = ns.single
|
||||||
fromfile = ns.fromfile
|
|
||||||
pgo = ns.pgo
|
pgo = ns.pgo
|
||||||
exclude = ns.exclude
|
|
||||||
test_dir = ns.testdir
|
test_dir = ns.testdir
|
||||||
starting_test = ns.start
|
|
||||||
randomize = ns.randomize
|
|
||||||
|
|
||||||
if single:
|
if single:
|
||||||
self.next_single_filename = os.path.join(self.tmp_dir, 'pynexttest')
|
self.next_single_filename = os.path.join(self.tmp_dir, 'pynexttest')
|
||||||
|
@ -203,12 +225,12 @@ class Regrtest:
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if fromfile:
|
if self.fromfile:
|
||||||
self.tests = []
|
self.tests = []
|
||||||
# regex to match 'test_builtin' in line:
|
# regex to match 'test_builtin' in line:
|
||||||
# '0:00:00 [ 4/400] test_builtin -- test_dict took 1 sec'
|
# '0:00:00 [ 4/400] test_builtin -- test_dict took 1 sec'
|
||||||
regex = re.compile(r'\btest_[a-zA-Z0-9_]+\b')
|
regex = re.compile(r'\btest_[a-zA-Z0-9_]+\b')
|
||||||
with open(os.path.join(os_helper.SAVEDCWD, fromfile)) as fp:
|
with open(os.path.join(os_helper.SAVEDCWD, self.fromfile)) as fp:
|
||||||
for line in fp:
|
for line in fp:
|
||||||
line = line.split('#', 1)[0]
|
line = line.split('#', 1)[0]
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
|
@ -223,14 +245,14 @@ class Regrtest:
|
||||||
setup_pgo_tests(ns)
|
setup_pgo_tests(ns)
|
||||||
|
|
||||||
exclude_tests = set()
|
exclude_tests = set()
|
||||||
if exclude:
|
if self.exclude:
|
||||||
for arg in ns.args:
|
for arg in ns.args:
|
||||||
exclude_tests.add(arg)
|
exclude_tests.add(arg)
|
||||||
ns.args = []
|
ns.args = []
|
||||||
|
|
||||||
alltests = findtests(testdir=test_dir, exclude=exclude_tests)
|
alltests = findtests(testdir=test_dir, exclude=exclude_tests)
|
||||||
|
|
||||||
if not fromfile:
|
if not self.fromfile:
|
||||||
self.selected = self.tests or ns.args
|
self.selected = self.tests or ns.args
|
||||||
if self.selected:
|
if self.selected:
|
||||||
self.selected = split_test_packages(self.selected)
|
self.selected = split_test_packages(self.selected)
|
||||||
|
@ -248,17 +270,17 @@ class Regrtest:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Remove all the selected tests that precede start if it's set.
|
# Remove all the selected tests that precede start if it's set.
|
||||||
if starting_test:
|
if self.starting_test:
|
||||||
try:
|
try:
|
||||||
del self.selected[:self.selected.index(starting_test)]
|
del self.selected[:self.selected.index(self.starting_test)]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
print(f"Cannot find starting test: {starting_test}")
|
print(f"Cannot find starting test: {self.starting_test}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if randomize:
|
if self.randomize:
|
||||||
if ns.random_seed is None:
|
if self.random_seed is None:
|
||||||
ns.random_seed = random.randrange(10000000)
|
self.random_seed = random.randrange(100_000_000)
|
||||||
random.seed(ns.random_seed)
|
random.seed(self.random_seed)
|
||||||
random.shuffle(self.selected)
|
random.shuffle(self.selected)
|
||||||
|
|
||||||
def list_tests(self):
|
def list_tests(self):
|
||||||
|
@ -279,7 +301,7 @@ class Regrtest:
|
||||||
ns = self.ns
|
ns = self.ns
|
||||||
test_dir = ns.testdir
|
test_dir = ns.testdir
|
||||||
support.verbose = False
|
support.verbose = False
|
||||||
support.set_match_tests(ns.match_tests, ns.ignore_tests)
|
support.set_match_tests(self.match_tests, self.ignore_tests)
|
||||||
|
|
||||||
skipped = []
|
skipped = []
|
||||||
for test_name in self.selected:
|
for test_name in self.selected:
|
||||||
|
@ -306,20 +328,18 @@ class Regrtest:
|
||||||
rerun_match_tests[result.test_name] = match_tests
|
rerun_match_tests[result.test_name] = match_tests
|
||||||
return rerun_match_tests
|
return rerun_match_tests
|
||||||
|
|
||||||
def _rerun_failed_tests(self, need_rerun):
|
def _rerun_failed_tests(self, need_rerun, runtests: RunTests):
|
||||||
# Configure the runner to re-run tests
|
# Configure the runner to re-run tests
|
||||||
ns = self.ns
|
ns = self.ns
|
||||||
ns.verbose = True
|
ns.verbose = True
|
||||||
ns.failfast = False
|
ns.failfast = False
|
||||||
ns.verbose3 = False
|
ns.verbose3 = False
|
||||||
ns.forever = False
|
|
||||||
if ns.use_mp is None:
|
if ns.use_mp is None:
|
||||||
ns.use_mp = 1
|
ns.use_mp = 1
|
||||||
|
|
||||||
# Get tests to re-run
|
# Get tests to re-run
|
||||||
tests = [result.test_name for result in need_rerun]
|
tests = [result.test_name for result in need_rerun]
|
||||||
match_tests = self.get_rerun_match(need_rerun)
|
match_tests = self.get_rerun_match(need_rerun)
|
||||||
self.set_tests(tests)
|
|
||||||
|
|
||||||
# Clear previously failed tests
|
# Clear previously failed tests
|
||||||
self.rerun_bad.extend(self.bad)
|
self.rerun_bad.extend(self.bad)
|
||||||
|
@ -328,11 +348,14 @@ class Regrtest:
|
||||||
|
|
||||||
# Re-run failed tests
|
# Re-run failed tests
|
||||||
self.log(f"Re-running {len(tests)} failed tests in verbose mode in subprocesses")
|
self.log(f"Re-running {len(tests)} failed tests in verbose mode in subprocesses")
|
||||||
runtests = RunTests(tuple(tests), match_tests=match_tests, rerun=True)
|
runtests = runtests.copy(tests=tuple(tests),
|
||||||
self.all_runtests.append(runtests)
|
match_tests=match_tests,
|
||||||
|
rerun=True,
|
||||||
|
forever=False)
|
||||||
|
self.set_tests(runtests)
|
||||||
self._run_tests_mp(runtests)
|
self._run_tests_mp(runtests)
|
||||||
|
|
||||||
def rerun_failed_tests(self, need_rerun):
|
def rerun_failed_tests(self, need_rerun, runtests: RunTests):
|
||||||
if self.ns.python:
|
if self.ns.python:
|
||||||
# Temp patch for https://github.com/python/cpython/issues/94052
|
# Temp patch for https://github.com/python/cpython/issues/94052
|
||||||
self.log(
|
self.log(
|
||||||
|
@ -344,7 +367,7 @@ class Regrtest:
|
||||||
self.first_state = self.get_tests_state()
|
self.first_state = self.get_tests_state()
|
||||||
|
|
||||||
print()
|
print()
|
||||||
self._rerun_failed_tests(need_rerun)
|
self._rerun_failed_tests(need_rerun, runtests)
|
||||||
|
|
||||||
if self.bad:
|
if self.bad:
|
||||||
print(count(len(self.bad), 'test'), "failed again:")
|
print(count(len(self.bad), 'test'), "failed again:")
|
||||||
|
@ -572,9 +595,9 @@ class Regrtest:
|
||||||
self.win_load_tracker.close()
|
self.win_load_tracker.close()
|
||||||
self.win_load_tracker = None
|
self.win_load_tracker = None
|
||||||
|
|
||||||
def set_tests(self, tests):
|
def set_tests(self, runtests: RunTests):
|
||||||
self.tests = tests
|
self.tests = runtests.tests
|
||||||
if self.ns.forever:
|
if runtests.forever:
|
||||||
self.test_count_text = ''
|
self.test_count_text = ''
|
||||||
self.test_count_width = 3
|
self.test_count_width = 3
|
||||||
else:
|
else:
|
||||||
|
@ -583,7 +606,7 @@ class Regrtest:
|
||||||
|
|
||||||
def run_tests(self):
|
def run_tests(self):
|
||||||
# For a partial run, we do not need to clutter the output.
|
# For a partial run, we do not need to clutter the output.
|
||||||
if (self.ns.header
|
if (self.want_header
|
||||||
or not(self.ns.pgo or self.ns.quiet or self.ns.single
|
or not(self.ns.pgo or self.ns.quiet or self.ns.single
|
||||||
or self.tests or self.ns.args)):
|
or self.tests or self.ns.args)):
|
||||||
self.display_header()
|
self.display_header()
|
||||||
|
@ -595,17 +618,18 @@ class Regrtest:
|
||||||
"3 warmup repetitions can give false positives!")
|
"3 warmup repetitions can give false positives!")
|
||||||
print(msg, file=sys.stdout, flush=True)
|
print(msg, file=sys.stdout, flush=True)
|
||||||
|
|
||||||
if self.ns.randomize:
|
if self.randomize:
|
||||||
print("Using random seed", self.ns.random_seed)
|
print("Using random seed", self.random_seed)
|
||||||
|
|
||||||
tests = self.selected
|
tests = self.selected
|
||||||
self.set_tests(tests)
|
runtests = RunTests(tuple(tests), forever=self.forever)
|
||||||
runtests = RunTests(tuple(tests), forever=self.ns.forever)
|
self.first_runtests = runtests
|
||||||
self.all_runtests.append(runtests)
|
self.set_tests(runtests)
|
||||||
if self.ns.use_mp:
|
if self.ns.use_mp:
|
||||||
self._run_tests_mp(runtests)
|
self._run_tests_mp(runtests)
|
||||||
else:
|
else:
|
||||||
self.run_tests_sequentially(runtests)
|
self.run_tests_sequentially(runtests)
|
||||||
|
return runtests
|
||||||
|
|
||||||
def finalize(self):
|
def finalize(self):
|
||||||
if self.next_single_filename:
|
if self.next_single_filename:
|
||||||
|
@ -627,11 +651,7 @@ class Regrtest:
|
||||||
|
|
||||||
def display_summary(self):
|
def display_summary(self):
|
||||||
duration = time.perf_counter() - self.start_time
|
duration = time.perf_counter() - self.start_time
|
||||||
first_runtests = self.all_runtests[0]
|
filtered = bool(self.match_tests) or bool(self.ignore_tests)
|
||||||
# the second runtests (re-run failed tests) disables forever,
|
|
||||||
# use the first runtests
|
|
||||||
forever = first_runtests.forever
|
|
||||||
filtered = bool(self.ns.match_tests) or bool(self.ns.ignore_tests)
|
|
||||||
|
|
||||||
# Total duration
|
# Total duration
|
||||||
print()
|
print()
|
||||||
|
@ -655,8 +675,8 @@ class Regrtest:
|
||||||
self.environment_changed, self.run_no_tests]
|
self.environment_changed, self.run_no_tests]
|
||||||
run = sum(map(len, all_tests))
|
run = sum(map(len, all_tests))
|
||||||
text = f'run={run}'
|
text = f'run={run}'
|
||||||
if not forever:
|
if not self.first_runtests.forever:
|
||||||
ntest = len(first_runtests.tests)
|
ntest = len(self.first_runtests.tests)
|
||||||
text = f"{text}/{ntest}"
|
text = f"{text}/{ntest}"
|
||||||
if filtered:
|
if filtered:
|
||||||
text = f"{text} (filtered)"
|
text = f"{text} (filtered)"
|
||||||
|
@ -788,7 +808,7 @@ class Regrtest:
|
||||||
|
|
||||||
self.fix_umask()
|
self.fix_umask()
|
||||||
|
|
||||||
if ns.cleanup:
|
if self.want_cleanup:
|
||||||
self.cleanup()
|
self.cleanup()
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
@ -838,12 +858,12 @@ class Regrtest:
|
||||||
return exitcode
|
return exitcode
|
||||||
|
|
||||||
def action_run_tests(self):
|
def action_run_tests(self):
|
||||||
self.run_tests()
|
runtests = self.run_tests()
|
||||||
self.display_result()
|
self.display_result()
|
||||||
|
|
||||||
need_rerun = self.need_rerun
|
need_rerun = self.need_rerun
|
||||||
if self.ns.rerun and need_rerun:
|
if self.ns.rerun and need_rerun:
|
||||||
self.rerun_failed_tests(need_rerun)
|
self.rerun_failed_tests(need_rerun, runtests)
|
||||||
|
|
||||||
self.display_summary()
|
self.display_summary()
|
||||||
self.finalize()
|
self.finalize()
|
||||||
|
@ -854,16 +874,16 @@ class Regrtest:
|
||||||
run_tests_worker(self.ns.worker_args)
|
run_tests_worker(self.ns.worker_args)
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.ns.wait:
|
if self.want_wait:
|
||||||
input("Press any key to continue...")
|
input("Press any key to continue...")
|
||||||
|
|
||||||
setup_tests(self.ns)
|
setup_tests(self.ns)
|
||||||
self.find_tests()
|
self.find_tests()
|
||||||
|
|
||||||
exitcode = 0
|
exitcode = 0
|
||||||
if self.ns.list_tests:
|
if self.want_list_tests:
|
||||||
self.list_tests()
|
self.list_tests()
|
||||||
elif self.ns.list_cases:
|
elif self.want_list_cases:
|
||||||
self.list_cases()
|
self.list_cases()
|
||||||
else:
|
else:
|
||||||
self.action_run_tests()
|
self.action_run_tests()
|
||||||
|
|
|
@ -212,6 +212,11 @@ class RunTests:
|
||||||
rerun: bool = False
|
rerun: bool = False
|
||||||
forever: bool = False
|
forever: bool = False
|
||||||
|
|
||||||
|
def copy(self, **override):
|
||||||
|
state = dataclasses.asdict(self)
|
||||||
|
state.update(override)
|
||||||
|
return RunTests(**state)
|
||||||
|
|
||||||
def get_match_tests(self, test_name) -> FilterTuple | None:
|
def get_match_tests(self, test_name) -> FilterTuple | None:
|
||||||
if self.match_tests is not None:
|
if self.match_tests is not None:
|
||||||
return self.match_tests.get(test_name, None)
|
return self.match_tests.get(test_name, None)
|
||||||
|
|
|
@ -589,7 +589,7 @@ class BaseTestCase(unittest.TestCase):
|
||||||
def parse_random_seed(self, output):
|
def parse_random_seed(self, output):
|
||||||
match = self.regex_search(r'Using random seed ([0-9]+)', output)
|
match = self.regex_search(r'Using random seed ([0-9]+)', output)
|
||||||
randseed = int(match.group(1))
|
randseed = int(match.group(1))
|
||||||
self.assertTrue(0 <= randseed <= 10000000, randseed)
|
self.assertTrue(0 <= randseed <= 100_000_000, randseed)
|
||||||
return randseed
|
return randseed
|
||||||
|
|
||||||
def run_command(self, args, input=None, exitcode=0, **kw):
|
def run_command(self, args, input=None, exitcode=0, **kw):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue