Skip to content

Commit 557e6b7

Browse files
This allows the test runner to execute test_multiprocessing and test_asyncio in parallel.
By sharding the individual tests in test_multiprocessing and test_asyncio we are able to reduce considerably the time it takes to run the whole test suite.
1 parent 44b5c21 commit 557e6b7

File tree

1 file changed

+51
-34
lines changed

1 file changed

+51
-34
lines changed

Lib/test/libregrtest/runtest.py

Lines changed: 51 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@
1919

2020
class TestResult:
2121
def __init__(
22-
self,
23-
name: str,
24-
duration_sec: float = 0.0,
25-
xml_data: list[str] | None = None,
22+
self,
23+
name: str,
24+
duration_sec: float = 0.0,
25+
xml_data: list[str] | None = None,
2626
) -> None:
2727
self.name = name
2828
self.duration_sec = duration_sec
@@ -39,12 +39,12 @@ def __str__(self) -> str:
3939

4040
class Failed(TestResult):
4141
def __init__(
42-
self,
43-
name: str,
44-
duration_sec: float = 0.0,
45-
xml_data: list[str] | None = None,
46-
errors: list[tuple[str, str]] | None = None,
47-
failures: list[tuple[str, str]] | None = None,
42+
self,
43+
name: str,
44+
duration_sec: float = 0.0,
45+
xml_data: list[str] | None = None,
46+
errors: list[tuple[str, str]] | None = None,
47+
failures: list[tuple[str, str]] | None = None,
4848
) -> None:
4949
super().__init__(name, duration_sec=duration_sec, xml_data=xml_data)
5050
self.errors = errors
@@ -128,21 +128,30 @@ def __str__(self) -> str:
128128
# small set of tests to determine if we have a basically functioning interpreter
129129
# (i.e. if any of these fail, then anything else is likely to follow)
130130
STDTESTS = [
131-
'test_grammar',
132-
'test_opcodes',
133-
'test_dict',
134-
'test_builtin',
135-
'test_exceptions',
136-
'test_types',
137-
'test_unittest',
138-
'test_doctest',
139-
'test_doctest2',
140-
'test_support'
131+
'test_grammar',
132+
'test_opcodes',
133+
'test_dict',
134+
'test_builtin',
135+
'test_exceptions',
136+
'test_types',
137+
'test_unittest',
138+
'test_doctest',
139+
'test_doctest2',
140+
'test_support'
141141
]
142142

143143
# set of tests that we don't want to be executed when using regrtest
144144
NOTTESTS = set()
145145

146+
#If these test directories are encountered recurse into them and treat each
147+
# test_ .py or dir as a separate test module. This can increase parallelism.
148+
# Beware this can't generally be done for any directory with sub-tests as the
149+
# __init__.py may do things which alter what tests are to be run.
150+
151+
SPLITTESTDIRS = {
152+
"test_asyncio",
153+
"test_compiler",
154+
}
146155

147156
# Storage of uncollectable objects
148157
FOUND_GARBAGE = []
@@ -158,16 +167,24 @@ def findtestdir(path=None):
158167
return path or os.path.dirname(os.path.dirname(__file__)) or os.curdir
159168

160169

161-
def findtests(testdir=None, stdtests=STDTESTS, nottests=NOTTESTS):
170+
def findtests(testdir=None, stdtests=STDTESTS, nottests=NOTTESTS, splittestdirs=SPLITTESTDIRS, base_mod=""):
162171
"""Return a list of all applicable test modules."""
163172
testdir = findtestdir(testdir)
164173
names = os.listdir(testdir)
165174
tests = []
166175
others = set(stdtests) | nottests
167176
for name in names:
168177
mod, ext = os.path.splitext(name)
169-
if mod[:5] == "test_" and ext in (".py", "") and mod not in others:
170-
tests.append(mod)
178+
if mod[:5] == "test_" and mod not in others:
179+
if mod in splittestdirs:
180+
subdir = os.path.join(testdir, mod)
181+
if len(base_mod):
182+
mod = f"{base_mod}.{mod}"
183+
else:
184+
mod = f"test.{mod}"
185+
tests.extend(findtests(subdir, [], nottests, splittestdirs, mod))
186+
elif ext in (".py", ""):
187+
tests.append(f"{base_mod}.{mod}" if len(base_mod) else mod)
171188
return stdtests + sorted(tests)
172189

