mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-11 12:33:53 -06:00
RetroPie was the only platform with status: archived, which kept it out of verify --all, slots, the pipeline and list_platforms unless --include-archived was typed. release_pack already decides which packs a release publishes and the installer serves every platform, so the status set nothing apart. The field, its schema entry and the flag are gone, and RetroPie is covered by every full run like any other platform.
275 lines
11 KiB
Python
275 lines
11 KiB
Python
"""A hashless platform entry takes no file filed under another maker.
|
|
|
|
AmberELEC's nec-pc-88 declares DISK.ROM with an empty md5. QUASI88 reads that
|
|
name as n88sub.rom, the 2 KB PC-88 disk ROM (libretro.c:1085), but the name
|
|
step served the 16 KB MSX disk ROM, the only primary called DISK.ROM. The
|
|
quasi88 alias could not point the name at the PC-88 file, since by_name
|
|
refuses an alias a primary already carries. by_shadowed_alias keeps it, and
|
|
the resolver reads it only once the system's own maker folder is all that
|
|
remains.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
import zipfile
|
|
from pathlib import Path
|
|
|
|
import yaml
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
|
|
|
import common # noqa: E402
|
|
import generate_db # noqa: E402
|
|
import generate_pack as builder # noqa: E402
|
|
import generate_site # noqa: E402
|
|
from verify import verify_platform # noqa: E402
|
|
|
|
MSX_DISK = b"M" * 16384
|
|
PC88_DISK = b"N" * 2048
|
|
|
|
QUASI88 = """\
|
|
emulator: QUASI88
|
|
type: libretro
|
|
systems: [nec-pc-88]
|
|
cores: [quasi88]
|
|
files:
|
|
- name: n88sub.rom
|
|
path: quasi88/n88sub.rom
|
|
required: false
|
|
aliases: [N88SUB.ROM, DISK.ROM]
|
|
"""
|
|
|
|
PLATFORM = {
|
|
"platform": "Handheld",
|
|
"version": "1",
|
|
"verification_mode": "md5",
|
|
"base_destination": "",
|
|
"cores": ["quasi88"],
|
|
"systems": {
|
|
"nec-pc-88": {
|
|
"files": [
|
|
{"name": "DISK.ROM", "destination": "bios/quasi88/DISK.ROM",
|
|
"required": True},
|
|
],
|
|
},
|
|
"microsoft-msx": {
|
|
"files": [
|
|
{"name": "DISK.ROM", "destination": "bios/Machines/DISK.ROM",
|
|
"required": True},
|
|
],
|
|
},
|
|
},
|
|
}
|
|
|
|
|
|
class _Collection(unittest.TestCase):
|
|
def setUp(self):
|
|
(REPO_ROOT / "tmp").mkdir(exist_ok=True)
|
|
self._tmp = tempfile.TemporaryDirectory(dir=REPO_ROOT / "tmp")
|
|
self._cwd = os.getcwd()
|
|
os.chdir(self._tmp.name)
|
|
for relative, payload in (
|
|
("bios/Microsoft/MSX/DISK.ROM", MSX_DISK),
|
|
("bios/NEC/PC-88/n88sub.rom", PC88_DISK),
|
|
):
|
|
Path(relative).parent.mkdir(parents=True, exist_ok=True)
|
|
Path(relative).write_bytes(payload)
|
|
Path("emulators").mkdir()
|
|
Path("platforms").mkdir()
|
|
Path("emulators/quasi88.yml").write_text(QUASI88)
|
|
Path("platforms/handheld.yml").write_text(yaml.dump(PLATFORM))
|
|
Path("platforms/_registry.yml").write_text(
|
|
yaml.dump({"platforms": {"handheld": {"config": "handheld.yml"}}})
|
|
)
|
|
subprocess.run(
|
|
[sys.executable, str(REPO_ROOT / "scripts/generate_db.py"), "--force",
|
|
"--bios-dir", "bios", "--output", "database.json"],
|
|
check=True, capture_output=True,
|
|
)
|
|
self.db = common.load_database("database.json")
|
|
common._platform_config_cache.clear()
|
|
common._emulator_profiles_cache.clear()
|
|
self.config = common.load_platform_config("handheld", "platforms")
|
|
self.profiles = common.load_emulator_profiles("emulators")
|
|
|
|
def tearDown(self):
|
|
common._platform_config_cache.clear()
|
|
common._emulator_profiles_cache.clear()
|
|
os.chdir(self._cwd)
|
|
self._tmp.cleanup()
|
|
|
|
def _entry(self, system: str) -> dict:
|
|
return self.config["systems"][system]["files"][0]
|
|
|
|
def _manifest(self) -> dict:
|
|
return builder.generate_manifest(
|
|
"handheld", "platforms", self.db, "bios", "platforms/_registry.yml",
|
|
emulators_dir="emulators", emu_profiles=self.profiles, offline=True,
|
|
)
|
|
|
|
|
|
class ShadowedAliasIndex(_Collection):
|
|
def test_the_alias_a_primary_carries_is_kept_apart(self):
|
|
sha1 = hashlib.sha1(PC88_DISK).hexdigest()
|
|
indexes = self.db["indexes"]
|
|
self.assertEqual(indexes["by_shadowed_alias"]["DISK.ROM"], [sha1])
|
|
self.assertNotIn(sha1, indexes["by_name"]["DISK.ROM"])
|
|
|
|
def test_an_entry_with_no_single_content_designates_nothing(self):
|
|
shadowed = [("a.rom", set(), ["B.ROM"]), ("c.rom", {"1" * 40}, ["B.ROM"])]
|
|
by_name = {"a.rom": ["2" * 40, "3" * 40], "B.ROM": ["4" * 40]}
|
|
self.assertEqual(
|
|
generate_db.shadowed_alias_index(shadowed, by_name), {"B.ROM": ["1" * 40]}
|
|
)
|
|
|
|
|
|
class DiskRomGoesToThePc88File(_Collection):
|
|
def test_the_entry_knows_its_system(self):
|
|
self.assertEqual(self._entry("nec-pc-88")["platform_system"], "nec-pc-88")
|
|
|
|
def test_resolution(self):
|
|
entry = self._entry("nec-pc-88")
|
|
path, status = common.resolve_local_file(
|
|
entry, self.db, dest_hint=entry["destination"]
|
|
)
|
|
self.assertEqual((path, status), ("bios/NEC/PC-88/n88sub.rom", "name_exact"))
|
|
|
|
def test_the_msx_system_keeps_the_msx_file(self):
|
|
entry = self._entry("microsoft-msx")
|
|
path, _status = common.resolve_local_file(
|
|
entry, self.db, dest_hint=entry["destination"]
|
|
)
|
|
self.assertEqual(path, "bios/Microsoft/MSX/DISK.ROM")
|
|
|
|
def test_a_declared_hash_still_decides(self):
|
|
entry = {**self._entry("nec-pc-88"), "md5": hashlib.md5(MSX_DISK).hexdigest()}
|
|
path, status = common.resolve_local_file(entry, self.db)
|
|
self.assertEqual((path, status), ("bios/Microsoft/MSX/DISK.ROM", "md5_exact"))
|
|
|
|
def test_an_emulator_entry_is_not_narrowed(self):
|
|
entry = {**self._entry("nec-pc-88"), "source_profile": "msx-emu"}
|
|
path, _status = common.resolve_local_file(entry, self.db)
|
|
self.assertEqual(path, "bios/Microsoft/MSX/DISK.ROM")
|
|
|
|
def test_no_pc88_copy_leaves_it_missing_not_mismatched(self):
|
|
Path("bios/NEC/PC-88/n88sub.rom").unlink()
|
|
entry = self._entry("nec-pc-88")
|
|
self.assertEqual(
|
|
common.resolve_local_file(entry, self.db, dest_hint=entry["destination"]),
|
|
(None, "not_found"),
|
|
)
|
|
|
|
def test_verify(self):
|
|
report = verify_platform(self.config, self.db, "emulators", self.profiles)
|
|
paths = {d["name"]: set() for d in report["details"]}
|
|
for d in report["details"]:
|
|
paths[d["name"]].add(d.get("path"))
|
|
self.assertEqual(
|
|
paths["DISK.ROM"],
|
|
{"bios/NEC/PC-88/n88sub.rom", "bios/Microsoft/MSX/DISK.ROM"},
|
|
)
|
|
|
|
def test_pack(self):
|
|
zip_path = builder.generate_pack(
|
|
"handheld", "platforms", self.db, "bios", "out",
|
|
emulators_dir="emulators", emu_profiles=self.profiles, offline=True,
|
|
)
|
|
with zipfile.ZipFile(zip_path) as zf:
|
|
self.assertEqual(zf.read("bios/quasi88/DISK.ROM"), PC88_DISK)
|
|
self.assertEqual(zf.read("bios/Machines/DISK.ROM"), MSX_DISK)
|
|
|
|
def test_the_manifest_changes_only_that_row(self):
|
|
manifest = self._manifest()
|
|
original = (common.load_platform_config, common.system_makers)
|
|
|
|
def untagged(*args, **kwargs):
|
|
config = original[0](*args, **kwargs)
|
|
for system in config["systems"].values():
|
|
for f in system.get("files") or []:
|
|
f.pop("platform_system", None)
|
|
return config
|
|
|
|
common._platform_config_cache.clear()
|
|
builder.load_platform_config = common.load_platform_config = untagged
|
|
common.system_makers = lambda *args: frozenset()
|
|
try:
|
|
before = self._manifest()
|
|
finally:
|
|
builder.load_platform_config = common.load_platform_config = original[0]
|
|
common.system_makers = original[1]
|
|
common._platform_config_cache.clear()
|
|
rows = {f["dest"]: f for f in manifest["files"]}
|
|
old_rows = {f["dest"]: f for f in before["files"]}
|
|
self.assertEqual(rows["bios/quasi88/DISK.ROM"]["size"], len(PC88_DISK))
|
|
self.assertEqual(old_rows["bios/quasi88/DISK.ROM"]["size"], len(MSX_DISK))
|
|
del rows["bios/quasi88/DISK.ROM"], old_rows["bios/quasi88/DISK.ROM"]
|
|
self.assertEqual(rows, old_rows)
|
|
moved = len(MSX_DISK) - len(PC88_DISK)
|
|
for key in ("total_size", "pack_size"):
|
|
self.assertEqual(before[key] - manifest[key], moved)
|
|
changed = {"files", "total_size", "pack_size", "generated"}
|
|
self.assertEqual(
|
|
json.dumps({k: v for k, v in manifest.items() if k not in changed}, sort_keys=True),
|
|
json.dumps({k: v for k, v in before.items() if k not in changed}, sort_keys=True),
|
|
)
|
|
|
|
|
|
class SiteSharesTheMakerTest(unittest.TestCase):
|
|
def test_one_copy(self):
|
|
self.assertIs(generate_site.names_manufacturer, common.names_manufacturer)
|
|
self.assertIs(generate_site.BUCKET_FOLDERS, common.BUCKET_FOLDERS)
|
|
|
|
|
|
class NoHashlessNameMatchCrossesMakers(unittest.TestCase):
|
|
"""Every registered platform: a name match never lands on another maker."""
|
|
|
|
def test_registry(self):
|
|
self.addCleanup(os.chdir, os.getcwd())
|
|
os.chdir(REPO_ROOT)
|
|
db_path = REPO_ROOT / "database.json"
|
|
if not db_path.exists():
|
|
self.skipTest("database.json not built")
|
|
db = common.load_database(str(db_path))
|
|
registry = common.load_data_dir_registry("platforms")
|
|
folders = {
|
|
parts[1]
|
|
for entry in db["files"].values()
|
|
if len(parts := entry["path"].split("/")) >= 3 and parts[0] == "bios"
|
|
} - common.BUCKET_FOLDERS
|
|
crossings = []
|
|
for platform in common.list_registered_platforms("platforms"):
|
|
config = common.load_platform_config(platform, "platforms")
|
|
for sys_id, system in config.get("systems", {}).items():
|
|
makers = {f for f in folders if common.names_manufacturer(sys_id, f)}
|
|
if not makers:
|
|
continue
|
|
for entry in system.get("files") or []:
|
|
if any(entry.get(k) for k in ("sha1", "md5", "sha256", "crc32")):
|
|
continue
|
|
path, status = common.resolve_local_file(
|
|
entry, db, dest_hint=entry.get("destination", ""),
|
|
data_dir_registry=registry,
|
|
)
|
|
parts = (path or "").split("/")
|
|
if (
|
|
status.startswith("name")
|
|
and len(parts) >= 3
|
|
and parts[0] == "bios"
|
|
and parts[1] not in common.BUCKET_FOLDERS
|
|
and parts[1] not in makers
|
|
):
|
|
crossings.append(f"{platform} {sys_id} {entry.get('name')}: {path}")
|
|
self.assertEqual(crossings, [])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|