| # Copyright 2016 The Brotli Authors. All rights reserved. | |
| # | |
| # Distributed under MIT license. | |
| # See file LICENSE for detail or copy at https://opensource.org/licenses/MIT | |
| import queue | |
| import random | |
| import threading | |
| import time | |
| import brotli | |
| import pytest | |
| from . import _test_utils | |
| @pytest.mark.parametrize("quality", [1, 6, 9, 11]) | |
| @pytest.mark.parametrize("text_name", _test_utils.gather_text_inputs()) | |
| def test_single_process(quality, text_name): | |
| original = _test_utils.take_input(text_name) | |
| compressor = brotli.Compressor(quality=quality) | |
| compressed = compressor.process(original) | |
| compressed += compressor.finish() | |
| decompressed = brotli.decompress(compressed) | |
| assert original == decompressed | |
| @pytest.mark.parametrize("quality", [1, 6, 9, 11]) | |
| @pytest.mark.parametrize("text_name", _test_utils.gather_text_inputs()) | |
| def test_multiple_process(quality, text_name): | |
| original = _test_utils.take_input(text_name) | |
| chunk_size = 2048 | |
| chunks = _test_utils.chunk_input(original, chunk_size) | |
| compressor = brotli.Compressor(quality=quality) | |
| compressed = b"" | |
| for chunk in chunks: | |
| compressed += compressor.process(chunk) | |
| compressed += compressor.finish() | |
| decompressed = brotli.decompress(compressed) | |
| assert original == decompressed | |
| @pytest.mark.parametrize("quality", [1, 6, 9, 11]) | |
| @pytest.mark.parametrize("text_name", _test_utils.gather_text_inputs()) | |
| def test_multiple_process_and_flush(quality, text_name): | |
| original = _test_utils.take_input(text_name) | |
| chunk_size = 2048 | |
| chunks = _test_utils.chunk_input(original, chunk_size) | |
| compressor = brotli.Compressor(quality=quality) | |
| compressed = b"" | |
| for chunk in chunks: | |
| compressed += compressor.process(chunk) | |
| compressed += compressor.flush() | |
| compressed += compressor.finish() | |
| decompressed = brotli.decompress(compressed) | |
| assert original == decompressed | |
| def make_input(size): | |
| abc = [bytes([b]) for b in b"abcdefghijklmnopqrstuvwxyz"] | |
| abc_cap = [bytes([b]) for b in b"ABCDEFGHIJKLMNOPQRSTUVWXYZ"] | |
| num_words_by_len = [0, 25, 100, 175, 1700, 1000, 1000, 1000] | |
| word_set = set() | |
| rng = random.Random() | |
| rng.seed(2025) | |
| words_by_len = [[]] | |
| for word_len in range(1, len(num_words_by_len)): | |
| num_words = num_words_by_len[word_len] | |
| words = [] | |
| for _ in range(num_words): | |
| while True: | |
| word = b"".join( | |
| [rng.choice(abc_cap)] | |
| + [rng.choice(abc) for _ in range(word_len - 1)] | |
| ) | |
| if word not in word_set: | |
| word_set.add(word) | |
| words.append(word) | |
| break | |
| words_by_len.append(words) | |
| total_size = 0 | |
| out = [] | |
| while total_size < size: | |
| word_len = rng.choice(range(1, len(num_words_by_len))) | |
| word = rng.choice(words_by_len[word_len]) | |
| total_size += len(word) | |
| out.append(word) | |
| return b"".join(out) | |
| def _thread_compress(original, compressor, results): | |
| compressed = compressor.process(original) | |
| compressed += compressor.finish() | |
| results.put(1) | |
| def _thread_concurrent_process(compressor, results): | |
| time.sleep(0.01) | |
| try: | |
| _ = compressor.process(b"whatever") | |
| except brotli.error: | |
| results.put(2) | |
| def _thread_concurrent_flush(compressor, results): | |
| time.sleep(0.02) | |
| try: | |
| _ = compressor.flush() | |
| except brotli.error: | |
| results.put(3) | |
| def _thread_concurrent_finish(compressor, results): | |
| time.sleep(0.03) | |
| try: | |
| _ = compressor.finish() | |
| except brotli.error: | |
| results.put(4) | |
| def test_concurrency(): | |
| original = make_input(2 * 1024 * 1024) | |
| compressor = brotli.Compressor(quality=9) | |
| results = queue.Queue() | |
| threads = [] | |
| threads.append( | |
| threading.Thread( | |
| target=_thread_compress, args=(original, compressor, results) | |
| ) | |
| ) | |
| threads.append( | |
| threading.Thread( | |
| target=_thread_concurrent_process, args=(compressor, results) | |
| ) | |
| ) | |
| threads.append( | |
| threading.Thread( | |
| target=_thread_concurrent_flush, args=(compressor, results) | |
| ) | |
| ) | |
| threads.append( | |
| threading.Thread( | |
| target=_thread_concurrent_finish, args=(compressor, results) | |
| ) | |
| ) | |
| for thread in threads: | |
| thread.start() | |
| for thread in threads: | |
| thread.join() | |
| assert sorted(results.queue) == [1, 2, 3, 4] |