From 046172a7a825627f792d58cdbc7c342d3da80df7 Mon Sep 17 00:00:00 2001 From: zolaski333 Date: Wed, 14 Oct 2026 20:32:31 +0200 Subject: [PATCH] gh-159425: Fix concurrent zlib compressor test with intermediate output --- Lib/test/test_free_threading/test_zlib.py | 49 +++++++++++++------ ...-10-14-15-00-00.gh-issue-159425.4RzJpQ.rst | 2 + 2 files changed, 35 insertions(+), 16 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2026-10-14-15-00-00.gh-issue-159425.4RzJpQ.rst diff --git a/Lib/test/test_free_threading/test_zlib.py b/Lib/test/test_free_threading/test_zlib.py index 7c4ed04f4a7c9e5..50928ab309d702c 100644 --- a/Lib/test/test_free_threading/test_zlib.py +++ b/Lib/test/test_free_threading/test_zlib.py @@ -15,22 +15,39 @@ @threading_helper.requires_working_threading() class TestZlib(unittest.TestCase): def test_compressor(self): - comp = zlib.compressobj() - - # First compress() outputs zlib header - header = comp.compress(HAMLET_SCENE) - self.assertGreater(len(header), 0) - - def worker(): - # it should return empty bytes as it buffers data internally - data = comp.compress(HAMLET_SCENE) - self.assertEqual(data, b"") - - run_concurrently(worker_func=worker, nthreads=NTHREADS - 1) - full_compressed = header + comp.flush() - decompressed = zlib.decompress(full_compressed) - # The decompressed data should be HAMLET_SCENE repeated NTHREADS times - self.assertEqual(decompressed, HAMLET_SCENE * NTHREADS) + # Also exercise intermediate output with large uncompressed blocks. + for level, input_data in ( + (zlib.Z_DEFAULT_COMPRESSION, HAMLET_SCENE), + (zlib.Z_NO_COMPRESSION, HAMLET_SCENE * 100), + ): + with self.subTest(level=level): + comp = zlib.compressobj(level) + compressed_parts = [] + + def worker(): + compressed_parts.append(comp.compress(input_data)) + + # Calls are serialized by the compressor, but threads may + # append their output in a different order. Use two threads + # so that both possible output orders can be checked. + nthreads = 2 + run_concurrently(worker_func=worker, nthreads=nthreads) + self.assertEqual(len(compressed_parts), nthreads) + final_part = comp.flush() + expected_data = input_data * nthreads + + for parts in itertools.permutations(compressed_parts): + full_compressed = b"".join(parts) + final_part + decomp = zlib.decompressobj() + try: + decompressed = decomp.decompress(full_compressed) + except zlib.error: + continue + if (decomp.eof and not decomp.unused_data + and decompressed == expected_data): + break + else: + self.fail("No output order reproduces the input") def test_decompressor_concurrent_attribute_reads(self): input_data = HAMLET_SCENE * NTHREADS diff --git a/Misc/NEWS.d/next/Tests/2026-10-14-15-00-00.gh-issue-159425.4RzJpQ.rst b/Misc/NEWS.d/next/Tests/2026-10-14-15-00-00.gh-issue-159425.4RzJpQ.rst new file mode 100644 index 000000000000000..4ae8bfdef559589 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2026-10-14-15-00-00.gh-issue-159425.4RzJpQ.rst @@ -0,0 +1,2 @@ +Fix ``test_free_threading.test_zlib`` to accept intermediate compressed output, +including when using the s390x hardware accelerator.