"""System pages and file cards (round 3, package 6). The DS section opened on five DSi files, the Game Boy Advance line printed BizHawk's file name where eight setups write another, the Neo Geo section opened on a file one emulator reads, no arcade card said which release its set matches, console headings carried an icon on 15 % of them, arcade boards sat under "Consoles and computers", and every system page shared one card. """ from __future__ import annotations import re import sys import types import unittest import zipfile from pathlib import Path from unittest import mock REPO_ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(REPO_ROOT / "scripts")) import generate_site as gs # noqa: E402 import make_og_cards # noqa: E402 DIST = REPO_ROOT / "website" / "dist" TMP = REPO_ROOT / "tmp" def _file(name: str, char: str, path: str) -> dict: return {"name": name, "sha1": char * 40, "md5": char * 32, "size": 4096, "path": path} def _consumers( readers: dict[str, list[tuple[str, dict]]], db_files: list[dict] = (), destinations: dict[str, dict[str, set[str]]] | None = None, ) -> types.SimpleNamespace: """Consumers where each sha1 is read by the given (emulator, entry).""" files_of: dict[str, list] = {} entries: dict[str, dict[str, int]] = {} emulators: dict[str, set[str]] = {} for sha1, read_by in readers.items(): for emulator, entry in read_by: files_of.setdefault(emulator, []).append(entry) entries.setdefault(sha1, {})[emulator] = len(files_of[emulator]) - 1 emulators.setdefault(sha1, set()).add(emulator) return types.SimpleNamespace( platforms={}, emulators=emulators, emulator_entries=entries, emulator_files=files_of, installed={}, platform_systems={}, platform_destinations=destinations or {}, _db={"files": {f["sha1"]: f for f in db_files}}, ) DS = ("Nintendo", "Nintendo DS") class TheSectionStartsWithItsOwnConsole(unittest.TestCase): """A file filed with the DS but read for the DSi is the DSi's.""" def setUp(self) -> None: folder = "bios/Nintendo/Nintendo DS" self.files = [_file("NDS_Bios7.bin", "7", f"{folder}/NDS_Bios7.bin"), _file("NDS_Bios9.bin", "9", f"{folder}/NDS_Bios9.bin"), _file("dsi_bios7.bin", "d", f"{folder}/dsi_bios7.bin")] hle = {"required": False, "hle_fallback": True, "system": "nintendo-ds"} self.consumers = _consumers({ "7" * 40: [("emu-a", {**hle, "name": "bios7.bin"}), ("emu-b", {**hle, "name": "bios7.bin"})], "9" * 40: [("emu-a", {**hle, "name": "bios9.bin"})], "d" * 40: [("emu-a", {"name": "dsi_bios7.bin", "required": True, "system": "nintendo-dsi"})], }) def test_no_dsi_file_on_the_ds_line(self) -> None: text = "\n".join(gs._start_with(self.files, self.consumers, section=DS)) self.assertNotIn("dsi", text) def test_a_console_its_emulators_run_without_says_so(self) -> None: self.assertEqual(gs._start_with(self.files, self.consumers, section=DS), [ "**No file needed** with emu-a or emu-b. For full accuracy: " "`bios7.bin`, `bios9.bin`.", ""]) def test_the_dsi_section_names_its_file(self) -> None: lines = gs._start_with(self.files, self.consumers, section=("Nintendo", "Nintendo DSi")) self.assertEqual(lines, ["**Start with**: `dsi_bios7.bin`.", ""]) def test_more_than_three_emulators_are_counted(self) -> None: hle = {"required": False, "hle_fallback": True, "system": "nintendo-ds"} consumers = _consumers({"7" * 40: [(f"emu-{n}", hle) for n in "abcd"]}) self.assertEqual(gs._start_with(self.files[:1], consumers, section=DS)[0], "**No file needed**: none of its 4 emulators requires one. " "For full accuracy: `NDS_Bios7.bin`.") def test_a_shipped_file_is_no_start(self) -> None: consumers = _consumers({"7" * 40: [("emu-a", {"required": True, "bundled": True, "system": "nintendo-ds"})]}) self.assertEqual(gs._start_with(self.files[:1], consumers, section=DS), []) class TheNameSetupsWrite(unittest.TestCase): def test_most_setups_win_over_the_collection_name(self) -> None: f = _file("GBA_bios.rom", "a", "bios/Nintendo/Game Boy Advance/GBA_bios.rom") consumers = _consumers( {"a" * 40: [("emu-a", {"name": "GBA_bios.rom", "required": True, "system": "nintendo-gba"})]}, destinations={"a" * 40: {"p1": {"gba_bios.bin"}, "p2": {"bios/gba_bios.bin"}, "p3": {"GBA_bios.rom"}}}) self.assertEqual(gs._start_with([f], consumers, section=("Nintendo", "Game Boy Advance")), ["**Start with**: `gba_bios.bin`.", ""]) def test_no_setup_then_the_emulators_name(self) -> None: f = _file("x.rom", "b", "bios/Nintendo/Game Boy Advance/x.rom") consumers = _consumers({"b" * 40: [("emu-a", {"name": "gba_bios.bin", "required": True})]}) self.assertEqual(gs._setup_name(f, consumers, [("emu-a", {"name": "gba_bios.bin"})]), "gba_bios.bin") class ArcadeSetsAreNamedWhereverFiled(unittest.TestCase): """The Neo Geo BIOS is under Arcade: the Neo Geo section named aes.zip, read by one emulator, and never neogeo.zip.""" def setUp(self) -> None: self.aes = _file("aes.zip", "1", "bios/SNK/Neo Geo/aes.zip") neogeo = _file("neogeo.zip", "2", "bios/Arcade/Arcade/neogeo.zip") mega_cd = _file("bios_CD_U.bin", "3", "bios/Sega/Mega CD/bios_CD_U.bin") shader = _file("tfx.glsl", "4", "bios/Other/emu/tfx.glsl") mvs = {"required": True, "system": "snk-neogeo-mvs"} self.consumers = _consumers({ "1" * 40: [("geolith", {"name": "aes.zip", "required": True, "system": "snk-neogeo-aes"})], "2" * 40: [(f"mame{n}", {**mvs, "name": "neogeo.zip"}) for n in range(3)], "3" * 40: [("bizhawk", {"name": "bios_CD_U.bin", "required": True, "system": "snk-neogeo-mvs"})], "4" * 40: [("emu", {"name": "tfx.glsl", "required": True, "system": "snk-neogeo-mvs"})], }, db_files=[self.aes, neogeo, mega_cd, shader]) def test_one_line_per_system_and_the_arcade_set_linked(self) -> None: lines = gs._start_with([self.aes], self.consumers, section=("SNK", "Neo Geo")) self.assertEqual(lines[0], "**Start with** the files of your machine:") link = f"[`neogeo.zip`](/retrobios/files/?sha1={'2' * 40})" self.assertIn(f"- **SNK Neo Geo MVS**: {link}", lines) self.assertIn("- **SNK Neo Geo AES**: `aes.zip`", lines) def test_another_consoles_file_or_an_emulators_folder_is_left_out(self) -> None: text = "\n".join(gs._start_with([self.aes], self.consumers, section=("SNK", "Neo Geo"))) self.assertNotIn("bios_CD_U.bin", text) self.assertNotIn("tfx.glsl", text) def test_the_card_read_by_most_emulators_comes_first(self) -> None: one = _file("aes.zip", "1", "bios/SNK/Neo Geo/aes.zip") many = _file("sp-s3.sp1", "5", "bios/SNK/Neo Geo/sp-s3.sp1") consumers = _consumers({ "1" * 40: [("geolith", {"required": True})], "5" * 40: [(f"emu{n}", {"required": True}) for n in range(3)], }) cards = gs._console_cards("SNK", [one, many], consumers, set()) self.assertIn("sp-s3.sp1", cards[0][1][2]) def test_a_long_list_names_the_first_files(self) -> None: files = [_file(f"rom{i}.bin", "abcdefgh"[i], f"bios/SNK/Neo Geo/rom{i}.bin") for i in range(8)] consumers = _consumers({f["sha1"]: [("emu", {"required": True})] for f in files}) self.assertEqual(gs._start_with(files, consumers, first_below=False)[0], "**Start with**: `rom0.bin`, `rom1.bin`, `rom2.bin` and 5 more files.") class CardsSayWhichReleaseTheSetMatches(unittest.TestCase): def setUp(self) -> None: TMP.mkdir(exist_ok=True) self.path = TMP / "r3_systems_set.zip" with zipfile.ZipFile(self.path, "w") as archive: archive.writestr("sp-s2.sp1", b"bios") crc = f"{zipfile.ZipFile(self.path).infolist()[0].CRC:08x}" self.releases = {"set.zip": [ ((("sp-s2.sp1", crc),), "mame", [(0, "0.190"), (1, "0.195"), (4, "0.220")]), ((("sp-s2.sp1", crc),), "fbneo", [(2, "1.0.0.03"), (7, "1.0.0.03")]), ((("other.bin", "00000000"),), "mame", [(9, "0.289")]), ]} def tearDown(self) -> None: self.path.unlink(missing_ok=True) def card(self) -> str: f = {"name": "set.zip", "sha1": "e" * 40, "md5": "e" * 32, "size": 4, "path": str(self.path)} with mock.patch.object(gs, "_romset_releases", return_value=self.releases): return "\n".join(gs._system_file_card(f, _consumers({}))) def test_the_matching_releases_as_runs(self) -> None: self.assertIn("
]*>gba_bios.bin")
def test_the_neo_geo_section_opens_on_the_neo_geo_bios(self) -> None:
page = (DIST / "systems" / "snk" / "index.html").read_text(encoding="utf-8")
section = page[page.index('id="neo-geo"'):]
opening = section[:section.index('class="rb-sys-file"')]
self.assertRegex(opening, r"]*>neogeo.zip")
self.assertIn("Which version each core reads", page)
def test_the_nintendo_summary_holds_its_budget(self) -> None:
size = (DIST / "systems" / "nintendo" / "index.html").stat().st_size
self.assertLess(size, 540_000)
class InstallBoxesCountTowardTheSummary(unittest.TestCase):
"""Ten install boxes were 295 KB of the 583 KB Nintendo page: the
weights that keep a section on its maker's summary counted cards only,
and the page passed its 600 KB budget once the DS files were refiled."""
def setUp(self) -> None:
import siterender
siterender.reset_console_pages()
self.addCleanup(siterender.reset_console_pages)
self.consoles = {
name: [_file(f"{name}{i}.bin", "0123456789"[index * 3 + i],
f"bios/Maker/{name}/{name}{i}.bin") for i in range(3)]
for index, name in enumerate(("Boxed", "Heavy", "Light"))
}
def pages(self, box: int) -> dict:
def install(console: str, _declared: list) -> list[str]:
return ["x" * box] if console == "Boxed" else []
with mock.patch.object(gs, "_console_install_block", side_effect=install), \
mock.patch.object(gs, "SYSTEM_SUMMARY_HTML", 50_000):
return gs.system_pages("Maker", self.consoles, _consumers({}), budget=30_000)
def test_a_section_with_a_heavy_install_box_moves(self) -> None:
pages = self.pages(20_000)
self.assertIn("maker-boxed", pages)
self.assertNotIn("maker-light", pages)
def test_a_light_install_box_stays(self) -> None:
self.assertEqual(list(self.pages(10)), ["maker"])