Files
svfs/tests/test_compression.py
T

113 lines
4.8 KiB
Python
Raw Normal View History

"""压缩算法分发层的直接单测。
`compression.py` 是"用哪种算法"的唯一分发点(core 只按 `compress_algo` 值分发,
不关心具体算法)。这里锁住它的契约,这样以后往 `ALGORITHMS` 里加算法
(包括自定义算法、或未来的加密层)时,有东西能兜住回归。
分层关系:`huffman.py` 只管纯算法(有自己的 test_huffman.py),
`compression.py` 管分发与算法名到数据的映射,`core.py` 管存取。
"""
import unittest
import zlib
import helpers # noqa: F401 -- 把 src 加入 sys.path
from sqlite_vfs import compression
from sqlite_vfs.huffman import FREQ_SIZE
class AlgorithmRegistryTest(unittest.TestCase):
def test_default_algorithm_is_zlib(self):
self.assertEqual("zlib", compression.DEFAULT_ALGORITHM)
def test_registry_contains_the_three_builtins(self):
self.assertEqual(("none", "zlib", "huffman"), compression.ALGORITHMS)
def test_every_registered_algorithm_roundtrips(self):
data = b"the quick brown fox jumps over the lazy dog. " * 60
for algorithm in compression.ALGORITHMS:
with self.subTest(algorithm=algorithm):
algo, freq, blob = compression.compress_with(data, algorithm)
self.assertEqual(algorithm, algo)
self.assertEqual(data, compression.decompress_with(algo, freq, blob))
def test_unknown_algorithm_is_rejected_on_both_sides(self):
with self.assertRaises(ValueError) as ctx:
compression.compress_with(b"x", "lzma")
self.assertIn("lzma", str(ctx.exception))
self.assertIn("none", str(ctx.exception), "报错要列出可用算法")
with self.assertRaises(ValueError):
compression.decompress_with("lzma", None, b"x")
class CompressContractTest(unittest.TestCase):
DATA = b"payload " * 100
def test_none_returns_the_input_unchanged(self):
algo, freq, blob = compression.compress_with(self.DATA, "none")
self.assertEqual("none", algo)
self.assertIsNone(freq)
self.assertIs(self.DATA, blob, "none 应当零拷贝直接返回原对象")
def test_zlib_produces_a_real_zlib_stream(self):
algo, freq, blob = compression.compress_with(self.DATA, "zlib")
self.assertIsNone(freq, "zlib 不需要附加数据")
self.assertEqual(self.DATA, zlib.decompress(blob))
self.assertLess(len(blob), len(self.DATA))
def test_huffman_carries_a_fixed_size_frequency_table(self):
algo, freq, blob = compression.compress_with(self.DATA, "huffman")
self.assertEqual("huffman", algo)
self.assertEqual(FREQ_SIZE, len(freq), "频率表固定 1024 字节")
self.assertEqual(self.DATA, compression.decompress_with(algo, freq, blob))
def test_huffman_without_frequency_table_is_rejected(self):
algo, _freq, blob = compression.compress_with(self.DATA, "huffman")
with self.assertRaises(ValueError) as ctx:
compression.decompress_with(algo, None, blob)
self.assertIn("频率表", str(ctx.exception))
def test_stored_size_counts_the_frequency_table(self):
_algo, freq, blob = compression.compress_with(self.DATA, "huffman")
self.assertEqual(
len(blob) + len(freq), compression.stored_size(blob, freq)
)
self.assertEqual(len(blob), compression.stored_size(blob, None))
def test_stored_size_ignores_an_empty_frequency_table(self):
self.assertEqual(3, compression.stored_size(b"abc", b""))
class AlgorithmTradeoffTest(unittest.TestCase):
"""把实测出来的取舍钉在测试里:默认算法不能悄悄退化成更差的那个。"""
#: 高度重复的文本——LZ77 的强项,纯熵编码的弱项
REPETITIVE = b"function render(el){return el.innerHTML}\n" * 800
def test_zlib_beats_huffman_by_a_lot_on_repetitive_text(self):
_a1, f1, z = compression.compress_with(self.REPETITIVE, "zlib")
_a2, f2, h = compression.compress_with(self.REPETITIVE, "huffman")
zlib_total = compression.stored_size(z, f1)
huffman_total = compression.stored_size(h, f2)
self.assertLess(
zlib_total * 10,
huffman_total,
f"zlib={zlib_total} huffman={huffman_total}——差距应当在一个数量级上",
)
def test_huffman_is_not_used_for_small_files(self):
"""huffman 每个文件要带 1024 字节频率表,小文件必然负收益。"""
small = b"tiny content"
_algo, freq, blob = compression.compress_with(small, "huffman")
self.assertGreater(
compression.stored_size(blob, freq),
len(small),
"小文件压完更大——这正是 core 用 COMPRESS_MIN_SIZE 挡住它的原因",
)
if __name__ == "__main__":
unittest.main()