@@ -204,39 +204,44 @@ class AbstractResult(unittest.TestResult):
204
204
def __init__ (self , test_suite : 'TestSuite' ):
205
205
super ().__init__ ()
206
206
self .test_suite = test_suite
207
+ self .start_time = None
207
208
208
209
def test_id (self , test ):
209
210
return TestId .from_test_case (self .test_suite .test_file , test )
210
211
212
+ def startTest (self , test ):
213
+ self .start_time = time .time ()
214
+
211
215
@abstractmethod
212
216
def report_result (self , result : TestResult ):
213
217
pass
214
218
219
+ def make_result (self , test , status : TestStatus , ** kwargs ):
220
+ return TestResult (status = status , test_id = self .test_id (test ), duration = (time .time () - self .start_time ), ** kwargs )
221
+
215
222
def addSuccess (self , test ):
216
223
super ().addSuccess (test )
217
- self .report_result (TestResult ( status = TestStatus .SUCCESS , test_id = self . test_id ( test ) ))
224
+ self .report_result (self . make_result ( test , status = TestStatus .SUCCESS ))
218
225
219
226
def addFailure (self , test , err ):
220
227
super ().addFailure (test , err )
221
- self .report_result (
222
- TestResult (status = TestStatus .FAILURE , test_id = self .test_id (test ), param = format_exception (err )))
228
+ self .report_result (self .make_result (test , status = TestStatus .FAILURE , param = format_exception (err )))
223
229
224
230
def addError (self , test , err ):
225
231
super ().addError (test , err )
226
- self .report_result (TestResult ( status = TestStatus .ERROR , test_id = self . test_id ( test ) , param = format_exception (err )))
232
+ self .report_result (self . make_result ( test , status = TestStatus .ERROR , param = format_exception (err )))
227
233
228
234
def addSkip (self , test , reason ):
229
235
super ().addSkip (test , reason )
230
- self .report_result (TestResult ( status = TestStatus .SKIPPED , test_id = self . test_id ( test ) , param = reason ))
236
+ self .report_result (self . make_result ( test , status = TestStatus .SKIPPED , param = reason ))
231
237
232
238
def addExpectedFailure (self , test , err ):
233
239
super ().addExpectedFailure (test , err )
234
- self .report_result (
235
- TestResult (status = TestStatus .EXPECTED_FAILURE , test_id = self .test_id (test ), param = format_exception (err )))
240
+ self .report_result (self .make_result (test , status = TestStatus .EXPECTED_FAILURE , param = format_exception (err )))
236
241
237
242
def addUnexpectedSuccess (self , test ):
238
243
super ().addUnexpectedSuccess (test )
239
- self .report_result (TestResult ( status = TestStatus .UNEXPECTED_SUCCESS , test_id = self . test_id ( test ) ))
244
+ self .report_result (self . make_result ( test , status = TestStatus .UNEXPECTED_SUCCESS ))
240
245
241
246
242
247
class DirectResult (AbstractResult ):
@@ -286,21 +291,14 @@ class TestRunner:
286
291
def __init__ (self , * , failfast , report_durations ):
287
292
self .failfast = failfast
288
293
self .report_durations = report_durations
289
- self .events = []
290
294
self .results = []
291
295
self .total_duration = 0.0
292
- self .last_started_test = None
293
- self .last_started_timestamp = None
294
296
295
- def report_start (self , test_id : TestId ):
296
- self .last_started_test = test_id
297
- self .last_started_timestamp = time .time ()
297
+ @staticmethod
298
+ def report_start (test_id : TestId ):
298
299
log (f"{ test_id } ... " , incomplete = True )
299
300
300
301
def report_result (self , result : TestResult ):
301
- result .duration = time .time () - self .last_started_timestamp
302
- self .last_started_timestamp = None
303
- self .last_started_test = None
304
302
self .results .append (result )
305
303
message = f"{ result .test_id } ... { result .status } "
306
304
if result .status == TestStatus .SKIPPED and result .param :
@@ -398,6 +396,7 @@ def generate_mx_report(self, path: str):
398
396
'duration' : result .duration ,
399
397
})
400
398
with open (path , 'w' ) as f :
399
+ # noinspection PyTypeChecker
401
400
json .dump (report_data , f )
402
401
403
402
@@ -422,7 +421,6 @@ def __init__(self, *, num_processes, subprocess_args, separate_workers, **kwargs
422
421
self .separate_workers = separate_workers
423
422
self .stop_event = threading .Event ()
424
423
self .crashes = []
425
- self .last_out_pos = 0
426
424
self .default_test_timeout = 600
427
425
428
426
def report_result (self , result : TestResult ):
@@ -484,42 +482,51 @@ def run_partitions_in_subprocesses(self, executor, partitions: list[list[TestId]
484
482
print ("Interrupted!" )
485
483
sys .exit (1 )
486
484
487
- def process_event (self , event , out_file ):
488
- self .events .append (event )
489
- match event ['event' ]:
490
- case 'testStarted' :
491
- self .last_out_pos = event ['out_pos' ]
492
- self .report_start (event ['test' ])
493
- case 'testResult' :
494
- out_end = event ['out_pos' ]
495
- test_output = ''
496
- if self .last_out_pos != out_end :
497
- out_file .seek (self .last_out_pos )
498
- test_output = out_file .read (out_end - self .last_out_pos )
499
- result = TestResult (
500
- test_id = event ['test' ],
501
- status = event ['status' ],
502
- param = event .get ('param' ),
503
- output = test_output ,
504
- )
505
- self .report_result (result )
506
- self .last_out_pos = event ['out_pos' ]
507
-
508
485
def run_in_subprocess_and_watch (self , tests : list [TestId ]):
486
+ remaining_tests = tests
487
+ last_started_test : TestId | None = None
488
+ last_started_time : float | None = None
489
+ last_out_pos = 0
509
490
conn , child_conn = multiprocessing .Pipe ()
510
491
with (
511
492
tempfile .NamedTemporaryFile (prefix = 'graalpytest-in-' , mode = 'w+' ) as tests_file ,
512
493
tempfile .NamedTemporaryFile (prefix = 'graalpytest-out-' , mode = 'w+' ) as out_file ,
513
494
):
514
495
env = os .environ .copy ()
515
496
env ['IN_PROCESS' ] = '1'
516
- remaining_tests = tests
497
+
498
+ def process_event (event ):
499
+ nonlocal remaining_tests , last_started_test , last_started_time , last_out_pos
500
+ match event ['event' ]:
501
+ case 'testStarted' :
502
+ remaining_tests .remove (event ['test' ])
503
+ self .report_start (event ['test' ])
504
+ last_started_test = event ['test' ]
505
+ last_started_time = time .time ()
506
+ last_out_pos = event ['out_pos' ]
507
+ case 'testResult' :
508
+ out_end = event ['out_pos' ]
509
+ test_output = ''
510
+ if last_out_pos != out_end :
511
+ out_file .seek (last_out_pos )
512
+ test_output = out_file .read (out_end - last_out_pos )
513
+ result = TestResult (
514
+ test_id = event ['test' ],
515
+ status = event ['status' ],
516
+ param = event .get ('param' ),
517
+ output = test_output ,
518
+ )
519
+ self .report_result (result )
520
+ last_started_test = None
521
+ last_started_time = None
522
+ last_out_pos = event ['out_pos' ]
523
+
517
524
while remaining_tests and not self .stop_event .is_set ():
518
- self .last_out_pos = out_file .tell ()
519
- python_args = ['-u' , * self .subprocess_args ]
525
+ last_out_pos = out_file .tell ()
520
526
cmd = [
521
527
sys .executable ,
522
- * python_args ,
528
+ '-u' ,
529
+ * self .subprocess_args ,
523
530
__file__ ,
524
531
'--pipe-fd' , str (child_conn .fileno ()),
525
532
'--tests-file' , tests_file .name ,
@@ -543,11 +550,11 @@ def run_in_subprocess_and_watch(self, tests: list[TestId]):
543
550
544
551
while process .poll () is None :
545
552
while conn .poll (0.1 ):
546
- self . process_event (conn .recv (), out_file )
553
+ process_event (conn .recv ())
547
554
if self .stop_event .is_set ():
548
555
interrupt_process (process )
549
556
break
550
- if self . last_started_timestamp is not None and time .time () - self . last_started_timestamp >= self .default_test_timeout :
557
+ if last_started_time is not None and time .time () - last_started_time >= self .default_test_timeout :
551
558
interrupt_process (process )
552
559
timed_out = True
553
560
# Drain the pipe
@@ -558,11 +565,11 @@ def run_in_subprocess_and_watch(self, tests: list[TestId]):
558
565
if self .stop_event .is_set ():
559
566
return
560
567
while conn .poll (0.1 ):
561
- self . process_event (conn .recv (), out_file )
568
+ process_event (conn .recv ())
562
569
if returncode != 0 or timed_out :
563
- out_file .seek (self . last_out_pos )
570
+ out_file .seek (last_out_pos )
564
571
output = out_file .read ()
565
- if self . last_started_test :
572
+ if last_started_test :
566
573
if timed_out :
567
574
message = "Timed out"
568
575
elif returncode >= 0 :
@@ -574,12 +581,11 @@ def run_in_subprocess_and_watch(self, tests: list[TestId]):
574
581
signal_name = str (- returncode )
575
582
message = f"Test process killed by signal { signal_name } "
576
583
self .report_result (TestResult (
577
- test_id = self . last_started_test ,
584
+ test_id = last_started_test ,
578
585
status = TestStatus .ERROR ,
579
586
param = message ,
580
587
output = output ,
581
588
))
582
- remaining_tests = remaining_tests [remaining_tests .index (self .last_started_test ) + 1 :]
583
589
continue
584
590
else :
585
591
# Crashed outside of tests, don't retry
@@ -893,9 +899,11 @@ def main():
893
899
894
900
if IS_GRAALPY :
895
901
if get_bool_env ('GRAALPYTEST_ALLOW_NO_JAVA_ASSERTIONS' ):
902
+ # noinspection PyUnresolvedReferences
896
903
if not __graalpython__ .java_assert ():
897
904
sys .exit (
898
905
"Java assertions are not enabled, refusing to run. Add --vm.ea to your invocation. Set GRAALPYTEST_ALLOW_NO_JAVA_ASSERTIONS=true to disable this check\n " )
906
+ # noinspection PyUnresolvedReferences
899
907
if not hasattr (__graalpython__ , 'tdebug' ):
900
908
sys .exit ("Needs to be run with --experimental-options --python.EnableDebuggingBuiltins\n " )
901
909
0 commit comments