diff --git a/scripts/packextras.py b/scripts/packextras.py index 72b85cb2..d144c172 100644 --- a/scripts/packextras.py +++ b/scripts/packextras.py @@ -912,6 +912,22 @@ def _kept(entries: list[dict], required_only: bool) -> list[dict]: return [e for e in entries if e.get("required") is not False] +def _ships(config: dict, file_entry: dict, db: dict, dest: str) -> bool: + """Whether the builder packs this declaration: resolved, and not refused + by a hash frontend. A dump whose md5 Recalbox would reject is left out + of the pack, so it cannot win a regional slot over a file that ships. + """ + from validation import baseline_verdict + + path, status = resolve_local_file(file_entry, db, dest_hint=dest) + if not path: + return False + return baseline_verdict( + file_entry, path, status, config.get("verification_mode", "existence"), + config.get("hash_rule", ""), + )[1] is None + + def platform_region_groups( config: dict, systems: dict, @@ -951,9 +967,9 @@ def platform_region_groups( ) if dest: members.append((dest, file_entry.get("name", ""))) - if held_out is not None and db is not None and resolve_local_file( - file_entry, db, dest_hint=dest - )[0]: + if held_out is not None and db is not None and _ships( + config, file_entry, db, dest + ): held_out.add(dest) extra_dests: dict[tuple[str, str, str], str] = {} diff --git a/tests/test_region_held.py b/tests/test_region_held.py new file mode 100644 index 00000000..158471de --- /dev/null +++ b/tests/test_region_held.py @@ -0,0 +1,50 @@ +"""A dump the frontend refuses does not hold a regional slot. + +platform_region_groups counted a declaration as held as soon as a file +answered it, hash_mismatch included. On a md5 platform the builder leaves +that file out as unsafe, so a US file with the wrong md5 displaced a correct +Japanese one, and the system shipped no BIOS at all. +""" + +from __future__ import annotations + +import hashlib +import os +import shutil +import sys +import tempfile +import unittest +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(REPO_ROOT / "scripts")) + +from packextras import _ships # noqa: E402 + + +class ARefusedDumpHoldsNothing(unittest.TestCase): + def test_held_follows_the_frontend(self): + (REPO_ROOT / "tmp").mkdir(exist_ok=True) + root = Path(tempfile.mkdtemp(dir=REPO_ROOT / "tmp")) + self.addCleanup(shutil.rmtree, root, True) + previous = os.getcwd() + os.chdir(root) + self.addCleanup(os.chdir, previous) + Path("bios").mkdir() + data = b"US BIOS, ANOTHER REVISION" + Path("bios/us.bin").write_bytes(data) + sha1 = hashlib.sha1(data).hexdigest() + db = { + "files": {sha1: {"path": "bios/us.bin", "name": "us.bin", "size": len(data), + "md5": hashlib.md5(data).hexdigest(), "crc32": ""}}, + "indexes": {"by_md5": {}, "by_name": {"us.bin": [sha1]}, "by_crc32": {}, + "by_path_suffix": {}}, + } + entry = {"name": "us.bin", "md5": "0" * 32} + self.assertFalse(_ships({"verification_mode": "md5"}, entry, db, "us.bin")) + self.assertTrue(_ships({"verification_mode": "existence"}, entry, db, "us.bin")) + self.assertTrue(_ships({"verification_mode": "md5"}, {"name": "us.bin"}, db, "us.bin")) + + +if __name__ == "__main__": + unittest.main()