44
44
import multiprocessing
45
45
import multiprocessing .connection
46
46
import os
47
+ import pickle
47
48
import re
48
49
import shlex
49
50
import signal
@@ -294,6 +295,15 @@ def emit(self, **data):
294
295
self .conn .send (data )
295
296
296
297
298
+ class SimpleResult (AbstractRemoteResult ):
299
+ def __init__ (self , test_suite : 'TestSuite' , data : list [dict ]):
300
+ super ().__init__ (test_suite )
301
+ self .data = data
302
+
303
+ def emit (self , ** data ):
304
+ self .data .append (data )
305
+
306
+
297
307
def test_path_to_module (path : Path ):
298
308
return str (path ).removesuffix ('.py' ).replace (os .sep , '.' )
299
309
@@ -504,25 +514,33 @@ def run_partitions_in_subprocesses(self, executor, partitions: list[list[TestId]
504
514
]
505
515
try :
506
516
concurrent .futures .wait (futures )
517
+ for future in futures :
518
+ future .result ()
507
519
except KeyboardInterrupt :
508
520
self .stop_event .set ()
509
521
concurrent .futures .wait (futures )
510
522
print ("Interrupted!" )
511
523
sys .exit (1 )
512
524
513
525
def run_in_subprocess_and_watch (self , tests : list [TestId ]):
526
+ # noinspection PyUnresolvedReferences
527
+ use_pipe = not IS_GRAALPY or __graalpython__ .posix_module_backend () == 'native'
528
+ use_pipe = False
514
529
remaining_tests = tests
515
530
last_started_test : TestId | None = None
516
531
last_started_time : float | None = None
517
- last_out_pos = 0
518
- conn , child_conn = multiprocessing .Pipe ()
519
532
with (
520
533
tempfile .NamedTemporaryFile (prefix = 'graalpytest-in-' , mode = 'w+' ) as tests_file ,
521
534
tempfile .NamedTemporaryFile (prefix = 'graalpytest-out-' , mode = 'w+' ) as out_file ,
522
535
):
523
536
env = os .environ .copy ()
524
537
env ['IN_PROCESS' ] = '1'
525
538
539
+ if use_pipe :
540
+ pipe , child_pipe = multiprocessing .Pipe ()
541
+ else :
542
+ result_file = out_file .name + '.result'
543
+
526
544
def process_event (event ):
527
545
nonlocal remaining_tests , last_started_test , last_started_time , last_out_pos
528
546
match event ['event' ]:
@@ -557,9 +575,12 @@ def process_event(event):
557
575
'-u' ,
558
576
* self .subprocess_args ,
559
577
__file__ ,
560
- '--pipe-fd' , str (child_conn .fileno ()),
561
578
'--tests-file' , tests_file .name ,
562
579
]
580
+ if use_pipe :
581
+ cmd += ['--pipe-fd' , str (child_pipe .fileno ())]
582
+ else :
583
+ cmd += ['--result-file' , result_file ]
563
584
if self .failfast :
564
585
cmd .append ('--failfast' )
565
586
# We communicate the tests through a temp file to avoid running into too long commandlines on windows
@@ -572,29 +593,37 @@ def process_event(event):
572
593
stdout = out_file ,
573
594
stderr = out_file ,
574
595
env = env ,
575
- pass_fds = [child_conn .fileno ()],
596
+ pass_fds = [child_pipe .fileno ()] if use_pipe else [ ],
576
597
)
577
598
578
599
timed_out = False
579
600
580
- while process .poll () is None :
581
- while conn .poll (0.1 ):
582
- process_event (conn .recv ())
583
- if self .stop_event .is_set ():
584
- interrupt_process (process )
585
- break
586
- if last_started_time is not None and time .time () - last_started_time >= self .default_test_timeout :
587
- interrupt_process (process )
588
- timed_out = True
589
- # Drain the pipe
590
- while conn .poll (0.1 ):
591
- conn .recv ()
592
- break
601
+ if use_pipe :
602
+ while process .poll () is None :
603
+ while pipe .poll (0.1 ):
604
+ process_event (pipe .recv ())
605
+ if self .stop_event .is_set ():
606
+ interrupt_process (process )
607
+ break
608
+ if last_started_time is not None and time .time () - last_started_time >= self .default_test_timeout :
609
+ interrupt_process (process )
610
+ timed_out = True
611
+ # Drain the pipe
612
+ while pipe .poll (0.1 ):
613
+ pipe .recv ()
614
+ break
615
+
593
616
returncode = process .wait ()
594
617
if self .stop_event .is_set ():
595
618
return
596
- while conn .poll (0.1 ):
597
- process_event (conn .recv ())
619
+ if use_pipe :
620
+ while pipe .poll (0.1 ):
621
+ process_event (pipe .recv ())
622
+ else :
623
+ with open (result_file , 'rb' ) as f :
624
+ for file_event in pickle .load (f ):
625
+ process_event (file_event )
626
+
598
627
if returncode != 0 or timed_out :
599
628
out_file .seek (last_out_pos )
600
629
output = out_file .read ()
@@ -618,8 +647,8 @@ def process_event(event):
618
647
continue
619
648
else :
620
649
# Crashed outside of tests, don't retry
621
- self .crashes .append (output )
622
- return
650
+ self .crashes .append (output or 'Runner subprocess crashed' )
651
+ return
623
652
624
653
625
654
def filter_tree (test_file : Path , test_suite : unittest .TestSuite , specifiers : list [TestSpecifier ],
@@ -867,7 +896,9 @@ def loadTestsFromModule(self, module, *, pattern=None):
867
896
868
897
def in_process ():
869
898
parser = argparse .ArgumentParser ()
870
- parser .add_argument ('--pipe-fd' , type = int , required = True )
899
+ group = parser .add_mutually_exclusive_group ()
900
+ group .add_argument ('--pipe-fd' , type = int )
901
+ group .add_argument ('--result-file' , type = Path )
871
902
parser .add_argument ('--tests-file' , type = Path , required = True )
872
903
parser .add_argument ('--failfast' , action = 'store_true' )
873
904
args = parser .parse_args ()
@@ -876,13 +907,26 @@ def in_process():
876
907
for line in f :
877
908
tests .append (TestSpecifier .from_str (line .strip ()))
878
909
879
- conn = multiprocessing .connection .Connection (args .pipe_fd )
910
+ data = []
911
+ if args .pipe_fd :
912
+ conn = multiprocessing .connection .Connection (args .pipe_fd )
913
+
914
+ def result_factory (suite ):
915
+ return PipeResult (suite , conn )
916
+ else :
917
+ def result_factory (suite ):
918
+ return SimpleResult (suite , data )
880
919
881
920
for test_suite in collect (tests ):
882
- result = PipeResult (test_suite , conn )
921
+ result = result_factory (test_suite )
883
922
result .failfast = args .failfast
884
923
test_suite .run (result )
885
924
925
+ if args .result_file :
926
+ with open (args .result_file , 'wb' ) as f :
927
+ # noinspection PyTypeChecker
928
+ pickle .dump (data , f )
929
+
886
930
887
931
def get_bool_env (name : str ):
888
932
return os .environ .get (name , '' ).lower () in ('true' , '1' )
0 commit comments