diff --git a/tests/test_extract.py b/tests/test_extract.py index b51774d..2c60ab7 100644 --- a/tests/test_extract.py +++ b/tests/test_extract.py @@ -14,27 +14,29 @@ from pathlib import Path REPO = Path(__file__).resolve().parent.parent FW = Path(os.environ.get('MTK_FW_DIR', '/lib/firmware/mediatek')) -# filename -> (chip_id, eco, n_region, hidden_trailer_string_or_None) -# Golden values observed from linux-firmware HEAD (2026-06-19 tree: -# mt7981/mt7986 = 20260515 builds; mt7915/mt7916 = 2022/2024 builds, -# unchanged since). When a value here fails against a newer snapshot, the -# blob changed — update the golden *and* record the diff (docs/reports/). -GOLDEN_WM = { - 'mt7981_wm.bin': (0x14, 0, 11, - 't-neptune-main-mt7915-1953-MT7981_MP2111_IMP-20260515121129'), - 'mt7986_wm.bin': (0x0f, 0, 11, - 't-neptune-main-mt7915-1953-MT7986_MP2111_IMP-20260515120228'), - 'mt7916_wm.bin': (0x13, 0, 9, - 't-neptune-main-mt7915-1953-MT7916_MP2111_IMP-20240823170147'), - 'mt7915_wm.bin': (0x0b, 1, 7, - 't-neptune-mp-mt7915-2045-MT7915_MP_7_4_2045-20220929103802'), -} -GOLDEN_WA = { - 'mt7981_wa.bin': (0x00, 0, 3, None), - 'mt7986_wa.bin': (0x00, 0, 3, None), - 'mt7916_wa.bin': (0x00, 0, 3, None), - 'mt7915_wa.bin': (0x00, 1, 3, None), -} +# Golden source of truth: dataset/history.jsonl (one record per +# linux-firmware revision, sha256-keyed). Any tested blob must be a known +# revision and its parse must match the recorded metadata. A new upstream +# build therefore fails here until the dataset is refreshed (by design): +# +# python3 tools/fw_history.py firmware/linux-firmware -o dataset/history.jsonl +import json + +HISTORY = json.loads( + '[' + ','.join(l for l in + (REPO / 'dataset' / 'history.jsonl').read_text().splitlines() + if l) + ']') +BY_SHA = {r['sha256']: r for r in HISTORY} + + +def _expected(path: Path, sha: str): + rec = BY_SHA.get(sha) + if rec is None: + raise AssertionError( + f'{path.name}: sha {sha} is not a known linux-firmware revision ' + f'in dataset/history.jsonl — refresh the dataset (see comment ' + f'above) or verify the blob provenance') + return rec def parse(path: Path, out: Path) -> dict: @@ -67,40 +69,43 @@ class LayoutClosure(unittest.TestCase): class GoldenRAM(unittest.TestCase): + """Parsed metadata must match the dataset record for the blob's sha.""" - def run_one(self, name, golden, outdir): - p = FW / name - if not p.exists(): - self.skipTest(f'{name} not present') - info = parse(p, outdir) - chip, eco, n, hidden = golden - self.assertEqual(info['chip_id'], chip, name) - self.assertEqual(info['eco'], eco, name) - self.assertEqual(info['n_region'], n, name) - ht = info.get('hidden_trailer', {}) - self.assertEqual(ht.get('string'), hidden, name) - - def test_wm(self): - for name, golden in GOLDEN_WM.items(): - with self.subTest(blob=name): - self.run_one(name, golden, REPO / 'extracted' / 'test_wm') - - def test_wa(self): - for name, golden in GOLDEN_WA.items(): - with self.subTest(blob=name): - self.run_one(name, golden, REPO / 'extracted' / 'test_wa') + def test_ram(self): + import hashlib + import sys as _sys + _sys.path.insert(0, str(REPO / 'tools')) + blobs = sorted(FW.glob('mt79*_wm.bin')) + sorted(FW.glob('mt79*_wa.bin')) + if not blobs: + self.skipTest('no mediatek blobs on this host') + for p in blobs: + with self.subTest(blob=p.name): + d = p.read_bytes() + rec = _expected(p, hashlib.sha256(d).hexdigest()[:16]) + info = parse(p, REPO / 'extracted' / 'test_ram') + self.assertEqual(info['chip_id'], rec.get('chip_id'), p.name) + self.assertEqual(info['eco'], rec.get('eco'), p.name) + self.assertEqual(info['n_region'], rec.get('n_region'), p.name) + self.assertEqual(info.get('hidden_trailer', {}).get('string'), + rec.get('hidden'), p.name) + self.assertEqual(info['build_date'], rec.get('trailer_date'), + p.name) class GoldenPatch(unittest.TestCase): def test_patch(self): - golden = {'mt7981_rom_patch.bin': 1, 'mt7986_rom_patch.bin': 1, - 'mt7916_rom_patch.bin': 1, 'mt7915_rom_patch.bin': 2} - for p in sorted(FW.glob('mt79*_rom_patch.bin')): + import hashlib + blobs = sorted(FW.glob('mt79*_rom_patch.bin')) + if not blobs: + self.skipTest('no mediatek blobs on this host') + for p in blobs: with self.subTest(blob=p.name): + d = p.read_bytes() + rec = _expected(p, hashlib.sha256(d).hexdigest()[:16]) info = parse(p, REPO / 'extracted' / 'test_patch') - self.assertEqual(info['platform'], 'ALPS') - self.assertEqual(info['n_region'], golden[p.name]) + self.assertEqual(info['platform'], rec.get('platform'), p.name) + self.assertEqual(info['n_region'], rec.get('n_section'), p.name) for sec in info['sections']: self.assertEqual(sec['type'], '0x30002') self.assertEqual(sec['enc_type'], 0) # plaintext (F4)