173190

@@ -186,7 +203,7 @@ def _runtest(ns: Namespace, test_name: str) -> TestResult:
186203
output_on_failure = ns.verbose3
187204

188205
use_timeout = (
189-
ns.timeout is not None and threading_helper.can_start_thread
206+
ns.timeout is not None and threading_helper.can_start_thread
190207
)
191208
if use_timeout:
192209
faulthandler.dump_traceback_later(ns.timeout, exit=True)
@@ -217,7 +234,7 @@ def _runtest(ns: Namespace, test_name: str) -> TestResult:
217234
print_warning.orig_stderr = stream
218235

219236
result = _runtest_inner(ns, test_name,
220-
display_failure=False)
237+
display_failure=False)
221238
if not isinstance(result, Passed):
222239
output = stream.getvalue()
223240
finally:
@@ -233,13 +250,13 @@ def _runtest(ns: Namespace, test_name: str) -> TestResult:
233250
support.verbose = ns.verbose
234251

235252
result = _runtest_inner(ns, test_name,
236-
display_failure=not ns.verbose)
253+
display_failure=not ns.verbose)
237254

238255
if xml_list:
239256
import xml.etree.ElementTree as ET
240257
result.xml_data = [
241-
ET.tostring(x).decode('us-ascii')
242-
for x in xml_list
258+
ET.tostring(x).decode('us-ascii')
259+
for x in xml_list
243260
]
244261

245262
result.duration_sec = time.perf_counter() - start_time
@@ -267,7 +284,7 @@ def runtest(ns: Namespace, test_name: str) -> TestResult:
267284
if not ns.pgo:
268285
msg = traceback.format_exc()
269286
print(f"test {test_name} crashed -- {msg}",
270-
file=sys.stderr, flush=True)
287+
file=sys.stderr, flush=True)
271288
return Failed(test_name)
272289

273290

@@ -328,7 +345,7 @@ def _runtest_inner2(ns: Namespace, test_name: str) -> bool:
328345
if gc.garbage:
329346
support.environment_altered = True
330347
print_warning(f"{test_name} created {len(gc.garbage)} "
331-
f"uncollectable object(s).")
348+
f"uncollectable object(s).")
332349

333350
# move the uncollectable objects somewhere,
334351
# so we don't see them again
@@ -341,7 +358,7 @@ def _runtest_inner2(ns: Namespace, test_name: str) -> bool:
341358

342359

343360
def _runtest_inner(
344-
ns: Namespace, test_name: str, display_failure: bool = True
361+
ns: Namespace, test_name: str, display_failure: bool = True
345362
) -> TestResult:
346363
# Detect environment changes, handle exceptions.
347364

@@ -387,7 +404,7 @@ def _runtest_inner(
387404
if not ns.pgo:
388405
msg = traceback.format_exc()
389406
print(f"test {test_name} crashed -- {msg}",
390-
file=sys.stderr, flush=True)
407+
file=sys.stderr, flush=True)
391408
return UncaughtException(test_name)
392409

393410
if refleak:
@@ -415,7 +432,7 @@ def cleanup_test_droppings(test_name: str, verbose: int) -> None:
415432
kind, nuker = "file", os.unlink
416433
else:
417434
raise RuntimeError(f"os.path says {name!r} exists but is neither "
418-
f"directory nor file")
435+
f"directory nor file")
419436

420437
if verbose:
421438
print_warning(f"{test_name} left behind {kind} {name!r}")
@@ -428,4 +445,4 @@ def cleanup_test_droppings(test_name: str, verbose: int) -> None:
428445
nuker(name)
429446
except Exception as exc:
430447
print_warning(f"{test_name} left behind {kind} {name!r} "
431-
f"and it couldn't be removed: {exc}")
448+
f"and it couldn't be removed: {exc}")

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy