diff --git a/scripts/packextras.py b/scripts/packextras.py index 9cb87158..83fe7264 100644 --- a/scripts/packextras.py +++ b/scripts/packextras.py @@ -136,9 +136,13 @@ def hidden_extras( profiles: dict, shipped: dict[str, str], target_cores: set[str] | None = None, + region_drops: set[str] | None = None, ) -> dict[tuple[str, str, str], str]: """Report entries the builder holds and still leaves out, with the reason. + A destination the region pass withdraws never takes its path in the + pack, so it blocks nothing here either. + The builder walks the core extras after the platform's own files and skips a destination that a case-insensitive filesystem folds onto one already packed, or that a packed file blocks as a directory. RetroPie's @@ -169,7 +173,7 @@ def hidden_extras( target_cores=target_cores, ): dest = sanitize_pack_path(fe.get("destination", fe.get("name", ""))) - if not dest: + if not dest or (region_drops and dest in region_drops): continue full = dest if not prefix or dest.startswith(f"{prefix}/") else f"{prefix}/{dest}" reason = placement_block(full, seen, lower, parents, case_insensitive) diff --git a/scripts/verify.py b/scripts/verify.py index 8f5a7e23..bddfc2ef 100644 --- a/scripts/verify.py +++ b/scripts/verify.py @@ -758,6 +758,7 @@ def _mark_hidden( emu_profiles: dict | None, 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.""" from packextras import hidden_extras @@ -766,7 +767,7 @@ def _mark_hidden( config, emulators_dir, db, emu_profiles if emu_profiles is not None else load_emulator_profiles(emulators_dir), - shipped, target_cores, + shipped, target_cores, region_drops, ) for u in undeclared: reason = hidden.get( @@ -1091,7 +1092,8 @@ def verify_platform( ] _mark_unplaced(config, undeclared, profiles) _mark_hidden( - config, undeclared, emulators_dir, db, emu_profiles, shipped, target_cores + config, undeclared, emulators_dir, db, emu_profiles, shipped, target_cores, + region_drops, ) exclusions = find_exclusion_notes( config, emulators_dir, emu_profiles, target_cores=target_cores diff --git a/tests/test_audit_regressions.py b/tests/test_audit_regressions.py index 72dd8507..3967206a 100644 --- a/tests/test_audit_regressions.py +++ b/tests/test_audit_regressions.py @@ -2255,6 +2255,38 @@ class AGroupPackIsWrittenUnderItsOwnName(unittest.TestCase): self.assertIn("pack_name=group_name", source) +class ARegionDroppedExtraBlocksNothing(unittest.TestCase): + """The builder skips a region-dropped core extra before placing it; + the report placed it anyway, so on a case-folding platform it hid the + next core's FOO.ROM that the pack does carry.""" + + def test_the_report_skips_what_the_region_pass_drops(self): + from unittest import mock + + from scripts import packextras + + with tempfile.TemporaryDirectory(dir=TMP_ROOT) as directory: + other = Path(directory) / "FOO.ROM" + other.write_bytes(b"other bytes") + extras = [ + {"name": "foo.rom", "destination": "foo.rom", "source_emulator": "a"}, + {"name": "FOO.ROM", "destination": "FOO.ROM", "source_emulator": "b"}, + ] + config = {"case_insensitive_fs": True, "base_destination": ""} + + def report(region_drops): + with mock.patch.object(packextras, "_collect_emulator_extras", + return_value=extras), \ + mock.patch.object(packextras, "resolve_local_file", + return_value=(str(other), "name_exact")): + return packextras.hidden_extras( + config, "emulators", {"files": {}}, {}, {}, None, region_drops + ) + + self.assertTrue(report(None)) + self.assertEqual(report({"foo.rom"}), {}) + + class TheDatabaseCacheIsWrittenWhole(unittest.TestCase): """A crash while serializing left a truncated db_cache.json behind, and the next run rehashed the whole collection from scratch."""