mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-16 08:23:28 -05:00
134 lines
5.6 KiB
Python
134 lines
5.6 KiB
Python
"""One rule picks the held file that satisfies the emulator.
|
|
|
|
verify credited a variant found through the emulator's hashes while the
|
|
platform pack only searched by name and kept the platform's md5: the report
|
|
called ATARIOSB.ROM satisfied while the pack shipped the dump atari800
|
|
rejects, and `verify --emulator azahar` credited an otp.bin the emulator pack
|
|
never swapped in. Both sides now read find_validated_variant.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
|
|
|
from validation import find_validated_variant # noqa: E402
|
|
|
|
|
|
class FindValidatedVariant(unittest.TestCase):
|
|
def setUp(self):
|
|
self._tmp = tempfile.TemporaryDirectory()
|
|
self.addCleanup(self._tmp.cleanup)
|
|
root = Path(self._tmp.name)
|
|
self.bad = root / "rom.bin"
|
|
self.bad.write_bytes(b"x" * 8)
|
|
self.good = root / "other_name.bin"
|
|
self.good.write_bytes(b"y" * 16)
|
|
self.alias = root / "v" / "rom.bin"
|
|
self.alias.parent.mkdir()
|
|
self.alias.write_bytes(b"z" * 16)
|
|
|
|
def entry(path: Path) -> tuple[str, dict]:
|
|
data = path.read_bytes()
|
|
sha1 = hashlib.sha1(data).hexdigest()
|
|
return sha1, {"path": str(path), "name": path.name, "sha1": sha1,
|
|
"md5": hashlib.md5(data).hexdigest(), "size": len(data)}
|
|
|
|
self.files = dict(entry(p) for p in (self.bad, self.good, self.alias))
|
|
self.good_sha1 = hashlib.sha1(self.good.read_bytes()).hexdigest()
|
|
self.db = {"files": self.files, "indexes": {
|
|
"by_name": {"rom.bin": [s for s, e in self.files.items() if e["name"] == "rom.bin"]},
|
|
"by_md5": {e["md5"]: s for s, e in self.files.items()},
|
|
}}
|
|
from validation import _build_validation_index
|
|
|
|
self.by_hash = _build_validation_index({"emu": {"files": [
|
|
{"name": "rom.bin", "validation": ["size", "sha1"], "size": 16,
|
|
"sha1": self.good_sha1}]}})
|
|
self.by_size = _build_validation_index({"emu": {"files": [
|
|
{"name": "rom.bin", "validation": ["size"], "size": 16}]}})
|
|
|
|
def test_a_dump_held_under_another_name_is_found_by_the_emulator_hash(self):
|
|
found = find_validated_variant({"name": "rom.bin"}, self.db, str(self.bad), self.by_hash)
|
|
self.assertEqual(found, str(self.good))
|
|
|
|
def test_a_digest_platform_only_takes_a_value_its_entry_declares(self):
|
|
alias_md5 = hashlib.md5(self.alias.read_bytes()).hexdigest()
|
|
entry = {"name": "rom.bin", "md5": alias_md5}
|
|
found = find_validated_variant(
|
|
entry, self.db, str(self.bad), self.by_size, platform_digest="md5")
|
|
self.assertEqual(found, str(self.alias))
|
|
entry = {"name": "rom.bin", "md5": "0" * 32}
|
|
self.assertIsNone(find_validated_variant(
|
|
entry, self.db, str(self.bad), self.by_size, platform_digest="md5"))
|
|
|
|
|
|
class OneImplementation(unittest.TestCase):
|
|
"""The report and the builders call the same finder and keep no copy."""
|
|
|
|
def test_no_module_carries_its_own_variant_search(self):
|
|
for name in ("verify.py", "generate_pack.py", "packresolve.py"):
|
|
source = (REPO_ROOT / "scripts" / name).read_text(encoding="utf-8")
|
|
with self.subTest(module=name):
|
|
self.assertNotRegex(source, r"def _find_best_variant|def _find_candidate_satisfying_both")
|
|
for name in ("verify.py", "generate_pack.py"):
|
|
source = (REPO_ROOT / "scripts" / name).read_text(encoding="utf-8")
|
|
with self.subTest(module=name):
|
|
# Directly or through validated_choice, the one place that
|
|
# picks the variant a destination ships.
|
|
calls = re.findall(r"(?:find_validated_variant|validated_choice)\(", source)
|
|
self.assertGreaterEqual(len(calls), 2 if name == "generate_pack.py" else 3)
|
|
|
|
|
|
_SEEDED = """
|
|
import hashlib, sys, tempfile
|
|
from pathlib import Path
|
|
sys.path.insert(0, sys.argv[1])
|
|
from validation import _build_validation_index, find_validated_variant
|
|
root = Path(sys.argv[2])
|
|
paths = [root / f"ctrl_{i}.rom" for i in range(8)]
|
|
files = {}
|
|
for i, path in enumerate(paths):
|
|
data = bytes([i]) * 16
|
|
path.write_bytes(data)
|
|
files[hashlib.sha1(data).hexdigest()] = {"path": str(path), "name": path.name}
|
|
bad = root / "MT32_CONTROL.ROM"
|
|
bad.write_bytes(b"x" * 16)
|
|
index = _build_validation_index({"emu": {"files": [
|
|
{"name": "MT32_CONTROL.ROM", "validation": ["sha1"], "sha1": sha1} for sha1 in files]}})
|
|
db = {"files": files, "indexes": {"by_name": {}}}
|
|
print(find_validated_variant({"name": "MT32_CONTROL.ROM"}, db, str(bad), index))
|
|
"""
|
|
|
|
|
|
class TheChoiceDoesNotDependOnTheRun(unittest.TestCase):
|
|
"""Eight MT-32 control ROMs pass dosbox_pure's sha1 check. The candidates
|
|
were walked in set order, which follows the per-process string hash: the
|
|
manifest named ctrl_mt32_2_07.rom in one run and ctrl_mt32_2_06.rom in
|
|
the next."""
|
|
|
|
def test_the_same_variant_under_every_hash_seed(self):
|
|
chosen = set()
|
|
for seed in ("1", "2", "3", "4", "5", "6"):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
out = subprocess.run(
|
|
[sys.executable, "-c", _SEEDED, str(REPO_ROOT / "scripts"), tmp],
|
|
env={**os.environ, "PYTHONHASHSEED": seed},
|
|
capture_output=True, text=True, check=True,
|
|
).stdout.strip()
|
|
chosen.add(Path(out).name)
|
|
self.assertEqual(len(chosen), 1, chosen)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|