Skip to content

Commit 7462923

Browse files
committed
Add file-base communication mode
1 parent 4c3d679 commit 7462923

File tree

1 file changed

+68
-24
lines changed
  • graalpython/com.oracle.graal.python.test/src

1 file changed

+68
-24
lines changed

graalpython/com.oracle.graal.python.test/src/runner.py

Lines changed: 68 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import multiprocessing
4545
import multiprocessing.connection
4646
import os
47+
import pickle
4748
import re
4849
import shlex
4950
import signal
@@ -294,6 +295,15 @@ def emit(self, **data):
294295
self.conn.send(data)
295296

296297

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+
297307
def test_path_to_module(path: Path):
298308
return str(path).removesuffix('.py').replace(os.sep, '.')
299309

@@ -504,25 +514,33 @@ def run_partitions_in_subprocesses(self, executor, partitions: list[list[TestId]
504514
]
505515
try:
506516
concurrent.futures.wait(futures)
517+
for future in futures:
518+
future.result()
507519
except KeyboardInterrupt:
508520
self.stop_event.set()
509521
concurrent.futures.wait(futures)
510522
print("Interrupted!")
511523
sys.exit(1)
512524

513525
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
514529
remaining_tests = tests
515530
last_started_test: TestId | None = None
516531
last_started_time: float | None = None
517-
last_out_pos = 0
518-
conn, child_conn = multiprocessing.Pipe()
519532
with (
520533
tempfile.NamedTemporaryFile(prefix='graalpytest-in-', mode='w+') as tests_file,
521534
tempfile.NamedTemporaryFile(prefix='graalpytest-out-', mode='w+') as out_file,
522535
):
523536
env = os.environ.copy()
524537
env['IN_PROCESS'] = '1'
525538

539+
if use_pipe:
540+
pipe, child_pipe = multiprocessing.Pipe()
541+
else:
542+
result_file = out_file.name + '.result'
543+
526544
def process_event(event):
527545
nonlocal remaining_tests, last_started_test, last_started_time, last_out_pos
528546
match event['event']:
@@ -557,9 +575,12 @@ def process_event(event):
557575
'-u',
558576
*self.subprocess_args,
559577
__file__,
560-
'--pipe-fd', str(child_conn.fileno()),
561578
'--tests-file', tests_file.name,
562579
]
580+
if use_pipe:
581+
cmd += ['--pipe-fd', str(child_pipe.fileno())]
582+
else:
583+
cmd += ['--result-file', result_file]
563584
if self.failfast:
564585
cmd.append('--failfast')
565586
# 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):
572593
stdout=out_file,
573594
stderr=out_file,
574595
env=env,
575-
pass_fds=[child_conn.fileno()],
596+
pass_fds=[child_pipe.fileno()] if use_pipe else [],
576597
)
577598

578599
timed_out = False
579600

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+
593616
returncode = process.wait()
594617
if self.stop_event.is_set():
595618
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+
598627
if returncode != 0 or timed_out:
599628
out_file.seek(last_out_pos)
600629
output = out_file.read()
@@ -618,8 +647,8 @@ def process_event(event):
618647
continue
619648
else:
620649
# 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
623652

624653

625654
def filter_tree(test_file: Path, test_suite: unittest.TestSuite, specifiers: list[TestSpecifier],
@@ -867,7 +896,9 @@ def loadTestsFromModule(self, module, *, pattern=None):
867896

868897
def in_process():
869898
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)
871902
parser.add_argument('--tests-file', type=Path, required=True)
872903
parser.add_argument('--failfast', action='store_true')
873904
args = parser.parse_args()
@@ -876,13 +907,26 @@ def in_process():
876907
for line in f:
877908
tests.append(TestSpecifier.from_str(line.strip()))
878909

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)
880919

881920
for test_suite in collect(tests):
882-
result = PipeResult(test_suite, conn)
921+
result = result_factory(test_suite)
883922
result.failfast = args.failfast
884923
test_suite.run(result)
885924

925+
if args.result_file:
926+
with open(args.result_file, 'wb') as f:
927+
# noinspection PyTypeChecker
928+
pickle.dump(data, f)
929+
886930

887931
def get_bool_env(name: str):
888932
return os.environ.get(name, '').lower() in ('true', '1')

0 commit comments

Comments
 (0)