forked from python/cpython
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
pythongh-108834: Cleanup libregrtest
Remove "global variables" from libregrtest: no longer pass 'ns' magic namespace which was used to get and set 'random' attributes. Instead, each class has its own attributes, and some classes are even read-only (frozen dataclass), like RunTests. Reorganize files: * Rename runtest.py to single.py. * Rename runtest_mp.py to mp_runner.py. * Create findtests.py, result.py, results.py and worker.py. Reorganize classes: * Copy command line options ('ns' namespace) to Regrtest and RunTests attributes: add many attributes. * Add Results class with methods: get_state(), display_result(), get_exitcode(), JUnit methods, etc. * Add Logger class: log(), display_progress(), system load tracker. * Remove WorkerJob class: the worker now gets a RunTests instance. Move function and methods: * Convert Regrtest.list_cases() to a function. * Convert display_header(), display_sanitizers(), set_temp_dir() and create_temp_dir() methods of Regrtest to functions in utils.py. Rename set_temp_dir() to select_temp_dir(). Rename create_temp_dir() to make_temp_dir(). * Move abs_module_name() and normalize_test_name() to utils.py. * Move capture_output() to utils.py. * Rename dash_R() to runtest_refleak() * Merge display_sanitizers() code into display_header(). cmdline.py changes: * Rename internal --worker-args command line option to --worker-json. * Rename ns.trace to ns.coverage. * No longer gets the number of CPUs: it's now done by Regrtest class. * Add missing attributes to Namespace: coverage, threshold, wait. Misc: * Add test_parse_memlimit() and test_set_memlimit() to test_support. * Add some type annotations.
- Loading branch information
Showing
21 changed files
with
1,820 additions
and
1,566 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,6 @@ | ||
from test.libregrtest.cmdline import _parse_args, RESOURCE_NAMES, ALL_RESOURCES | ||
from test.libregrtest.main import main | ||
from test.support import TestStats | ||
from .cmdline import _parse_args, RESOURCE_NAMES, ALL_RESOURCES | ||
from .result import FilterTuple, State, TestResult | ||
from .runtests import TestsTuple, FilterDict, RunTests | ||
from .results import TestsList, Results | ||
from .main import main |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import os.path | ||
import sys | ||
import unittest | ||
from test import support | ||
|
||
from .result import FilterTuple | ||
from .utils import abs_module_name, count, printlist | ||
|
||
|
||
#If these test directories are encountered recurse into them and treat each | ||
# test_ .py or dir as a separate test module. This can increase parallelism. | ||
# Beware this can't generally be done for any directory with sub-tests as the | ||
# __init__.py may do things which alter what tests are to be run. | ||
|
||
SPLITTESTDIRS = { | ||
"test_asyncio", | ||
"test_concurrent_futures", | ||
"test_multiprocessing_fork", | ||
"test_multiprocessing_forkserver", | ||
"test_multiprocessing_spawn", | ||
} | ||
|
||
|
||
def findtestdir(path=None): | ||
return path or os.path.dirname(os.path.dirname(__file__)) or os.curdir | ||
|
||
|
||
def findtests(*, testdir=None, exclude=(), | ||
split_test_dirs=SPLITTESTDIRS, base_mod=""): | ||
"""Return a list of all applicable test modules.""" | ||
testdir = findtestdir(testdir) | ||
tests = [] | ||
for name in os.listdir(testdir): | ||
mod, ext = os.path.splitext(name) | ||
if (not mod.startswith("test_")) or (mod in exclude): | ||
continue | ||
if mod in split_test_dirs: | ||
subdir = os.path.join(testdir, mod) | ||
mod = f"{base_mod or 'test'}.{mod}" | ||
tests.extend(findtests(testdir=subdir, exclude=exclude, | ||
split_test_dirs=split_test_dirs, base_mod=mod)) | ||
elif ext in (".py", ""): | ||
tests.append(f"{base_mod}.{mod}" if base_mod else mod) | ||
return sorted(tests) | ||
|
||
|
||
def split_test_packages(tests, *, testdir=None, exclude=(), | ||
split_test_dirs=SPLITTESTDIRS): | ||
testdir = findtestdir(testdir) | ||
splitted = [] | ||
for name in tests: | ||
if name in split_test_dirs: | ||
subdir = os.path.join(testdir, name) | ||
splitted.extend(findtests(testdir=subdir, exclude=exclude, | ||
split_test_dirs=split_test_dirs, | ||
base_mod=name)) | ||
else: | ||
splitted.append(name) | ||
return splitted | ||
|
||
|
||
def _list_cases(suite): | ||
for test in suite: | ||
if isinstance(test, unittest.loader._FailedTest): | ||
continue | ||
if isinstance(test, unittest.TestSuite): | ||
_list_cases(test) | ||
elif isinstance(test, unittest.TestCase): | ||
if support.match_test(test): | ||
print(test.id()) | ||
|
||
def list_cases(tests, *, test_dir: str, | ||
match_tests: FilterTuple | None = None, | ||
ignore_tests: FilterTuple | None = None): | ||
support.verbose = False | ||
support.set_match_tests(match_tests, ignore_tests) | ||
|
||
skipped = [] | ||
for test_name in tests: | ||
module_name = abs_module_name(test_name, test_dir) | ||
try: | ||
suite = unittest.defaultTestLoader.loadTestsFromName(module_name) | ||
_list_cases(suite) | ||
except unittest.SkipTest: | ||
skipped.append(test_name) | ||
|
||
if skipped: | ||
sys.stdout.flush() | ||
stderr = sys.stderr | ||
print(file=stderr) | ||
print(count(len(skipped), "test"), "skipped:", file=stderr) | ||
printlist(skipped, file=stderr) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import os | ||
import sys | ||
import time | ||
|
||
from . import RunTests | ||
|
||
|
||
class Logger: | ||
def __init__(self, pgo: bool): | ||
self.start_time = time.perf_counter() | ||
self.win_load_tracker = None | ||
self.pgo = pgo | ||
|
||
# used to display the progress bar "[ 3/100]" | ||
self.test_count_text = '' | ||
self.test_count_width = 1 | ||
|
||
def set_tests(self, runtests: RunTests): | ||
if runtests.forever: | ||
self.test_count_text = '' | ||
self.test_count_width = 3 | ||
else: | ||
self.test_count_text = '/{}'.format(len(runtests.tests)) | ||
self.test_count_width = len(self.test_count_text) - 1 | ||
|
||
def start_load_tracker(self): | ||
if sys.platform != 'win32': | ||
return | ||
|
||
# If we're on windows and this is the parent runner (not a worker), | ||
# track the load average. | ||
from .win_utils import WindowsLoadTracker | ||
|
||
try: | ||
self.win_load_tracker = WindowsLoadTracker() | ||
except PermissionError as error: | ||
# Standard accounts may not have access to the performance | ||
# counters. | ||
print(f'Failed to create WindowsLoadTracker: {error}') | ||
|
||
def stop_load_tracker(self): | ||
if self.win_load_tracker is not None: | ||
self.win_load_tracker.close() | ||
self.win_load_tracker = None | ||
|
||
def get_time(self): | ||
return time.perf_counter() - self.start_time | ||
|
||
def getloadavg(self): | ||
if self.win_load_tracker is not None: | ||
return self.win_load_tracker.getloadavg() | ||
|
||
if hasattr(os, 'getloadavg'): | ||
return os.getloadavg()[0] | ||
|
||
return None | ||
|
||
def log(self, line=''): | ||
empty = not line | ||
|
||
# add the system load prefix: "load avg: 1.80 " | ||
load_avg = self.getloadavg() | ||
if load_avg is not None: | ||
line = f"load avg: {load_avg:.2f} {line}" | ||
|
||
# add the timestamp prefix: "0:01:05 " | ||
test_time = self.get_time() | ||
|
||
mins, secs = divmod(int(test_time), 60) | ||
hours, mins = divmod(mins, 60) | ||
test_time = "%d:%02d:%02d" % (hours, mins, secs) | ||
|
||
line = f"{test_time} {line}" | ||
if empty: | ||
line = line[:-1] | ||
|
||
print(line, flush=True) | ||
|
||
def display_progress(self, test_index, text, results, runtests): | ||
quiet = runtests.quiet | ||
if quiet: | ||
return | ||
|
||
# "[ 51/405/1] test_tcl passed" | ||
line = f"{test_index:{self.test_count_width}}{self.test_count_text}" | ||
fails = len(results.bad) + len(results.environment_changed) | ||
if fails and not self.pgo: | ||
line = f"{line}/{fails}" | ||
self.log(f"[{line}] {text}") |
Oops, something went wrong.