diff --git a/scripts/cross_reference.py b/scripts/cross_reference.py index d30b3a25..3e1ca3f7 100644 --- a/scripts/cross_reference.py +++ b/scripts/cross_reference.py @@ -427,6 +427,20 @@ def _resolve_archive_source( return result if result is not None else "missing" +def slot_key(f: dict) -> tuple: + """The slot a profile entry fills: its entries on one slot are the + versions its code accepts there, and holding any of them fills it.""" + fname = f.get("name", "") + return ( + fname, + f.get("archive"), + f.get("path") or fname, + f.get("system"), + f.get("variant_group"), + f.get("mode", "both"), + ) + + def _cross_reference_profile( emu_name: str, profile: dict, @@ -470,17 +484,6 @@ def _cross_reference_profile( archive_gaps: dict[tuple, dict] = {} seen_files: set[tuple] = set() - def slot_key(f: dict) -> tuple: - fname = f.get("name", "") - return ( - fname, - f.get("archive"), - 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. diff --git a/scripts/generate_site.py b/scripts/generate_site.py index 8cdda8f9..7a33f638 100644 --- a/scripts/generate_site.py +++ b/scripts/generate_site.py @@ -2354,19 +2354,36 @@ def _availability_check(db: dict, data_names): def _emulator_file_summary(files: list[dict], available) -> list[str]: - """The counts above an emulator's file table, and its categories.""" - in_repo_count = sum(1 for f in files if available(f)) - unsourceable_count = sum(1 for f in files if f.get("unsourceable")) - missing_count = len(files) - in_repo_count - unsourceable_count - req_count = sum(1 for f in files if f.get("required")) - hle_count = sum(1 for f in files if f.get("hle_fallback")) + """The counts above an emulator's file table, and its categories. + + Counted by slot, as verify --emulator and the gaps page count: raze + lists every GRP its code accepts under one name, and holding one fills + the slot. Counted per entry, the page announced 46 missing where 12 + slots are empty. + """ + from cross_reference import slot_key + + slots: dict[tuple, list[dict]] = {} + for f in files: + slots.setdefault(slot_key(f), []).append(f) + in_repo_count = sum(1 for group in slots.values() if any(available(f) for f in group)) + unsourceable_count = sum( + 1 for group in slots.values() + if not any(available(f) for f in group) and all(f.get("unsourceable") for f in group) + ) + missing_count = len(slots) - in_repo_count - unsourceable_count + req_count = sum(1 for group in slots.values() if any(f.get("required") for f in group)) + hle_count = sum(1 for group in slots.values() if any(f.get("hle_fallback") for f in group)) held = f"{in_repo_count} in repo, {missing_count} missing" if unsourceable_count: held += f", {unsourceable_count} unsourceable" + total = f"**{len(slots)} files**" + if len(slots) != len(files): + total += f" ({len(files)} accepted versions)" parts = [ - f"**{len(files)} files**", - f"{req_count} required, {len(files) - req_count} optional", + total, + f"{req_count} required, {len(slots) - req_count} optional", held, ] if hle_count: diff --git a/tests/test_audit_regressions.py b/tests/test_audit_regressions.py index 43602209..7ed3470a 100644 --- a/tests/test_audit_regressions.py +++ b/tests/test_audit_regressions.py @@ -3266,5 +3266,23 @@ class EachRootReadsItsOwnRegistry(unittest.TestCase): self.assertNotIn('startswith("bios/")\n }', source) + +class TheSiteCountsSlotsNotVersions(unittest.TestCase): + """raze lists every GRP its code accepts under one name: the site said + 59 files, 46 missing, where verify --emulator said 23 slots, 12 missing.""" + + def test_one_held_version_fills_the_slot(self): + from scripts.generate_site import _emulator_file_summary + + files = [ + {"name": "DUKE3D.GRP", "crc32": "1"}, + {"name": "DUKE3D.GRP", "crc32": "2"}, + {"name": "BLOOD.RFF", "crc32": "3"}, + ] + line = _emulator_file_summary(files, lambda f: f.get("crc32") == "2")[0] + self.assertIn("**2 files** (3 accepted versions)", line) + self.assertIn("1 in repo, 1 missing", line) + + if __name__ == "__main__": unittest.main()