Skip to content

Commit 47f1e64

Browse files
committed
gh-101634: regrtest reports decoding error as failed test
When running the Python test suite with -jN option, if a worker stdout cannot be decoded from the locale encoding report a failed testn so the exitcode is non-zero. Patch by Victor Stinner.
1 parent 84caa33 commit 47f1e64

File tree

3 files changed

+49
-6
lines changed

3 files changed

+49
-6
lines changed

Lib/test/libregrtest/runtest_mp.py

+3
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ def _runtest(self, test_name: str) -> MultiprocessResult:
277277
encoding = locale.getencoding()
278278
else:
279279
encoding = sys.stdout.encoding
280+
280281
# gh-94026: Write stdout+stderr to a tempfile as workaround for
281282
# non-blocking pipes on Emscripten with NodeJS.
282283
with tempfile.TemporaryFile('w+', encoding=encoding) as stdout_fh:
@@ -481,6 +482,8 @@ def _process_result(self, item: QueueOutput) -> bool:
481482
# Thread got an exception
482483
format_exc = item[1]
483484
print_warning(f"regrtest worker thread failed: {format_exc}")
485+
result = ChildError("<regrtest worker>")
486+
self.regrtest.accumulate_result(result)
484487
return True
485488

486489
self.test_index += 1

Lib/test/test_regrtest.py

+43-6
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ def check_executed_tests(self, output, tests, skipped=(), failed=(),
422422
env_changed=(), omitted=(),
423423
rerun={}, no_test_ran=(),
424424
randomize=False, interrupted=False,
425-
fail_env_changed=False):
425+
fail_env_changed=False, executed=None, good=None):
426426
if isinstance(tests, str):
427427
tests = [tests]
428428
if isinstance(skipped, str):
@@ -435,12 +435,14 @@ def check_executed_tests(self, output, tests, skipped=(), failed=(),
435435
omitted = [omitted]
436436
if isinstance(no_test_ran, str):
437437
no_test_ran = [no_test_ran]
438+
if executed is None:
439+
executed = tests
438440

439-
executed = self.parse_executed_tests(output)
441+
get_executed = self.parse_executed_tests(output)
440442
if randomize:
441-
self.assertEqual(set(executed), set(tests), output)
443+
self.assertEqual(set(get_executed), set(executed), output)
442444
else:
443-
self.assertEqual(executed, tests, output)
445+
self.assertEqual(get_executed, executed, output)
444446

445447
def plural(count):
446448
return 's' if count != 1 else ''
@@ -482,8 +484,9 @@ def list_regex(line_format, tests):
482484
regex = list_regex('%s test%s run no tests', no_test_ran)
483485
self.check_line(output, regex)
484486

485-
good = (len(tests) - len(skipped) - len(failed)
486-
- len(omitted) - len(env_changed) - len(no_test_ran))
487+
if good is None:
488+
good = (len(tests) - len(skipped) - len(failed)
489+
- len(omitted) - len(env_changed) - len(no_test_ran))
487490
if good:
488491
regex = r'%s test%s OK\.$' % (good, plural(good))
489492
if not skipped and not failed and good > 1:
@@ -1551,6 +1554,40 @@ def test_leak_tmp_file(self):
15511554
f"files (1): mytmpfile",
15521555
output)
15531556

1557+
def test_mp_decode_error(self):
1558+
# gh-101634: If a worker stdout cannot be decoded, report a failed test
1559+
# and a non-zero exit code.
1560+
if sys.platform == 'win32':
1561+
encoding = locale.getencoding()
1562+
else:
1563+
encoding = sys.stdout.encoding
1564+
1565+
nonascii = b"byte:\xa0\xa9\xff\n"
1566+
try:
1567+
nonascii.decode(encoding)
1568+
except UnicodeDecodeError:
1569+
pass
1570+
else:
1571+
self.skipTest(f"{encoding} can decode non-ASCII bytes {nonascii!a}")
1572+
1573+
code = textwrap.dedent(fr"""
1574+
import sys
1575+
# bytes which cannot be decoded from UTF-8
1576+
nonascii = {nonascii!a}
1577+
sys.stdout.buffer.write(nonascii)
1578+
sys.stdout.buffer.flush()
1579+
""")
1580+
testname = self.create_test(code=code)
1581+
1582+
output = self.run_tests("--fail-env-changed", "-v", "-j1", testname,
1583+
exitcode=EXITCODE_BAD_TEST)
1584+
self.check_executed_tests(output, [testname],
1585+
executed=[],
1586+
good=0,
1587+
failed="<regrtest worker>",
1588+
omitted=[testname],
1589+
randomize=True)
1590+
15541591

15551592
class TestUtils(unittest.TestCase):
15561593
def test_format_duration(self):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
When running the Python test suite with ``-jN`` option, if a worker stdout
2+
cannot be decoded from the locale encoding report a failed testn so the
3+
exitcode is non-zero. Patch by Victor Stinner.

0 commit comments

Comments
 (0)