1515from test .libregrtest .logger import Logger
1616from test .libregrtest .result import State
1717from test .libregrtest .runtests import RunTests , HuntRefleak
18- from test .libregrtest .setup import setup_tests , setup_test_dir
18+ from test .libregrtest .setup import setup_process , setup_test_dir
1919from test .libregrtest .single import run_single_test , PROGRESS_MIN_TIME
2020from test .libregrtest .pgo import setup_pgo_tests
2121from test .libregrtest .results import TestResults
2222from test .libregrtest .utils import (
23- StrPath , StrJSON , TestName , TestList , FilterTuple ,
23+ StrPath , StrJSON , TestName , TestList , TestTuple , FilterTuple ,
2424 strip_py_suffix , count , format_duration ,
2525 printlist , get_build_info , get_temp_dir , get_work_dir , exit_timeout ,
2626 abs_module_name )
@@ -51,7 +51,7 @@ class Regrtest:
5151 """
5252 def __init__ (self , ns : Namespace ):
5353 # Log verbosity
54- self .verbose : bool = ns .verbose
54+ self .verbose : int = int ( ns .verbose )
5555 self .quiet : bool = ns .quiet
5656 self .pgo : bool = ns .pgo
5757 self .pgo_extended : bool = ns .pgo_extended
@@ -122,8 +122,6 @@ def __init__(self, ns: Namespace):
122122 self .tmp_dir : StrPath | None = ns .tempdir
123123
124124 # tests
125- self .tests = []
126- self .selected : TestList = []
127125 self .first_runtests : RunTests | None = None
128126
129127 # used by --slowest
@@ -140,18 +138,18 @@ def __init__(self, ns: Namespace):
140138 def log (self , line = '' ):
141139 self .logger .log (line )
142140
143- def find_tests (self ) :
141+ def find_tests (self , tests : TestList | None = None ) -> tuple [ TestTuple , TestList | None ] :
144142 if self .single_test_run :
145143 self .next_single_filename = os .path .join (self .tmp_dir , 'pynexttest' )
146144 try :
147145 with open (self .next_single_filename , 'r' ) as fp :
148146 next_test = fp .read ().strip ()
149- self . tests = [next_test ]
147+ tests = [next_test ]
150148 except OSError :
151149 pass
152150
153151 if self .fromfile :
154- self . tests = []
152+ tests = []
155153 # regex to match 'test_builtin' in line:
156154 # '0:00:00 [ 4/400] test_builtin -- test_dict took 1 sec'
157155 regex = re .compile (r'\btest_[a-zA-Z0-9_]+\b' )
@@ -161,9 +159,9 @@ def find_tests(self):
161159 line = line .strip ()
162160 match = regex .search (line )
163161 if match is not None :
164- self . tests .append (match .group ())
162+ tests .append (match .group ())
165163
166- strip_py_suffix (self . tests )
164+ strip_py_suffix (tests )
167165
168166 if self .pgo :
169167 # add default PGO tests if no tests are specified
@@ -179,26 +177,26 @@ def find_tests(self):
179177 exclude = exclude_tests )
180178
181179 if not self .fromfile :
182- self . selected = self . tests or self .cmdline_args
183- if self . selected :
184- self . selected = split_test_packages (self . selected )
180+ selected = tests or self .cmdline_args
181+ if selected :
182+ selected = split_test_packages (selected )
185183 else :
186- self . selected = alltests
184+ selected = alltests
187185 else :
188- self . selected = self . tests
186+ selected = tests
189187
190188 if self .single_test_run :
191- self . selected = self . selected [:1 ]
189+ selected = selected [:1 ]
192190 try :
193- pos = alltests .index (self . selected [0 ])
191+ pos = alltests .index (selected [0 ])
194192 self .next_single_test = alltests [pos + 1 ]
195193 except IndexError :
196194 pass
197195
198196 # Remove all the selected tests that precede start if it's set.
199197 if self .starting_test :
200198 try :
201- del self . selected [:self . selected .index (self .starting_test )]
199+ del selected [:selected .index (self .starting_test )]
202200 except ValueError :
203201 print (f"Cannot find starting test: { self .starting_test } " )
204202 sys .exit (1 )
@@ -207,10 +205,12 @@ def find_tests(self):
207205 if self .random_seed is None :
208206 self .random_seed = random .randrange (100_000_000 )
209207 random .seed (self .random_seed )
210- random .shuffle (self .selected )
208+ random .shuffle (selected )
209+
210+ return (tuple (selected ), tests )
211211
212212 @staticmethod
213- def list_tests (tests : TestList ):
213+ def list_tests (tests : TestTuple ):
214214 for name in tests :
215215 print (name )
216216
@@ -224,12 +224,12 @@ def _list_cases(self, suite):
224224 if support .match_test (test ):
225225 print (test .id ())
226226
227- def list_cases (self ):
227+ def list_cases (self , tests : TestTuple ):
228228 support .verbose = False
229229 support .set_match_tests (self .match_tests , self .ignore_tests )
230230
231231 skipped = []
232- for test_name in self . selected :
232+ for test_name in tests :
233233 module_name = abs_module_name (test_name , self .test_dir )
234234 try :
235235 suite = unittest .defaultTestLoader .loadTestsFromName (module_name )
@@ -247,6 +247,10 @@ def list_cases(self):
247247 def _rerun_failed_tests (self , runtests : RunTests ):
248248 # Configure the runner to re-run tests
249249 if self .num_workers == 0 :
250+ # Always run tests in fresh processes to have more deterministic
251+ # initial state. Don't re-run tests in parallel but limit to a
252+ # single worker process to have side effects (on the system load
253+ # and timings) between tests.
250254 self .num_workers = 1
251255
252256 tests , match_tests_dict = self .results .prepare_rerun ()
@@ -294,7 +298,8 @@ def display_result(self, runtests):
294298 print ()
295299 print (f"== Tests result: { state } ==" )
296300
297- self .results .display_result (self .selected , self .quiet , self .print_slowest )
301+ self .results .display_result (runtests .tests ,
302+ self .quiet , self .print_slowest )
298303
299304 def run_test (self , test_name : TestName , runtests : RunTests , tracer ):
300305 if tracer is not None :
@@ -404,7 +409,7 @@ def get_state(self):
404409 return state
405410
406411 def _run_tests_mp (self , runtests : RunTests , num_workers : int ) -> None :
407- from test .libregrtest .runtest_mp import RunWorkers
412+ from test .libregrtest .run_workers import RunWorkers
408413 RunWorkers (num_workers , runtests , self .logger , self .results ).run ()
409414
410415 def finalize_tests (self , tracer ):
@@ -454,39 +459,9 @@ def cleanup_temp_dir(tmp_dir: StrPath):
454459 print ("Remove file: %s" % name )
455460 os_helper .unlink (name )
456461
457- def main (self , tests : TestList | None = None ):
458- if self .junit_filename and not os .path .isabs (self .junit_filename ):
459- self .junit_filename = os .path .abspath (self .junit_filename )
460-
461- self .tests = tests
462-
463- strip_py_suffix (self .cmdline_args )
464-
465- self .tmp_dir = get_temp_dir (self .tmp_dir )
466-
467- if self .want_cleanup :
468- self .cleanup_temp_dir (self .tmp_dir )
469- sys .exit (0 )
470-
471- os .makedirs (self .tmp_dir , exist_ok = True )
472- work_dir = get_work_dir (parent_dir = self .tmp_dir )
473-
474- with exit_timeout ():
475- # Run the tests in a context manager that temporarily changes the
476- # CWD to a temporary and writable directory. If it's not possible
477- # to create or change the CWD, the original CWD will be used.
478- # The original CWD is available from os_helper.SAVEDCWD.
479- with os_helper .temp_cwd (work_dir , quiet = True ):
480- # When using multiprocessing, worker processes will use
481- # work_dir as their parent temporary directory. So when the
482- # main process exit, it removes also subdirectories of worker
483- # processes.
484-
485- self ._main ()
486-
487- def create_run_tests (self ):
462+ def create_run_tests (self , tests : TestTuple ):
488463 return RunTests (
489- tuple ( self . selected ) ,
464+ tests ,
490465 fail_fast = self .fail_fast ,
491466 match_tests = self .match_tests ,
492467 ignore_tests = self .ignore_tests ,
@@ -506,7 +481,7 @@ def create_run_tests(self):
506481 python_cmd = self .python_cmd ,
507482 )
508483
509- def run_tests (self ) -> int :
484+ def _run_tests (self , selected : TestTuple , tests : TestList | None ) -> int :
510485 if self .hunt_refleak and self .hunt_refleak .warmups < 3 :
511486 msg = ("WARNING: Running tests with --huntrleaks/-R and "
512487 "less than 3 warmup repetitions can give false positives!" )
@@ -520,17 +495,17 @@ def run_tests(self) -> int:
520495 # For a partial run, we do not need to clutter the output.
521496 if (self .want_header
522497 or not (self .pgo or self .quiet or self .single_test_run
523- or self . tests or self .cmdline_args )):
498+ or tests or self .cmdline_args )):
524499 self .display_header ()
525500
526501 if self .randomize :
527502 print ("Using random seed" , self .random_seed )
528503
529- runtests = self .create_run_tests ()
504+ runtests = self .create_run_tests (selected )
530505 self .first_runtests = runtests
531506 self .logger .set_tests (runtests )
532507
533- setup_tests ( runtests )
508+ setup_process ( )
534509
535510 self .logger .start_load_tracker ()
536511 try :
@@ -553,20 +528,48 @@ def run_tests(self) -> int:
553528 return self .results .get_exitcode (self .fail_env_changed ,
554529 self .fail_rerun )
555530
556- def _main (self ):
531+ def run_tests (self , selected : TestTuple , tests : TestList | None ) -> int :
532+ os .makedirs (self .tmp_dir , exist_ok = True )
533+ work_dir = get_work_dir (parent_dir = self .tmp_dir )
534+
535+ # Put a timeout on Python exit
536+ with exit_timeout ():
537+ # Run the tests in a context manager that temporarily changes the
538+ # CWD to a temporary and writable directory. If it's not possible
539+ # to create or change the CWD, the original CWD will be used.
540+ # The original CWD is available from os_helper.SAVEDCWD.
541+ with os_helper .temp_cwd (work_dir , quiet = True ):
542+ # When using multiprocessing, worker processes will use
543+ # work_dir as their parent temporary directory. So when the
544+ # main process exit, it removes also subdirectories of worker
545+ # processes.
546+ return self ._run_tests (selected , tests )
547+
548+ def main (self , tests : TestList | None = None ):
549+ if self .junit_filename and not os .path .isabs (self .junit_filename ):
550+ self .junit_filename = os .path .abspath (self .junit_filename )
551+
552+ strip_py_suffix (self .cmdline_args )
553+
554+ self .tmp_dir = get_temp_dir (self .tmp_dir )
555+
556+ if self .want_cleanup :
557+ self .cleanup_temp_dir (self .tmp_dir )
558+ sys .exit (0 )
559+
557560 if self .want_wait :
558561 input ("Press any key to continue..." )
559562
560563 setup_test_dir (self .test_dir )
561- self .find_tests ()
564+ selected , tests = self .find_tests (tests )
562565
563566 exitcode = 0
564567 if self .want_list_tests :
565- self .list_tests (self . selected )
568+ self .list_tests (selected )
566569 elif self .want_list_cases :
567- self .list_cases ()
570+ self .list_cases (selected )
568571 else :
569- exitcode = self .run_tests ()
572+ exitcode = self .run_tests (selected , tests )
570573
571574 sys .exit (exitcode )
572575
0 commit comments