mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
gh-109162: libregrtest: rename runtest_mp.py to run_workers.py (#109248)
* Rename runtest_mp.py to run_workers.py * Move exit_timeout() and temp_cwd() context managers from Regrtest.main() to Regrtest.run_tests(). Actions like --list-tests or --list-cases don't need these protections. * Regrtest: remove selected and tests attributes. Pass 'selected' to list_tests(), list_cases() and run_tests(). display_result() now expects a TestTuple, instead of TestList. * Rename setup_tests() to setup_process() and rename setup_support() to setup_tests(). * Move _adjust_resource_limits() to utils and rename it to adjust_rlimit_nofile(). * Move replace_stdout() to utils. * Fix RunTests.verbose type: it's an int.
This commit is contained in:
parent
0b6b05391b
commit
0c139b5f2f
9 changed files with 171 additions and 164 deletions
|
@ -15,12 +15,12 @@ from test.libregrtest.findtests import findtests, split_test_packages
|
||||||
from test.libregrtest.logger import Logger
|
from test.libregrtest.logger import Logger
|
||||||
from test.libregrtest.result import State
|
from test.libregrtest.result import State
|
||||||
from test.libregrtest.runtests import RunTests, HuntRefleak
|
from test.libregrtest.runtests import RunTests, HuntRefleak
|
||||||
from test.libregrtest.setup import setup_tests, setup_test_dir
|
from test.libregrtest.setup import setup_process, setup_test_dir
|
||||||
from test.libregrtest.single import run_single_test, PROGRESS_MIN_TIME
|
from test.libregrtest.single import run_single_test, PROGRESS_MIN_TIME
|
||||||
from test.libregrtest.pgo import setup_pgo_tests
|
from test.libregrtest.pgo import setup_pgo_tests
|
||||||
from test.libregrtest.results import TestResults
|
from test.libregrtest.results import TestResults
|
||||||
from test.libregrtest.utils import (
|
from test.libregrtest.utils import (
|
||||||
StrPath, StrJSON, TestName, TestList, FilterTuple,
|
StrPath, StrJSON, TestName, TestList, TestTuple, FilterTuple,
|
||||||
strip_py_suffix, count, format_duration,
|
strip_py_suffix, count, format_duration,
|
||||||
printlist, get_build_info, get_temp_dir, get_work_dir, exit_timeout,
|
printlist, get_build_info, get_temp_dir, get_work_dir, exit_timeout,
|
||||||
abs_module_name)
|
abs_module_name)
|
||||||
|
@ -51,7 +51,7 @@ class Regrtest:
|
||||||
"""
|
"""
|
||||||
def __init__(self, ns: Namespace):
|
def __init__(self, ns: Namespace):
|
||||||
# Log verbosity
|
# Log verbosity
|
||||||
self.verbose: bool = ns.verbose
|
self.verbose: int = int(ns.verbose)
|
||||||
self.quiet: bool = ns.quiet
|
self.quiet: bool = ns.quiet
|
||||||
self.pgo: bool = ns.pgo
|
self.pgo: bool = ns.pgo
|
||||||
self.pgo_extended: bool = ns.pgo_extended
|
self.pgo_extended: bool = ns.pgo_extended
|
||||||
|
@ -122,8 +122,6 @@ class Regrtest:
|
||||||
self.tmp_dir: StrPath | None = ns.tempdir
|
self.tmp_dir: StrPath | None = ns.tempdir
|
||||||
|
|
||||||
# tests
|
# tests
|
||||||
self.tests = []
|
|
||||||
self.selected: TestList = []
|
|
||||||
self.first_runtests: RunTests | None = None
|
self.first_runtests: RunTests | None = None
|
||||||
|
|
||||||
# used by --slowest
|
# used by --slowest
|
||||||
|
@ -140,18 +138,18 @@ class Regrtest:
|
||||||
def log(self, line=''):
|
def log(self, line=''):
|
||||||
self.logger.log(line)
|
self.logger.log(line)
|
||||||
|
|
||||||
def find_tests(self):
|
def find_tests(self, tests: TestList | None = None) -> tuple[TestTuple, TestList | None]:
|
||||||
if self.single_test_run:
|
if self.single_test_run:
|
||||||
self.next_single_filename = os.path.join(self.tmp_dir, 'pynexttest')
|
self.next_single_filename = os.path.join(self.tmp_dir, 'pynexttest')
|
||||||
try:
|
try:
|
||||||
with open(self.next_single_filename, 'r') as fp:
|
with open(self.next_single_filename, 'r') as fp:
|
||||||
next_test = fp.read().strip()
|
next_test = fp.read().strip()
|
||||||
self.tests = [next_test]
|
tests = [next_test]
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if self.fromfile:
|
if self.fromfile:
|
||||||
self.tests = []
|
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')
|
||||||
|
@ -161,9 +159,9 @@ class Regrtest:
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
match = regex.search(line)
|
match = regex.search(line)
|
||||||
if match is not None:
|
if match is not None:
|
||||||
self.tests.append(match.group())
|
tests.append(match.group())
|
||||||
|
|
||||||
strip_py_suffix(self.tests)
|
strip_py_suffix(tests)
|
||||||
|
|
||||||
if self.pgo:
|
if self.pgo:
|
||||||
# add default PGO tests if no tests are specified
|
# add default PGO tests if no tests are specified
|
||||||
|
@ -179,18 +177,18 @@ class Regrtest:
|
||||||
exclude=exclude_tests)
|
exclude=exclude_tests)
|
||||||
|
|
||||||
if not self.fromfile:
|
if not self.fromfile:
|
||||||
self.selected = self.tests or self.cmdline_args
|
selected = tests or self.cmdline_args
|
||||||
if self.selected:
|
if selected:
|
||||||
self.selected = split_test_packages(self.selected)
|
selected = split_test_packages(selected)
|
||||||
else:
|
else:
|
||||||
self.selected = alltests
|
selected = alltests
|
||||||
else:
|
else:
|
||||||
self.selected = self.tests
|
selected = tests
|
||||||
|
|
||||||
if self.single_test_run:
|
if self.single_test_run:
|
||||||
self.selected = self.selected[:1]
|
selected = selected[:1]
|
||||||
try:
|
try:
|
||||||
pos = alltests.index(self.selected[0])
|
pos = alltests.index(selected[0])
|
||||||
self.next_single_test = alltests[pos + 1]
|
self.next_single_test = alltests[pos + 1]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
pass
|
pass
|
||||||
|
@ -198,7 +196,7 @@ class Regrtest:
|
||||||
# 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 self.starting_test:
|
if self.starting_test:
|
||||||
try:
|
try:
|
||||||
del self.selected[:self.selected.index(self.starting_test)]
|
del selected[:selected.index(self.starting_test)]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
print(f"Cannot find starting test: {self.starting_test}")
|
print(f"Cannot find starting test: {self.starting_test}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
@ -207,10 +205,12 @@ class Regrtest:
|
||||||
if self.random_seed is None:
|
if self.random_seed is None:
|
||||||
self.random_seed = random.randrange(100_000_000)
|
self.random_seed = random.randrange(100_000_000)
|
||||||
random.seed(self.random_seed)
|
random.seed(self.random_seed)
|
||||||
random.shuffle(self.selected)
|
random.shuffle(selected)
|
||||||
|
|
||||||
|
return (tuple(selected), tests)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def list_tests(tests: TestList):
|
def list_tests(tests: TestTuple):
|
||||||
for name in tests:
|
for name in tests:
|
||||||
print(name)
|
print(name)
|
||||||
|
|
||||||
|
@ -224,12 +224,12 @@ class Regrtest:
|
||||||
if support.match_test(test):
|
if support.match_test(test):
|
||||||
print(test.id())
|
print(test.id())
|
||||||
|
|
||||||
def list_cases(self):
|
def list_cases(self, tests: TestTuple):
|
||||||
support.verbose = False
|
support.verbose = False
|
||||||
support.set_match_tests(self.match_tests, self.ignore_tests)
|
support.set_match_tests(self.match_tests, self.ignore_tests)
|
||||||
|
|
||||||
skipped = []
|
skipped = []
|
||||||
for test_name in self.selected:
|
for test_name in tests:
|
||||||
module_name = abs_module_name(test_name, self.test_dir)
|
module_name = abs_module_name(test_name, self.test_dir)
|
||||||
try:
|
try:
|
||||||
suite = unittest.defaultTestLoader.loadTestsFromName(module_name)
|
suite = unittest.defaultTestLoader.loadTestsFromName(module_name)
|
||||||
|
@ -247,6 +247,10 @@ class Regrtest:
|
||||||
def _rerun_failed_tests(self, runtests: RunTests):
|
def _rerun_failed_tests(self, runtests: RunTests):
|
||||||
# Configure the runner to re-run tests
|
# Configure the runner to re-run tests
|
||||||
if self.num_workers == 0:
|
if self.num_workers == 0:
|
||||||
|
# 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.
|
||||||
self.num_workers = 1
|
self.num_workers = 1
|
||||||
|
|
||||||
tests, match_tests_dict = self.results.prepare_rerun()
|
tests, match_tests_dict = self.results.prepare_rerun()
|
||||||
|
@ -294,7 +298,8 @@ class Regrtest:
|
||||||
print()
|
print()
|
||||||
print(f"== Tests result: {state} ==")
|
print(f"== Tests result: {state} ==")
|
||||||
|
|
||||||
self.results.display_result(self.selected, self.quiet, self.print_slowest)
|
self.results.display_result(runtests.tests,
|
||||||
|
self.quiet, self.print_slowest)
|
||||||
|
|
||||||
def run_test(self, test_name: TestName, runtests: RunTests, tracer):
|
def run_test(self, test_name: TestName, runtests: RunTests, tracer):
|
||||||
if tracer is not None:
|
if tracer is not None:
|
||||||
|
@ -404,7 +409,7 @@ class Regrtest:
|
||||||
return state
|
return state
|
||||||
|
|
||||||
def _run_tests_mp(self, runtests: RunTests, num_workers: int) -> None:
|
def _run_tests_mp(self, runtests: RunTests, num_workers: int) -> None:
|
||||||
from test.libregrtest.runtest_mp import RunWorkers
|
from test.libregrtest.run_workers import RunWorkers
|
||||||
RunWorkers(num_workers, runtests, self.logger, self.results).run()
|
RunWorkers(num_workers, runtests, self.logger, self.results).run()
|
||||||
|
|
||||||
def finalize_tests(self, tracer):
|
def finalize_tests(self, tracer):
|
||||||
|
@ -454,39 +459,9 @@ class Regrtest:
|
||||||
print("Remove file: %s" % name)
|
print("Remove file: %s" % name)
|
||||||
os_helper.unlink(name)
|
os_helper.unlink(name)
|
||||||
|
|
||||||
def main(self, tests: TestList | None = None):
|
def create_run_tests(self, tests: TestTuple):
|
||||||
if self.junit_filename and not os.path.isabs(self.junit_filename):
|
|
||||||
self.junit_filename = os.path.abspath(self.junit_filename)
|
|
||||||
|
|
||||||
self.tests = tests
|
|
||||||
|
|
||||||
strip_py_suffix(self.cmdline_args)
|
|
||||||
|
|
||||||
self.tmp_dir = get_temp_dir(self.tmp_dir)
|
|
||||||
|
|
||||||
if self.want_cleanup:
|
|
||||||
self.cleanup_temp_dir(self.tmp_dir)
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
os.makedirs(self.tmp_dir, exist_ok=True)
|
|
||||||
work_dir = get_work_dir(parent_dir=self.tmp_dir)
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
self._main()
|
|
||||||
|
|
||||||
def create_run_tests(self):
|
|
||||||
return RunTests(
|
return RunTests(
|
||||||
tuple(self.selected),
|
tests,
|
||||||
fail_fast=self.fail_fast,
|
fail_fast=self.fail_fast,
|
||||||
match_tests=self.match_tests,
|
match_tests=self.match_tests,
|
||||||
ignore_tests=self.ignore_tests,
|
ignore_tests=self.ignore_tests,
|
||||||
|
@ -506,7 +481,7 @@ class Regrtest:
|
||||||
python_cmd=self.python_cmd,
|
python_cmd=self.python_cmd,
|
||||||
)
|
)
|
||||||
|
|
||||||
def run_tests(self) -> int:
|
def _run_tests(self, selected: TestTuple, tests: TestList | None) -> int:
|
||||||
if self.hunt_refleak and self.hunt_refleak.warmups < 3:
|
if self.hunt_refleak and self.hunt_refleak.warmups < 3:
|
||||||
msg = ("WARNING: Running tests with --huntrleaks/-R and "
|
msg = ("WARNING: Running tests with --huntrleaks/-R and "
|
||||||
"less than 3 warmup repetitions can give false positives!")
|
"less than 3 warmup repetitions can give false positives!")
|
||||||
|
@ -520,17 +495,17 @@ class Regrtest:
|
||||||
# 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.want_header
|
if (self.want_header
|
||||||
or not(self.pgo or self.quiet or self.single_test_run
|
or not(self.pgo or self.quiet or self.single_test_run
|
||||||
or self.tests or self.cmdline_args)):
|
or tests or self.cmdline_args)):
|
||||||
self.display_header()
|
self.display_header()
|
||||||
|
|
||||||
if self.randomize:
|
if self.randomize:
|
||||||
print("Using random seed", self.random_seed)
|
print("Using random seed", self.random_seed)
|
||||||
|
|
||||||
runtests = self.create_run_tests()
|
runtests = self.create_run_tests(selected)
|
||||||
self.first_runtests = runtests
|
self.first_runtests = runtests
|
||||||
self.logger.set_tests(runtests)
|
self.logger.set_tests(runtests)
|
||||||
|
|
||||||
setup_tests(runtests)
|
setup_process()
|
||||||
|
|
||||||
self.logger.start_load_tracker()
|
self.logger.start_load_tracker()
|
||||||
try:
|
try:
|
||||||
|
@ -553,20 +528,48 @@ class Regrtest:
|
||||||
return self.results.get_exitcode(self.fail_env_changed,
|
return self.results.get_exitcode(self.fail_env_changed,
|
||||||
self.fail_rerun)
|
self.fail_rerun)
|
||||||
|
|
||||||
def _main(self):
|
def run_tests(self, selected: TestTuple, tests: TestList | None) -> int:
|
||||||
|
os.makedirs(self.tmp_dir, exist_ok=True)
|
||||||
|
work_dir = get_work_dir(parent_dir=self.tmp_dir)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
def main(self, tests: TestList | None = None):
|
||||||
|
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)
|
||||||
|
|
||||||
|
if self.want_cleanup:
|
||||||
|
self.cleanup_temp_dir(self.tmp_dir)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
if self.want_wait:
|
if self.want_wait:
|
||||||
input("Press any key to continue...")
|
input("Press any key to continue...")
|
||||||
|
|
||||||
setup_test_dir(self.test_dir)
|
setup_test_dir(self.test_dir)
|
||||||
self.find_tests()
|
selected, tests = self.find_tests(tests)
|
||||||
|
|
||||||
exitcode = 0
|
exitcode = 0
|
||||||
if self.want_list_tests:
|
if self.want_list_tests:
|
||||||
self.list_tests(self.selected)
|
self.list_tests(selected)
|
||||||
elif self.want_list_cases:
|
elif self.want_list_cases:
|
||||||
self.list_cases()
|
self.list_cases(selected)
|
||||||
else:
|
else:
|
||||||
exitcode = self.run_tests()
|
exitcode = self.run_tests(selected, tests)
|
||||||
|
|
||||||
sys.exit(exitcode)
|
sys.exit(exitcode)
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ from typing import Any
|
||||||
from test.support import TestStats
|
from test.support import TestStats
|
||||||
|
|
||||||
from test.libregrtest.utils import (
|
from test.libregrtest.utils import (
|
||||||
TestName, FilterTuple,
|
StrJSON, TestName, FilterTuple,
|
||||||
format_duration, normalize_test_name, print_warning)
|
format_duration, normalize_test_name, print_warning)
|
||||||
|
|
||||||
|
|
||||||
|
@ -160,7 +160,7 @@ class TestResult:
|
||||||
json.dump(self, file, cls=_EncodeTestResult)
|
json.dump(self, file, cls=_EncodeTestResult)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_json(worker_json) -> 'TestResult':
|
def from_json(worker_json: StrJSON) -> 'TestResult':
|
||||||
return json.loads(worker_json, object_hook=_decode_test_result)
|
return json.loads(worker_json, object_hook=_decode_test_result)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -106,7 +106,7 @@ class TestResults:
|
||||||
|
|
||||||
xml_data = result.xml_data
|
xml_data = result.xml_data
|
||||||
if xml_data:
|
if xml_data:
|
||||||
self.add_junit(result.xml_data)
|
self.add_junit(xml_data)
|
||||||
|
|
||||||
def need_rerun(self):
|
def need_rerun(self):
|
||||||
return bool(self.bad_results)
|
return bool(self.bad_results)
|
||||||
|
@ -163,7 +163,7 @@ class TestResults:
|
||||||
for s in ET.tostringlist(root):
|
for s in ET.tostringlist(root):
|
||||||
f.write(s)
|
f.write(s)
|
||||||
|
|
||||||
def display_result(self, tests: TestList, quiet: bool, print_slowest: bool):
|
def display_result(self, tests: TestTuple, quiet: bool, print_slowest: bool):
|
||||||
if self.interrupted:
|
if self.interrupted:
|
||||||
print("Test suite interrupted by signal SIGINT.")
|
print("Test suite interrupted by signal SIGINT.")
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@ from test import support
|
||||||
from test.support import os_helper
|
from test.support import os_helper
|
||||||
|
|
||||||
from test.libregrtest.logger import Logger
|
from test.libregrtest.logger import Logger
|
||||||
from test.libregrtest.main import Regrtest
|
|
||||||
from test.libregrtest.result import TestResult, State
|
from test.libregrtest.result import TestResult, State
|
||||||
from test.libregrtest.results import TestResults
|
from test.libregrtest.results import TestResults
|
||||||
from test.libregrtest.runtests import RunTests
|
from test.libregrtest.runtests import RunTests
|
||||||
|
@ -154,10 +153,10 @@ class WorkerThread(threading.Thread):
|
||||||
) -> MultiprocessResult:
|
) -> MultiprocessResult:
|
||||||
return MultiprocessResult(test_result, stdout, err_msg)
|
return MultiprocessResult(test_result, stdout, err_msg)
|
||||||
|
|
||||||
def _run_process(self, worker_job, output_file: TextIO,
|
def _run_process(self, runtests: RunTests, output_file: TextIO,
|
||||||
tmp_dir: StrPath | None = None) -> int:
|
tmp_dir: StrPath | None = None) -> int:
|
||||||
try:
|
try:
|
||||||
popen = create_worker_process(worker_job, output_file, tmp_dir)
|
popen = create_worker_process(runtests, output_file, tmp_dir)
|
||||||
|
|
||||||
self._killed = False
|
self._killed = False
|
||||||
self._popen = popen
|
self._popen = popen
|
|
@ -27,14 +27,14 @@ class RunTests:
|
||||||
pgo_extended: bool = False
|
pgo_extended: bool = False
|
||||||
output_on_failure: bool = False
|
output_on_failure: bool = False
|
||||||
timeout: float | None = None
|
timeout: float | None = None
|
||||||
verbose: bool = False
|
verbose: int = 0
|
||||||
quiet: bool = False
|
quiet: bool = False
|
||||||
hunt_refleak: HuntRefleak | None = None
|
hunt_refleak: HuntRefleak | None = None
|
||||||
test_dir: StrPath | None = None
|
test_dir: StrPath | None = None
|
||||||
use_junit: bool = False
|
use_junit: bool = False
|
||||||
memory_limit: str | None = None
|
memory_limit: str | None = None
|
||||||
gc_threshold: int | None = None
|
gc_threshold: int | None = None
|
||||||
use_resources: list[str] = None
|
use_resources: list[str] = dataclasses.field(default_factory=list)
|
||||||
python_cmd: list[str] | None = None
|
python_cmd: list[str] | None = None
|
||||||
|
|
||||||
def copy(self, **override):
|
def copy(self, **override):
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import atexit
|
|
||||||
import faulthandler
|
import faulthandler
|
||||||
import os
|
import os
|
||||||
import signal
|
import signal
|
||||||
|
@ -13,7 +12,8 @@ except ImportError:
|
||||||
|
|
||||||
from test.libregrtest.runtests import RunTests
|
from test.libregrtest.runtests import RunTests
|
||||||
from test.libregrtest.utils import (
|
from test.libregrtest.utils import (
|
||||||
setup_unraisable_hook, setup_threading_excepthook, fix_umask)
|
setup_unraisable_hook, setup_threading_excepthook, fix_umask,
|
||||||
|
replace_stdout, adjust_rlimit_nofile)
|
||||||
|
|
||||||
|
|
||||||
UNICODE_GUARD_ENV = "PYTHONREGRTEST_UNICODE_GUARD"
|
UNICODE_GUARD_ENV = "PYTHONREGRTEST_UNICODE_GUARD"
|
||||||
|
@ -26,19 +26,7 @@ def setup_test_dir(testdir: str | None) -> None:
|
||||||
sys.path.insert(0, os.path.abspath(testdir))
|
sys.path.insert(0, os.path.abspath(testdir))
|
||||||
|
|
||||||
|
|
||||||
def setup_support(runtests: RunTests):
|
def setup_process():
|
||||||
support.PGO = runtests.pgo
|
|
||||||
support.PGO_EXTENDED = runtests.pgo_extended
|
|
||||||
support.set_match_tests(runtests.match_tests, runtests.ignore_tests)
|
|
||||||
support.failfast = runtests.fail_fast
|
|
||||||
support.verbose = runtests.verbose
|
|
||||||
if runtests.use_junit:
|
|
||||||
support.junit_xml_list = []
|
|
||||||
else:
|
|
||||||
support.junit_xml_list = None
|
|
||||||
|
|
||||||
|
|
||||||
def setup_tests(runtests):
|
|
||||||
fix_umask()
|
fix_umask()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -62,7 +50,7 @@ def setup_tests(runtests):
|
||||||
for signum in signals:
|
for signum in signals:
|
||||||
faulthandler.register(signum, chain=True, file=stderr_fd)
|
faulthandler.register(signum, chain=True, file=stderr_fd)
|
||||||
|
|
||||||
_adjust_resource_limits()
|
adjust_rlimit_nofile()
|
||||||
replace_stdout()
|
replace_stdout()
|
||||||
support.record_original_stdout(sys.stdout)
|
support.record_original_stdout(sys.stdout)
|
||||||
|
|
||||||
|
@ -83,19 +71,6 @@ def setup_tests(runtests):
|
||||||
if getattr(module, '__file__', None):
|
if getattr(module, '__file__', None):
|
||||||
module.__file__ = os.path.abspath(module.__file__)
|
module.__file__ = os.path.abspath(module.__file__)
|
||||||
|
|
||||||
if runtests.hunt_refleak:
|
|
||||||
unittest.BaseTestSuite._cleanup = False
|
|
||||||
|
|
||||||
if runtests.memory_limit is not None:
|
|
||||||
support.set_memlimit(runtests.memory_limit)
|
|
||||||
|
|
||||||
if runtests.gc_threshold is not None:
|
|
||||||
gc.set_threshold(runtests.gc_threshold)
|
|
||||||
|
|
||||||
support.suppress_msvcrt_asserts(runtests.verbose and runtests.verbose >= 2)
|
|
||||||
|
|
||||||
support.use_resources = runtests.use_resources
|
|
||||||
|
|
||||||
if hasattr(sys, 'addaudithook'):
|
if hasattr(sys, 'addaudithook'):
|
||||||
# Add an auditing hook for all tests to ensure PySys_Audit is tested
|
# Add an auditing hook for all tests to ensure PySys_Audit is tested
|
||||||
def _test_audit_hook(name, args):
|
def _test_audit_hook(name, args):
|
||||||
|
@ -105,6 +80,36 @@ def setup_tests(runtests):
|
||||||
setup_unraisable_hook()
|
setup_unraisable_hook()
|
||||||
setup_threading_excepthook()
|
setup_threading_excepthook()
|
||||||
|
|
||||||
|
# Ensure there's a non-ASCII character in env vars at all times to force
|
||||||
|
# tests consider this case. See BPO-44647 for details.
|
||||||
|
if TESTFN_UNDECODABLE and os.supports_bytes_environ:
|
||||||
|
os.environb.setdefault(UNICODE_GUARD_ENV.encode(), TESTFN_UNDECODABLE)
|
||||||
|
elif FS_NONASCII:
|
||||||
|
os.environ.setdefault(UNICODE_GUARD_ENV, FS_NONASCII)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_tests(runtests: RunTests):
|
||||||
|
support.verbose = runtests.verbose
|
||||||
|
support.failfast = runtests.fail_fast
|
||||||
|
support.PGO = runtests.pgo
|
||||||
|
support.PGO_EXTENDED = runtests.pgo_extended
|
||||||
|
|
||||||
|
support.set_match_tests(runtests.match_tests, runtests.ignore_tests)
|
||||||
|
|
||||||
|
if runtests.use_junit:
|
||||||
|
support.junit_xml_list = []
|
||||||
|
from test.support.testresult import RegressionTestResult
|
||||||
|
RegressionTestResult.USE_XML = True
|
||||||
|
else:
|
||||||
|
support.junit_xml_list = None
|
||||||
|
|
||||||
|
if runtests.memory_limit is not None:
|
||||||
|
support.set_memlimit(runtests.memory_limit)
|
||||||
|
|
||||||
|
support.suppress_msvcrt_asserts(runtests.verbose >= 2)
|
||||||
|
|
||||||
|
support.use_resources = runtests.use_resources
|
||||||
|
|
||||||
timeout = runtests.timeout
|
timeout = runtests.timeout
|
||||||
if timeout is not None:
|
if timeout is not None:
|
||||||
# For a slow buildbot worker, increase SHORT_TIMEOUT and LONG_TIMEOUT
|
# For a slow buildbot worker, increase SHORT_TIMEOUT and LONG_TIMEOUT
|
||||||
|
@ -117,61 +122,8 @@ def setup_tests(runtests):
|
||||||
support.SHORT_TIMEOUT = min(support.SHORT_TIMEOUT, timeout)
|
support.SHORT_TIMEOUT = min(support.SHORT_TIMEOUT, timeout)
|
||||||
support.LONG_TIMEOUT = min(support.LONG_TIMEOUT, timeout)
|
support.LONG_TIMEOUT = min(support.LONG_TIMEOUT, timeout)
|
||||||
|
|
||||||
if runtests.use_junit:
|
if runtests.hunt_refleak:
|
||||||
from test.support.testresult import RegressionTestResult
|
unittest.BaseTestSuite._cleanup = False
|
||||||
RegressionTestResult.USE_XML = True
|
|
||||||
|
|
||||||
# Ensure there's a non-ASCII character in env vars at all times to force
|
if runtests.gc_threshold is not None:
|
||||||
# tests consider this case. See BPO-44647 for details.
|
gc.set_threshold(runtests.gc_threshold)
|
||||||
if TESTFN_UNDECODABLE and os.supports_bytes_environ:
|
|
||||||
os.environb.setdefault(UNICODE_GUARD_ENV.encode(), TESTFN_UNDECODABLE)
|
|
||||||
elif FS_NONASCII:
|
|
||||||
os.environ.setdefault(UNICODE_GUARD_ENV, FS_NONASCII)
|
|
||||||
|
|
||||||
|
|
||||||
def replace_stdout():
|
|
||||||
"""Set stdout encoder error handler to backslashreplace (as stderr error
|
|
||||||
handler) to avoid UnicodeEncodeError when printing a traceback"""
|
|
||||||
stdout = sys.stdout
|
|
||||||
try:
|
|
||||||
fd = stdout.fileno()
|
|
||||||
except ValueError:
|
|
||||||
# On IDLE, sys.stdout has no file descriptor and is not a TextIOWrapper
|
|
||||||
# object. Leaving sys.stdout unchanged.
|
|
||||||
#
|
|
||||||
# Catch ValueError to catch io.UnsupportedOperation on TextIOBase
|
|
||||||
# and ValueError on a closed stream.
|
|
||||||
return
|
|
||||||
|
|
||||||
sys.stdout = open(fd, 'w',
|
|
||||||
encoding=stdout.encoding,
|
|
||||||
errors="backslashreplace",
|
|
||||||
closefd=False,
|
|
||||||
newline='\n')
|
|
||||||
|
|
||||||
def restore_stdout():
|
|
||||||
sys.stdout.close()
|
|
||||||
sys.stdout = stdout
|
|
||||||
atexit.register(restore_stdout)
|
|
||||||
|
|
||||||
|
|
||||||
def _adjust_resource_limits():
|
|
||||||
"""Adjust the system resource limits (ulimit) if needed."""
|
|
||||||
try:
|
|
||||||
import resource
|
|
||||||
from resource import RLIMIT_NOFILE
|
|
||||||
except ImportError:
|
|
||||||
return
|
|
||||||
fd_limit, max_fds = resource.getrlimit(RLIMIT_NOFILE)
|
|
||||||
# On macOS the default fd limit is sometimes too low (256) for our
|
|
||||||
# test suite to succeed. Raise it to something more reasonable.
|
|
||||||
# 1024 is a common Linux default.
|
|
||||||
desired_fds = 1024
|
|
||||||
if fd_limit < desired_fds and fd_limit < max_fds:
|
|
||||||
new_fd_limit = min(desired_fds, max_fds)
|
|
||||||
try:
|
|
||||||
resource.setrlimit(RLIMIT_NOFILE, (new_fd_limit, max_fds))
|
|
||||||
print(f"Raised RLIMIT_NOFILE: {fd_limit} -> {new_fd_limit}")
|
|
||||||
except (ValueError, OSError) as err:
|
|
||||||
print(f"Unable to raise RLIMIT_NOFILE from {fd_limit} to "
|
|
||||||
f"{new_fd_limit}: {err}.")
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ from test.support import threading_helper
|
||||||
from test.libregrtest.result import State, TestResult
|
from test.libregrtest.result import State, TestResult
|
||||||
from test.libregrtest.runtests import RunTests
|
from test.libregrtest.runtests import RunTests
|
||||||
from test.libregrtest.save_env import saved_test_environment
|
from test.libregrtest.save_env import saved_test_environment
|
||||||
from test.libregrtest.setup import setup_support
|
from test.libregrtest.setup import setup_tests
|
||||||
from test.libregrtest.utils import (
|
from test.libregrtest.utils import (
|
||||||
TestName,
|
TestName,
|
||||||
clear_caches, remove_testfn, abs_module_name, print_warning)
|
clear_caches, remove_testfn, abs_module_name, print_warning)
|
||||||
|
@ -201,7 +201,7 @@ def _runtest(result: TestResult, runtests: RunTests) -> None:
|
||||||
faulthandler.dump_traceback_later(timeout, exit=True)
|
faulthandler.dump_traceback_later(timeout, exit=True)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
setup_support(runtests)
|
setup_tests(runtests)
|
||||||
|
|
||||||
if output_on_failure:
|
if output_on_failure:
|
||||||
support.verbose = True
|
support.verbose = True
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import atexit
|
||||||
import contextlib
|
import contextlib
|
||||||
import faulthandler
|
import faulthandler
|
||||||
import math
|
import math
|
||||||
|
@ -471,3 +472,55 @@ def normalize_test_name(test_full_name, *, is_error=False):
|
||||||
rpar = test_full_name.index(')')
|
rpar = test_full_name.index(')')
|
||||||
return test_full_name[lpar + 1: rpar].split('.')[-1]
|
return test_full_name[lpar + 1: rpar].split('.')[-1]
|
||||||
return short_name
|
return short_name
|
||||||
|
|
||||||
|
|
||||||
|
def replace_stdout():
|
||||||
|
"""Set stdout encoder error handler to backslashreplace (as stderr error
|
||||||
|
handler) to avoid UnicodeEncodeError when printing a traceback"""
|
||||||
|
stdout = sys.stdout
|
||||||
|
try:
|
||||||
|
fd = stdout.fileno()
|
||||||
|
except ValueError:
|
||||||
|
# On IDLE, sys.stdout has no file descriptor and is not a TextIOWrapper
|
||||||
|
# object. Leaving sys.stdout unchanged.
|
||||||
|
#
|
||||||
|
# Catch ValueError to catch io.UnsupportedOperation on TextIOBase
|
||||||
|
# and ValueError on a closed stream.
|
||||||
|
return
|
||||||
|
|
||||||
|
sys.stdout = open(fd, 'w',
|
||||||
|
encoding=stdout.encoding,
|
||||||
|
errors="backslashreplace",
|
||||||
|
closefd=False,
|
||||||
|
newline='\n')
|
||||||
|
|
||||||
|
def restore_stdout():
|
||||||
|
sys.stdout.close()
|
||||||
|
sys.stdout = stdout
|
||||||
|
atexit.register(restore_stdout)
|
||||||
|
|
||||||
|
|
||||||
|
def adjust_rlimit_nofile():
|
||||||
|
"""
|
||||||
|
On macOS the default fd limit (RLIMIT_NOFILE) is sometimes too low (256)
|
||||||
|
for our test suite to succeed. Raise it to something more reasonable. 1024
|
||||||
|
is a common Linux default.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
import resource
|
||||||
|
except ImportError:
|
||||||
|
return
|
||||||
|
|
||||||
|
fd_limit, max_fds = resource.getrlimit(resource.RLIMIT_NOFILE)
|
||||||
|
|
||||||
|
desired_fds = 1024
|
||||||
|
|
||||||
|
if fd_limit < desired_fds and fd_limit < max_fds:
|
||||||
|
new_fd_limit = min(desired_fds, max_fds)
|
||||||
|
try:
|
||||||
|
resource.setrlimit(resource.RLIMIT_NOFILE,
|
||||||
|
(new_fd_limit, max_fds))
|
||||||
|
print(f"Raised RLIMIT_NOFILE: {fd_limit} -> {new_fd_limit}")
|
||||||
|
except (ValueError, OSError) as err:
|
||||||
|
print_warning(f"Unable to raise RLIMIT_NOFILE from {fd_limit} to "
|
||||||
|
f"{new_fd_limit}: {err}.")
|
||||||
|
|
|
@ -6,7 +6,7 @@ from typing import TextIO, NoReturn
|
||||||
from test import support
|
from test import support
|
||||||
from test.support import os_helper
|
from test.support import os_helper
|
||||||
|
|
||||||
from test.libregrtest.setup import setup_tests, setup_test_dir
|
from test.libregrtest.setup import setup_process, setup_test_dir
|
||||||
from test.libregrtest.runtests import RunTests
|
from test.libregrtest.runtests import RunTests
|
||||||
from test.libregrtest.single import run_single_test
|
from test.libregrtest.single import run_single_test
|
||||||
from test.libregrtest.utils import (
|
from test.libregrtest.utils import (
|
||||||
|
@ -60,7 +60,7 @@ def worker_process(worker_json: StrJSON) -> NoReturn:
|
||||||
match_tests: FilterTuple | None = runtests.match_tests
|
match_tests: FilterTuple | None = runtests.match_tests
|
||||||
|
|
||||||
setup_test_dir(runtests.test_dir)
|
setup_test_dir(runtests.test_dir)
|
||||||
setup_tests(runtests)
|
setup_process()
|
||||||
|
|
||||||
if runtests.rerun:
|
if runtests.rerun:
|
||||||
if match_tests:
|
if match_tests:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue