From 2bd9f8a264638b16293604cd481d0e681f6a91fe Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Mon, 19 Oct 2026 23:02:54 +0200 Subject: [PATCH] fix: verify applies storage tiers like the pack The builder never looks up a user_provided entry (it writes instructions in its place) nor an external one (fetched from its URL at build time). verify resolved both by name, so a same-named file in bios/ made it credit a file the pack does not carry. --- scripts/verify.py | 36 +++++++++++++++++++++++++++++++++-- tests/test_unplaced_extras.py | 32 +++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/scripts/verify.py b/scripts/verify.py index 7c32a9e9..02d40761 100644 --- a/scripts/verify.py +++ b/scripts/verify.py @@ -782,6 +782,31 @@ def _mark_unplaced(config: dict, undeclared: list[dict], profiles: dict) -> None u["omitted"] = _UNPLACED_REASONS[reason] +def _storage_verdict(file_entry: dict, emulator_pack: bool) -> dict | None: + """The result a storage tier fixes before any lookup, as the builder sees it. + + A user_provided entry is never taken from the collection: a platform pack + writes its instructions in the slot and counts it filled, an emulator pack + lists it to provide. An external one is fetched from its URL when the pack + is built, never read from bios/. Resolving either by name credited a + same-named file the pack does not carry. + """ + storage = file_entry.get("storage", "embedded") + base = {"name": file_entry.get("name", ""), "required": file_entry.get("required", True)} + if storage == "user_provided": + if emulator_pack: + stated = file_entry.get("unsourceable") + why = ( + stated if isinstance(stated, str) and stated + else file_entry.get("instructions") or "the user provides it" + ) + return {**base, "status": Status.MISSING, "unsourceable": why} + return {**base, "status": Status.OK, "user_provided": True} + if storage == "external": + return {**base, "status": Status.MISSING, "reason": "fetched from its URL when the pack is built"} + return None + + _HIDDEN_REASONS = { "case_twin": "a packed file of the same name in another case takes its place", "path_conflict": "a packed file occupies its directory", @@ -1064,7 +1089,11 @@ def verify_platform( destination = sanitize_pack_path( file_entry.get("destination", file_entry.get("name", "")) ) - if not reads_file_contents(mode): + stored = _storage_verdict(file_entry, emulator_pack=False) + if stored is not None: + result = stored + local_path = None + elif not reads_file_contents(mode): result = verify_entry_existence( file_entry, local_path, @@ -1795,7 +1824,10 @@ def verify_emulator( required = file_entry.get("required", True) hle = file_entry.get("hle_fallback", False) - if not local_path: + stored = _storage_verdict(file_entry, emulator_pack=True) + if stored is not None: + result = stored + elif not local_path: result = {"name": name, "status": Status.MISSING, "required": required} # An entry the profile documents as unsourceable is absent by # design: a per-user key, a slot the user fills, a dump nobody diff --git a/tests/test_unplaced_extras.py b/tests/test_unplaced_extras.py index 46c5037d..a17a185f 100644 --- a/tests/test_unplaced_extras.py +++ b/tests/test_unplaced_extras.py @@ -242,5 +242,37 @@ class FlatFirmwareFolder(unittest.TestCase): self.assertEqual([f["dest"] for f in files if f["dest"].count("/") > 1], []) +class UserProvidedIsNeverTakenFromTheCollection(unittest.TestCase): + """The builder writes instructions for a user_provided entry and never + looks it up. verify resolved it by name, so a same-named file in bios/ + made it report 1/1 OK for a pack that holds no such file.""" + + def test_verify_matches_the_builder(self): + from verify import Status, verify_emulator + + with tempfile.TemporaryDirectory() as tmp: + previous = os.getcwd() + os.chdir(tmp) + self.addCleanup(os.chdir, previous) + for d in ("emulators", "bios/Sony"): + Path(d).mkdir(parents=True) + Path("bios/Sony/scph5501.bin").write_bytes(b"psx") + sha1 = hashlib.sha1(b"psx").hexdigest() + files = {sha1: {"path": "bios/Sony/scph5501.bin", "name": "scph5501.bin", + "size": 3, "sha1": sha1, "md5": hashlib.md5(b"psx").hexdigest(), + "sha256": hashlib.sha256(b"psx").hexdigest(), "crc32": "00000001"}} + db = {"files": files, "indexes": generate_db.build_indexes(files, {})} + Path("emulators/fake.yml").write_text( + "emulator: Fake\ntype: libretro\nsystems: [psx]\ncores: [fake]\n" + "files:\n - name: scph5501.bin\n storage: user_provided\n" + ) + common._emulator_profiles_cache.clear() + self.addCleanup(common._emulator_profiles_cache.clear) + result = verify_emulator(["fake"], "emulators", db) + statuses = [d["status"] for d in result["details"] if d["name"] == "scph5501.bin"] + self.assertEqual(statuses, [Status.MISSING]) + self.assertNotIn(Status.OK, result["status_counts"]) + + if __name__ == "__main__": unittest.main()