From 151dd5ea04a24af134a97591c1f7953d6cd53827 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:10:48 +0200 Subject: [PATCH] fix: judge a slot on the declaration the pack ships --- scripts/slots.py | 15 ++++++++++++++- tests/test_slots.py | 14 ++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/scripts/slots.py b/scripts/slots.py index a42d9e2c..f43f567c 100644 --- a/scripts/slots.py +++ b/scripts/slots.py @@ -193,6 +193,13 @@ def profile_claims( return claims +def _claim_rank(claim: Claim) -> int: + """How strongly a platform claim speaks for what the pack ships.""" + if resolution_is_hash_exact(claim.status): + return 2 + return 1 if claim.is_proven else 0 + + def find_conflicts( config: dict, profiles: dict, @@ -211,7 +218,13 @@ def find_conflicts( for claim in platform_claims( config, db, base_dest, zip_contents, data_dir_registry ): - by_dest.setdefault(_normalize(claim.destination), claim) + key = _normalize(claim.destination) + held = by_dest.get(key) + # The pack ships the declaration a hash proves (_preferred_entries), + # so a bare sibling met first must not stand for the destination: + # it would hide the contradiction with the file actually shipped. + if held is None or _claim_rank(claim) > _claim_rank(held): + by_dest[key] = claim # Grouped before judging: a profile may declare several revisions that are # all acceptable at one destination, and the platform choosing one of them diff --git a/tests/test_slots.py b/tests/test_slots.py index be440b2c..dedd8b89 100644 --- a/tests/test_slots.py +++ b/tests/test_slots.py @@ -168,6 +168,20 @@ class TestConflicts(unittest.TestCase): profile = {"dolphin": {"files": [{"name": "IPL.bin", "path": "slot/IPL.bin"}]}} self.assertEqual(slots.find_conflicts(config, profile, REGIONS_DB), []) + def test_a_bare_sibling_met_first_does_not_hide_the_contradiction(self): + """RetroArch declares dsi_bios7.bin bare under one system and with a + hash under another; the pack ships the hashed one, so the slot is + judged on it, whichever system is read first.""" + config = { + "systems": { + "a-console": {"files": [{"name": "IPL.bin", "destination": "GC/JAP/IPL.bin"}]}, + "b-console": {"files": [ + {"name": "IPL.bin", "destination": "GC/JAP/IPL.bin", "md5": "m" * 32}]}, + } + } + conflicts = slots.find_conflicts(config, self._profile("GC/JAP/IPL.bin"), REGIONS_DB) + self.assertEqual([c.destination for c in conflicts], ["GC/JAP/IPL.bin"]) + def test_launchers_and_aliases_never_claim(self): profile = { "launcher": {