Skip to content

Commit 4303918

Browse files
authored
Merge 1e68f59 into db5bfe9
2 parents db5bfe9 + 1e68f59 commit 4303918

File tree

4 files changed

+123
-99
lines changed

4 files changed

+123
-99
lines changed

Lib/test/libregrtest/logger.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import os
2+
import time
3+
4+
from test.libregrtest.runtest import RunTests
5+
from test.libregrtest.utils import print_warning, MS_WINDOWS
6+
7+
if MS_WINDOWS:
8+
from test.libregrtest.win_utils import WindowsLoadTracker
9+
10+
11+
class Logger:
12+
def __init__(self):
13+
self.start_time = time.perf_counter()
14+
self.test_count_text = ''
15+
self.test_count_width = 3
16+
self.win_load_tracker = None
17+
18+
def log(self, line: str = '') -> None:
19+
empty = not line
20+
21+
# add the system load prefix: "load avg: 1.80 "
22+
load_avg = self.get_load_avg()
23+
if load_avg is not None:
24+
line = f"load avg: {load_avg:.2f} {line}"
25+
26+
# add the timestamp prefix: "0:01:05 "
27+
test_time = time.perf_counter() - self.start_time
28+
29+
mins, secs = divmod(int(test_time), 60)
30+
hours, mins = divmod(mins, 60)
31+
test_time = "%d:%02d:%02d" % (hours, mins, secs)
32+
33+
line = f"{test_time} {line}"
34+
if empty:
35+
line = line[:-1]
36+
37+
print(line, flush=True)
38+
39+
def get_load_avg(self) -> float | None:
40+
if hasattr(os, 'getloadavg'):
41+
return os.getloadavg()[0]
42+
if self.win_load_tracker is not None:
43+
return self.win_load_tracker.getloadavg()
44+
return None
45+
46+
def set_tests(self, runtests: RunTests) -> None:
47+
if runtests.forever:
48+
self.test_count_text = ''
49+
self.test_count_width = 3
50+
else:
51+
self.test_count_text = '/{}'.format(len(runtests.tests))
52+
self.test_count_width = len(self.test_count_text) - 1
53+
54+
def start_load_tracker(self) -> None:
55+
if not MS_WINDOWS:
56+
return
57+
58+
try:
59+
self.win_load_tracker = WindowsLoadTracker()
60+
except PermissionError as error:
61+
# Standard accounts may not have access to the performance
62+
# counters.
63+
print_warning(f'Failed to create WindowsLoadTracker: {error}')
64+
65+
def stop_load_tracker(self) -> None:
66+
if self.win_load_tracker is None:
67+
return
68+
self.win_load_tracker.close()
69+
self.win_load_tracker = None

Lib/test/libregrtest/main.py

Lines changed: 50 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import time
1111
import unittest
1212
from test.libregrtest.cmdline import _parse_args, Namespace
13+
from test.libregrtest.logger import Logger
1314
from test.libregrtest.runtest import (
1415
findtests, split_test_packages, run_single_test, abs_module_name,
1516
PROGRESS_MIN_TIME, State, RunTests, HuntRefleak,
@@ -54,6 +55,8 @@ class Regrtest:
5455
on the command line.
5556
"""
5657
def __init__(self, ns: Namespace):
58+
self.logger = Logger()
59+
5760
# Actions
5861
self.want_header: bool = ns.header
5962
self.want_list_tests: bool = ns.list_tests
@@ -137,29 +140,8 @@ def __init__(self, ns: Namespace):
137140
self.next_single_test: TestName | None = None
138141
self.next_single_filename: StrPath | None = None
139142

140-
# misc
141-
self.win_load_tracker = None
142-
143143
def log(self, line=''):
144-
empty = not line
145-
146-
# add the system load prefix: "load avg: 1.80 "
147-
load_avg = self.getloadavg()
148-
if load_avg is not None:
149-
line = f"load avg: {load_avg:.2f} {line}"
150-
151-
# add the timestamp prefix: "0:01:05 "
152-
test_time = time.perf_counter() - self.start_time
153-
154-
mins, secs = divmod(int(test_time), 60)
155-
hours, mins = divmod(mins, 60)
156-
test_time = "%d:%02d:%02d" % (hours, mins, secs)
157-
158-
line = f"{test_time} {line}"
159-
if empty:
160-
line = line[:-1]
161-
162-
print(line, flush=True)
144+
self.logger.log(line)
163145

164146
def display_progress(self, test_index, text):
165147
if self.quiet:
@@ -293,7 +275,7 @@ def _rerun_failed_tests(self, runtests: RunTests):
293275
fail_fast=False,
294276
match_tests_dict=match_tests_dict,
295277
output_on_failure=False)
296-
self.set_tests(runtests)
278+
self.logger.set_tests(runtests)
297279
self._run_tests_mp(runtests, self.num_workers)
298280
return runtests
299281

@@ -437,44 +419,7 @@ def get_state(self):
437419

438420
def _run_tests_mp(self, runtests: RunTests, num_workers: int) -> None:
439421
from test.libregrtest.runtest_mp import RunWorkers
440-
441-
# If we're on windows and this is the parent runner (not a worker),
442-
# track the load average.
443-
if sys.platform == 'win32':
444-
from test.libregrtest.win_utils import WindowsLoadTracker
445-
446-
try:
447-
self.win_load_tracker = WindowsLoadTracker()
448-
except PermissionError as error:
449-
# Standard accounts may not have access to the performance
450-
# counters.
451-
print(f'Failed to create WindowsLoadTracker: {error}')
452-
453-
try:
454-
RunWorkers(self, runtests, num_workers).run()
455-
finally:
456-
if self.win_load_tracker is not None:
457-
self.win_load_tracker.close()
458-
self.win_load_tracker = None
459-
460-
def set_tests(self, runtests: RunTests):
461-
self.tests = runtests.tests
462-
if runtests.forever:
463-
self.test_count_text = ''
464-
self.test_count_width = 3
465-
else:
466-
self.test_count_text = '/{}'.format(len(self.tests))
467-
self.test_count_width = len(self.test_count_text) - 1
468-
469-
def run_tests(self, runtests: RunTests):
470-
self.first_runtests = runtests
471-
self.set_tests(runtests)
472-
if self.num_workers:
473-
self._run_tests_mp(runtests, self.num_workers)
474-
tracer = None
475-
else:
476-
tracer = self.run_tests_sequentially(runtests)
477-
return tracer
422+
RunWorkers(self, runtests, num_workers).run()
478423

479424
def finalize_tests(self, tracer):
480425
if self.next_single_filename:
@@ -496,7 +441,7 @@ def finalize_tests(self, tracer):
496441
self.results.write_junit(self.junit_filename)
497442

498443
def display_summary(self):
499-
duration = time.perf_counter() - self.start_time
444+
duration = time.perf_counter() - self.logger.start_time
500445
filtered = bool(self.match_tests) or bool(self.ignore_tests)
501446

502447
# Total duration
@@ -619,35 +564,8 @@ def main(self, tests: TestList | None = None):
619564

620565
sys.exit(exc.code)
621566

622-
def getloadavg(self):
623-
if self.win_load_tracker is not None:
624-
return self.win_load_tracker.getloadavg()
625-
626-
if hasattr(os, 'getloadavg'):
627-
return os.getloadavg()[0]
628-
629-
return None
630-
631-
def action_run_tests(self):
632-
if self.hunt_refleak and self.hunt_refleak.warmups < 3:
633-
msg = ("WARNING: Running tests with --huntrleaks/-R and "
634-
"less than 3 warmup repetitions can give false positives!")
635-
print(msg, file=sys.stdout, flush=True)
636-
637-
# For a partial run, we do not need to clutter the output.
638-
if (self.want_header
639-
or not(self.pgo or self.quiet or self.single_test_run
640-
or self.tests or self.cmdline_args)):
641-
self.display_header()
642-
643-
if self.randomize:
644-
print("Using random seed", self.random_seed)
645-
646-
if self.num_workers < 0:
647-
# Use all cores + extras for tests that like to sleep
648-
self.num_workers = 2 + (os.cpu_count() or 1)
649-
650-
runtests = RunTests(
567+
def create_run_tests(self):
568+
return RunTests(
651569
tuple(self.selected),
652570
fail_fast=self.fail_fast,
653571
match_tests=self.match_tests,
@@ -668,17 +586,53 @@ def action_run_tests(self):
668586
python_cmd=self.python_cmd,
669587
)
670588

589+
def run_tests(self) -> int:
590+
if self.hunt_refleak and self.hunt_refleak.warmups < 3:
591+
msg = ("WARNING: Running tests with --huntrleaks/-R and "
592+
"less than 3 warmup repetitions can give false positives!")
593+
print(msg, file=sys.stdout, flush=True)
594+
595+
if self.num_workers < 0:
596+
# Use all CPUs + 2 extra worker processes for tests
597+
# that like to sleep
598+
self.num_workers = (os.cpu_count() or 1) + 2
599+
600+
# For a partial run, we do not need to clutter the output.
601+
if (self.want_header
602+
or not(self.pgo or self.quiet or self.single_test_run
603+
or self.tests or self.cmdline_args)):
604+
self.display_header()
605+
606+
if self.randomize:
607+
print("Using random seed", self.random_seed)
608+
609+
runtests = self.create_run_tests()
610+
self.first_runtests = runtests
611+
self.logger.set_tests(runtests)
612+
671613
setup_tests(runtests)
672614

673-
tracer = self.run_tests(runtests)
674-
self.display_result(runtests)
615+
self.logger.start_load_tracker()
616+
try:
617+
if self.num_workers:
618+
self._run_tests_mp(runtests, self.num_workers)
619+
tracer = None
620+
else:
621+
tracer = self.run_tests_sequentially(runtests)
675622

676-
if self.want_rerun and self.results.need_rerun():
677-
self.rerun_failed_tests(runtests)
623+
self.display_result(runtests)
624+
625+
if self.want_rerun and self.results.need_rerun():
626+
self.rerun_failed_tests(runtests)
627+
finally:
628+
self.logger.stop_load_tracker()
678629

679630
self.display_summary()
680631
self.finalize_tests(tracer)
681632

633+
return self.results.get_exitcode(self.fail_env_changed,
634+
self.fail_rerun)
635+
682636
def _main(self):
683637
if self.is_worker():
684638
from test.libregrtest.runtest_mp import worker_process
@@ -697,9 +651,7 @@ def _main(self):
697651
elif self.want_list_cases:
698652
self.list_cases()
699653
else:
700-
self.action_run_tests()
701-
exitcode = self.results.get_exitcode(self.fail_env_changed,
702-
self.fail_rerun)
654+
exitcode = self.run_tests()
703655

704656
sys.exit(exitcode)
705657

Lib/test/libregrtest/runtest_mp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ def get_running(workers: list[WorkerThread]) -> list[str]:
433433
class RunWorkers:
434434
def __init__(self, regrtest: Regrtest, runtests: RunTests, num_workers: int) -> None:
435435
self.results: TestResults = regrtest.results
436-
self.log = regrtest.log
436+
self.log = regrtest.logger.log
437437
self.display_progress = regrtest.display_progress
438438
self.num_workers = num_workers
439439
self.runtests = runtests

Lib/test/libregrtest/utils.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
from test import support
77

88

9+
MS_WINDOWS = (sys.platform == 'win32')
10+
11+
912
def format_duration(seconds):
1013
ms = math.ceil(seconds * 1e3)
1114
seconds, ms = divmod(ms, 1000)

0 commit comments

Comments
 (0)