|
| 1 | +import unittest |
| 2 | + |
| 3 | +from test.support import import_helper, threading_helper |
| 4 | +from test.support.threading_helper import run_concurrently |
| 5 | + |
| 6 | +lzma = import_helper.import_module("lzma") |
| 7 | +from lzma import LZMACompressor, LZMADecompressor |
| 8 | + |
| 9 | +from test.test_lzma import INPUT |
| 10 | + |
| 11 | + |
| 12 | +NTHREADS = 10 |
| 13 | + |
| 14 | + |
| 15 | +@threading_helper.requires_working_threading() |
| 16 | +class TestLZMA(unittest.TestCase): |
| 17 | + def test_compressor(self): |
| 18 | + lzc = LZMACompressor() |
| 19 | + |
| 20 | + # First compress() outputs LZMA header |
| 21 | + header = lzc.compress(INPUT) |
| 22 | + self.assertGreater(len(header), 0) |
| 23 | + |
| 24 | + def worker(): |
| 25 | + # it should return empty bytes as it buffers data internally |
| 26 | + data = lzc.compress(INPUT) |
| 27 | + self.assertEqual(data, b"") |
| 28 | + |
| 29 | + run_concurrently(worker_func=worker, nthreads=NTHREADS - 1) |
| 30 | + full_compressed = header + lzc.flush() |
| 31 | + decompressed = lzma.decompress(full_compressed) |
| 32 | + # The decompressed data should be INPUT repeated NTHREADS times |
| 33 | + self.assertEqual(decompressed, INPUT * NTHREADS) |
| 34 | + |
| 35 | + def test_decompressor(self): |
| 36 | + chunk_size = 128 |
| 37 | + chunks = [bytes([ord("a") + i]) * chunk_size for i in range(NTHREADS)] |
| 38 | + input_data = b"".join(chunks) |
| 39 | + compressed = lzma.compress(input_data) |
| 40 | + |
| 41 | + lzd = LZMADecompressor() |
| 42 | + output = [] |
| 43 | + |
| 44 | + def worker(): |
| 45 | + data = lzd.decompress(compressed, chunk_size) |
| 46 | + self.assertEqual(len(data), chunk_size) |
| 47 | + output.append(data) |
| 48 | + # Read attributes concurrently with other threads decompressing |
| 49 | + self.assertEqual(lzd.check, lzma.CHECK_CRC64) |
| 50 | + self.assertIsInstance(lzd.eof, bool) |
| 51 | + self.assertIsInstance(lzd.needs_input, bool) |
| 52 | + self.assertIsInstance(lzd.unused_data, bytes) |
| 53 | + |
| 54 | + run_concurrently(worker_func=worker, nthreads=NTHREADS) |
| 55 | + self.assertEqual(len(output), NTHREADS) |
| 56 | + # Verify the expected chunks (order doesn't matter due to append race) |
| 57 | + self.assertSetEqual(set(output), set(chunks)) |
| 58 | + self.assertEqual(lzd.check, lzma.CHECK_CRC64) |
| 59 | + self.assertTrue(lzd.eof) |
| 60 | + self.assertFalse(lzd.needs_input) |
| 61 | + # Each thread added full compressed data to the buffer, but only 1 copy |
| 62 | + # is consumed to produce the output. The rest remains as unused_data. |
| 63 | + self.assertEqual( |
| 64 | + len(lzd.unused_data), len(compressed) * (NTHREADS - 1) |
| 65 | + ) |
| 66 | + |
| 67 | + |
| 68 | +if __name__ == "__main__": |
| 69 | + unittest.main() |
0 commit comments