mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-24 04:39:06 -05:00
226 lines
9.7 KiB
Python
226 lines
9.7 KiB
Python
"""Wiki (round 5, package 5).
|
|
|
|
Each class names the finding it holds: a "no setup" command that sent
|
|
macOS and Steam Deck files where RetroArch never reads, a level 0 rule that
|
|
every emulator needs one exact name, FAQ answers that ended at a ZIP most
|
|
setups lack, a packager section pointing at release assets that do not
|
|
exist, and two copy fixes. Every claim is held against install.py, the
|
|
setup guides or the generator that writes the page a link opens.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
import sys
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
import yaml
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(REPO_ROOT))
|
|
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
|
|
|
import sitediagrams # noqa: E402
|
|
from siteguides import INSTALL_BASE # noqa: E402
|
|
from tests import sitepages # noqa: E402
|
|
|
|
WIKI = REPO_ROOT / "wiki"
|
|
FENCED = re.compile(r"^```.*?^```", re.S | re.M)
|
|
INSTALL_PY = (REPO_ROOT / "install.py").read_text(encoding="utf-8")
|
|
|
|
|
|
def _wiki(name: str) -> str:
|
|
return (WIKI / name).read_text(encoding="utf-8")
|
|
|
|
|
|
def _front(text: str) -> dict:
|
|
return yaml.safe_load(text.split("---", 2)[1])
|
|
|
|
|
|
def _section(text: str, heading: str) -> str:
|
|
start = re.search(rf"(?m)^#+ {re.escape(heading)}(?:\s*\{{[^}}]*\}})?\n", text)
|
|
if start is None:
|
|
raise AssertionError(f"no section {heading!r}")
|
|
rest = text[start.end():]
|
|
blank = FENCED.sub(lambda m: re.sub(r"[^\n]", " ", m.group(0)), rest)
|
|
end = re.search(r"(?m)^#{1,2} ", blank)
|
|
return rest[: end.start()] if end else rest
|
|
|
|
|
|
def _flat(text: str) -> str:
|
|
return " ".join(text.split())
|
|
|
|
|
|
def _guide_folders(name: str) -> dict[str, str]:
|
|
guides = yaml.safe_load((REPO_ROOT / "platforms" / "_guides.yml").read_text(encoding="utf-8"))
|
|
return {row["where"]: row["path"] for row in guides["guides"][name]["folders"]}
|
|
|
|
|
|
class NoSetupFoundGivesEachSystemItsFolder(unittest.TestCase):
|
|
"""C3: macOS, Linux and Steam Deck shared `~/.config/retroarch/system`."""
|
|
|
|
def setUp(self):
|
|
self.section = _section(_wiki("troubleshooting.md"), "If the command fails")
|
|
|
|
def test_the_label_is_the_installer_message(self):
|
|
self.assertIn("**`No supported platform detected`:**", self.section)
|
|
self.assertIn('print(" No supported platform detected.")', INSTALL_PY)
|
|
|
|
def test_the_lead_names_the_menu(self):
|
|
self.assertIn("Not sure? Open RetroArch's **Settings > Directory > System/BIOS**",
|
|
_flat(self.section))
|
|
|
|
def test_each_tab_carries_the_folder_retroarch_reads_there(self):
|
|
expected = {
|
|
# install.py's Windows default and the hint it prints.
|
|
"Windows": "C:\\RetroArch-Win64\\system",
|
|
"macOS": "~/Documents/RetroArch/system",
|
|
"Linux": "~/.config/retroarch/system",
|
|
"Steam Deck": "~/.var/app/org.libretro.RetroArch/config/retroarch/system",
|
|
}
|
|
self.assertIn('_host_path("C:/RetroArch-Win64/system")', INSTALL_PY)
|
|
self.assertIn('"windows": "C:\\\\RetroArch-Win64\\\\system"', INSTALL_PY)
|
|
self.assertIn('return Path.home() / "Documents" / "RetroArch"', INSTALL_PY)
|
|
folders = _guide_folders("retroarch")
|
|
self.assertEqual(folders["macOS"], f"`{expected['macOS']}/`")
|
|
self.assertEqual(folders["Linux"], f"`{expected['Linux']}/`")
|
|
self.assertEqual(folders["Steam Deck and Linux Flatpak"], f"`{expected['Steam Deck']}/`")
|
|
|
|
tabs = sitediagrams.expand_commands(self.section)
|
|
parts = re.split(r'<TabItem label="([^"]+)">', tabs)
|
|
bodies = dict(zip(parts[1::2], parts[2::2]))
|
|
self.assertEqual(set(bodies), set(expected))
|
|
for label, folder in expected.items():
|
|
with self.subTest(tab=label):
|
|
self.assertIn(f"--platform retroarch --dest {folder}\n", bodies[label])
|
|
others = {f for key, f in expected.items() if key != label}
|
|
for other in others:
|
|
self.assertNotIn(f"--dest {other}\n", bodies[label])
|
|
|
|
def test_the_built_page_shows_four_folders(self):
|
|
sitepages.require()
|
|
built = sitepages.page("wiki/troubleshooting.md").read_text(encoding="utf-8")
|
|
self.assertNotIn("os-tabs", built)
|
|
for folder in ("C:\\RetroArch-Win64\\system", "~/Documents/RetroArch/system",
|
|
"~/.config/retroarch/system",
|
|
"~/.var/app/org.libretro.RetroArch/config/retroarch/system"):
|
|
with self.subTest(folder=folder):
|
|
self.assertIn(f"--dest {folder}", built)
|
|
|
|
|
|
class OsTabsTakeOptionsPerSystem(unittest.TestCase):
|
|
"""The marker gives any tab its own options, and refuses words it cannot read."""
|
|
|
|
PAGE = ("<!-- os-tabs windows: --dest E:\\x macos: --dest /m steamdeck: --dest /d -->\n"
|
|
f"```sh\ncurl -fsSL {INSTALL_BASE}/install.sh | sh -s -- --dest /l\n```\n")
|
|
|
|
def test_each_named_tab_uses_its_own(self):
|
|
parts = re.split(r'<TabItem label="([^"]+)">', sitediagrams.expand_commands(self.PAGE))
|
|
bodies = dict(zip(parts[1::2], parts[2::2]))
|
|
self.assertIn("--dest E:\\x\n", bodies["Windows"])
|
|
self.assertIn("--dest /m\n", bodies["macOS"])
|
|
self.assertIn("--dest /l\n", bodies["Linux"])
|
|
self.assertIn("--dest /d\n", bodies["Steam Deck"])
|
|
|
|
def test_text_before_a_system_name_fails_the_build(self):
|
|
page = self.PAGE.replace("os-tabs windows:", "os-tabs typo windows:")
|
|
with self.assertRaises(ValueError):
|
|
sitediagrams.expand_commands(page)
|
|
|
|
|
|
class MostEmulatorsNotEvery(unittest.TestCase):
|
|
"""C8: level 0 said every emulator needs one exact name."""
|
|
|
|
def test_the_rule_names_its_exception(self):
|
|
section = _flat(_section(_wiki("what-are-bios-files.md"), "The name must match"))
|
|
self.assertIn("Most emulators look for one exact name, like `scph5501.bin`.", section)
|
|
self.assertIn("A few, like DuckStation, read any name and check what the file holds.",
|
|
section)
|
|
profile = yaml.safe_load((REPO_ROOT / "emulators" / "duckstation.yml").read_text(
|
|
encoding="utf-8"))
|
|
self.assertEqual(profile["found_by"], "content")
|
|
|
|
|
|
class FaqAnswersLeadSomewhere(unittest.TestCase):
|
|
"""C9: "Use the ZIP" for iOS, and a dead end for 12 setups without one."""
|
|
|
|
def setUp(self):
|
|
self.text = _wiki("faq.md")
|
|
self.generator = (REPO_ROOT / "scripts" / "generate_site.py").read_text(encoding="utf-8")
|
|
|
|
def test_ios_goes_to_the_iphone_section(self):
|
|
section = _flat(_section(self.text, "My setup is not in the list"))
|
|
self.assertIn("iPhone, iPad (iOS): no terminal. [Pick your consoles and copy them "
|
|
"to the app](../which-pack.md#iphone-and-ipad).", section)
|
|
self.assertNotIn("Use the ZIP", section)
|
|
self.assertIn('"## iPhone and iPad { #iphone-and-ipad }"', self.generator)
|
|
|
|
def test_antivirus_answer_covers_setups_without_a_zip(self):
|
|
section = _flat(_section(self.text, "Windows or my antivirus blocks the command"))
|
|
self.assertIn("Use your setup's ZIP. None? [Pick your consoles]"
|
|
"(../which-pack.md#only-some-consoles) and download their files.",
|
|
section)
|
|
self.assertNotIn("if any", section)
|
|
self.assertIn('"## Only some consoles?"', self.generator)
|
|
|
|
|
|
class PackagersPinACommit(unittest.TestCase):
|
|
"""J4: the section fetched install.py from a release that carries none."""
|
|
|
|
def setUp(self):
|
|
self.section = _section(_wiki("installer.md"), "For packagers")
|
|
|
|
def test_the_pin_read_is_the_one_install_sh_writes(self):
|
|
sed = re.search(r"sed -n '(s/\^DEFAULT_INSTALL_SHA256=.*?/p)'", self.section)
|
|
self.assertIsNotNone(sed)
|
|
bootstrap = (REPO_ROOT / "install.sh").read_text(encoding="utf-8")
|
|
lines = re.findall(r'(?m)^DEFAULT_INSTALL_SHA256="([0-9a-f]{64})"$', bootstrap)
|
|
self.assertEqual(len(lines), 1)
|
|
|
|
def test_the_manifest_comes_from_the_same_commit(self):
|
|
self.assertIn('-O "$base/install/retroarch.json"', self.section)
|
|
self.assertTrue((REPO_ROOT / "install" / "retroarch.json").is_file())
|
|
self.assertIn("RETROBIOS_REF=$commit python3 install.py --manifest retroarch.json",
|
|
self.section)
|
|
self.assertIn('"--manifest",', INSTALL_PY)
|
|
self.assertIn('"https://raw.githubusercontent.com/Abdess/retrobios/"', INSTALL_PY)
|
|
|
|
def test_search_words(self):
|
|
terms = _front(_wiki("installer.md"))["terms"]
|
|
for word in ("packager", "pin", "reproducible"):
|
|
with self.subTest(word=word):
|
|
self.assertIn(word, terms)
|
|
|
|
|
|
class SearchWords(unittest.TestCase):
|
|
"""Contract with the search gate: each page carries its own words."""
|
|
|
|
def test_troubleshooting(self):
|
|
terms = _front(_wiki("troubleshooting.md"))["terms"]
|
|
for word in ("check my bios folder", "check bios folder"):
|
|
with self.subTest(word=word):
|
|
self.assertIn(word, terms)
|
|
|
|
def test_legal(self):
|
|
self.assertIn("license", _front(_wiki("legal.md"))["terms"])
|
|
|
|
|
|
class SmallCopyFixes(unittest.TestCase):
|
|
"""C-other: the RetroPie share had no name, and Mega CD two spellings."""
|
|
|
|
def test_retropie_share_is_named_with_its_source(self):
|
|
section = _section(_wiki("first-install.md"), "Error: Python 3.8 or newer is required.")
|
|
self.assertIn("share open, `\\\\RETROPIE\\bios` from Windows:", section)
|
|
self.assertIn("samba.sh:49-52", section)
|
|
self.assertIn("Transferring-Roms.md:29", section)
|
|
|
|
def test_mega_cd_is_spelled_once(self):
|
|
for path in sorted(WIKI.glob("*.md")):
|
|
with self.subTest(page=path.name):
|
|
self.assertNotIn("Mega-CD", path.read_text(encoding="utf-8"))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|