mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-16 16:33:27 -05:00
fix: keep every revision a core accepts in truth
This commit is contained in:
1 parent
b2e6a2b978
commit
12fcdd52a6
2 files changed
+66
-4
No files matched your search
+24
-4
@@ -198,6 +198,7 @@ def _contents_disagree(a: dict, b: dict) -> bool:
|
||||
|
||||
def _fold_into(existing: dict, file_entry: dict, emu_name: str) -> None:
|
||||
"""Add one core's declaration to the entry that already stands for it."""
|
||||
revision = emu_name in existing.get("_cores", ())
|
||||
existing["_cores"] = existing.get("_cores", set()) | {emu_name}
|
||||
sr = file_entry.get("source_ref")
|
||||
if sr is not None:
|
||||
@@ -210,7 +211,7 @@ def _fold_into(existing: dict, file_entry: dict, emu_name: str) -> None:
|
||||
# Which cores need it: `required` above is the union, true when
|
||||
# any core needs it, and a package of one core must not read it.
|
||||
existing["_required_by"] = existing.get("_required_by", set()) | {emu_name}
|
||||
_merge_hashes(existing, file_entry, emu_name)
|
||||
_merge_hashes(existing, file_entry, emu_name, revision)
|
||||
# Merge non-hash data fields if existing lacks them.
|
||||
# A core that creates an entry without size/path/validation may be
|
||||
# enriched by a sibling core that has those fields.
|
||||
@@ -220,7 +221,15 @@ def _fold_into(existing: dict, file_entry: dict, emu_name: str) -> None:
|
||||
_merge_priority(existing, file_entry.get("priority"))
|
||||
|
||||
|
||||
def _merge_hashes(existing: dict, file_entry: dict, emu_name: str) -> None:
|
||||
def _merge_hashes(
|
||||
existing: dict, file_entry: dict, emu_name: str, revision: bool = False
|
||||
) -> None:
|
||||
"""Fold one declaration's hashes into the entry.
|
||||
|
||||
A core that lists several revisions under one name accepts each of them
|
||||
(dosbox's MT-32 control ROMs, raze's GRP versions): ``revision`` keeps
|
||||
the union. Two cores that disagree keep the first value and are reported.
|
||||
"""
|
||||
for h in _HASH_FIELDS:
|
||||
theirs = file_entry.get(h, "")
|
||||
ours = existing.get(h, "")
|
||||
@@ -229,9 +238,14 @@ def _merge_hashes(existing: dict, file_entry: dict, emu_name: str) -> None:
|
||||
if not ours:
|
||||
existing[h] = theirs
|
||||
continue
|
||||
# Normalize to sets for multi-hash comparison
|
||||
t_list = theirs if isinstance(theirs, list) else [theirs]
|
||||
o_list = ours if isinstance(ours, list) else [ours]
|
||||
if revision:
|
||||
known = {str(v).lower() for v in o_list}
|
||||
added = [v for v in t_list if str(v).lower() not in known]
|
||||
if added:
|
||||
existing[h] = [*o_list, *added]
|
||||
continue
|
||||
if not {str(v).lower() for v in t_list} & {str(v).lower() for v in o_list}:
|
||||
print(
|
||||
f"WARNING: hash conflict for {file_entry['name']} "
|
||||
@@ -618,7 +632,13 @@ def _pair_rank(truth_entry: dict, scraped_entry: dict) -> tuple[bool, bool, bool
|
||||
|
||||
|
||||
def _hash_mismatch(t_entry: dict, s_entry: dict) -> dict | None:
|
||||
"""The first hash both sides declare without a value in common."""
|
||||
"""The first hash both sides declare without a value in common.
|
||||
|
||||
A value shared on any field settles it: the truth holds every revision a
|
||||
core accepts, and a profile may give one revision's sha1 without its md5.
|
||||
"""
|
||||
if _hash_set(t_entry) & _hash_set(s_entry):
|
||||
return None
|
||||
for h in ("sha1", "md5", "crc32"):
|
||||
t_hash = t_entry.get(h, "")
|
||||
s_hash = s_entry.get(h, "")
|
||||
|
||||
@@ -131,5 +131,47 @@ class AnUnattributedEntryIsNotGuessed(unittest.TestCase):
|
||||
["apple2gs.rom", "zx81.rom"],
|
||||
)
|
||||
|
||||
|
||||
class EveryRevisionACoreAcceptsStays(unittest.TestCase):
|
||||
"""dosbox lists each MT-32 control ROM it accepts under one name. The
|
||||
truth kept the first sha1 and dropped the others, so a frontend holding
|
||||
1.07 read as a mismatch against a core that loads it."""
|
||||
|
||||
def truth(self, profiles):
|
||||
config = {"cores": list(profiles), "systems": {}}
|
||||
return generate_platform_truth("p", config, {}, profiles)
|
||||
|
||||
@staticmethod
|
||||
def entry(name, sha1):
|
||||
return {"name": name, "system": "dos", "sha1": sha1}
|
||||
|
||||
def test_one_core_keeps_the_union(self):
|
||||
profile = {"emulator": "DOSBox", "type": "libretro", "systems": ["dos"],
|
||||
"files": [self.entry("MT32_CONTROL.ROM", "a" * 40),
|
||||
self.entry("MT32_CONTROL.ROM", "b" * 40)]}
|
||||
files = self.truth({"dosbox": profile})["systems"]["dos"]["files"]
|
||||
self.assertEqual([f["sha1"] for f in files], [["a" * 40, "b" * 40]])
|
||||
|
||||
def test_two_cores_that_disagree_keep_the_first(self):
|
||||
first = {"emulator": "A", "type": "libretro", "systems": ["dos"],
|
||||
"files": [self.entry("x.rom", "a" * 40)]}
|
||||
second = {"emulator": "B", "type": "libretro", "systems": ["dos"],
|
||||
"files": [{**self.entry("x.rom", "b" * 40), "path": "x.rom"}]}
|
||||
files = self.truth({"a": first, "b": second})["systems"]["dos"]["files"]
|
||||
self.assertEqual([f["sha1"] for f in files], ["a" * 40])
|
||||
|
||||
def test_a_revision_known_by_one_hash_is_no_mismatch(self):
|
||||
"""1.04 carries md5 and sha1, 1.07 only its sha1: a platform entry
|
||||
giving 1.07's sha1 and md5 is that revision, not a mismatch."""
|
||||
from truth import _hash_mismatch
|
||||
|
||||
truth = {"name": "MT32_CONTROL.ROM", "md5": "c" * 32, "sha1": ["a" * 40, "b" * 40]}
|
||||
scraped = {"name": "MT32_CONTROL.ROM", "md5": "d" * 32, "sha1": "b" * 40}
|
||||
self.assertIsNone(_hash_mismatch(truth, scraped))
|
||||
self.assertEqual(
|
||||
_hash_mismatch(truth, {**scraped, "sha1": "e" * 40})["hash_type"], "sha1"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user