From af0bffdc6ae854548503d4c9b23a8224f794b917 Mon Sep 17 00:00:00 2001
From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com>
Date: Sat, 24 Oct 2026 01:19:54 +0200
Subject: [PATCH] chore: test the round 5 system page fixes
---
tests/test_site_r5_systems.py | 297 ++++++++++++++++++++++++++++++++++
1 file changed, 297 insertions(+)
create mode 100644 tests/test_site_r5_systems.py
diff --git a/tests/test_site_r5_systems.py b/tests/test_site_r5_systems.py
new file mode 100644
index 00000000..a8662903
--- /dev/null
+++ b/tests/test_site_r5_systems.py
@@ -0,0 +1,297 @@
+"""System pages and boards (round 5, package 6).
+
+NAOMI opened on hod2bios.zip, a set 13 of the 19 emulators listing it read
+for one game. Lines read other names for one file as files to get: "Start
+with: fdsbios.zip, disksys.rom". Twelve neogeo.zip were on five pages, the
+SNK ones closed under Variants. The Atom, two MZ computers, a sound chip and
+a toy had board pages, and the NAOMI and Atomiswave sets under the
+Dreamcast folder stayed off their boards' pages. ScummVM's files were on
+three pages, one section named ".variants". Board pages said their name
+three times before the first file.
+"""
+
+from __future__ import annotations
+
+import json
+import re
+import sys
+import types
+import unittest
+from pathlib import Path
+
+REPO_ROOT = Path(__file__).resolve().parents[1]
+sys.path.insert(0, str(REPO_ROOT / "scripts"))
+
+import generate_site as gs # noqa: E402
+import siterender # noqa: E402
+
+CONTENT = REPO_ROOT / "website" / "src" / "content" / "docs"
+SYSTEMS = CONTENT / "systems"
+
+
+def _file(name: str, char: str, path: str = "") -> dict:
+ return {"name": name, "sha1": char * 40, "md5": char * 32, "size": 4096,
+ "path": path or f"bios/Sega/NAOMI/{name}"}
+
+
+def _consumers(reads: dict[str, list[tuple[str, dict]]]) -> types.SimpleNamespace:
+ """Consumers where each sha1 is read by (emulator, entry) pairs, an
+ emulator's entries kept in one list as a profile keeps them."""
+ files_of: dict[str, list] = {}
+ entries: dict[str, dict[str, int]] = {}
+ emulators: dict[str, set[str]] = {}
+ for sha1, read_by in reads.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={}, _db={"files": {}},
+ )
+
+
+def _page(stem: str) -> str:
+ path = SYSTEMS / f"{stem}.md"
+ if not path.is_file():
+ raise unittest.SkipTest(f"systems/{stem}.md not generated")
+ return path.read_text(encoding="utf-8")
+
+
+def _start_line(text: str) -> str:
+ match = re.search(r"^\*\*(Start with|No file needed)\*\*.*$", text, re.M)
+ return match[0] if match else ""
+
+
+def _database() -> dict:
+ path = REPO_ROOT / "database.json"
+ if not path.is_file():
+ raise unittest.SkipTest("database.json is not built")
+ return json.loads(path.read_text(encoding="utf-8"))
+
+
+class AStartFileIsNoOnesExtra(unittest.TestCase):
+ """6.1: a file most of whose readers take beside another one is no
+ start file, whoever requires it."""
+
+ def test_the_board_opens_on_its_bios_alone(self) -> None:
+ naomi, hod2 = _file("naomi.zip", "1"), _file("hod2bios.zip", "2")
+ both = [(f"flycast{n}", {"name": "hod2bios.zip", "required": True}) for n in range(2)]
+ extra = [(f"mame{n}", {"name": "hod2bios.zip", "required": False}) for n in range(3)]
+ consumers = _consumers({
+ "1" * 40: [(e, {"name": "naomi.zip", "required": True}) for e, _ in both + extra],
+ "2" * 40: both + extra,
+ })
+ self.assertEqual(gs._start_with([naomi, hod2], consumers),
+ ["**Start with**: `naomi.zip`.", ""])
+
+ def test_the_naomi_page_opens_on_naomi_zip(self) -> None:
+ self.assertEqual(_start_line(_page("arcade-sega-naomi")), "**Start with**: `naomi.zip`.")
+
+ def test_a_file_several_require_beside_the_lead_stays(self) -> None:
+ bios7, bios9 = _file("bios7.bin", "3"), _file("bios9.bin", "4")
+ consumers = _consumers({
+ "3" * 40: [(e, {"name": "bios7.bin", "required": True}) for e in ("a", "b")],
+ "4" * 40: [(e, {"name": "bios9.bin", "required": True}) for e in ("a", "b")],
+ })
+ self.assertEqual(gs._start_with([bios7, bios9], consumers)[0],
+ "**Start with**: `bios7.bin`, `bios9.bin`.")
+
+ def test_some_files_each_is_not_every_file_for_one(self) -> None:
+ """The SNES said bsnes and four more require 22 files."""
+ files = [_file(f"chip{n}.rom", str(n)) for n in range(1, 4)]
+ reads = {f["sha1"]: [(f"free{k}", {"name": f["name"], "required": False,
+ "hle_fallback": True}) for k in range(5)]
+ for f in files}
+ reads["1" * 40].append(("bsnes", {"name": "chip1.rom", "required": True}))
+ reads["2" * 40].append(("jgenesis", {"name": "chip2.rom", "required": True}))
+ reads["3" * 40].append(("jgenesis", {"name": "chip3.rom", "required": True}))
+ line = gs._start_with(files, _consumers(reads))[0]
+ self.assertIn("require some of these files: ", line)
+
+
+class OtherNamesForOneSlotReadAsAlternatives(unittest.TestCase):
+ """6.2: "Start with: fdsbios.zip, disksys.rom" read as two files."""
+
+ def test_the_other_name_is_read_instead(self) -> None:
+ disk, fds = _file("disksys.rom", "5"), _file("fdsbios.zip", "6")
+ consumers = _consumers({
+ "5" * 40: [(e, {"name": "disksys.rom", "required": True})
+ for e in ("mesen", "nestopia", "fceumm")],
+ "6" * 40: [("fbneo", {"name": "fdsbios.zip", "required": True})],
+ })
+ self.assertEqual(gs._start_with([disk, fds], consumers)[0],
+ "**Start with**: `disksys.rom`. FinalBurn Neo reads `fdsbios.zip` instead.")
+
+ def test_a_card_read_under_another_name_says_both(self) -> None:
+ disk, card = _file("disksys.rom", "5"), _file("long name (1986).bin", "7")
+ consumers = _consumers({
+ "5" * 40: [(e, {"name": "disksys.rom", "required": True}) for e in ("a", "b")],
+ "7" * 40: [("c", {"name": "short.rom", "required": True})],
+ })
+ self.assertIn("reads `long name (1986).bin` (as `short.rom`) instead.",
+ gs._start_with([disk, card], consumers)[0])
+
+ def test_a_regional_line_names_one_file_per_reader(self) -> None:
+ mega_cd = _page("sega").split("{ #mega-cd }", 1)[1].split("\n## ", 1)[0]
+ regions = [line for line in mega_cd.splitlines() if line.startswith("- **")]
+ self.assertTrue(regions)
+ for line in regions:
+ lead = line.split(". ", 1)[0]
+ self.assertEqual(len(re.findall(r"`[^`]+`", lead)), 1, line)
+
+ def test_the_fds_line(self) -> None:
+ fds = _page("nintendo").split("{ #famicom-disk-system }", 1)[1]
+ self.assertRegex(_start_line(fds), r"^\*\*Start with\*\*: `disksys.rom`\. FinalBurn Neo "
+ r"reads \[?`fdsbios.zip`.* instead\.$")
+
+
+class TheNeoGeoSetsSitWithTheNeoGeo(unittest.TestCase):
+ """6.3: every neogeo.zip has its card on SNK's pages, side by side and
+ open, and the ares sentence names the card."""
+
+ def test_every_neogeo_zip_is_on_snk(self) -> None:
+ db = _database()
+ sha1s = [sha1 for sha1, f in db["files"].items() if f["name"] == "neogeo.zip"]
+ held = "".join(path.read_text(encoding="utf-8") for path in SYSTEMS.glob("snk*.md"))
+ if not held:
+ self.skipTest("SNK pages not generated")
+ self.assertEqual([s for s in sha1s if f'id="sha1-{s[:12]}"' not in held], [])
+ others = "".join(path.read_text(encoding="utf-8")
+ for path in SYSTEMS.glob("arcade*.md"))
+ self.assertEqual([s for s in sha1s if f'id="sha1-{s[:12]}"' in others], [])
+
+ def test_the_sets_are_open_and_together(self) -> None:
+ section = _page("snk").split("{ #neo-geo }", 1)[1].split("\n## ", 1)[0]
+ titles = re.findall(r'class="rb-file-title"[^>]*>([^<]+)', section)
+ sets = [n for n, name in enumerate(titles) if name == "neogeo.zip"]
+ self.assertGreater(len(sets), 1)
+ self.assertEqual(sets, list(range(len(sets))))
+ variants = section.split("Variants (", 1)[1] if "Variants (" in section else ""
+ self.assertNotIn("neogeo.zip", variants)
+
+ def test_ares_is_told_the_card_it_reads(self) -> None:
+ self.assertIn("ares reads `neo-epo.bin` and `sp-45.sp1` instead.", _page("snk"))
+
+
+class ABoardIsAnArcadeMachine(unittest.TestCase):
+ """6.4: computers, a sound chip and a toy had board pages."""
+
+ def test_no_board_has_a_parent_outside_arcade(self) -> None:
+ wrong = sorted(board for board in siterender.arcade_boards()
+ if board in siterender.SYSTEM_PARENTS)
+ self.assertEqual(wrong, [])
+
+ def test_no_board_page_for_a_machine_filed_elsewhere(self) -> None:
+ if not SYSTEMS.is_dir():
+ self.skipTest("pages not generated")
+ for stem in ("arcade-acorn-atom", "arcade-sharp-mz-2000", "arcade-sharp-mz-2500",
+ "arcade-yamaha-ym2608", "arcade-miuchiz"):
+ self.assertFalse((SYSTEMS / f"{stem}.md").exists(), stem)
+
+ def test_the_computers_sets_are_on_their_makers_page(self) -> None:
+ acorn = _page("acorn")
+ self.assertIn("{ #atom }", acorn)
+ self.assertIn("atom_gdos.zip", acorn)
+ cards = re.findall(r'class="rb-file-title"[^>]*>([^<]+)', _page("sharp"))
+ self.assertEqual(cards.count("mz2000.zip"), 2)
+
+
+class TheDreamcastBoardsAreFiledByBoard(unittest.TestCase):
+ """6.5: the NAOMI and Atomiswave sets under the Dreamcast folder."""
+
+ def test_the_board_sets_left_the_dreamcast(self) -> None:
+ sega = _page("sega")
+ dreamcast = sega.split("{ #dreamcast }", 1)[1].split("\n## ", 1)[0]
+ for name in ("naomi.zip", "awbios.zip", "naomi2.zip"):
+ self.assertNotIn(f"{name}", dreamcast)
+ self.assertIn("(/retrobios/systems/arcade-sega-naomi/)", dreamcast)
+ self.assertIn("(/retrobios/systems/arcade-sammy-atomiswave/)", dreamcast)
+
+ def test_every_naomi_set_is_on_its_board(self) -> None:
+ db = _database()
+ page = _page("arcade-sega-naomi")
+ missing = [sha1 for sha1, f in db["files"].items()
+ if f["name"] == "naomi.zip" and f'id="sha1-{sha1[:12]}"' not in page]
+ self.assertEqual(missing, [])
+
+ def test_the_dreamcast_arcade_id_leads_to_naomi(self) -> None:
+ siterender.reset_console_pages()
+ for board in siterender.arcade_boards():
+ siterender.record_board_section(board)
+ try:
+ self.assertEqual(siterender.system_section("sega-dreamcast-arcade"),
+ ("arcade", "Sega NAOMI"))
+ finally:
+ siterender.reset_console_pages()
+
+
+class EnginesGoByEngine(unittest.TestCase):
+ """6.6: no section named after a folder of variants or a file."""
+
+ def test_no_section_or_row_is_a_folder_or_a_file(self) -> None:
+ db = _database()
+ names = set(db["indexes"]["by_name"])
+ index = _page("index")
+ rows = re.findall(r"^\| \[([^\]]+)\]\(", index, re.M)
+ headings = [h.strip() for path in SYSTEMS.glob("*.md")
+ for h in re.findall(r"^## ([^{\n]+)", path.read_text(encoding="utf-8"), re.M)]
+ wrong = sorted({n for n in rows + headings if n.startswith(".") or n in names})
+ self.assertEqual(wrong, [])
+
+ def test_scummvm_has_one_page(self) -> None:
+ self.assertFalse((SYSTEMS / "other-scummvm.md").exists())
+ self.assertIn("## ScummVM {", _page("scummvm"))
+ self.assertNotIn("](/retrobios/systems/other/#scummvm)", _page("index"))
+
+ def test_a_folder_named_by_a_profile_key_reads_the_engine(self) -> None:
+ index = _page("index")
+ self.assertIn("| [EasyRPG Player](/retrobios/systems/rpg-maker/#easyrpg) |", index)
+ self.assertIn("| [SDLPAL](", index)
+
+ def test_moved_pages_redirect(self) -> None:
+ if not SYSTEMS.is_dir():
+ self.skipTest("pages not generated")
+ for old, new in gs.SYSTEM_PAGES_MOVED.items():
+ self.assertFalse((CONTENT / old).exists(), old)
+ self.assertTrue((CONTENT / new).exists(), new)
+ redirects = CONTENT.parents[1] / "generated" / "redirects.json"
+ if redirects.is_file():
+ table = json.loads(redirects.read_text(encoding="utf-8"))
+ self.assertEqual(table.get("/systems/other-scummvm/"), "/systems/scummvm/")
+
+
+class OneLabelOnceOnABoardPage(unittest.TestCase):
+ """6.7, 6.8, 6.9, 6.10: the words a board or part page repeats."""
+
+ def test_a_label_naming_files_installs_these_files(self) -> None:
+ block = "\n".join(gs._console_install_block("Other arcade files", [("mame", "arcade")]))
+ self.assertIn("Install these files", block)
+ block = "\n".join(gs._console_install_block("Sega NAOMI", [("mame", "arcade")]))
+ self.assertIn("Install the files of Sega NAOMI", block)
+
+ def test_a_board_page_has_no_heading_of_its_name(self) -> None:
+ page = _page("arcade-sega-naomi")
+ self.assertNotRegex(page, r"^## Sega NAOMI", )
+ self.assertNotIn("](/retrobios/systems/arcade-sega-naomi/#sega-naomi)",
+ _page("index"))
+
+ def test_no_part_says_where_it_sits(self) -> None:
+ if not SYSTEMS.is_dir():
+ self.skipTest("pages not generated")
+ said = sorted(p.name for p in SYSTEMS.glob("*.md")
+ if "\nPart of [" in p.read_text(encoding="utf-8"))
+ self.assertEqual(said, [])
+
+ def test_a_part_goes_by_the_emulators_name(self) -> None:
+ titles = [re.search(r"^title: (.*)$", p.read_text(encoding="utf-8"), re.M)[1].strip("'")
+ for p in SYSTEMS.glob("other-mame*.md")]
+ if not titles:
+ self.skipTest("pages not generated")
+ self.assertEqual([t for t in titles if not t.startswith(("MAME files", "Other: MAME"))],
+ [])
+
+
+if __name__ == "__main__":
+ unittest.main()