From a58b65754beb17ce238ec6de15866764180e8683 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Sat, 17 Oct 2026 18:14:26 +0200 Subject: [PATCH] fix: count a core file filling a declaration In existence mode the builder packs a core's file at a destination the platform declares and does not hold, and counts the declaration met. verify now reads the same placement and reports it OK, so both sides agree on the platform's count. --- scripts/packextras.py | 6 +- scripts/verify.py | 56 ++++++++----- tests/test_core_file_fills_declaration.py | 97 +++++++++++++++++++++++ 3 files changed, 139 insertions(+), 20 deletions(-) create mode 100644 tests/test_core_file_fills_declaration.py diff --git a/scripts/packextras.py b/scripts/packextras.py index b0475562..3be6f835 100644 --- a/scripts/packextras.py +++ b/scripts/packextras.py @@ -137,6 +137,7 @@ def hidden_extras( shipped: dict[str, str], target_cores: set[str] | None = None, region_drops: set[str] | None = None, + placed: set[str] | None = None, ) -> dict[tuple[str, str, str], str]: """Report entries the builder holds and still leaves out, with the reason. @@ -150,7 +151,8 @@ def hidden_extras( while the report counted it in the pack. A twin carrying the same bytes serves the core on the filesystem that folds the names, so it is not a file left out. ``shipped`` maps the full destination of each platform - file the pack carries to its local copy. + file the pack carries to its local copy. ``placed``, when given, + receives the full destination of each held extra the pack carries. """ from packpaths import _register_path, placement_block @@ -198,6 +200,8 @@ def hidden_extras( content[full.lower()] = held if case_insensitive: lower.add(full.lower()) + if placed is not None and local: + placed.add(full) return hidden diff --git a/scripts/verify.py b/scripts/verify.py index 7c49f955..62fac910 100644 --- a/scripts/verify.py +++ b/scripts/verify.py @@ -763,15 +763,19 @@ def _mark_hidden( shipped: dict[str, str], target_cores: set[str] | None, region_drops: set[str] | None = None, -) -> None: - """What the builder holds but cannot place beside the files it packed.""" +) -> set[str]: + """What the builder holds but cannot place beside the files it packed. + + Returns the full destinations the builder fills with a core's file. + """ from packextras import hidden_extras + placed: set[str] = set() hidden = hidden_extras( config, emulators_dir, db, emu_profiles if emu_profiles is not None else load_emulator_profiles(emulators_dir), - shipped, target_cores, region_drops, + shipped, target_cores, region_drops, placed, ) for u in undeclared: reason = hidden.get( @@ -780,6 +784,7 @@ def _mark_hidden( if reason and u.get("in_repo") and u.get("in_pack", True): u["in_pack"] = False u["omitted"] = _HIDDEN_REASONS[reason] + return placed def _twin_index( @@ -987,6 +992,7 @@ def verify_platform( ) shipped: dict[str, str] = {} + results_at: dict[str, list[dict]] = {} for sys_id, system in verify_systems.items(): for file_entry in system.get("files", []): if region_drops and ( @@ -1055,6 +1061,7 @@ def verify_platform( dest = file_entry.get("destination", file_entry.get("name", "")) if not dest: dest = f"{sys_id}/{file_entry.get('name', '')}" + results_at.setdefault(dest, []).append(result) required = file_entry.get("required", True) cur = result["status"] prev = file_status.get(dest) @@ -1069,21 +1076,6 @@ def verify_platform( ): file_severity[dest] = sev - # Count by severity - counts = { - Severity.OK: 0, - Severity.INFO: 0, - Severity.WARNING: 0, - Severity.CRITICAL: 0, - } - for s in file_severity.values(): - counts[s] = counts.get(s, 0) + 1 - - # Count by file status (ok/untested/missing) - status_counts: dict[str, int] = {} - for s in file_status.values(): - status_counts[s] = status_counts.get(s, 0) + 1 - # Cross-reference undeclared files if supplemental_names is None: from cross_reference import _build_supplemental_index @@ -1107,10 +1099,36 @@ def verify_platform( not in region_drops ] _mark_unplaced(config, undeclared, profiles) - _mark_hidden( + placed = _mark_hidden( config, undeclared, emulators_dir, db, emu_profiles, shipped, target_cores, region_drops, ) + if not reads_file_contents(mode): + # An existence frontend is satisfied by any file at the path, and the + # builder fills a free platform destination with a core's file. + for dest, status in file_status.items(): + full = f"{base_dest}/{dest}" if base_dest else dest + if status == Status.MISSING and full in placed: + file_status[dest] = Status.OK + file_severity[dest] = Severity.OK + for result in results_at.get(dest, []): + result["status"] = Status.OK + + # Count by severity + counts = { + Severity.OK: 0, + Severity.INFO: 0, + Severity.WARNING: 0, + Severity.CRITICAL: 0, + } + for s in file_severity.values(): + counts[s] = counts.get(s, 0) + 1 + + # Count by file status (ok/untested/missing) + status_counts: dict[str, int] = {} + for s in file_status.values(): + status_counts[s] = status_counts.get(s, 0) + 1 + exclusions = find_exclusion_notes( config, emulators_dir, emu_profiles, target_cores=target_cores ) diff --git a/tests/test_core_file_fills_declaration.py b/tests/test_core_file_fills_declaration.py new file mode 100644 index 00000000..79097678 --- /dev/null +++ b/tests/test_core_file_fills_declaration.py @@ -0,0 +1,97 @@ +"""A core's file at a free platform destination answers an existence check. + +The builder packs a core extra at a destination the platform declares and +does not hold, and in existence mode counts the declaration met: the +frontend sees a file at the path. verify kept it MISSING, so the pack said +1/1 OK and verify 0/1 for the same platform. +""" + +from __future__ import annotations + +import hashlib +import io +import os +import shutil +import sys +import tempfile +import unittest +import zipfile +from contextlib import redirect_stdout +from pathlib import Path + +import yaml + +REPO_ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(REPO_ROOT / "scripts")) + +from common import _emulator_profiles_cache, load_platform_config # noqa: E402 +from generate_pack import generate_pack # noqa: E402 +from verify import verify_platform # noqa: E402 + + +class ACoreFileFillsAFreeDeclaration(unittest.TestCase): + def setUp(self): + _emulator_profiles_cache.clear() + (REPO_ROOT / "tmp").mkdir(exist_ok=True) + self.root = Path(tempfile.mkdtemp(dir=REPO_ROOT / "tmp")) + self.addCleanup(shutil.rmtree, self.root, True) + bios = self.root / "bios" / "Maker" / "Console" + bios.mkdir(parents=True) + data = b"HELD BOOT ROM" + (bios / "boot.bin").write_bytes(data) + sha1 = hashlib.sha1(data).hexdigest() + md5 = hashlib.md5(data).hexdigest() + self.db = { + "files": {sha1: { + "path": str(bios / "boot.bin"), "name": "boot.bin", + "md5": md5, "crc32": "", "size": len(data), + }}, + "indexes": { + "by_md5": {md5: sha1}, "by_name": {"boot.bin": [sha1]}, + "by_crc32": {}, "by_path_suffix": {}, + }, + } + self.platforms = self.root / "platforms" + self.emulators = self.root / "emulators" + self.platforms.mkdir() + self.emulators.mkdir() + (self.platforms / "fakeplat.yml").write_text(yaml.safe_dump({ + "platform": "FakePlat", + "verification_mode": "existence", + "base_destination": "system", + "cores": ["fakecore"], + "systems": {"console-a": {"files": [ + {"name": "zzz_absent.bin", "destination": "zzz_absent.bin", + "required": True}, + ]}}, + })) + # The entry's name differs from the destination it fills, as + # descriptive names do ("MDA font ROM" at mda.rom). + (self.emulators / "fakecore.yml").write_text(yaml.safe_dump({ + "emulator": "FakeCore", "type": "libretro", "cores": ["fakecore"], + "systems": ["console-a"], + "files": [{"name": "Fake boot ROM", "path": "zzz_absent.bin", + "sha1": sha1, "required": True}], + })) + self.config = load_platform_config("fakeplat", str(self.platforms)) + + def test_verify_counts_what_the_pack_counts(self): + result = verify_platform( + self.config, self.db, str(self.emulators), supplemental_names=set(), + ) + out = self.root / "out" + with redirect_stdout(io.StringIO()): + zip_path = generate_pack( + "fakeplat", str(self.platforms), self.db, str(self.root / "bios"), + str(out), emulators_dir=str(self.emulators), offline=True, + ) + with zipfile.ZipFile(zip_path) as zf: + self.assertIn("zzz_absent.bin", zf.namelist()) + self.assertEqual(result["status_counts"], {"ok": 1}) + self.assertEqual( + [d["status"] for d in result["details"]], ["ok"], + ) + + +if __name__ == "__main__": + unittest.main()