fix: keep the mame clone map across runs

A clone group is only visible while both copies are on disk, and the
run then deletes the clone, so writing only what this run saw erased
every mapping an earlier run had recorded. One real run took the map
from 69 entries to 1, and the canonical zips silently stopped answering
to the names they stand in for.

The map is merged now, the 68 lost entries are restored, and the one
whose canonical file is no longer in the collection is dropped.
This commit is contained in:
Abdessamad Derraz committed 2026-08-11 18:41:04 +02:00
1 parent 8c018849ae
commit 4ecb65b8d9
3 files changed
+87

No files matched your search

+14
View File
@@ -173,6 +173,20 @@
"sha1": "2d7d999e2acb4da55c26d1517934d39e7d0a0c86",
"total_copies": 1
},
"bbb.zip": {
"clones": [
"aaa_long_name.zip"
],
"sha1": "0ebda75e11e0fdce192562b5bb65a00e3e3bf240",
"total_copies": 1
},
"bbc_m87.zip": {
"clones": [
"bbc_24bbc.zip"
],
"sha1": "0ebda75e11e0fdce192562b5bb65a00e3e3bf240",
"total_copies": 1
},
"beebsid.zip": {
"clones": [
"bbc_opus3.zip"
+15
View File
@@ -228,6 +228,21 @@ def deduplicate(bios_dir: str, dry_run: bool = False) -> dict:
# Write MAME clone mapping
if mame_clones:
clone_path = "_mame_clones.json"
# A group is only visible while both copies are on disk, and this run
# has just deleted the clone. Writing only what was seen this time
# therefore erases every mapping an earlier run recorded, and the
# canonical zip stops answering to the names it stands in for. The
# older entries stay valid: their canonical file is still here.
if not dry_run:
try:
with open(clone_path) as f:
previous = json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
previous = {}
if isinstance(previous, dict):
merged = dict(previous)
merged.update(mame_clones)
mame_clones = merged
if dry_run:
print(f"\nWould write MAME clone map: {clone_path}")
print(
+58
View File
@@ -178,6 +178,64 @@ class EmptyVariantCleanup(_Bios):
self.assertTrue((self.bios / "Sony" / "PS" / ".variants").is_dir())
class CloneMapSurvivesASecondRun(_Bios):
"""The clone map must not be destroyed by running dedup again.
A clone group is only discovered while both copies are on disk, and the
run then deletes the clone. Rewriting the file with just what this run saw
therefore drops every mapping an earlier run recorded, and the canonical
zip stops answering to the names it was standing in for. One real run took
the map from 69 entries to 1.
"""
def setUp(self):
super().setUp()
self._cwd = os.getcwd()
os.chdir(self._tmp.name)
def tearDown(self):
os.chdir(self._cwd)
super().tearDown()
def _map(self) -> dict:
import json
path = Path(self._tmp.name) / "_mame_clones.json"
return json.loads(path.read_text()) if path.exists() else {}
def test_a_recorded_mapping_survives_a_later_run(self):
self.write("Arcade/MAME/bbc_m87.zip")
self.write("Arcade/MAME/bbc_24bbc.zip")
self.run_dedup()
first = self._map()
self.assertTrue(first, "first run recorded no clone map")
# Second run: the clones are gone, so nothing new is found.
self.write("Arcade/MAME/other_a.zip", b"OTHER CONTENT")
self.write("Arcade/MAME/other_b.zip", b"OTHER CONTENT")
self.run_dedup()
second = self._map()
for canonical, entry in first.items():
self.assertIn(canonical, second, f"{canonical} was dropped")
self.assertEqual(entry["clones"], second[canonical]["clones"])
def test_a_new_group_is_added_beside_the_existing_ones(self):
self.write("Arcade/MAME/bbc_m87.zip")
self.write("Arcade/MAME/bbc_24bbc.zip")
self.run_dedup()
before = set(self._map())
self.write("Arcade/MAME/new_a.zip", b"NEW CONTENT")
self.write("Arcade/MAME/new_b.zip", b"NEW CONTENT")
self.run_dedup()
self.assertTrue(set(self._map()) > before, "the new group was not added")
def test_a_dry_run_never_writes_the_map(self):
self.write("Arcade/MAME/bbc_m87.zip")
self.write("Arcade/MAME/bbc_24bbc.zip")
self.run_dedup(dry_run=True)
self.assertEqual(self._map(), {})
class PathPriority(unittest.TestCase):
def test_shorter_paths_sort_first(self):
short = dedup.path_priority("bios/Sony/PS/a.bin")