"""System pages and file cards (round 2, package 6). The Sony page opened on LibPS.exe, its card titles read "scph5501.bin permalink" in the site search, the two setup lines of a card were defined only in a code comment, and the systems index named no console. """ from __future__ import annotations import re import sys import types import unittest 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 siterender # noqa: E402 from cross_reference import FileConsumers # noqa: E402 DIST = REPO_ROOT / "website" / "dist" def _file(name: str, char: str, path: str = "") -> dict: return {"name": name, "sha1": char * 40, "md5": char * 32, "size": 524288, "path": path or f"bios/Sony/PlayStation/{name}"} def _consumers(readers: dict[str, list[dict]], platforms: dict[str, set[str]] | None = None ) -> types.SimpleNamespace: """Consumers where each sha1 is read by the given profile entries, one emulator per 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 number, entry in enumerate(read_by): emulator = f"emu{number}-{sha1[:4]}" files_of[emulator] = [entry] entries.setdefault(sha1, {})[emulator] = 0 emulators.setdefault(sha1, set()).add(emulator) return types.SimpleNamespace( platforms=platforms or {}, emulators=emulators, emulator_entries=entries, emulator_files=files_of, installed={}, platform_systems={}, platform_destinations={}, ) class TheConsoleBiosComesFirst(unittest.TestCase): """Cards were sorted by byte order: capitals first, so LibPS.exe and PSX_DTLH2000.bin opened the PlayStation section and scph5501.bin was 28th.""" def setUp(self): self.files = [ _file("LibPS.exe", "1"), _file("PSX_DTLH2000.bin", "2"), _file("dtlh1100.bin", "3"), _file("scph5501.bin", "4"), _file("scph5500.bin", "5"), _file("scph1001.bin", "6"), _file("openbios.bin", "7"), ] self.consumers = _consumers({ "4" * 40: [{"name": "scph5501.bin", "required": True, "priority": 2, "region": ["north-america"]}], "5" * 40: [{"name": "scph5500.bin", "required": True, "priority": 5, "region": ["japan"]}], "6" * 40: [{"name": "scph1001.bin", "required": False, "priority": 1}], "2" * 40: [{"name": "PSX_DTLH2000.bin", "required": False}], }, platforms={"6" * 40: {"a", "b"}, "3" * 40: {"a", "b", "c"}}) def names(self) -> list[str]: cards = gs._console_cards("Sony", self.files, self.consumers, set()) return [re.search(r"(.*?)", card[2])[1] for _v, card, _s in cards] def test_required_then_priority_then_setups_then_name(self): self.assertEqual(self.names(), [ "scph5501.bin", "scph5500.bin", # required, by priority "scph1001.bin", # optional with a priority "dtlh1100.bin", # listed by three setups "LibPS.exe", "openbios.bin", "PSX_DTLH2000.bin", # by name, any case ]) def test_a_console_cut_in_parts_goes_by_name(self): cards = gs._console_cards("Sony", self.files, self.consumers, set(), by_name=True) names = [re.search(r"(.*?)", card[2])[1] for _v, card, _s in cards] self.assertEqual(names, sorted(names, key=str.casefold)) def required_only(self) -> types.SimpleNamespace: """The BIOS readers alone: an emulator reading only an optional file runs without the BIOS, and two of four would say so.""" return _consumers({ "4" * 40: [{"name": "scph5501.bin", "required": True, "priority": 2, "region": ["north-america"]}], "5" * 40: [{"name": "scph5500.bin", "required": True, "priority": 5, "region": ["japan"]}], }) def test_the_section_opens_on_the_bios_by_region(self): lines = gs._start_with(self.files, self.required_only()) self.assertEqual(lines[0], "**Start with** the BIOS for the region of your games:") self.assertIn("- **North America**: `scph5501.bin`", lines) self.assertIn("- **Japan**: `scph5500.bin`", lines) self.assertLess(lines.index("- **North America**: `scph5501.bin`"), lines.index("- **Japan**: `scph5500.bin`")) def test_regions_needing_the_same_file_share_a_line(self): consumers = _consumers({"4" * 40: [ {"required": True, "region": ["north-america", "latin-america", "brazil"]}]}) lines = gs._start_with([_file("scph5501.bin", "4")], consumers) # Brazil says nothing Latin America does not. self.assertIn("- **North America, Latin America**: `scph5501.bin`", lines) def test_a_long_list_names_the_first_and_counts_the_rest(self): files = [_file(f"rom{i}.bin", "abcdefgh"[i]) for i in range(8)] # One emulator requires all eight: files each required by another # emulator are other names for one slot (round 5, 6.2). consumers = _consumers({f["sha1"]: [{"required": True}] for f in files}) consumers.emulator_files = {"emu": [{"required": True}] * len(files)} consumers.emulator_entries = {f["sha1"]: {"emu": i} for i, f in enumerate(files)} self.assertEqual(gs._start_with(files, consumers)[0], "**Start with**: `rom0.bin`, `rom1.bin`, `rom2.bin` and 5 more files. " "They come first below.") def test_no_required_file_no_line(self): self.assertEqual(gs._start_with([_file("a.bin", "a")], _consumers({})), []) def test_the_page_carries_the_line_under_the_console(self): page = gs.generate_system_page("Sony", {"PlayStation": self.files}, self.required_only()) heading = page.index("## PlayStation") self.assertLess(heading, page.index("**Start with**")) self.assertLess(page.index("**Start with**"), page.index('class="rb-sys-file"')) class ThePermalinkIsAnIcon(unittest.TestCase): """The word "permalink" was link text inside every card title: the search showed "scph5501.bin permalink" and screen readers read it.""" def test_no_word_and_out_of_the_search(self): lines = gs._system_file_card(_file("scph5501.bin", "4"), FileConsumers({"files": {}})) title = lines[2] link = re.search(r']*>(.*?)', title) self.assertNotIn("permalink", re.sub(r"<[^>]+>", "", title)) self.assertIn('aria-label="Permanent link to scph5501.bin"', link[0]) self.assertIn("data-pagefind-ignore", link[0]) self.assertIn('', link[1]) def test_the_page_draws_the_icon_once(self): page = gs.generate_system_page( "Sony", {"PlayStation": [_file("a.bin", "a"), _file("b.bin", "b")]}, FileConsumers({"files": {}})) self.assertEqual(page.count('On the setup's list
scph5501.bin: Batocera
", text) self.assertIn("
Packed for its emulators by
bios/scph5501.bin: " "RetroBat
", text) self.assertNotIn("Platforms:", text) self.assertNotIn("Also installed by", text) def test_the_facts_are_out_of_the_search(self): # The setup list made every card's excerpt a run of names and # paths (round 4, V6): the card's title is what the search reads. lists = [line for line in self.lines if line.startswith("']) def test_the_page_defines_both_lines(self): page = gs.generate_system_page("Sony", {"PlayStation": [_file("scph5501.bin", "4")]}, self.consumers) key = page.index("**On the setup's list** names") self.assertLess(key, page.index('class="rb-sys-file"')) self.assertIn("**Packed for its emulators by** names", page) def test_a_file_nothing_reads_says_so(self): title = gs._system_file_card(_file("GameIndex.yaml", "9"), FileConsumers({"files": {}}))[2] self.assertIn(">No reader", title) self.assertNotIn("No reader", self.lines[2]) class FileFactsAreAGrid(unittest.TestCase): """Each card was two bullet lists of code chips.""" def test_one_value_cell_per_label_and_plain_hashes(self): entry = {**_file("scph5501.bin", "4"), "crc32": "8d8cb7e4", "provenance": {"redump": {"name": "ps-30a.bin"}, "tosec": {"name": "SCPH-5501 (1996).bin"}}} lines = gs._system_file_card(entry, FileConsumers({"files": {}})) facts = [line for line in lines if line.startswith("
")] self.assertTrue(facts) for line in facts: self.assertEqual(line.count("
"), 1, line) self.assertEqual(line.count("
"), 1, line) text = "\n".join(lines) self.assertIn('
SHA1
' + "4" * 40 + "
", text) self.assertNotIn("", text) catalogued = next(line for line in facts if line.startswith("
Catalogued")) self.assertIn("
", catalogued) self.assertNotRegex(text, r"(?m)^- ") def test_collection_path_keeps_its_chip(self): text = "\n".join(gs._system_file_card(_file("a.bin", "a"), FileConsumers({"files": {}}))) self.assertIn("
Collection path
bios/Sony/PlayStation/a.bin", text) class ConsoleHeadingsStartWithTheName(unittest.TestCase): """The icon and a space came before the name: "Section titled “ PlayStation”" on five of seven Sony headings. Round 3 removed the icon altogether.""" def test_no_icon_even_when_one_exists(self): icon = siterender._icon_name("Sony", "PlayStation") with mock.patch.dict(siterender._icon_available, {icon: True}): self.assertEqual(gs._console_heading("Sony", "PlayStation"), "## PlayStation { #playstation }") def test_no_icon_no_image(self): icon = siterender._icon_name("Nobody", "Nothing") with mock.patch.dict(siterender._icon_available, {icon: False}): self.assertEqual(gs._console_heading("Nobody", "Nothing"), "## Nothing { #nothing }") class TheSystemsIndexListsConsoles(unittest.TestCase): """The index listed 74 manufacturers and no console: PlayStation had to be known to be under Sony.""" def test_one_row_per_console_by_its_name(self): groups = { "Sony": {"PlayStation": [_file("a.bin", "a")], "PlayStation 2": [_file("b.bin", "b")]}, "Other": {"ScummVM": [_file("c.bin", "c")]}, "Game Engines": {"TheXTech": [_file("d.png", "d")]}, } page = gs.generate_systems_index(groups) self.assertIn("| [Sony PlayStation](sony.md#playstation) | 1 | [Sony](sony.md) |", page) self.assertIn("| [Sony PlayStation 2](sony.md#playstation-2) | 1 | [Sony](sony.md) |", page) # A program's folder under Other is named alone, in its own table. consoles = page.split("## Emulator folders")[0] self.assertNotIn("ScummVM", consoles) self.assertIn("| [ScummVM](../emulators/scummvm.md) | [1 file](other.md#scummvm) |", page.split("## Emulator folders")[1]) self.assertNotIn("TheXTech", consoles) self.assertIn("| [TheXTech](game-engines.md#thextech)", page) def test_rows_go_by_name_not_by_maker(self): groups = {"Sega": {"Saturn": [_file("a.bin", "a")]}, "Atari": {"Lynx": [_file("b.bin", "b")]}} page = gs.generate_systems_index(groups) self.assertLess(page.index("Atari Lynx"), page.index("Sega Saturn")) class FindAFileSaysWhatAHashIs(unittest.TestCase): def test_the_sentence_above_the_box(self): page = gs.generate_files_page() intro = page.split('
]*class="rb-file-title".*?', self.text, re.S) self.assertTrue(titles) for title in titles: self.assertNotIn("permalink", re.sub(r"<[^>]+>", "", title)) def test_playstation_opens_on_a_bios_it_needs(self): section = self.text.split('id="playstation"', 1)[1].split("]*>]*>(.*?)', own)[1] self.assertNotIn(first, {"LibPS.exe", "PSX_DTLH2000.bin"}) self.assertRegex(first, r"^scph\d+") def test_headings_have_no_leading_space(self): self.assertNotRegex(self.text, r'

]*>\s') if __name__ == "__main__": unittest.main()