@@ -422,7 +422,7 @@ def check_executed_tests(self, output, tests, skipped=(), failed=(),
422
422
env_changed = (), omitted = (),
423
423
rerun = {}, no_test_ran = (),
424
424
randomize = False , interrupted = False ,
425
- fail_env_changed = False ):
425
+ fail_env_changed = False , executed = None , good = None ):
426
426
if isinstance (tests , str ):
427
427
tests = [tests ]
428
428
if isinstance (skipped , str ):
@@ -435,12 +435,14 @@ def check_executed_tests(self, output, tests, skipped=(), failed=(),
435
435
omitted = [omitted ]
436
436
if isinstance (no_test_ran , str ):
437
437
no_test_ran = [no_test_ran ]
438
+ if executed is None :
439
+ executed = tests
438
440
439
- executed = self .parse_executed_tests (output )
441
+ get_executed = self .parse_executed_tests (output )
440
442
if randomize :
441
- self .assertEqual (set (executed ), set (tests ), output )
443
+ self .assertEqual (set (get_executed ), set (executed ), output )
442
444
else :
443
- self .assertEqual (executed , tests , output )
445
+ self .assertEqual (get_executed , executed , output )
444
446
445
447
def plural (count ):
446
448
return 's' if count != 1 else ''
@@ -482,8 +484,9 @@ def list_regex(line_format, tests):
482
484
regex = list_regex ('%s test%s run no tests' , no_test_ran )
483
485
self .check_line (output , regex )
484
486
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 ))
487
490
if good :
488
491
regex = r'%s test%s OK\.$' % (good , plural (good ))
489
492
if not skipped and not failed and good > 1 :
@@ -1551,6 +1554,40 @@ def test_leak_tmp_file(self):
1551
1554
f"files (1): mytmpfile" ,
1552
1555
output )
1553
1556
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
+
1554
1591
1555
1592
class TestUtils (unittest .TestCase ):
1556
1593
def test_format_duration (self ):
0 commit comments