◐ Shell
reader mode source ↗
Skip to content
Merged
Show file tree
Changes from all commits
File filter
Conversations
Jump to
Diff view
Apply and reload
Show whitespace
Diff view
Apply and reload
4 changes: 3 additions & 1 deletion Lib/test/libregrtest/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from test.libregrtest.runtest import (
findtests, runtest, get_abs_module,
STDTESTS, NOTTESTS, PASSED, FAILED, ENV_CHANGED, SKIPPED, RESOURCE_DENIED,
INTERRUPTED, CHILD_ERROR, TEST_DID_NOT_RUN,
PROGRESS_MIN_TIME, format_test_result, is_failed)
from test.libregrtest.setup import setup_tests
from test.libregrtest.utils import removepy, count, format_duration, printlist
Expand Down Expand Up @@ -114,6 +114,8 @@ def accumulate_result(self, result, rerun=False):
self.run_no_tests.append(test_name)
elif ok == INTERRUPTED:
self.interrupted = True
else:
raise ValueError("invalid test result: %r" % ok)

Expand Down
10 changes: 8 additions & 2 deletions Lib/test/libregrtest/runtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from test import support
from test.libregrtest.refleak import dash_R, clear_caches
from test.libregrtest.save_env import saved_test_environment
from test.libregrtest.utils import print_warning


# Test result constants.
Expand All @@ -25,6 +25,7 @@
INTERRUPTED = -4
CHILD_ERROR = -5 # error in a child process
TEST_DID_NOT_RUN = -6

_FORMAT_TEST_RESULT = {
PASSED: '%s passed',
Expand All @@ -35,6 +36,7 @@
INTERRUPTED: '%s interrupted',
CHILD_ERROR: '%s crashed',
TEST_DID_NOT_RUN: '%s run no tests',
}

# Minimum duration of a test to display its duration or to mention that
Expand Down Expand Up @@ -75,7 +77,10 @@ def is_failed(result, ns):

def format_test_result(result):
fmt = _FORMAT_TEST_RESULT.get(result.result, "%s")
return fmt % result.test_name


def findtestdir(path=None):
Expand Down Expand Up @@ -179,6 +184,7 @@ def runtest(ns, test_name):
FAILED test failed
PASSED test passed
EMPTY_TEST_SUITE test ran no subtests.

If ns.xmlpath is not None, xml_data is a list containing each
generated testsuite element.
Expand Down
49 changes: 38 additions & 11 deletions Lib/test/libregrtest/runtest_mp.py
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@

from test.libregrtest.runtest import (
runtest, INTERRUPTED, CHILD_ERROR, PROGRESS_MIN_TIME,
format_test_result, TestResult, is_failed)
from test.libregrtest.setup import setup_tests
from test.libregrtest.utils import format_duration

Expand Down Expand Up @@ -103,11 +103,12 @@ class ExitThread(Exception):


class MultiprocessThread(threading.Thread):
def __init__(self, pending, output, ns):
super().__init__()
self.pending = pending
self.output = output
self.ns = ns
self.current_test_name = None
self.start_time = None
self._popen = None
Expand All @@ -126,6 +127,12 @@ def __repr__(self):
return '<%s>' % ' '.join(info)

def kill(self):
self._killed = True

popen = self._popen
Expand All @@ -136,6 +143,13 @@ def kill(self):
# does not hang
popen.stdout.close()
popen.stderr.close()

def _runtest(self, test_name):
try:
Expand All @@ -154,7 +168,19 @@ def _runtest(self, test_name):
raise ExitThread

try:
stdout, stderr = popen.communicate()
except OSError:
if self._killed:
# kill() has been called: communicate() fails
Expand All @@ -163,7 +189,6 @@ def _runtest(self, test_name):
raise
except:
self.kill()
popen.wait()
raise

retcode = popen.wait()
Expand Down @@ -191,8 +216,7 @@ def _runtest(self, test_name):
err_msg = "Failed to parse worker JSON: %s" % exc

if err_msg is not None:
test_time = time.monotonic() - self.start_time
result = TestResult(test_name, CHILD_ERROR, test_time, None)

return MultiprocessResult(result, stdout, stderr, err_msg)

Expand Down Expand Up @@ -236,13 +260,16 @@ def __init__(self, regrtest):
self.output = queue.Queue()
self.pending = MultiprocessIterator(self.regrtest.tests)
if self.ns.timeout is not None:
self.test_timeout = self.ns.timeout * 1.5
else:
self.test_timeout = None
self.workers = None

def start_workers(self):
self.workers = [MultiprocessThread(self.pending, self.output, self.ns)
for _ in range(self.ns.use_mp)]
print("Run tests in parallel using %s child processes"
% len(self.workers))
Expand Down Expand Up @@ -274,8 +301,8 @@ def _get_result(self):
return None

while True:
if self.test_timeout is not None:
faulthandler.dump_traceback_later(self.test_timeout, exit=True)

# wait for a thread
timeout = max(PROGRESS_UPDATE, PROGRESS_MIN_TIME)
Expand Down Expand Up @@ -343,7 +370,7 @@ def run_tests(self):
print()
self.regrtest.interrupted = True
finally:
if self.test_timeout is not None:
faulthandler.cancel_dump_traceback_later()

# a test failed (and --failfast is set) or all tests completed
Expand Down
3 changes: 2 additions & 1 deletion Lib/test/libregrtest/win_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@
class WindowsLoadTracker():
"""
This class asynchronously interacts with the `typeperf` command to read
the system load on Windows. Mulitprocessing and threads can't be used
here because they interfere with the test suite's cases for those
modules.
"""

def __init__(self):
self.load = 0.0
self.start()

def start(self):
Expand Down
27 changes: 27 additions & 0 deletions Lib/test/test_regrtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -1121,6 +1121,33 @@ def test_garbage(self):
env_changed=[testname],
fail_env_changed=True)

def test_cleanup(self):
dirname = os.path.join(self.tmptestdir, "test_python_123")
os.mkdir(dirname)
Expand Down
Toggle all file notes Toggle all file annotations