Files
libretro/tests/test_bundled_identity.py
T
Abdessamad Derraz 9f8a2a9fe2 chore: mark what each emulator install brings
Every bundled flag was checked against the release or the build recipe of the build that reads the file. A libretro core zip holds the core alone: fMSX, Nuance, ep128emu and PCEE2 files the source tree carries are no longer bundled, and blueMSX, ARMSX2, YAPS2, Cemu and Jazz2 entries are split per build. Files the release ships and the profile missed are now bundled (POM2, ZQuest Classic, EmuCoreX, Linux Loader). Platform packages that differ are named: Batocera installs hcl and Hurrican without their data, ROCKNIX ships touchHLE's Liberation Sans fonts only, Batocera and RetroPie install blueMSX's machines beside the core.
2026-10-20 06:11:39 +02:00

143 lines
4.5 KiB
Python

"""A bundled file is identified by its bytes, not its name.
ZEsarUX ships ace.rom and a patched alternate_roms/ace.rom, Azahar a default
keys.txt, ARMSX2 an OpenGL and a Vulkan cas.glsl. Declared without a hash,
each resolved by its bare name to whatever shared it: the patched ROM at the
original's slot, the Wii U disc keys at the 3DS key slot, the OpenGL shader
at the Vulkan one. An entry with a hash resolves by content first.
512 bundled entries still carry neither a hash nor `unsourceable:`. The
count per profile may only fall, and the table records each fall: a new
entry is written with the hash of the file the emulator ships.
"""
from __future__ import annotations
import hashlib
import sys
import unittest
from pathlib import Path
import yaml
REPO_ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(REPO_ROOT / "scripts"))
from common import load_database, resolve_local_file # noqa: E402
UNIDENTIFIED = {
"abuse": 3,
"aethersx2": 1,
"altirra": 2,
"arcadeflashweb": 8,
"armsx2": 42,
"ax360e": 2,
"c64-emu": 8,
"cdogs": 16,
"citra-mmj": 9,
"drastic": 10,
"eka2l1": 88,
"emucorev": 2,
"emucorex": 23,
"epsxe": 2,
"fpse": 7,
"fpse-ng": 7,
"future-pinball": 18,
"future-pinball-fploader": 46,
"gba-emu": 1,
"hakux": 1,
"hcl": 3,
"hurrican": 1,
"idtech4a++": 1,
"ikemen": 32,
"ines": 1,
"jl-mod": 22,
"kemulator": 4,
"mastergear": 2,
"mfme": 57,
"msx-emu": 9,
"my-boy": 1,
"neo-emu": 3,
"nethersx2": 2,
"nethersx2-turnip": 2,
"nethersx2-turnip-classic": 2,
"pcsx2": 1,
"project64": 9,
"sixtyforce": 5,
"skyline": 9,
"ssf": 1,
"supermodel-dojo": 4,
"x1-box": 1,
"xendroid": 1,
"xm6pro68k": 2,
}
def _unidentified() -> dict[str, int]:
counts: dict[str, int] = {}
for path in sorted((REPO_ROOT / "emulators").glob("*.yml")):
document = yaml.safe_load(path.read_text(encoding="utf-8")) or {}
for entry in document.get("files") or []:
if not isinstance(entry, dict) or not entry.get("bundled"):
continue
if entry.get("unsourceable") or entry.get("type") == "directory":
continue
if any(entry.get(key) for key in ("sha1", "md5", "sha256", "crc32")):
continue
counts[path.stem] = counts.get(path.stem, 0) + 1
return counts
class BundledFilesCarryTheirIdentity(unittest.TestCase):
def test_no_profile_adds_an_unidentified_bundled_file(self):
grown = {
name: (count, UNIDENTIFIED.get(name, 0))
for name, count in _unidentified().items()
if count > UNIDENTIFIED.get(name, 0)
}
self.assertEqual(grown, {}, "bundled entries need the shipped file's hash")
def test_the_table_records_every_fall(self):
# A baseline above the real count lets that profile grow back unseen:
# j2me-loader kept 14 after its 18 bundled files were all identified.
self.assertEqual(_unidentified(), UNIDENTIFIED)
class TheFixedEntriesGetTheirOwnBytes(unittest.TestCase):
CASES = (
("azahar", "sysdata/keys.txt"),
("azaharplus", "sysdata/keys.txt"),
("zesarux", "ace.rom"),
("zesarux", "alternate_roms/ace.rom"),
("zesarux", "coleco.rom"),
("zesarux", "trdos.rom"),
("armsx2", "pcsx2/resources/shaders/vulkan/cas.glsl"),
)
def test_each_resolves_to_the_declared_hash(self):
db_path = REPO_ROOT / "database.json"
if not db_path.exists():
self.skipTest("database.json is not built")
db = load_database(str(db_path))
for profile, destination in self.CASES:
with self.subTest(profile=profile, destination=destination):
document = yaml.safe_load(
(REPO_ROOT / "emulators" / f"{profile}.yml").read_text(encoding="utf-8")
)
entry = next(
e for e in document["files"]
if (e.get("path") or e["name"]) == destination
)
local, _status = resolve_local_file(
{**entry, "source_profile": profile}, db, dest_hint=destination
)
self.assertIsNotNone(local)
self.assertEqual(
hashlib.sha1((REPO_ROOT / local).read_bytes()).hexdigest(),
entry["sha1"],
)
if __name__ == "__main__":
unittest.main()