mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-10 13:33:24 -05:00
fix: resolve profile entries with their owner
This commit is contained in:
1 parent
8a661932f2
commit
af7b3133a4
4 files changed
+80
-5
No files matched your search
@@ -242,10 +242,17 @@ class FileConsumers:
|
|||||||
self.platforms: dict[str, set[str]] = {}
|
self.platforms: dict[str, set[str]] = {}
|
||||||
self.emulators: dict[str, set[str]] = {}
|
self.emulators: dict[str, set[str]] = {}
|
||||||
|
|
||||||
def sha1_of(self, entry: dict, dest: str = "") -> str | None:
|
def sha1_of(self, entry: dict, dest: str = "", owner: str = "") -> str | None:
|
||||||
"""The collected file an entry resolves to, or None."""
|
"""The collected file an entry resolves to, or None.
|
||||||
|
|
||||||
|
A profile entry names its owner, as the builder does: without it the
|
||||||
|
name step chose another tree's copy, and the page credited the file
|
||||||
|
the pack does not ship.
|
||||||
|
"""
|
||||||
if not entry.get("name"):
|
if not entry.get("name"):
|
||||||
return None
|
return None
|
||||||
|
if owner:
|
||||||
|
entry = {**entry, "source_profile": owner}
|
||||||
path, status = resolve_local_file(
|
path, status = resolve_local_file(
|
||||||
entry, self._db, self._zip_contents, dest_hint=dest
|
entry, self._db, self._zip_contents, dest_hint=dest
|
||||||
)
|
)
|
||||||
@@ -266,7 +273,7 @@ class FileConsumers:
|
|||||||
if profile.get("type") == "alias":
|
if profile.get("type") == "alias":
|
||||||
continue
|
continue
|
||||||
for entry in profile.get("files", []):
|
for entry in profile.get("files", []):
|
||||||
sha1 = self.sha1_of(entry, entry.get("path") or "")
|
sha1 = self.sha1_of(entry, entry.get("path") or "", owner=name)
|
||||||
if sha1:
|
if sha1:
|
||||||
self.emulators.setdefault(sha1, set()).add(name)
|
self.emulators.setdefault(sha1, set()).add(name)
|
||||||
|
|
||||||
|
|||||||
@@ -213,7 +213,9 @@ def _agnostic_scan_extras(
|
|||||||
if not isinstance(candidate, dict):
|
if not isinstance(candidate, dict):
|
||||||
continue
|
continue
|
||||||
local, status = resolve_local_file(
|
local, status = resolve_local_file(
|
||||||
candidate, db, dest_hint=candidate.get("path", "")
|
{**candidate, "source_profile": emu_name},
|
||||||
|
db,
|
||||||
|
dest_hint=candidate.get("path", ""),
|
||||||
)
|
)
|
||||||
if not local or "/" not in local:
|
if not local or "/" not in local:
|
||||||
continue
|
continue
|
||||||
|
|||||||
+2
-1
@@ -175,8 +175,9 @@ def profile_claims(
|
|||||||
if not dest:
|
if not dest:
|
||||||
continue
|
continue
|
||||||
full = f"{base_dest}/{dest}" if base_dest else dest
|
full = f"{base_dest}/{dest}" if base_dest else dest
|
||||||
|
# The owner's own copy is the one the builder ships.
|
||||||
local, status = resolve_local_file(
|
local, status = resolve_local_file(
|
||||||
entry,
|
{**entry, "source_profile": emu_name},
|
||||||
db,
|
db,
|
||||||
zip_contents,
|
zip_contents,
|
||||||
dest_hint=dest,
|
dest_hint=dest,
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
"""Every reader of a profile entry names its owner, as the builder does.
|
||||||
|
|
||||||
|
The resolver prefers the owning profile's copy when a name has several. The
|
||||||
|
builder passes `source_profile`; the site's consumer index, the slot claims
|
||||||
|
and the agnostic scan did not, and resolved 95 entries to other files: the
|
||||||
|
site credited aethersx2's game_controller_db.txt to armsx2 and left the copy
|
||||||
|
armsx2's pack ships without a reader, and MAME's neogeo.zip without MAME.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import unittest
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||||
|
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
||||||
|
|
||||||
|
import cross_reference # noqa: E402
|
||||||
|
import slots # noqa: E402
|
||||||
|
from common import ( # noqa: E402
|
||||||
|
build_zip_contents_index,
|
||||||
|
load_database,
|
||||||
|
load_emulator_profiles,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ReadersNameTheOwner(unittest.TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
db_path = REPO_ROOT / "database.json"
|
||||||
|
if not db_path.exists():
|
||||||
|
raise unittest.SkipTest("database.json is not built")
|
||||||
|
cls.db = load_database(str(db_path))
|
||||||
|
cls.profiles = load_emulator_profiles(str(REPO_ROOT / "emulators"))
|
||||||
|
cls.sha1_by_path = {r["path"]: s for s, r in cls.db["files"].items()}
|
||||||
|
|
||||||
|
def test_the_site_credits_the_copy_the_pack_ships(self):
|
||||||
|
consumers = cross_reference.FileConsumers(
|
||||||
|
self.db, build_zip_contents_index(self.db)
|
||||||
|
)
|
||||||
|
consumers.add_emulators(self.profiles)
|
||||||
|
for path, owner in (
|
||||||
|
("bios/Other/armsx2/game_controller_db.txt", "armsx2"),
|
||||||
|
("bios/Arcade/MAME/neogeo.zip", "mame"),
|
||||||
|
):
|
||||||
|
with self.subTest(path=path):
|
||||||
|
sha1 = self.sha1_by_path.get(path)
|
||||||
|
if sha1 is None:
|
||||||
|
self.skipTest(f"{path} is not collected")
|
||||||
|
self.assertIn(owner, consumers.emulators.get(sha1, set()))
|
||||||
|
|
||||||
|
def test_slot_claims_resolve_the_owners_tree(self):
|
||||||
|
claims = slots.profile_claims({"pcem": self.profiles["pcem"]}, self.db)
|
||||||
|
held = {
|
||||||
|
claim.destination.rsplit("/", 1)[-1]: claim.local_path for claim in claims
|
||||||
|
}
|
||||||
|
local = held.get("ide_xt.bin")
|
||||||
|
if local is None:
|
||||||
|
self.skipTest("ide_xt.bin is not claimed")
|
||||||
|
self.assertTrue(local.startswith("bios/Other/pcem/"), local)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in new issue
Block a user