| import unittest | |
| from test.support import import_helper, threading_helper | |
| from test.support.threading_helper import run_concurrently | |
| lzma = import_helper.import_module("lzma") | |
| from lzma import LZMACompressor, LZMADecompressor | |
| from test.test_lzma import INPUT | |
| NTHREADS = 10 | |
| @threading_helper.requires_working_threading() | |
| class TestLZMA(unittest.TestCase): | |
| def test_compressor(self): | |
| lzc = LZMACompressor() | |
| # First compress() outputs LZMA header | |
| header = lzc.compress(INPUT) | |
| self.assertGreater(len(header), 0) | |
| def worker(): | |
| # it should return empty bytes as it buffers data internally | |
| data = lzc.compress(INPUT) | |
| self.assertEqual(data, b"") | |
| run_concurrently(worker_func=worker, nthreads=NTHREADS - 1) | |
| full_compressed = header + lzc.flush() | |
| decompressed = lzma.decompress(full_compressed) | |
| # The decompressed data should be INPUT repeated NTHREADS times | |
| self.assertEqual(decompressed, INPUT * NTHREADS) | |
| def test_decompressor(self): | |
| chunk_size = 128 | |
| chunks = [bytes([ord("a") + i]) * chunk_size for i in range(NTHREADS)] | |
| input_data = b"".join(chunks) | |
| compressed = lzma.compress(input_data) | |
| lzd = LZMADecompressor() | |
| output = [] | |
| def worker(): | |
| data = lzd.decompress(compressed, chunk_size) | |
| self.assertEqual(len(data), chunk_size) | |
| output.append(data) | |
| # Read attributes concurrently with other threads decompressing | |
| self.assertEqual(lzd.check, lzma.CHECK_CRC64) | |
| self.assertIsInstance(lzd.eof, bool) | |
| self.assertIsInstance(lzd.needs_input, bool) | |
| self.assertIsInstance(lzd.unused_data, bytes) | |
| run_concurrently(worker_func=worker, nthreads=NTHREADS) | |
| self.assertEqual(len(output), NTHREADS) | |
| # Verify the expected chunks (order doesn't matter due to append race) | |
| self.assertSetEqual(set(output), set(chunks)) | |
| self.assertEqual(lzd.check, lzma.CHECK_CRC64) | |
| self.assertTrue(lzd.eof) | |
| self.assertFalse(lzd.needs_input) | |
| # Each thread added full compressed data to the buffer, but only 1 copy | |
| # is consumed to produce the output. The rest remains as unused_data. | |
| self.assertEqual( | |
| len(lzd.unused_data), len(compressed) * (NTHREADS - 1) | |
| ) | |
| if __name__ == "__main__": | |
| unittest.main() |