"""A declaration that conflicts with a packed path is counted by what it resolves to. RetroArch declares SGB1.sfc as a file and SGB1.sfc/ as files inside it. The builder ships one shape and counted every conflicting declaration OK, resolved or not; verify.py resolves each on its own. A required file absent from the collection read as covered in the pack report and as missing in verify. """ from __future__ import annotations import contextlib import hashlib import io import re import sys import tempfile import unittest from pathlib import Path import yaml REPO_ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(REPO_ROOT / "scripts")) import common # noqa: E402 import generate_db # noqa: E402 import generate_pack as builder # noqa: E402 from verify import verify_platform # noqa: E402 class ConflictingDeclarations(unittest.TestCase): def setUp(self): self._tmp = tempfile.TemporaryDirectory() self.root = Path(self._tmp.name) bios = self.root / "bios" / "Nintendo" self.platforms = self.root / "platforms" self.emulators = self.root / "emulators" for directory in (bios, self.platforms, self.emulators): directory.mkdir(parents=True) payload = b"super game boy" (bios / "SGB1.sfc").write_bytes(payload) sha1 = hashlib.sha1(payload).hexdigest() files = { sha1: { "path": str(bios / "SGB1.sfc"), "name": "SGB1.sfc", "size": len(payload), "sha1": sha1, "md5": hashlib.md5(payload).hexdigest(), "sha256": hashlib.sha256(payload).hexdigest(), "crc32": "00000001", } } self.db = {"files": files, "indexes": generate_db.build_indexes(files, {})} platform = { "platform": "Conflict", "verification_mode": "existence", "base_destination": "system", "cores": [], "systems": { "nintendo-sgb": {"files": [ {"name": "SGB1.sfc", "destination": "SGB1.sfc", "required": True}, {"name": "nothere.rom", "destination": "SGB1.sfc/nothere.rom", "required": True}, ]}, }, } (self.platforms / "conflict.yml").write_text(yaml.dump(platform)) (self.platforms / "_registry.yml").write_text( yaml.dump({"platforms": {"conflict": {"status": "active"}}}) ) common._platform_config_cache.clear() common._emulator_profiles_cache.clear() def tearDown(self): common._platform_config_cache.clear() self._tmp.cleanup() def test_the_pack_and_verify_count_alike(self): out = self.root / "dist" out.mkdir() report = io.StringIO() with contextlib.redirect_stdout(report): builder.generate_pack( "conflict", str(self.platforms), self.db, str(self.root / "bios"), str(out), emulators_dir=str(self.emulators), emu_profiles={}, offline=True, ) packed = re.search(r"(\d+)/(\d+) files OK", report.getvalue()) self.assertIsNotNone(packed, report.getvalue()) config = common.load_platform_config("conflict", str(self.platforms)) verified = verify_platform(config, self.db, str(self.emulators)) counts = verified["status_counts"] self.assertEqual(counts.get("missing"), 1) self.assertEqual( (int(packed.group(1)), int(packed.group(2))), (counts.get("ok", 0), verified["total_files"]), ) if __name__ == "__main__": unittest.main()