mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-23 20:29:06 -05:00
The 404 page opens on Start here instead of tabs numbered 0 to 4. A group label is a group, or in Explore one link to its overview, never a link to its first page, and a crumb names the section that introduces the page. Emulators sit one level deep by initial. Previous and next run within a tab (Start here into Your setup) and skip system, emulator and platform pages, and follow a page's Next steps list. Reference pages group under Checking files and Data, a wiki page joining by its group: front matter. redirects.json maps alternative core names to their profile.
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'<span id="{anchor}"></span>', 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()
|