79 lines
2.7 KiB
Python
79 lines
2.7 KiB
Python
"""Codec tests. The heavy differential sweep against Boxsim.exe lives in
|
|||
|
|
tools/difftest.py; these guard regressions against the digests it recorded.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import hashlib
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
from boxsim import a2f, container
|
||
|
|
from boxsim.krypt import KryptPRNG
|
||
|
|
|
||
|
|
ROOT = Path(__file__).resolve().parent.parent
|
||
|
|
|
||
|
|
|
||
|
|
def test_keystream_is_stable():
|
||
|
|
"""Regression guard on the keystream every Boxsim file is scrambled with.
|
||
|
|
|
||
|
|
The value is not independently derived, it is the keystream under which all
|
||
|
|
134 containers in the distribution decode byte-identically to Boxsim's own
|
||
|
|
decoder (see tools/difftest.py). Its worth here is catching an accidental
|
||
|
|
change to the generator, not proving the generator correct.
|
||
|
|
"""
|
||
|
|
ks = KryptPRNG().keystream(8)
|
||
|
|
assert ks.hex() == "ebe14331cb3183e1"
|
||
|
|
|
||
|
|
|
||
|
|
def test_rejects_foreign_file():
|
||
|
|
with pytest.raises(container.ContainerError, match="not a MyIni2CK file"):
|
||
|
|
container.decode(b"not a boxsim file at all")
|
||
|
|
|
||
|
|
|
||
|
|
def test_truncated_stream_fails_loudly():
|
||
|
|
raw = (ROOT / "boxsim_dl" / "BoxSim 2.1 DE (Stand 12.2025)"
|
||
|
|
/ "Projekte" / "Default.BPJ")
|
||
|
|
if not raw.exists():
|
||
|
|
pytest.skip("distribution not present")
|
||
|
|
data = raw.read_bytes()
|
||
|
|
with pytest.raises(a2f.A2FError):
|
||
|
|
container.decode(data[: len(data) // 2])
|
||
|
|
|
||
|
|
|
||
|
|
def test_padding_allowance_is_bounded():
|
||
|
|
# A stream of zeros must not decode forever; it has to hit the limit.
|
||
|
|
with pytest.raises(a2f.A2FError):
|
||
|
|
a2f.expand(b"MyIni2CK", skip_magic=8)
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.needs_dist
|
||
|
|
def test_matches_oracle_digests(manifest: dict):
|
||
|
|
"""Every container the oracle decoded must still decode identically."""
|
||
|
|
files = manifest["files"]
|
||
|
|
assert files, "manifest has no entries"
|
||
|
|
checked = 0
|
||
|
|
for rel, expected in files.items():
|
||
|
|
path = ROOT / rel
|
||
|
|
if not path.exists():
|
||
|
|
pytest.fail(f"manifest references missing file {rel}")
|
||
|
|
payload = container.decode(path.read_bytes())
|
||
|
|
digest = hashlib.sha256(payload).hexdigest()
|
||
|
|
assert digest == expected["sha256"], f"{rel} digest changed"
|
||
|
|
assert len(payload) == expected["len"], f"{rel} length changed"
|
||
|
|
checked += 1
|
||
|
|
assert checked == len(files)
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.needs_dist
|
||
|
|
def test_pre20_containers_are_rejected(manifest: dict):
|
||
|
|
"""The BoxSim 1.2 files use an older container and must stay rejected."""
|
||
|
|
rejected = manifest["rejected_not_myini2ck"]
|
||
|
|
assert rejected, "manifest recorded no rejected files"
|
||
|
|
for rel in rejected:
|
||
|
|
path = ROOT / rel
|
||
|
|
if not path.exists():
|
||
|
|
continue
|
||
|
|
with pytest.raises(container.ContainerError):
|
||
|
|
container.decode(path.read_bytes())
|