Files
svfs/tests/test_huffman.py
T

131 lines
5.0 KiB
Python
Raw Normal View History

"""huffman.py 的往返测试。
这个模块是独立的、只有标准库依赖的算法实现,理论上应当全绿;
它在测试里的作用是:当打包/解包链路出问题时,可以快速排除"压缩算法本身写错了"
这一可能,把问题范围锁到 core.py 的集成层。
"""
import random
import struct
import unittest
import helpers # noqa: F401 -- 把 src 加入 sys.path
from sqlite_vfs.huffman import (
FREQ_SIZE,
MAGIC,
compress_bytes,
decompress_bytes,
)
class RoundTripTest(unittest.TestCase):
def assert_roundtrip(self, data, label=""):
freq_blob, compressed_blob = compress_bytes(data)
self.assertEqual(
FREQ_SIZE, len(freq_blob), f"{label}: 频率表必须是 256 个 uint32"
)
self.assertEqual(MAGIC, compressed_blob[:4], f"{label}: 魔数不正确")
self.assertEqual(
len(data),
struct.unpack(">Q", compressed_blob[4:12])[0],
f"{label}: 头部记录的原长度不正确",
)
self.assertEqual(
data, decompress_bytes(freq_blob, compressed_blob), f"{label}: 还原结果不一致"
)
return compressed_blob
def test_empty(self):
freq_blob, compressed_blob = compress_bytes(b"")
self.assertEqual(FREQ_SIZE, len(freq_blob))
self.assertEqual(b"", decompress_bytes(freq_blob, compressed_blob))
def test_single_byte(self):
self.assert_roundtrip(b"\x00", "单字节")
self.assert_roundtrip(b"Z", "单字节")
def test_single_symbol_repeated(self):
self.assert_roundtrip(b"\x7f" * 5000, "单符号重复")
def test_two_symbols(self):
self.assert_roundtrip(b"ab" * 3000, "双符号")
def test_all_256_byte_values(self):
self.assert_roundtrip(bytes(range(256)), "全字节值")
self.assert_roundtrip(bytes(range(256)) * 40, "全字节值重复")
def test_incompressible_random(self):
data = random.Random(1).randbytes(20000)
compressed_blob = self.assert_roundtrip(data, "随机数据")
self.assertGreater(
len(compressed_blob),
len(data),
"随机数据的压缩结果应当大于原始数据(这正是 core 会回退到 none 的原因)",
)
def test_repetitive_text_compresses(self):
data = b"the quick brown fox jumps over the lazy dog. " * 2000
compressed_blob = self.assert_roundtrip(data, "重复文本")
# 该文本有 20 个不同字符,理论码长 ~4.5 bit/字符,压缩后约为原始的 56%
self.assertLess(
len(compressed_blob),
int(len(data) * 0.7),
f"高度重复的文本应当被显著压缩,实际 {len(compressed_blob)}/{len(data)}",
)
def test_utf8_chinese(self):
self.assert_roundtrip("中文内容,测试哈夫曼编码是否无损。\n".encode("utf-8") * 200, "中文")
def test_binary_with_nulls_and_high_bytes(self):
data = bytes([0, 255, 0, 128, 64, 0, 7]) * 500
self.assert_roundtrip(data, "二进制含 0x00/0xFF")
def test_just_above_core_threshold(self):
# core.add_file 的压缩阈值是 1024 字节,边界两侧都要能正确往返
self.assert_roundtrip(b"A" * 1024, "1024 字节")
self.assert_roundtrip(b"A" * 1025, "1025 字节")
class HeaderValidationTest(unittest.TestCase):
def setUp(self):
# 刻意用多符号数据:单符号数据在解压时走快捷路径,不读 payload
self.freq_blob, self.compressed_blob = compress_bytes(
b"the quick brown fox. " * 500
)
def test_wrong_freq_blob_size_rejected(self):
with self.assertRaises(ValueError) as ctx:
decompress_bytes(self.freq_blob[:-4], self.compressed_blob)
self.assertIn("频率表", str(ctx.exception))
def test_too_short_blob_rejected(self):
with self.assertRaises(ValueError) as ctx:
decompress_bytes(self.freq_blob, b"HUF")
self.assertIn("太短", str(ctx.exception))
def test_bad_magic_rejected(self):
broken = b"XXXX" + self.compressed_blob[4:]
with self.assertRaises(ValueError) as ctx:
decompress_bytes(self.freq_blob, broken)
self.assertIn("魔数", str(ctx.exception))
def test_truncated_payload_rejected(self):
truncated = self.compressed_blob[:-50]
with self.assertRaises(ValueError) as ctx:
decompress_bytes(self.freq_blob, truncated)
self.assertIn("提前结束", str(ctx.exception))
def test_single_symbol_payload_is_not_validated(self):
"""记录既有行为(非缺陷):单符号数据解压时直接按原长度展开,完全不读 payload,
因此 payload 被截断也检测不出来。"""
freq_blob, compressed_blob = compress_bytes(b"A" * 10000)
self.assertEqual(
b"A" * 10000, decompress_bytes(freq_blob, compressed_blob[:20])
)
if __name__ == "__main__":
unittest.main()