mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-24 04:39:06 -05:00
Two test modules still unpacked nine values from verify_pack_against_platform. The category evidence joined readers with semicolons, which the prose rule refuses on generated pages, and three comments used clause semicolons.
193 lines
8.5 KiB
Python
193 lines
8.5 KiB
Python
"""The pack integrity check counts the core files the builder places.
|
|
|
|
It rebuilt the core extras with an empty set of taken paths and tested
|
|
presence alone. On a Recalbox pack it credited 68 core files at a path the
|
|
platform's own file holds, 15 of them other bytes (fuse/trdos.rom, the PS1
|
|
BIOS DuckStation reads), and printed a core count that contradicted the
|
|
builder's in the same run.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import os
|
|
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 packverify # noqa: E402
|
|
|
|
|
|
def _held(files: dict, by_name: dict, path: str, payload: bytes) -> str:
|
|
Path(path).parent.mkdir(parents=True, exist_ok=True)
|
|
Path(path).write_bytes(payload)
|
|
sha1 = hashlib.sha1(payload).hexdigest()
|
|
name = Path(path).name
|
|
files[sha1] = {"path": path, "name": name, "sha1": sha1,
|
|
"md5": hashlib.md5(payload).hexdigest(), "size": len(payload)}
|
|
by_name.setdefault(name, []).append(sha1)
|
|
return sha1
|
|
|
|
|
|
class CoreFilesAsTheBuilderPlacesThem(unittest.TestCase):
|
|
def setUp(self):
|
|
tmp = tempfile.TemporaryDirectory(dir=REPO_ROOT / "tmp")
|
|
self.addCleanup(tmp.cleanup)
|
|
previous = os.getcwd()
|
|
os.chdir(tmp.name)
|
|
self.addCleanup(os.chdir, previous)
|
|
files: dict = {}
|
|
by_name: dict = {}
|
|
_held(files, by_name, "bios/plat/trdos.rom", b"platform dump")
|
|
core_trdos = _held(files, by_name, "bios/core/trdos.rom", b"core dump")
|
|
_held(files, by_name, "bios/core/other.rom", b"core file")
|
|
self.db = {"files": files, "indexes": {
|
|
"by_name": by_name, "by_md5": {e["md5"]: s for s, e in files.items()},
|
|
"by_path_suffix": {}, "by_crc32": {}, "by_sha256": {}}}
|
|
Path("platforms").mkdir()
|
|
config = {"platform": "P", "verification_mode": "existence", "cores": ["fuse"],
|
|
"systems": {"zx": {"files": [
|
|
{"name": "trdos.rom", "destination": "fuse/trdos.rom"}]}}}
|
|
Path("platforms/p.yml").write_text(yaml.safe_dump(config), encoding="utf-8")
|
|
Path("emulators").mkdir()
|
|
self.profiles = {"fuse": {
|
|
"emulator": "fuse", "type": "libretro", "systems": ["zx"], "files": [
|
|
{"name": "trdos.rom", "path": "fuse/trdos.rom", "sha1": core_trdos},
|
|
{"name": "other.rom", "path": "core/other.rom"},
|
|
]}}
|
|
|
|
def _check(self, other: bytes) -> tuple:
|
|
with zipfile.ZipFile("pack.zip", "w") as zf:
|
|
zf.writestr("fuse/trdos.rom", b"platform dump")
|
|
zf.writestr("core/other.rom", other)
|
|
return packverify.verify_pack_against_platform(
|
|
"pack.zip", "p", "platforms", self.db, emulators_dir="emulators",
|
|
emu_profiles=self.profiles,
|
|
)
|
|
|
|
def test_a_path_the_platform_file_holds_is_not_a_core_file(self):
|
|
ok, _checked, _present, errors, _bc, _bp, core_checked, core_present, _ex, _absent = (
|
|
self._check(b"core file")
|
|
)
|
|
self.assertTrue(ok, errors)
|
|
self.assertEqual((core_checked, core_present), (1, 1))
|
|
|
|
def test_other_bytes_at_a_core_path_are_an_error(self):
|
|
ok, _checked, _present, errors, *_rest = self._check(b"something else")
|
|
self.assertFalse(ok)
|
|
self.assertTrue(
|
|
any(e.startswith("core file differs: core/other.rom") for e in errors), errors
|
|
)
|
|
|
|
|
|
class NarrowedPacksAgainstTheirOwnList(unittest.TestCase):
|
|
"""--verify-packs after --all-variants said "hashes checked during the
|
|
build" for the five narrowed packs and compared none of them: a narrowed
|
|
pack missing a file its narrowed list requires passed. Each variant is
|
|
now held to the platform list narrowed the way it was built."""
|
|
|
|
def setUp(self):
|
|
import common
|
|
import generate_db
|
|
import generate_pack
|
|
|
|
self.gp = generate_pack
|
|
tmp = tempfile.TemporaryDirectory(dir=REPO_ROOT / "tmp")
|
|
self.addCleanup(tmp.cleanup)
|
|
previous = os.getcwd()
|
|
os.chdir(tmp.name)
|
|
self.addCleanup(os.chdir, previous)
|
|
files: dict = {}
|
|
for rel in ("bios/p/req.bin", "bios/p/opt.bin", "bios/c/core.bin", "bios/c/extra.bin"):
|
|
payload = rel.encode()
|
|
Path(rel).parent.mkdir(parents=True, exist_ok=True)
|
|
Path(rel).write_bytes(payload)
|
|
sha1 = hashlib.sha1(payload).hexdigest()
|
|
files[sha1] = {"path": rel, "name": Path(rel).name, "size": len(payload),
|
|
"sha1": sha1, "md5": hashlib.md5(payload).hexdigest(),
|
|
"sha256": hashlib.sha256(payload).hexdigest(),
|
|
"crc32": "00000001"}
|
|
self.db = {"files": files, "indexes": generate_db.build_indexes(files, {})}
|
|
Path("platforms").mkdir()
|
|
config = {"platform": "P", "verification_mode": "existence", "cores": ["c"],
|
|
"systems": {"s": {"files": [
|
|
{"name": "req.bin", "destination": "req.bin", "required": True},
|
|
{"name": "opt.bin", "destination": "opt.bin", "required": False},
|
|
]}}}
|
|
Path("platforms/p.yml").write_text(yaml.safe_dump(config), encoding="utf-8")
|
|
Path("platforms/_registry.yml").write_text(
|
|
yaml.safe_dump({"platforms": {"p": {"status": "active"}}}), encoding="utf-8"
|
|
)
|
|
Path("emulators").mkdir()
|
|
Path("emulators/c.yml").write_text(yaml.safe_dump({
|
|
"emulator": "c", "type": "libretro", "systems": ["s"], "files": [
|
|
{"name": "core.bin", "path": "core.bin", "required": True},
|
|
{"name": "extra.bin", "path": "extra.bin", "required": False},
|
|
]}), encoding="utf-8")
|
|
common._platform_config_cache.clear()
|
|
common._emulator_profiles_cache.clear()
|
|
self.addCleanup(common._platform_config_cache.clear)
|
|
self.addCleanup(common._emulator_profiles_cache.clear)
|
|
self.profiles = common.load_emulator_profiles("emulators")
|
|
|
|
def _build(self, source: str, required: bool) -> tuple[str, set[str]]:
|
|
zip_path = self.gp.generate_pack(
|
|
"p", "platforms", self.db, "bios", "out", emulators_dir="emulators",
|
|
emu_profiles=self.profiles, source=source, required_only=required,
|
|
offline=True,
|
|
)
|
|
with zipfile.ZipFile(zip_path) as zf:
|
|
return zip_path, set(zf.namelist()) - {"README.txt"}
|
|
|
|
def _check(self, zip_path: str, source: str, required: bool) -> tuple:
|
|
return packverify.verify_pack_against_platform(
|
|
zip_path, "p", "platforms", self.db, emulators_dir="emulators",
|
|
emu_profiles=self.profiles, source=source, required_only=required,
|
|
)
|
|
|
|
def test_every_variant_passes_as_built(self):
|
|
expected = {
|
|
("full", False): {"req.bin", "opt.bin", "core.bin", "extra.bin"},
|
|
("full", True): {"req.bin", "core.bin"},
|
|
("platform", False): {"req.bin", "opt.bin"},
|
|
("platform", True): {"req.bin"},
|
|
("truth", False): {"core.bin", "extra.bin"},
|
|
("truth", True): {"core.bin"},
|
|
}
|
|
for (source, required), members in expected.items():
|
|
with self.subTest(source=source, required=required):
|
|
zip_path, held = self._build(source, required)
|
|
self.assertEqual(held, members)
|
|
ok, _c, _p, errors, *_rest = self._check(zip_path, source, required)
|
|
self.assertTrue(ok, errors)
|
|
self.assertEqual(
|
|
self.gp._pack_variant(os.path.basename(zip_path)), (source, required)
|
|
)
|
|
|
|
def test_a_narrowed_pack_missing_a_required_file_fails(self):
|
|
for source, required, lost in (
|
|
("full", True, "core.bin"), ("platform", True, "req.bin"),
|
|
("truth", False, "extra.bin"), ("truth", True, "core.bin"),
|
|
):
|
|
with self.subTest(source=source, required=required):
|
|
zip_path, held = self._build(source, required)
|
|
with zipfile.ZipFile(zip_path) as zf:
|
|
kept = {n: zf.read(n) for n in held if n != lost}
|
|
with zipfile.ZipFile(zip_path, "w") as zf:
|
|
for name, data in kept.items():
|
|
zf.writestr(name, data)
|
|
ok, _c, _p, errors, *_rest = self._check(zip_path, source, required)
|
|
self.assertFalse(ok)
|
|
self.assertTrue(any(lost in e for e in errors), errors)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|