mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-23 20:29:06 -05:00
Romset row and line on MAME and FinalBurn pages, files an app ships under their own badge and section, collection counts under the table, empty or constant summary columns dropped, short hashes with the full value in the title, the profile card and cards past Required folded, data directory pages kept out of the search.
179 lines
8.2 KiB
Python
179 lines
8.2 KiB
Python
"""Pages a phone can open: split pages, light navigation, short tables of contents.
|
|
|
|
systems/game-engines/ was one 10.5 MB page, emulators/mame/ 3 MB, and each
|
|
of 600 emulator pages carried 65 KB of navigation. A large page is now a
|
|
summary with pages of its own, and every address cited before the split
|
|
still lands: a console's heading stays on the summary, a card's sha1 anchor
|
|
is looked up by content, an emulator card's anchor sits on its row.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
import unittest
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
|
|
|
import generate_site # noqa: E402
|
|
import sitestarlight # noqa: E402
|
|
import siterender # noqa: E402
|
|
import sitemarkup as markup # noqa: E402
|
|
from cross_reference import FileConsumers # noqa: E402
|
|
|
|
|
|
def _file(name: str, index: int, console: str, maker: str = "Maker") -> dict:
|
|
sha1 = f"{index:012x}" + "0" * 28
|
|
return {"name": name, "sha1": sha1, "md5": f"{index:032x}", "size": 10,
|
|
"path": f"bios/{maker}/{console}/{name}"}
|
|
|
|
|
|
class ASystemPageOverItsBudgetIsSplit(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
siterender.reset_console_pages()
|
|
self.addCleanup(siterender.reset_console_pages)
|
|
self.consoles = {
|
|
"Big": [_file(f"big{i:03d}.bin", i, "Big") for i in range(60)],
|
|
"Small": [_file("small.bin", 900, "Small")],
|
|
}
|
|
|
|
def _pages(self, budget: int) -> dict:
|
|
return generate_site.system_pages(
|
|
"Maker", self.consoles, FileConsumers({"files": {}}), budget=budget)
|
|
|
|
def test_a_page_within_its_budget_stays_whole(self):
|
|
pages = self._pages(10_000_000)
|
|
self.assertEqual(list(pages), ["maker"])
|
|
self.assertIn("## Big", pages["maker"][0])
|
|
|
|
def test_the_largest_section_moves_and_is_cut_in_parts(self):
|
|
pages = self._pages(20_000)
|
|
summary = pages["maker"][0]
|
|
# Both headings stay on the summary: systems/maker/#big still lands.
|
|
self.assertIn("## Big", summary)
|
|
self.assertIn("## Small", summary)
|
|
# The small section keeps its cards in place.
|
|
self.assertIn('id="sha1-' + f"{900:012x}", summary)
|
|
parts = [stem for stem in pages if stem.startswith("maker-big")]
|
|
self.assertGreater(len(parts), 1)
|
|
self.assertEqual(parts[0], "maker-big")
|
|
for stem in parts:
|
|
self.assertIn(f"]({stem}.md)", summary)
|
|
self.assertLessEqual(len(pages[stem][0]), 20_000 + 5_000)
|
|
self.assertIn("## Files", pages[stem][0])
|
|
# Each card is on exactly one part, and the links follow it.
|
|
held = [f["sha1"] for stem in parts for f in pages[stem][1]["Big"]]
|
|
self.assertEqual(sorted(held), sorted(f["sha1"] for f in self.consoles["Big"]))
|
|
last = self.consoles["Big"][-1]
|
|
self.assertEqual(siterender.card_page(last["path"], last["sha1"]), parts[-1])
|
|
small = self.consoles["Small"][0]
|
|
self.assertEqual(siterender.card_page(small["path"], small["sha1"]), "maker")
|
|
self.assertEqual(siterender.console_href("maker", "Big"), "systems/maker.md#big")
|
|
|
|
def test_a_console_on_one_page_is_linked_directly(self):
|
|
self.consoles["Big"] = self.consoles["Big"][:12]
|
|
# The budget is measured in the Markdown it was set on.
|
|
with markup.using("mkdocs"):
|
|
whole = self._pages(10**9)["maker"][0]
|
|
pages = self._pages(len(whole) - 1)
|
|
self.assertIn("maker-big", pages)
|
|
self.assertNotIn("maker-big-2", pages)
|
|
self.assertEqual(siterender.console_href("maker", "Big", "../"),
|
|
"../systems/maker-big.md")
|
|
|
|
|
|
class AnEmulatorWithTooManyCardsIsSplit(unittest.TestCase):
|
|
def test_parts_are_balanced_and_rows_keep_the_anchors(self):
|
|
files = [{"name": f"set{i:04d}.zip", "required": i % 2 == 0, "category": "bios_zip"}
|
|
for i in range(generate_site.EMULATOR_CARDS_PER_PAGE * 2 + 1)]
|
|
parts = generate_site.emulator_parts("big", files)
|
|
self.assertEqual([stem for stem, _ in parts], ["big-files-1", "big-files-2", "big-files-3"])
|
|
sizes = [len(entries) for _, entries in parts]
|
|
self.assertLessEqual(max(sizes) - min(sizes), 1)
|
|
self.assertEqual(generate_site.emulator_parts("small", files[:3]), [])
|
|
|
|
pages = generate_site.emulator_pages(
|
|
"big", {"emulator": "Big", "type": "standalone", "files": files}, {"files": {}})
|
|
self.assertEqual(sorted(pages), ["big", "big-files-1", "big-files-2", "big-files-3"])
|
|
main = pages["big"]
|
|
anchors = generate_site.emulator_file_anchors(files)
|
|
for anchor in anchors:
|
|
self.assertIn(f'<a id="{anchor}" href=', main)
|
|
self.assertIn('class="rb-card-index"', main)
|
|
self.assertNotIn("rb-file-title", main)
|
|
self.assertIn(f'id="{anchors[0]}" class="rb-file-title"', pages["big-files-1"])
|
|
|
|
def test_cards_are_linked_on_the_page_that_holds_them(self):
|
|
files = [{"name": f"set{i:04d}.zip"} for i in range(generate_site.EMULATOR_CARDS_PER_PAGE + 1)]
|
|
generate_site.plan_emulator_pages({"big": {"files": files}})
|
|
anchors = generate_site.emulator_file_anchors(files)
|
|
self.assertEqual(siterender.emulator_card_page("big", anchors[-1]), "big-files-2")
|
|
self.assertEqual(siterender.emulator_card_page("other", "file-x"), "other")
|
|
|
|
|
|
class APlatformSystemOverItsBudgetMoves(unittest.TestCase):
|
|
def test_the_heading_stays_and_the_list_moves(self):
|
|
files = [{"name": f"f{i:04d}.zip", "status": "ok"} for i in range(40)]
|
|
config = {("arcade", f["name"]): [{"name": f["name"], "md5": "a" * 32}] for f in files}
|
|
moved: dict[str, str] = {}
|
|
budget = generate_site.PLATFORM_SECTION_BUDGET
|
|
generate_site.PLATFORM_SECTION_BUDGET = 500
|
|
try:
|
|
lines = generate_site._render_platform_systems(
|
|
{"arcade": files}, config, {}, ("deck", "Deck"), moved)
|
|
finally:
|
|
generate_site.PLATFORM_SECTION_BUDGET = budget
|
|
text = "\n".join(lines)
|
|
self.assertIn("{ #arcade }", text)
|
|
self.assertIn("](deck-arcade.md)", text)
|
|
self.assertNotIn("f0001.zip", text)
|
|
self.assertIn("f0001.zip", moved["deck-arcade"])
|
|
self.assertIn("[Deck page](deck.md#arcade)", moved["deck-arcade"])
|
|
|
|
|
|
class TheNavigationOpensOneInitial(unittest.TestCase):
|
|
def test_one_group_per_initial_one_level_deep(self):
|
|
profiles = {
|
|
key: {"emulator": name, "systems": ["s"]}
|
|
for key, name in (("a1", "Alpha"), ("b1", "Beta"), ("b2", "bravo"), ("z", "Zed"),
|
|
("n", "2ship"))
|
|
}
|
|
nav = generate_site._emulator_nav(profiles)
|
|
self.assertEqual(nav[1:], [
|
|
{"0 to 9": [{"2ship": "emulators/n.md"}]},
|
|
{"A": [{"Alpha": "emulators/a1.md"}]},
|
|
{"B": [{"Beta": "emulators/b1.md"}, {"bravo": "emulators/b2.md"}]},
|
|
{"Z": [{"Zed": "emulators/z.md"}]},
|
|
])
|
|
|
|
|
|
class TheTableOfContentsOfACardPageStopsAtLevelTwo(unittest.TestCase):
|
|
@staticmethod
|
|
@staticmethod
|
|
def _contents(rel: str, sections: int):
|
|
import yaml # noqa: PLC0415
|
|
|
|
body = "# T\n\n" + "".join(f"## S{i}\n\n### card\n\n" for i in range(sections))
|
|
_target, text = sitestarlight.finalize_page(
|
|
rel, body, level="", weight=None, browser_title="T", description="d",
|
|
structured={}, terms={})
|
|
return yaml.safe_load(text.split("---\n")[1]).get("tableOfContents", "default")
|
|
|
|
def test_cards_leave_the_toc_and_wiki_pages_keep_level_three(self):
|
|
level_two = {"minHeadingLevel": 2, "maxHeadingLevel": 2}
|
|
for rel, sections, expected in (
|
|
("systems/sony.md", 3, level_two), ("platforms/retroarch.md", 5, level_two),
|
|
("emulators/mame.md", 3, level_two), ("wiki/faq.md", 3, "default"),
|
|
# One or two headings over hundreds of cards: no table at all,
|
|
# whose script watched every block of the page.
|
|
("emulators/mame-files-2.md", 1, False), ("systems/arcade-arcade.md", 2, False),
|
|
):
|
|
with self.subTest(rel=rel):
|
|
self.assertEqual(self._contents(rel, sections), expected)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|