import unittest from test.support import import_helper, threading_helper from test.support.threading_helper import run_concurrently bz2 = import_helper.import_module("bz2") from bz2 import BZ2Compressor, BZ2Decompressor from test.test_bz2 import ext_decompress, BaseTest NTHREADS = 10 TEXT = BaseTest.TEXT @threading_helper.requires_working_threading() class TestBZ2(unittest.TestCase): def test_compressor(self): bz2c = BZ2Compressor() def worker(): # it should return empty bytes as it buffers data internally data = bz2c.compress(TEXT) self.assertEqual(data, b"") run_concurrently(worker_func=worker, nthreads=NTHREADS) data = bz2c.flush() # The decompressed data should be TEXT repeated NTHREADS times decompressed = ext_decompress(data) self.assertEqual(decompressed, TEXT * 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 = bz2.compress(input_data) bz2d = BZ2Decompressor() output = [] def worker(): data = bz2d.decompress(compressed, chunk_size) self.assertEqual(len(data), chunk_size) output.append(data) # Read attributes concurrently with other threads decompressing self.assertIsInstance(bz2d.eof, bool) self.assertIsInstance(bz2d.needs_input, bool) self.assertIsInstance(bz2d.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.assertEqual(set(output), set(chunks)) self.assertTrue(bz2d.eof) self.assertFalse(bz2d.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(bz2d.unused_data), len(compressed) * (NTHREADS - 1) ) if __name__ == "__main__": unittest.main()