fix: same bytes under two paths agree in slots

A data directory cache and a bios/ copy of one file were judged by path, so 56 bluemsx destinations on AmberELEC and EmuELEC read as contradicted.
This commit is contained in:
Abdessamad Derraz committed 2026-11-06 03:19:18 +01:00
1 parent b873c6a9fb
commit 35ad19ff02
4 files changed
+48 -2466

No files matched your search

File diff suppressed because it is too large. Load diff
+1 -1233
View File
File diff suppressed because it is too large. Load diff
+21 -1
View File
@@ -22,6 +22,7 @@ from typing import TYPE_CHECKING
import nativemode
from common import (
compute_hashes,
platform_arg,
archive_entry,
build_zip_contents_index,
@@ -759,12 +760,18 @@ def find_collisions(
def _satisfies(profile: Claim, platform: Claim, db: dict) -> bool:
"""Whether the two layers agree on what ships at this destination.
They agree when they resolve to the same file, when the platform's own
They agree when they resolve to the same bytes, when the platform's own
hash list accepts the profile's file, or when the profile's accepts the
platform's. A contradiction needs both lists to reject the other side.
"""
if profile.local_path == platform.local_path:
return True
# Two paths can hold one content: AmberELEC's bluemsx declarations
# resolve into the data/bluemsx cache, the profile's into bios/.
if profile.local_path and platform.local_path:
shipped = _content_sha1(platform.local_path, db)
if shipped is not None and shipped == _content_sha1(profile.local_path, db):
return True
members = profile.entry.get("members")
if members and platform.local_path:
return holds_required_members(platform.local_path, members)
@@ -821,6 +828,19 @@ def _record_at(path: str, db: dict) -> dict | None:
_PATH_INDEX: dict[int, tuple[dict, dict[str, dict]]] = {}
def _content_sha1(path: str, db: dict) -> str | None:
"""The sha1 of the file at path, from the database or read from disk.
A data directory cache is not in the database, so its files are hashed.
"""
record = _record_at(path, db)
if record is not None:
return record.get("sha1")
if not os.path.isfile(path):
return None
return compute_hashes(path, frozenset({"sha1"}))["sha1"]
def _accepted_by_all(path: str, entries: list[dict], db: dict) -> bool:
"""Whether every declaration's own hashes accept the file at path."""
record = _record_at(path, db)
+26
View File
@@ -936,6 +936,32 @@ class ARecordIsFoundByItsPathOnce(unittest.TestCase):
self.assertEqual(Counted.walks, 1)
class TwoPathsHoldingOneContentAgree(unittest.TestCase):
"""AmberELEC's bluemsx declarations resolve into the data/bluemsx cache,
the profile into bios/. Neither declares a hash and the bytes are the
same, yet the paths differed and 56 destinations read as contradicted."""
def test_same_bytes_under_two_paths_satisfy(self):
with tempfile.TemporaryDirectory() as tmp:
cache = Path(tmp, "data", "MSX2EXT.rom")
stored = Path(tmp, "bios", "MSX2EXT.rom")
other = Path(tmp, "bios", "other.rom")
for path, payload in ((cache, b"rom"), (stored, b"rom"), (other, b"xyz")):
path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(payload)
db = {"files": {}}
platform = Claim("platform", "Machines/MSX2EXT.rom", "MSX2EXT.rom",
local_path=str(cache), status="path_exact")
same = Claim("profile", "Machines/MSX2EXT.rom", "MSX2EXT.rom",
emulator="bluemsx", local_path=str(stored),
status="path_exact")
different = Claim("profile", "Machines/MSX2EXT.rom", "MSX2EXT.rom",
emulator="bluemsx", local_path=str(other),
status="path_exact")
self.assertTrue(slots._satisfies(same, platform, db))
self.assertFalse(slots._satisfies(different, platform, db))
class TheSlotsSummaryClosesTheRun(unittest.TestCase):
"""The totals came before the collision section and ignored it, and the
help said the default run fails on conflicts the pack should settle,