mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-11 12:33:53 -06:00
build_port's four archives carry install.py pinned by SHA-256, so they change with the installer, not with the packs. The site now builds them from its own commit and serves them under get/handheld/. Each get/ page of Batocera, KNULLI, ROCKNIX and AmberELEC names its file, size, hash, folder and menu, and get/linux-handhelds/ does the same for muOS, ArkOS, dArkOS and JELOS. The installer's handheld section opens on a download table, both tables written from sitehandheld.LAUNCHERS.
169 lines
8.2 KiB
Python
169 lines
8.2 KiB
Python
"""The handheld launchers the site serves.
|
|
|
|
The four archives build_port.py writes carry install.py pinned by SHA-256,
|
|
so they left the release (owner, 2026-11-10): the site builds them from its
|
|
own commit, serves them under get/handheld/, and the get/ page of each
|
|
system names the file, its size and hash, where it goes and which menu
|
|
starts it.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import tests # noqa: F401 offline, scratch under tmp/, the suite's own cache
|
|
|
|
import contextlib
|
|
import hashlib
|
|
import re
|
|
import shutil
|
|
import sys
|
|
import tempfile
|
|
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 build_port # noqa: E402
|
|
import common # noqa: E402
|
|
import sitehandheld # noqa: E402
|
|
|
|
PLATFORMS = str(REPO_ROOT / "platforms")
|
|
|
|
|
|
def _pages() -> dict[str, str]:
|
|
"""Every page that carries a launcher, by site path."""
|
|
import generate_site # noqa: PLC0415
|
|
|
|
with contextlib.chdir(REPO_ROOT):
|
|
pages = {f"get/{key}.md": generate_site.generate_get_page(key)
|
|
for key in sorted(set(sitehandheld.PLATFORM_OF.values()))}
|
|
pages[f"get/{sitehandheld.GROUP_PAGE}.md"] = sitehandheld.group_page(REPO_ROOT)
|
|
return pages
|
|
|
|
|
|
class TheSiteServesTheLaunchers(unittest.TestCase):
|
|
def setUp(self):
|
|
scratch = REPO_ROOT / "tmp"
|
|
scratch.mkdir(exist_ok=True)
|
|
self.tmp = Path(tempfile.mkdtemp(dir=scratch, prefix="site-handheld-"))
|
|
self.addCleanup(shutil.rmtree, self.tmp, True)
|
|
|
|
def test_the_written_archives_are_the_ones_build_port_writes(self):
|
|
written = sitehandheld.write_launchers(self.tmp / "public", REPO_ROOT)
|
|
self.assertEqual([path.name for path in written], list(build_port.ARCHIVES))
|
|
for path in written:
|
|
with self.subTest(archive=path.name):
|
|
self.assertEqual(path.parent, self.tmp / "public" / "get" / "handheld")
|
|
built = build_port.build_archive(path.name, self.tmp / "built" / path.name)
|
|
self.assertEqual(path.read_bytes(), built.read_bytes())
|
|
|
|
def test_an_unchanged_archive_is_not_rewritten(self):
|
|
first = sitehandheld.write_launchers(self.tmp / "public", REPO_ROOT)
|
|
stamps = {path: path.stat().st_mtime_ns for path in first}
|
|
sitehandheld.write_launchers(self.tmp / "public", REPO_ROOT)
|
|
self.assertEqual({path: path.stat().st_mtime_ns for path in first}, stamps)
|
|
|
|
def test_a_stale_pin_stops_the_build(self):
|
|
"""build_port refuses an install.py the bootstraps do not pin: served,
|
|
that launcher would refuse its own installer."""
|
|
with mock.patch.dict(sitehandheld._built, clear=True), \
|
|
mock.patch.object(build_port, "pinned_installer_sha256", return_value="0" * 64):
|
|
with self.assertRaisesRegex(ValueError, "re-pin the bootstraps"):
|
|
sitehandheld.write_launchers(self.tmp / "public", REPO_ROOT)
|
|
self.assertFalse(list((self.tmp / "public").rglob("RetroBIOS*")))
|
|
|
|
def test_every_archive_is_on_a_page_with_its_link_and_hash(self):
|
|
public = self.tmp / "public"
|
|
written = {path.name: path for path in sitehandheld.write_launchers(public, REPO_ROOT)}
|
|
pages = _pages()
|
|
for name in build_port.ARCHIVES:
|
|
with self.subTest(archive=name):
|
|
data = written[name].read_bytes()
|
|
digest = hashlib.sha256(data).hexdigest()
|
|
holding = [page for page, text in pages.items()
|
|
if f'href="handheld/{name}"' in text]
|
|
self.assertTrue(holding, f"no page links {name}")
|
|
for page in holding:
|
|
text = pages[page]
|
|
# The link resolves to the file written under public/.
|
|
target = (Path(page).parent / "handheld" / name).as_posix()
|
|
self.assertTrue((public / target).is_file(), target)
|
|
block = text.split(f'href="handheld/{name}"', 1)[1].split("\n\n", 2)[1]
|
|
self.assertIn(f"SHA-256: `{digest}`", block)
|
|
self.assertIn(sitehandheld._size(len(data)), block)
|
|
self.assertRegex(block, r"- (Copy it to|Unzip it into) .+\.")
|
|
self.assertRegex(block, r"- Start it from .+\.")
|
|
|
|
|
|
class WhereEachSystemFindsItsFile(unittest.TestCase):
|
|
def test_each_platform_page_is_a_registered_platform(self):
|
|
registered = set(common.list_registered_platforms(PLATFORMS))
|
|
self.assertLessEqual(set(sitehandheld.PLATFORM_OF.values()), registered)
|
|
self.assertNotIn(sitehandheld.GROUP_PAGE, registered)
|
|
|
|
def test_the_group_page_sits_with_the_handheld_guide(self):
|
|
"""It shares the guide's name, so the sidebar files it with it, and
|
|
the guide a muOS or ArkOS owner lands on links it."""
|
|
import generate_site # noqa: PLC0415
|
|
|
|
guide = generate_site.HANDHELD_GUIDE
|
|
self.assertEqual(sitehandheld.GROUP_PAGE, guide.page)
|
|
with contextlib.chdir(REPO_ROOT):
|
|
self.assertIn(f"(../get/{sitehandheld.GROUP_PAGE}.md)",
|
|
generate_site.generate_handheld_guide(guide))
|
|
self.assertIn(f"]({sitehandheld.GROUP_PAGE}.md)", generate_site.generate_get_index())
|
|
|
|
def test_every_system_has_its_launchers_on_its_page(self):
|
|
pages = _pages()
|
|
for system in sitehandheld.systems():
|
|
page = sitehandheld.page_of(system)
|
|
with self.subTest(system=system):
|
|
self.assertIn(page, pages)
|
|
for launcher in sitehandheld.launchers_for(system):
|
|
self.assertIn(f'href="handheld/{launcher.archive}"', pages[page])
|
|
self.assertIn(launcher.folder_for(system), pages[page])
|
|
self.assertIn(launcher.start, pages[page])
|
|
for system in sitehandheld.group_systems():
|
|
self.assertIn(f"\n## {system}\n", pages[f"get/{sitehandheld.GROUP_PAGE}.md"])
|
|
|
|
def test_the_installer_table_is_written_from_the_same_rows(self):
|
|
text = (REPO_ROOT / "wiki" / "installer.md").read_text(encoding="utf-8")
|
|
block = re.search(r"<!-- generated: handheld -->\n(.*?)<!-- /generated: handheld -->",
|
|
text, re.S)
|
|
self.assertIsNotNone(block)
|
|
self.assertEqual(block.group(1).strip(), "\n".join(sitehandheld.table_rows()))
|
|
self.assertNotIn("releases/latest", text.split("## On a handheld", 1)[1].split("\n## ")[0])
|
|
|
|
def test_the_installer_section_opens_on_the_downloads(self):
|
|
"""The section was long, with no link to click (owner, 2026-11-10):
|
|
it opens on one row per system, each file a link to its copy on the
|
|
site, before the detail."""
|
|
text = (REPO_ROOT / "wiki" / "installer.md").read_text(encoding="utf-8")
|
|
section = text.split("## On a handheld", 1)[1].split("\n## ")[0]
|
|
block = re.search(r"<!-- generated: handheld-downloads -->\n(.*?)"
|
|
r"<!-- /generated: handheld-downloads -->", section, re.S)
|
|
self.assertIsNotNone(block)
|
|
self.assertEqual(block.group(1).strip(), "\n".join(sitehandheld.download_rows()))
|
|
self.assertLess(block.start(), section.index("### Where each file goes"))
|
|
self.assertLess(len(section[:block.start()].split()), 40)
|
|
for system in sitehandheld.systems():
|
|
with self.subTest(system=system):
|
|
row = next(line for line in block.group(1).splitlines()
|
|
if line.startswith(f"| {system} |"))
|
|
for launcher in sitehandheld.launchers_for(system):
|
|
self.assertIn(f"](../{sitehandheld.SERVED_DIR}/{launcher.archive})", row)
|
|
self.assertIn(f"](../{sitehandheld.page_of(system)}#", row)
|
|
for name in build_port.ARCHIVES:
|
|
self.assertIn(f"](../{sitehandheld.SERVED_DIR}/{name})", block.group(1))
|
|
|
|
def test_the_generator_writes_them(self):
|
|
source = (REPO_ROOT / "scripts" / "generate_site.py").read_text(encoding="utf-8")
|
|
self.assertIn("sitehandheld.write_launchers(public, _REPO_ROOT)", source)
|
|
self.assertIn("sitehandheld.platform_block(platform_name, _REPO_ROOT)", source)
|
|
self.assertIn("sitehandheld.group_page(_REPO_ROOT)", source)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|