mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-13 05:23:51 -06:00
chore: merge audit batch, verify and pack
This commit is contained in:
commit
3f50163b0a
15 files changed
+748
-127
No files matched your search
@@ -4830,7 +4830,7 @@ class VerifyAllSummaryNamesWhatIsWrong(unittest.TestCase):
|
||||
summary = verify._overall_summary(
|
||||
[(clean, ["RetroArch"]), (warned, ["RetroDECK"]), (core_miss, ["ROCKNIX"])]
|
||||
)
|
||||
self.assertIn("1 warning (RetroDECK)", summary)
|
||||
self.assertIn("1 warning (RetroDECK 1)", summary)
|
||||
self.assertIn("ROCKNIX: n64.psh", summary)
|
||||
self.assertIn("0 critical,", summary)
|
||||
|
||||
|
||||
@@ -87,6 +87,59 @@ class CoreFilesAsTheBuilderPlacesThem(unittest.TestCase):
|
||||
)
|
||||
|
||||
|
||||
class ARefusedPinLeavesItsPathToTheCore(unittest.TestCase):
|
||||
"""EmuELEC pins naomi.zip by an md5 the collection does not hold: the
|
||||
builder refuses the held copy and ships the core's file at that path.
|
||||
The check marked the path taken by the refused platform file, so the
|
||||
core file the pack carries there was never compared."""
|
||||
|
||||
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/dc/naomi.zip", b"the core's naomi")
|
||||
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": "md5", "cores": ["flycast"],
|
||||
"systems": {"dc": {"files": [{
|
||||
"name": "naomi.zip", "destination": "dc/naomi.zip",
|
||||
"md5": hashlib.md5(b"a bundle nobody holds").hexdigest(),
|
||||
}]}}}
|
||||
Path("platforms/p.yml").write_text(yaml.safe_dump(config), encoding="utf-8")
|
||||
Path("emulators").mkdir()
|
||||
self.profiles = {"flycast": {
|
||||
"emulator": "flycast", "type": "libretro", "systems": ["dc"], "files": [
|
||||
{"name": "naomi.zip", "path": "dc/naomi.zip"}]}}
|
||||
|
||||
def _check(self, member: bytes) -> tuple:
|
||||
with zipfile.ZipFile("pack.zip", "w") as zf:
|
||||
zf.writestr("dc/naomi.zip", member)
|
||||
return packverify.verify_pack_against_platform(
|
||||
"pack.zip", "p", "platforms", self.db, emulators_dir="emulators",
|
||||
emu_profiles=self.profiles,
|
||||
)
|
||||
|
||||
def test_the_core_file_at_the_refused_path_is_compared(self):
|
||||
_ok, _c, _p, errors, *_mid, core_checked, _cp, _ex, _ab = self._check(b"other")
|
||||
self.assertEqual(core_checked, 1)
|
||||
self.assertTrue(
|
||||
any(e.startswith("core file differs: dc/naomi.zip") for e in errors), errors
|
||||
)
|
||||
|
||||
def test_the_right_core_file_passes(self):
|
||||
_ok, _c, _p, errors, *_mid, core_checked, core_present, _ex, _ab = (
|
||||
self._check(b"the core's naomi")
|
||||
)
|
||||
self.assertEqual((core_checked, core_present), (1, 1))
|
||||
self.assertFalse([e for e in errors if e.startswith("core")], 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
|
||||
|
||||
@@ -420,6 +420,24 @@ class TestArbitration(unittest.TestCase):
|
||||
served = slots.format_decision(slots.arbitrate(self._conflict(), "existence"))
|
||||
self.assertIn("both are satisfied", served)
|
||||
|
||||
def test_each_emulator_is_given_the_file_it_reads(self):
|
||||
"""Recalbox's neocdz.zip line said geolith reads the archive fbneo
|
||||
reads, while geolith pins another one."""
|
||||
conflict = self._conflict()
|
||||
conflict.profile_claims = [
|
||||
slots.Claim("profile", "neocdz.zip", "neocdz.zip", emulator="fbneo",
|
||||
local_path="a/neocdz.zip.ba20a7be", status="members_held"),
|
||||
slots.Claim("profile", "neocdz.zip", "neocdz.zip", emulator="geolith",
|
||||
local_path="b/neocdz.zip.712816a3", status="md5_exact"),
|
||||
]
|
||||
kept = slots.format_decision(slots.arbitrate(conflict, "md5"))
|
||||
self.assertIn("a/neocdz.zip.ba20a7be, the file fbneo reads", kept)
|
||||
self.assertIn("b/neocdz.zip.712816a3, the file geolith reads", kept)
|
||||
self.assertEqual(conflict.files_read(), [
|
||||
("a/neocdz.zip.ba20a7be", ["fbneo"], "members_held"),
|
||||
("b/neocdz.zip.712816a3", ["geolith"], "md5_exact"),
|
||||
])
|
||||
|
||||
|
||||
class TheStrongestProofTakesAnUndeclaredPath(unittest.TestCase):
|
||||
"""No platform declares RetroDECK's hiscore.dat. fbalpha2012 asks for it
|
||||
|
||||
+169
-2
@@ -4,11 +4,13 @@
|
||||
under the target's name (Recalbox, Lakka, BizHawk, MiSTer, ROCKNIX). The
|
||||
conformance check read the same target without the --all tolerance, raised
|
||||
FileNotFoundError on the first of those packs and stopped the run in a
|
||||
traceback after every pack had been written.
|
||||
traceback after every pack had been written. Such a platform is now left
|
||||
out of a targeted --all run, and the check refuses it as the build does.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
import contextlib
|
||||
import io
|
||||
@@ -31,7 +33,10 @@ class CheckReadsTheTargetLikeTheBuild(unittest.TestCase):
|
||||
contextlib.redirect_stdout(io.StringIO()):
|
||||
with self.assertRaises(ValueError):
|
||||
build_target_cores_cache(["recalbox"], "x86_64", platforms, is_all=True)
|
||||
self.assertIsNone(_target_cores_for("recalbox", "x86_64", platforms))
|
||||
# The build never packs such a platform under the target, so the
|
||||
# check refuses it too instead of reading the full pack.
|
||||
with self.assertRaises(FileNotFoundError):
|
||||
_target_cores_for("recalbox", "x86_64", platforms)
|
||||
|
||||
def test_a_platform_with_a_target_file_is_narrowed(self):
|
||||
cores = _target_cores_for("retroarch", "switch", str(REPO_ROOT / "platforms"))
|
||||
@@ -54,5 +59,167 @@ class ANameClaimsOnlyANarrowingThatHappened(unittest.TestCase):
|
||||
)
|
||||
|
||||
|
||||
class AMistypedTargetIsRefusedFirst(unittest.TestCase):
|
||||
"""`--platform retroarch --target swich` took the dist lock and waited on
|
||||
the data directory refresh, 22 s here and hours behind a pipeline, before
|
||||
saying the target does not exist."""
|
||||
|
||||
def test_no_lock_and_no_refresh_before_the_refusal(self):
|
||||
from unittest import mock
|
||||
|
||||
import generate_pack
|
||||
|
||||
def touched(*_a, **_kw):
|
||||
raise AssertionError("lock or refresh taken before the target check")
|
||||
|
||||
argv = ["generate_pack.py", "--platform", "retroarch", "--target", "swich",
|
||||
"--output-dir", str(REPO_ROOT / "tmp" / "never-written")]
|
||||
err = io.StringIO()
|
||||
with mock.patch.object(sys, "argv", argv), \
|
||||
mock.patch.object(generate_pack, "_pack_output_lock", touched), \
|
||||
mock.patch.object(generate_pack, "hold_data_dirs", touched), \
|
||||
contextlib.redirect_stdout(io.StringIO()), \
|
||||
contextlib.redirect_stderr(err), \
|
||||
self.assertRaises(SystemExit) as ctx:
|
||||
generate_pack.main()
|
||||
self.assertEqual(ctx.exception.code, 1)
|
||||
self.assertIn("Unknown target 'swich'", err.getvalue())
|
||||
|
||||
|
||||
class ASystemTheTargetDropsIsNamedAsSuch(unittest.TestCase):
|
||||
"""`--platform retroarch --system sony-playstation-2 --target switch`:
|
||||
verify printed 0/0 with exit 0 under --strict, and generate_pack said
|
||||
RetroArch has no such system and suggested sony-playstation."""
|
||||
|
||||
PLATFORMS = str(REPO_ROOT / "platforms")
|
||||
|
||||
def test_both_tools_read_one_scope(self):
|
||||
from common import (
|
||||
load_emulator_profiles, load_platform_config, platform_scope_systems,
|
||||
)
|
||||
|
||||
config = load_platform_config("retroarch", self.PLATFORMS)
|
||||
profiles = load_emulator_profiles(str(REPO_ROOT / "emulators"))
|
||||
switch = _target_cores_for("retroarch", "switch", self.PLATFORMS)
|
||||
wanted = ["sony-playstation-2"]
|
||||
self.assertEqual(platform_scope_systems(config, profiles, switch, wanted), {})
|
||||
self.assertEqual(list(platform_scope_systems(config, profiles, None, wanted)), wanted)
|
||||
|
||||
def test_the_message_names_the_target(self):
|
||||
from common import system_scope_error
|
||||
|
||||
config = {"systems": {"sony-playstation-2": {}, "sony-playstation": {}}}
|
||||
self.assertEqual(
|
||||
system_scope_error(config, "retroarch", ["sony-playstation-2"], "switch"),
|
||||
"no core retroarch runs on target switch emulates sony-playstation-2",
|
||||
)
|
||||
undeclared = system_scope_error(config, "retroarch", ["nec-pc-98"], "switch")
|
||||
self.assertTrue(undeclared.startswith("retroarch has no system matching nec-pc-98."))
|
||||
|
||||
def test_verify_refuses_it(self):
|
||||
import subprocess
|
||||
|
||||
proc = subprocess.run(
|
||||
[sys.executable, "scripts/verify.py", "--platform", "retroarch",
|
||||
"--system", "sony-playstation-2", "--target", "switch", "--strict"],
|
||||
capture_output=True, text=True, cwd=str(REPO_ROOT), timeout=600, check=False,
|
||||
)
|
||||
self.assertEqual(proc.returncode, 1, proc.stdout[-300:])
|
||||
self.assertIn("no core retroarch runs on target switch emulates sony-playstation-2",
|
||||
proc.stderr)
|
||||
|
||||
|
||||
class ASplitBuildsOnlyTheTargetsSystems(unittest.TestCase):
|
||||
"""`--split --target switch` asked for a part per declared system, and
|
||||
each one the target drops ended on 'retroarch has no system matching'."""
|
||||
|
||||
def test_no_part_is_asked_for_a_dropped_system(self):
|
||||
from unittest import mock
|
||||
|
||||
import generate_pack
|
||||
from common import load_emulator_profiles
|
||||
|
||||
platforms = str(REPO_ROOT / "platforms")
|
||||
profiles = load_emulator_profiles(str(REPO_ROOT / "emulators"))
|
||||
asked: list[list[str]] = []
|
||||
|
||||
def record(*_a, **kw):
|
||||
if kw.get("system_filter"):
|
||||
asked.append(kw["system_filter"])
|
||||
return None
|
||||
|
||||
with tempfile.TemporaryDirectory(dir=REPO_ROOT / "tmp") as out, \
|
||||
mock.patch.object(generate_pack, "generate_pack", record), \
|
||||
mock.patch.object(generate_pack, "_collect_emulator_extras",
|
||||
return_value=[]):
|
||||
generate_pack.generate_split_packs(
|
||||
"retroarch", platforms, {"files": {}}, "bios", out,
|
||||
emu_profiles=profiles,
|
||||
target_cores=_target_cores_for("retroarch", "switch", platforms),
|
||||
target_name="nintendo-switch",
|
||||
)
|
||||
systems = {sid for ids in asked for sid in ids}
|
||||
self.assertTrue(systems)
|
||||
self.assertNotIn("sony-playstation-2", systems)
|
||||
self.assertIn("sony-playstation", systems)
|
||||
|
||||
|
||||
class APlatformWithoutTheSystemIsOutOfScope(unittest.TestCase):
|
||||
"""`--all --system sony-playstation-2` built the five PS2 packs and
|
||||
exited 1 on 'no pack for misterfpga', which has no PS2."""
|
||||
|
||||
def _scope(self, **args):
|
||||
import argparse
|
||||
|
||||
import generate_pack
|
||||
from common import load_emulator_profiles
|
||||
|
||||
values = {"platforms_dir": str(REPO_ROOT / "platforms"), "target": None,
|
||||
"all": True}
|
||||
values.update(args)
|
||||
out = io.StringIO()
|
||||
with contextlib.redirect_stdout(out), contextlib.redirect_stderr(out):
|
||||
kept = generate_pack._platforms_with_systems(
|
||||
argparse.Namespace(**values), ["retroarch", "misterfpga"],
|
||||
["sony-playstation-2"],
|
||||
load_emulator_profiles(str(REPO_ROOT / "emulators")), {},
|
||||
)
|
||||
return kept, out.getvalue()
|
||||
|
||||
def test_all_skips_it(self):
|
||||
kept, out = self._scope()
|
||||
self.assertEqual(kept, ["retroarch"])
|
||||
self.assertIn("INFO: Skipping misterfpga: no system sony-playstation-2", out)
|
||||
|
||||
def test_a_named_platform_is_refused(self):
|
||||
with self.assertRaises(SystemExit) as ctx:
|
||||
self._scope(all=False)
|
||||
self.assertEqual(ctx.exception.code, 1)
|
||||
|
||||
|
||||
class TheSplitDirectoryReadsThePackStem(unittest.TestCase):
|
||||
"""The split prefix was assembled by hand: a version 'release/1.22' made
|
||||
'RetroArch_release/1.22_Split', a nested directory the check never
|
||||
found, beside a full pack named 'RetroArch_release-1.22_BIOS_Pack.zip'."""
|
||||
|
||||
def test_a_reserved_character_is_made_safe_as_in_the_full_pack(self):
|
||||
from unittest import mock
|
||||
|
||||
import generate_pack
|
||||
|
||||
config = {"platform": "Foo: Bar", "version": "release/1.22", "systems": {}}
|
||||
with tempfile.TemporaryDirectory(dir=REPO_ROOT / "tmp") as out, \
|
||||
mock.patch.object(generate_pack, "load_platform_config",
|
||||
return_value=config), \
|
||||
mock.patch.object(generate_pack, "_collect_emulator_extras",
|
||||
return_value=[]):
|
||||
generate_pack.generate_split_packs(
|
||||
"foo", "platforms", {"files": {}}, "bios", out, emu_profiles={},
|
||||
)
|
||||
stem = generate_pack._platform_pack_stem(["foo"], "foo", "platforms")
|
||||
self.assertEqual(os.listdir(out), [f"{stem}_Split"])
|
||||
self.assertEqual(stem, "Foo-_Bar_release-1.22")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
+116
-1
@@ -132,7 +132,7 @@ class TheVerifySummaryCountsDiscrepancies(unittest.TestCase):
|
||||
({"severity_counts": {}, "details": details}, ["RetroArch"]),
|
||||
({"severity_counts": {}, "details": []}, ["Batocera"]),
|
||||
])
|
||||
self.assertIn("1 discrepancy (RetroArch)", summary)
|
||||
self.assertIn("1 discrepancy (RetroArch 1)", summary)
|
||||
|
||||
def test_a_shared_destination_names_the_path_and_both_sides(self):
|
||||
result = {"name": "disk.rom", "status": verify.Status.UNTESTED}
|
||||
@@ -145,6 +145,121 @@ class TheVerifySummaryCountsDiscrepancies(unittest.TestCase):
|
||||
self.assertIn(part, message)
|
||||
|
||||
|
||||
class EveryPlatformInTheSummaryCarriesItsCount(unittest.TestCase):
|
||||
"""'17 discrepancies (Lakka 5, RetroArch 5, RetroBat 2, RetroDECK 4,
|
||||
ROCKNIX)': the one platform with a single entry had no number, and the
|
||||
named counts did not add up to the total."""
|
||||
|
||||
def test_a_count_of_one_is_written(self):
|
||||
one = {"severity_counts": {verify.Severity.WARNING: 1}, "details": []}
|
||||
two = {"severity_counts": {verify.Severity.WARNING: 2}, "details": []}
|
||||
summary = verify._overall_summary([(two, ["Lakka"]), (one, ["ROCKNIX"])])
|
||||
self.assertIn("3 warning (Lakka 2, ROCKNIX 1)", summary)
|
||||
|
||||
|
||||
class HeldButNotPackedNamesEachReason(unittest.TestCase):
|
||||
"""'Core files held but not packed: 16 (a packed file occupies its
|
||||
directory)' on Lakka, where 14 of the 16 are installed with their
|
||||
emulator: the first entry's reason stood for the whole count."""
|
||||
|
||||
@staticmethod
|
||||
def _entry(name: str, reason: str, category: str = "bios") -> dict:
|
||||
return {"name": name, "emulator": "x", "required": True, "in_repo": True,
|
||||
"in_pack": False, "omitted": reason, "category": category}
|
||||
|
||||
def _printed(self, entries: list[dict]) -> str:
|
||||
out = io.StringIO()
|
||||
with contextlib.redirect_stdout(out):
|
||||
verify._print_undeclared_section({"undeclared_files": entries}, False)
|
||||
return out.getvalue()
|
||||
|
||||
def test_each_reason_carries_its_count(self):
|
||||
entries = [self._entry("a.rom", "a packed file occupies its directory")]
|
||||
entries += [self._entry(f"{i}.rom", "installed with its emulator") for i in range(3)]
|
||||
entries += [self._entry(f"g{i}", "installed with its emulator", "game_data")
|
||||
for i in range(2)]
|
||||
entries.append(self._entry("g9", "no platform slug for its system", "game_data"))
|
||||
text = self._printed(entries)
|
||||
self.assertIn(
|
||||
"Core files held but not packed: 4 (3 installed with its emulator, "
|
||||
"1 a packed file occupies its directory)", text)
|
||||
self.assertIn(
|
||||
"Game data held but not packed: 3 (2 installed with its emulator, "
|
||||
"1 no platform slug for its system)", text)
|
||||
|
||||
def test_a_missing_file_follows_the_line_that_counts_it(self):
|
||||
"""Indented under 'held but not packed: 102 (installed with its
|
||||
emulator)', Batocera's one missing sfiii2a.zip read as one of them."""
|
||||
missing = {"name": "sfiii2a.zip", "emulator": "FinalBurn Neo", "required": True,
|
||||
"in_repo": False, "category": "bios"}
|
||||
lines = self._printed(
|
||||
[missing, self._entry("a.rom", "installed with its emulator")]
|
||||
).splitlines()
|
||||
self.assertTrue(lines[0].startswith(" Core files: "), lines)
|
||||
self.assertIn("MISSING (required): FinalBurn Neo needs sfiii2a.zip", lines[1])
|
||||
self.assertTrue(lines[2].startswith(" Core files held but not packed: "), lines)
|
||||
|
||||
def test_one_name_read_at_two_paths_prints_both_paths(self):
|
||||
"""MyMinUI printed 'FinalBurn Neo needs sfiii2a.zip' twice, word for
|
||||
word, for Bios/FBN/ and Bios/NG/."""
|
||||
base = {"name": "sfiii2a.zip", "emulator": "FinalBurn Neo", "required": True,
|
||||
"in_repo": False, "category": "bios"}
|
||||
lone = dict(base, name="other.zip", path="Bios/FBN/other.zip")
|
||||
text = self._printed([dict(base, path="Bios/FBN/sfiii2a.zip"),
|
||||
dict(base, path="Bios/NG/sfiii2a.zip"), lone])
|
||||
self.assertIn("FinalBurn Neo needs Bios/FBN/sfiii2a.zip", text)
|
||||
self.assertIn("FinalBurn Neo needs Bios/NG/sfiii2a.zip", text)
|
||||
self.assertIn("FinalBurn Neo needs other.zip\n", text)
|
||||
|
||||
def test_a_single_reason_reads_as_before(self):
|
||||
text = self._printed([self._entry("a.rom", "installed with its emulator")] * 2)
|
||||
self.assertIn("Core files held but not packed: 2 (installed with its emulator)", text)
|
||||
|
||||
|
||||
class APlatformWithoutTargetsSaysSo(unittest.TestCase):
|
||||
"""`--platform misterfpga --target switch` printed 'No target config for
|
||||
platform 'misterfpga': platforms/targets/misterfpga.yml', which reads as
|
||||
a file missing from the repository."""
|
||||
|
||||
def test_the_message_names_the_reason_and_the_platforms_with_targets(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
(Path(tmp) / "targets").mkdir()
|
||||
for name in ("retroarch", "batocera", "_overrides"):
|
||||
(Path(tmp) / "targets" / f"{name}.yml").write_text("targets: {}\n")
|
||||
with self.assertRaises(FileNotFoundError) as caught:
|
||||
common.load_target_config("misterfpga", "switch", tmp)
|
||||
self.assertEqual(
|
||||
str(caught.exception),
|
||||
"misterfpga has no hardware targets: drop --target "
|
||||
"(platforms with targets: batocera, retroarch)",
|
||||
)
|
||||
|
||||
|
||||
class OneNameListsThePlatforms(unittest.TestCase):
|
||||
"""install.py --list-platforms, generate_pack.py --list, and no listing
|
||||
at all in verify.py: three scripts, three answers to one question."""
|
||||
|
||||
def _run(self, script: str, flag: str) -> subprocess.CompletedProcess:
|
||||
return subprocess.run(
|
||||
[sys.executable, f"scripts/{script}", flag], capture_output=True, text=True,
|
||||
cwd=str(REPO_ROOT), timeout=120, check=False,
|
||||
)
|
||||
|
||||
def test_both_tools_take_it_and_agree(self):
|
||||
verify_out = self._run("verify.py", "--list-platforms")
|
||||
pack_out = self._run("generate_pack.py", "--list-platforms")
|
||||
self.assertEqual(verify_out.returncode, 0, verify_out.stderr)
|
||||
self.assertEqual(pack_out.returncode, 0, pack_out.stderr)
|
||||
self.assertEqual(verify_out.stdout, pack_out.stdout)
|
||||
self.assertIn("retroarch", verify_out.stdout.split())
|
||||
self.assertIn("--list-platforms", (REPO_ROOT / "install.py").read_text())
|
||||
|
||||
def test_the_installer_name_is_the_one_documented(self):
|
||||
tools = (REPO_ROOT / "wiki" / "tools.md").read_text()
|
||||
self.assertIn("generate_pack.py --list-platforms", tools)
|
||||
self.assertIn("verify.py --list-platforms", tools)
|
||||
|
||||
|
||||
class OneSpellingPerOption(unittest.TestCase):
|
||||
"""verify.py -p batocera worked and slots.py -p batocera was refused."""
|
||||
|
||||
|
||||
@@ -116,6 +116,24 @@ class VerifyPackChoice(unittest.TestCase):
|
||||
dict(zip((name for _p, name in checked), self.variants)), names
|
||||
)
|
||||
|
||||
def test_a_target_the_platform_lacks_is_refused(self):
|
||||
"""`--platform bizhawk --target rpi4typo --verify-packs` verified the
|
||||
full pack and said `all OK`, where the build refused the target."""
|
||||
import contextlib
|
||||
import io
|
||||
|
||||
bizhawk = self.gp._platform_pack_stem(["bizhawk"], "bizhawk", "platforms")
|
||||
self._touch(f"{bizhawk}_BIOS_Pack.zip")
|
||||
self._touch(f"{self.stem}_BIOS_Pack.zip")
|
||||
for platform, target in (("bizhawk", "rpi4typo"), ("retroarch", "swich")):
|
||||
with self.subTest(platform=platform), \
|
||||
contextlib.redirect_stdout(io.StringIO()), \
|
||||
contextlib.redirect_stderr(io.StringIO()), \
|
||||
self.assertRaises(SystemExit) as ctx:
|
||||
self._run(self._args(platform=platform, target=target))
|
||||
self.assertEqual(ctx.exception.code, 1)
|
||||
self.assertEqual(self.variants, [])
|
||||
|
||||
def test_the_run_ends_on_a_summary(self):
|
||||
"""A failing pack was named only above its own error lines, and the
|
||||
log ended on the last platform's OK line."""
|
||||
|
||||
@@ -89,6 +89,12 @@ class ReleaseAssetNotCached(unittest.TestCase):
|
||||
self.assertEqual(entry["status"], Status.OK)
|
||||
self.assertEqual(entry["release_asset"], REGISTERED[0])
|
||||
|
||||
def test_its_path_is_a_pack_destination(self):
|
||||
"""The pack holds the fetched file at its path: absent from
|
||||
pack_destinations, step 5 reported it 'pack only'."""
|
||||
report = verify_platform(self.config, self.db, "emulators", {})
|
||||
self.assertIn("big.bin", report["pack_destinations"])
|
||||
|
||||
def test_offline_counts_it_missing(self):
|
||||
packresolve.set_offline(True)
|
||||
self.assertEqual(self._entry()["status"], Status.MISSING)
|
||||
@@ -117,6 +123,59 @@ class ReleaseAssetNotCachedMd5(ReleaseAssetNotCached):
|
||||
mode = "md5"
|
||||
|
||||
|
||||
class CoreExtraReleaseAssetNotCached(ReleaseAssetNotCached):
|
||||
"""The same asset read by a core and declared by no platform entry: the
|
||||
builder fetched and packed it online, verify's replay of the extras
|
||||
resolved it offline and left its path out of the pack."""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.config["systems"]["sys"]["files"] = []
|
||||
self.config["cores"] = ["bigcore"]
|
||||
Path("platforms/big.yml").write_text(yaml.dump(self.config))
|
||||
self.profiles = {"bigcore": {
|
||||
"emulator": "Big Core", "type": "libretro", "systems": ["sys"],
|
||||
"files": [{"name": "big.bin", "required": True, "sha1": SHA1}],
|
||||
}}
|
||||
Path("emulators/bigcore.yml").write_text(yaml.dump(self.profiles["bigcore"]))
|
||||
common._platform_config_cache.clear()
|
||||
common._emulator_profiles_cache.clear()
|
||||
|
||||
def _report(self) -> dict:
|
||||
return verify_platform(self.config, self.db, "emulators", self.profiles)
|
||||
|
||||
def test_counted_as_fetched_when_the_pack_is_built(self):
|
||||
report = self._report()
|
||||
self.assertIn("big.bin", report["pack_destinations"])
|
||||
self.assertEqual(report["packed_extras"]["bios"], 1)
|
||||
|
||||
def test_its_path_is_a_pack_destination(self):
|
||||
cache = Path("cache/big.bin")
|
||||
cache.parent.mkdir()
|
||||
cache.write_bytes(PAYLOAD)
|
||||
with mock.patch.object(
|
||||
packresolve, "fetch_large_file", return_value=str(cache)
|
||||
):
|
||||
zip_path = builder.generate_pack(
|
||||
"big", "platforms", self.db, "bios", "out",
|
||||
emulators_dir="emulators", emu_profiles=self.profiles, offline=False,
|
||||
)
|
||||
with zipfile.ZipFile(zip_path) as zf:
|
||||
packed = {n for n in zf.namelist() if not n.endswith("/")}
|
||||
self.assertIn("big.bin", packed)
|
||||
self.assertIn("big.bin", self._report()["pack_destinations"])
|
||||
|
||||
def test_offline_counts_it_missing(self):
|
||||
packresolve.set_offline(True)
|
||||
self.assertNotIn("big.bin", self._report()["pack_destinations"])
|
||||
|
||||
def test_an_asset_with_other_content_is_not_counted(self):
|
||||
self.profiles["bigcore"]["files"][0]["sha1"] = "0" * 40
|
||||
self.assertNotIn("big.bin", self._report()["pack_destinations"])
|
||||
|
||||
test_the_online_build_packs_it = None
|
||||
|
||||
|
||||
class OfflinePipelineVerifiesOffline(unittest.TestCase):
|
||||
"""The offline pipeline builds packs without release assets: its verify
|
||||
step has to count them the same way, or step 5 compares two answers."""
|
||||
|
||||
Reference in new issue
Block a user