From 1afe428e161d287309830476a0d34a2b35628cfa Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Sat, 17 Oct 2026 08:55:02 +0200 Subject: [PATCH] fix: credit any accepted version in the gap report --- scripts/cross_reference.py | 30 ++++++++++++++++++++------- tests/test_cross_reference_sources.py | 16 ++++++++++++++ 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/scripts/cross_reference.py b/scripts/cross_reference.py index f6116e24..51a8d7be 100644 --- a/scripts/cross_reference.py +++ b/scripts/cross_reference.py @@ -383,17 +383,29 @@ def _cross_reference_profile( unsourceable_list: list[dict] = [] archive_gaps: dict[tuple, dict] = {} seen_files: set[tuple] = set() - for f in emu_files: + + def slot_key(f: dict) -> tuple: fname = f.get("name", "") - effective_path = f.get("path") or fname - seen_key = ( + return ( fname, f.get("archive"), - effective_path, + f.get("path") or fname, f.get("system"), f.get("variant_group"), f.get("mode", "both"), ) + + # The entries of one profile on one slot are the versions its code + # accepts there (raze's ten DUKE3D.GRP): holding any of them fills the + # slot, as verify --emulator credits it. + versions: dict[tuple, list[dict]] = {} + for f in emu_files: + versions.setdefault(slot_key(f), []).append(f) + + for f in emu_files: + fname = f.get("name", "") + effective_path = f.get("path") or fname + seen_key = slot_key(f) if not fname or seen_key in seen_files: continue @@ -471,9 +483,13 @@ def _cross_reference_profile( if storage in ("release", "large_file"): source = "large_file" else: - source = entry_source(f, index) - if source is None: - source = "missing" + source = next( + ( + found for version in versions[seen_key] + if (found := entry_source(version, index)) is not None + ), + "missing", + ) in_repo = source != "missing" diff --git a/tests/test_cross_reference_sources.py b/tests/test_cross_reference_sources.py index 27697eba..6371f832 100644 --- a/tests/test_cross_reference_sources.py +++ b/tests/test_cross_reference_sources.py @@ -54,6 +54,22 @@ class GapReportSources(unittest.TestCase): report = cross_reference(profiles, declared, db) self.assertEqual(report["gpgx"]["gaps"], 0) + def test_any_accepted_version_fills_the_slot(self): + """raze accepts ten DUKE3D.GRP; the report judged the first alone and + listed the file to acquire while the collection holds the tenth.""" + profiles = {"raze": {"emulator": "Raze", "systems": ["dos"], "files": [ + {"name": "DUKE3D.GRP", "path": "DUKE3D.GRP", "size": 1, "sha1": "a" * 40}, + {"name": "DUKE3D.GRP", "path": "DUKE3D.GRP", "size": 2, "sha1": "b" * 40}, + ]}} + db = {"files": {"b" * 40: {"path": "bios/DUKE3D.GRP", "name": "DUKE3D.GRP"}}, + "indexes": {"by_name": {}, "by_md5": {}, "by_crc32": {}, "by_sha256": {}, + "by_path_suffix": {}}} + report = cross_reference(profiles, {}, db) + self.assertEqual(report["raze"]["gaps"], 1) + self.assertEqual( + [g["source"] for g in report["raze"]["gap_details"]], ["bios"] + ) + if __name__ == "__main__": unittest.main()