]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.11] gh-101634: regrtest reports decoding error as failed test (#106169) (#106175)
authorVictor Stinner <vstinner@python.org>
Wed, 28 Jun 2023 02:58:34 +0000 (04:58 +0200)
committerGitHub <noreply@github.com>
Wed, 28 Jun 2023 02:58:34 +0000 (02:58 +0000)
gh-101634: regrtest reports decoding error as failed test (#106169)

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.

(cherry picked from commit 2ac3eec103cf450aaaebeb932e51155d2e7fb37b)

Lib/test/libregrtest/runtest_mp.py
Lib/test/test_regrtest.py
Misc/NEWS.d/next/Tests/2023-06-28-02-51-08.gh-issue-101634.Rayczr.rst [new file with mode: 0644]

index c6a4dae2df86a9d44c14124da94109840f6dd73e..8587e64e5153e625bd67c89ec5eb5089dd0e4bc4 100644 (file)
@@ -269,6 +269,7 @@ class TestWorkerProcess(threading.Thread):
             encoding = locale.getencoding()
         else:
             encoding = sys.stdout.encoding
+
         # gh-94026: Write stdout+stderr to a tempfile as workaround for
         # non-blocking pipes on Emscripten with NodeJS.
         with tempfile.TemporaryFile('w+', encoding=encoding) as stdout_fh:
@@ -277,7 +278,14 @@ class TestWorkerProcess(threading.Thread):
             # Python finalization: too late for libregrtest.
             retcode = self._run_process(test_name, stdout_fh)
             stdout_fh.seek(0)
-            stdout = stdout_fh.read().strip()
+
+            try:
+                stdout = stdout_fh.read().strip()
+            except Exception as exc:
+                # gh-101634: Catch UnicodeDecodeError if stdout cannot be
+                # decoded from encoding
+                err_msg = f"Cannot read process stdout: {exc}"
+                return self.mp_result_error(ChildError(test_name), '', err_msg)
 
         if retcode is None:
             return self.mp_result_error(Timeout(test_name), stdout)
@@ -452,6 +460,8 @@ class MultiprocessTestRunner:
             # Thread got an exception
             format_exc = item[1]
             print_warning(f"regrtest worker thread failed: {format_exc}")
+            result = ChildError("<regrtest worker>")
+            self.regrtest.accumulate_result(result)
             return True
 
         self.test_index += 1
index f692dd1e3e65b47673d3f2ec552287050977e3f8..78552590a7289cd1b401814c65c894158e7c86ef 100644 (file)
@@ -7,6 +7,7 @@ Note: test_regrtest cannot be run twice in parallel.
 import contextlib
 import glob
 import io
+import locale
 import os.path
 import platform
 import re
@@ -1518,6 +1519,41 @@ class ArgsTestCase(BaseTestCase):
         for name in names:
             self.assertFalse(os.path.exists(name), name)
 
+    def test_mp_decode_error(self):
+        # gh-101634: If a worker stdout cannot be decoded, report a failed test
+        # and a non-zero exit code.
+        if sys.platform == 'win32':
+            encoding = locale.getencoding()
+        else:
+            encoding = sys.stdout.encoding
+            if encoding is None:
+                encoding = sys.__stdout__.encoding
+                if encoding is None:
+                    self.skipTest(f"cannot get regrtest worker encoding")
+
+        nonascii = b"byte:\xa0\xa9\xff\n"
+        try:
+            nonascii.decode(encoding)
+        except UnicodeDecodeError:
+            pass
+        else:
+            self.skipTest(f"{encoding} can decode non-ASCII bytes {nonascii!a}")
+
+        code = textwrap.dedent(fr"""
+            import sys
+            # bytes which cannot be decoded from UTF-8
+            nonascii = {nonascii!a}
+            sys.stdout.buffer.write(nonascii)
+            sys.stdout.buffer.flush()
+        """)
+        testname = self.create_test(code=code)
+
+        output = self.run_tests("--fail-env-changed", "-v", "-j1", testname,
+                                exitcode=EXITCODE_BAD_TEST)
+        self.check_executed_tests(output, [testname],
+                                  failed=[testname],
+                                  randomize=True)
+
 
 class TestUtils(unittest.TestCase):
     def test_format_duration(self):
diff --git a/Misc/NEWS.d/next/Tests/2023-06-28-02-51-08.gh-issue-101634.Rayczr.rst b/Misc/NEWS.d/next/Tests/2023-06-28-02-51-08.gh-issue-101634.Rayczr.rst
new file mode 100644 (file)
index 0000000..6fbfc84
--- /dev/null
@@ -0,0 +1,3 @@
+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.