Kaydet (Commit) 76f756d9 authored tarafından Victor Stinner's avatar Victor Stinner

Issue #25220: Enhance regrtest -jN

Running the Python test suite with -jN now:

- Display the duration of tests which took longer than 30 seconds
- Display the tests currently running since at least 30 seconds
- Display the tests we are waiting for when the test suite is interrupted

Clenaup also run_test_in_subprocess() code.
üst 02319804
import json import json
import os import os
import re
import sys import sys
import time
import traceback import traceback
import unittest import unittest
from queue import Queue from queue import Queue
...@@ -15,7 +15,12 @@ except ImportError: ...@@ -15,7 +15,12 @@ except ImportError:
from test.libregrtest.runtest import runtest, INTERRUPTED, CHILD_ERROR from test.libregrtest.runtest import runtest, INTERRUPTED, CHILD_ERROR
def run_tests_in_subprocess(testname, ns): # Minimum duration of a test to display its duration or to mention that
# the test is running in background
PROGRESS_MIN_TIME = 30.0 # seconds
def run_test_in_subprocess(testname, ns):
"""Run the given test in a subprocess with --slaveargs. """Run the given test in a subprocess with --slaveargs.
ns is the option Namespace parsed from command-line arguments. regrtest ns is the option Namespace parsed from command-line arguments. regrtest
...@@ -24,26 +29,33 @@ def run_tests_in_subprocess(testname, ns): ...@@ -24,26 +29,33 @@ def run_tests_in_subprocess(testname, ns):
3-tuple. 3-tuple.
""" """
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
base_cmd = ([sys.executable] + support.args_from_interpreter_flags() +
['-X', 'faulthandler', '-m', 'test.regrtest']) args = (testname, ns.verbose, ns.quiet)
kwargs = dict(huntrleaks=ns.huntrleaks,
slaveargs = ( use_resources=ns.use_resources,
(testname, ns.verbose, ns.quiet), output_on_failure=ns.verbose3,
dict(huntrleaks=ns.huntrleaks, timeout=ns.timeout,
use_resources=ns.use_resources, failfast=ns.failfast,
output_on_failure=ns.verbose3, match_tests=ns.match_tests)
timeout=ns.timeout, failfast=ns.failfast, slaveargs = (args, kwargs)
match_tests=ns.match_tests)) slaveargs = json.dumps(slaveargs)
cmd = [sys.executable, *support.args_from_interpreter_flags(),
'-X', 'faulthandler',
'-m', 'test.regrtest',
'--slaveargs', slaveargs]
# Running the child from the same working directory as regrtest's original # Running the child from the same working directory as regrtest's original
# invocation ensures that TEMPDIR for the child is the same when # invocation ensures that TEMPDIR for the child is the same when
# sysconfig.is_python_build() is true. See issue 15300. # sysconfig.is_python_build() is true. See issue 15300.
popen = Popen(base_cmd + ['--slaveargs', json.dumps(slaveargs)], popen = Popen(cmd,
stdout=PIPE, stderr=PIPE, stdout=PIPE, stderr=PIPE,
universal_newlines=True, universal_newlines=True,
close_fds=(os.name != 'nt'), close_fds=(os.name != 'nt'),
cwd=support.SAVEDCWD) cwd=support.SAVEDCWD)
stdout, stderr = popen.communicate() with popen:
retcode = popen.wait() stdout, stderr = popen.communicate()
retcode = popen.wait()
return retcode, stdout, stderr return retcode, stdout, stderr
...@@ -90,30 +102,45 @@ class MultiprocessThread(threading.Thread): ...@@ -90,30 +102,45 @@ class MultiprocessThread(threading.Thread):
self.pending = pending self.pending = pending
self.output = output self.output = output
self.ns = ns self.ns = ns
self.current_test = None
self.start_time = None
def _runtest(self):
try:
test = next(self.pending)
except StopIteration:
self.output.put((None, None, None, None))
return True
try:
self.start_time = time.monotonic()
self.current_test = test
retcode, stdout, stderr = run_test_in_subprocess(test, self.ns)
finally:
self.current_test = None
stdout, _, result = stdout.strip().rpartition("\n")
if retcode != 0:
result = (CHILD_ERROR, "Exit code %s" % retcode)
self.output.put((test, stdout.rstrip(), stderr.rstrip(),
result))
return True
if not result:
self.output.put((None, None, None, None))
return True
result = json.loads(result)
self.output.put((test, stdout.rstrip(), stderr.rstrip(),
result))
return False
def run(self): def run(self):
# A worker thread.
try: try:
while True: stop = False
try: while not stop:
test = next(self.pending) stop = self._runtest()
except StopIteration:
self.output.put((None, None, None, None))
return
retcode, stdout, stderr = run_tests_in_subprocess(test,
self.ns)
stdout, _, result = stdout.strip().rpartition("\n")
if retcode != 0:
result = (CHILD_ERROR, "Exit code %s" % retcode)
self.output.put((test, stdout.rstrip(), stderr.rstrip(),
result))
return
if not result:
self.output.put((None, None, None, None))
return
result = json.loads(result)
self.output.put((test, stdout.rstrip(), stderr.rstrip(),
result))
except BaseException: except BaseException:
self.output.put((None, None, None, None)) self.output.put((None, None, None, None))
raise raise
...@@ -136,13 +163,33 @@ def run_tests_multiprocess(regrtest): ...@@ -136,13 +163,33 @@ def run_tests_multiprocess(regrtest):
finished += 1 finished += 1
continue continue
regrtest.accumulate_result(test, result) regrtest.accumulate_result(test, result)
regrtest.display_progress(test_index, test)
# Display progress
text = test
ok, test_time = result
if (ok not in (CHILD_ERROR, INTERRUPTED)
and test_time >= PROGRESS_MIN_TIME):
text += ' (%.0f sec)' % test_time
running = []
for worker in workers:
current_test = worker.current_test
if not current_test:
continue
dt = time.monotonic() - worker.start_time
if dt >= PROGRESS_MIN_TIME:
running.append('%s (%.0f sec)' % (current_test, dt))
if running:
text += ' -- running: %s' % ', '.join(running)
regrtest.display_progress(test_index, text)
# Copy stdout and stderr from the child process
if stdout: if stdout:
print(stdout) print(stdout)
if stderr: if stderr:
print(stderr, file=sys.stderr) print(stderr, file=sys.stderr)
sys.stdout.flush() sys.stdout.flush()
sys.stderr.flush() sys.stderr.flush()
if result[0] == INTERRUPTED: if result[0] == INTERRUPTED:
raise KeyboardInterrupt raise KeyboardInterrupt
if result[0] == CHILD_ERROR: if result[0] == CHILD_ERROR:
...@@ -152,5 +199,11 @@ def run_tests_multiprocess(regrtest): ...@@ -152,5 +199,11 @@ def run_tests_multiprocess(regrtest):
except KeyboardInterrupt: except KeyboardInterrupt:
regrtest.interrupted = True regrtest.interrupted = True
pending.interrupted = True pending.interrupted = True
print()
running = [worker.current_test for worker in workers]
running = list(filter(bool, running))
if running:
print("Waiting for %s" % ', '.join(running))
for worker in workers: for worker in workers:
worker.join() worker.join()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment