mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-23 20:29:06 -05:00
chore: merge round 2 fixes (installer)
This commit is contained in:
commit
c8c4f3f486
17 files changed
+1154
-286
No files matched your search
@@ -617,7 +617,11 @@ class PackReadmesSayWhatTheInstallerDoes(unittest.TestCase):
|
||||
def _guide(self, platform: str) -> str:
|
||||
from packreadme import _build_readme
|
||||
|
||||
text = _build_readme(platform, platform, "", 1, 1)
|
||||
registry = yaml.safe_load((ROOT / "platforms" / "_registry.yml").read_text())
|
||||
install = registry["platforms"][platform].get("install") or {}
|
||||
text = _build_readme(platform, platform, "", 1, 1,
|
||||
detect=install.get("detect", []),
|
||||
standalone_copies=install.get("standalone_copies", []))
|
||||
return text[text.index("INSTALLATION"):text.index("TROUBLESHOOTING")]
|
||||
|
||||
def test_retroarch_offers_the_windows_installer(self):
|
||||
@@ -642,7 +646,6 @@ class PackReadmesSayWhatTheInstallerDoes(unittest.TestCase):
|
||||
named = {
|
||||
path.rstrip("/") for path in re.findall(r"-> (~/\S+)", guide)
|
||||
}
|
||||
self.assertTrue(named)
|
||||
self.assertEqual(sorted(named - targets), [])
|
||||
|
||||
def test_retropie_never_sends_a_pc_to_the_linux_partition(self):
|
||||
@@ -3847,7 +3850,8 @@ class EveryReadmeNamesItsFolder(unittest.TestCase):
|
||||
if not folder:
|
||||
continue
|
||||
with self.subTest(platform=name):
|
||||
text = _build_readme(name, name, "", 1, 1, bios_paths=[folder])
|
||||
text = _build_readme(name, name, "", 1, 1,
|
||||
detect=[{"os": "linux", "bios_path": folder}])
|
||||
self.assertIn(folder, text)
|
||||
lakka = _build_readme("lakka", "Lakka", "", 1, 1)
|
||||
self.assertNotIn("retroarch/system", lakka)
|
||||
|
||||
@@ -73,6 +73,26 @@ def _which_pack() -> str:
|
||||
return generate_site.generate_which_pack()
|
||||
|
||||
|
||||
class TheDownloadTablesFollowTheRegistry(unittest.TestCase):
|
||||
"""EmuDeck has a Windows edition the installer detects, and the Windows
|
||||
table of the download page left it out."""
|
||||
|
||||
def test_every_detected_system_lists_the_platform(self):
|
||||
import yaml # noqa: PLC0415
|
||||
|
||||
registry = yaml.safe_load(_read("platforms/_registry.yml"))
|
||||
names = {"retroarch": "RetroArch", "retrobat": "RetroBat", "emudeck": "EmuDeck",
|
||||
"bizhawk": "BizHawk"}
|
||||
page = _which_pack()
|
||||
for os_name, heading in (("windows", "### Windows"), ("darwin", "### macOS")):
|
||||
table = _section(page, heading)
|
||||
for platform, entry in registry["platforms"].items():
|
||||
systems = {rule["os"] for rule in (entry.get("install") or {}).get("detect", [])}
|
||||
if os_name in systems:
|
||||
with self.subTest(os=os_name, platform=platform):
|
||||
self.assertIn(f"[{names[platform]}](", table)
|
||||
|
||||
|
||||
class DownloadLinksNameTheFile(unittest.TestCase):
|
||||
def test_a_whole_pack_links_its_asset(self):
|
||||
self.assertEqual(
|
||||
|
||||
+280
-2
@@ -376,8 +376,10 @@ class TestFreeSpaceGuard(unittest.TestCase):
|
||||
self.assertEqual((out, code), ("", 0))
|
||||
|
||||
def test_short_of_space_names_both_numbers_and_stops(self):
|
||||
"""Nothing was installed: code 2, as the exit table says, not the 1
|
||||
of a partial install."""
|
||||
out, code = self._run(free=1_000, needed=5_000)
|
||||
self.assertEqual(code, 1)
|
||||
self.assertEqual(code, 2)
|
||||
self.assertIn("Needed", out)
|
||||
self.assertIn("Free", out)
|
||||
self.assertIn("--dest", out)
|
||||
@@ -385,7 +387,52 @@ class TestFreeSpaceGuard(unittest.TestCase):
|
||||
def test_probe_climbs_to_an_existing_ancestor(self):
|
||||
missing = self.root / "not" / "created" / "yet"
|
||||
out, code = self._run(free=1, needed=2, path=missing)
|
||||
self.assertEqual(code, 1)
|
||||
self.assertEqual(code, 2)
|
||||
|
||||
def test_the_space_check_comes_before_the_folder(self):
|
||||
source = (REPO_ROOT / "install.py").read_text(encoding="utf-8")
|
||||
check = source.index("_check_free_space(bios_path, dl_size")
|
||||
self.assertLess(check, source.index("bios_path.mkdir(parents=True, exist_ok=True)"))
|
||||
|
||||
|
||||
class AMissingDestIsSaid(unittest.TestCase):
|
||||
"""--dest ~/.config/retroarh/system (a typo) was created with every
|
||||
missing parent and reported done, and --check on it said only
|
||||
"0/7 present"."""
|
||||
|
||||
def _call(self, dest: Path, ask: bool, answer: str = "") -> tuple[str, object]:
|
||||
out = io.StringIO()
|
||||
code = None
|
||||
with contextlib.redirect_stdout(out), \
|
||||
unittest.mock.patch("builtins.input", return_value=answer):
|
||||
try:
|
||||
install._missing_dest(dest, ask)
|
||||
except SystemExit as exc:
|
||||
code = exc.code
|
||||
return out.getvalue(), code
|
||||
|
||||
def test_an_existing_folder_says_nothing(self):
|
||||
root = Path(_tempdir(self))
|
||||
self.assertEqual(self._call(root, True), ("", None))
|
||||
|
||||
def test_a_deep_missing_path_is_asked_and_no_is_the_default(self):
|
||||
root = Path(_tempdir(self))
|
||||
dest = root / "retroarh" / "system"
|
||||
out, code = self._call(dest, True, "")
|
||||
self.assertEqual(code, 2)
|
||||
self.assertIn(f"The nearest folder that does: {root}", out)
|
||||
self.assertFalse((root / "retroarh").exists())
|
||||
self.assertEqual(self._call(dest, True, "y")[1], None)
|
||||
|
||||
def test_one_missing_level_or_no_terminal_is_said_not_asked(self):
|
||||
root = Path(_tempdir(self))
|
||||
# A "no" that was asked would stop the run with 2.
|
||||
out, code = self._call(root / "bios", True, "n")
|
||||
self.assertIsNone(code)
|
||||
self.assertIn("does not exist", out)
|
||||
out, code = self._call(root / "a" / "b", False, "n")
|
||||
self.assertIsNone(code)
|
||||
self.assertIn("does not exist", out)
|
||||
|
||||
|
||||
class AFolderThatRefusesWritesIsNamed(unittest.TestCase):
|
||||
@@ -1110,6 +1157,21 @@ class DetectionFollowsTheRegistry(unittest.TestCase):
|
||||
install._detect_by_path("linux", found)
|
||||
self.assertEqual(found, [("emudeck", home / "elsewhere" / "bios")])
|
||||
|
||||
def test_emudeck_on_macos_is_found_by_its_settings(self):
|
||||
"""EmuDeck's macOS build reads ~/.config/EmuDeck/settings.sh as the
|
||||
Linux one does (functions/all.sh:3-4,24, vars.sh:2): a Mac with
|
||||
EmuDeck detected nothing and was offered RetroArch's folder."""
|
||||
home = self._home()
|
||||
settings = home / ".config" / "EmuDeck" / "settings.sh"
|
||||
settings.parent.mkdir(parents=True)
|
||||
settings.write_text(f"emulationPath={home / 'Games' / 'Emulation'}\n")
|
||||
self.assertIn(("emudeck", home / "Games" / "Emulation" / "bios"),
|
||||
install.detect_platforms("darwin"))
|
||||
settings.unlink()
|
||||
(home / "Emulation" / "bios").mkdir(parents=True)
|
||||
self.assertIn(("emudeck", home / "Emulation" / "bios"),
|
||||
install.detect_platforms("darwin"))
|
||||
|
||||
|
||||
class RetroArchIsFoundWhereWindowsInstallsPutIt(unittest.TestCase):
|
||||
"""Program Files\\RetroArch-Win64, a Steam library on another drive and
|
||||
@@ -2071,6 +2133,48 @@ class TestAvailablePlatforms(unittest.TestCase):
|
||||
self.assertTrue(marker.is_file(), "piped bootstrap did not download install.py")
|
||||
self.assertIn("retroarch", proc.stdout)
|
||||
|
||||
def test_busybox_wget_downloads_the_installer(self):
|
||||
"""BusyBox wget, the only one in the RomM image, refuses
|
||||
--https-only: with no curl the one-liner always failed and blamed
|
||||
the network."""
|
||||
scratch = REPO_ROOT / "tmp" / "tests"
|
||||
scratch.mkdir(parents=True, exist_ok=True)
|
||||
with tempfile.TemporaryDirectory(dir=scratch) as directory:
|
||||
root = Path(directory)
|
||||
fake_bin = root / "bin"
|
||||
fake_bin.mkdir()
|
||||
for tool in ("mktemp", "wc", "tr", "sha256sum", "awk", "rm", "rmdir",
|
||||
"grep", "tail", "cp", "dirname", "cat", "python3"):
|
||||
found = shutil.which(tool)
|
||||
if found:
|
||||
(fake_bin / tool).symlink_to(found)
|
||||
(fake_bin / "wget").write_text(
|
||||
"#!/bin/sh\n"
|
||||
"output=\n"
|
||||
"for arg in \"$@\"; do\n"
|
||||
" case \"$arg\" in\n"
|
||||
" --help) echo 'Usage: wget [-cqS] [-O FILE] [-T SEC] URL' >&2; exit 1 ;;\n"
|
||||
" --https-only) echo \"wget: unrecognized option '$arg'\" >&2; exit 1 ;;\n"
|
||||
" --output-document=*) output=${arg#--output-document=} ;;\n"
|
||||
" esac\n"
|
||||
"done\n"
|
||||
"cp -- \"$FAKE_INSTALLER_SOURCE\" \"$output\"\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(fake_bin / "wget").chmod(0o755)
|
||||
env = {
|
||||
"PATH": str(fake_bin), "HOME": str(root), "TMPDIR": str(root),
|
||||
"RETROBIOS_INSTALL_URL": "https://example.invalid/install.py",
|
||||
"FAKE_INSTALLER_SOURCE": str(REPO_ROOT / "install.py"),
|
||||
}
|
||||
proc = subprocess.run(
|
||||
["/bin/sh", "-s", "--", "--list-platforms"], cwd=root, env=env,
|
||||
input=(REPO_ROOT / "install.sh").read_text(encoding="utf-8"),
|
||||
capture_output=True, text=True, timeout=60, check=False,
|
||||
)
|
||||
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
|
||||
self.assertIn("retroarch", proc.stdout)
|
||||
|
||||
def test_a_failed_download_says_so_in_one_message(self):
|
||||
"""Offline, the one-liner printed curl's progress table and
|
||||
"curl: (6) ...", then exited 6, a code the table does not list."""
|
||||
@@ -2274,6 +2378,89 @@ class RetroArchWhereItReallyLives(unittest.TestCase):
|
||||
self.assertIn(("retroarch", support / "system"), found)
|
||||
|
||||
|
||||
class NoPythonNamesThisSystemsCommand(unittest.TestCase):
|
||||
"""Without Python the bootstrap said `sudo apt install python3` on every
|
||||
system: Termux has no sudo and installs with `pkg install python`, and
|
||||
macOS has no apt."""
|
||||
|
||||
def _run(self, extra_env: dict, uname: str = "Linux") -> subprocess.CompletedProcess:
|
||||
root = Path(_tempdir(self))
|
||||
fake_bin = root / "bin"
|
||||
fake_bin.mkdir()
|
||||
for tool in ("sed", "tr", "dirname", "cat"):
|
||||
found = shutil.which(tool)
|
||||
if found:
|
||||
(fake_bin / tool).symlink_to(found)
|
||||
(fake_bin / "uname").write_text(f"#!/bin/sh\necho {uname}\n", encoding="utf-8")
|
||||
(fake_bin / "uname").chmod(0o755)
|
||||
# No terminal: the Termux offer is a question asked on /dev/tty.
|
||||
return subprocess.run(
|
||||
["/bin/sh", str(REPO_ROOT / "install.sh")], cwd=root,
|
||||
env={"PATH": str(fake_bin), "HOME": str(root), **extra_env},
|
||||
capture_output=True, text=True, timeout=30, check=False,
|
||||
start_new_session=True,
|
||||
)
|
||||
|
||||
def test_termux_is_told_pkg(self):
|
||||
proc = self._run({"TERMUX_VERSION": "0.118",
|
||||
"PREFIX": "/data/data/com.termux/files/usr"})
|
||||
self.assertEqual(proc.returncode, 1)
|
||||
self.assertIn("Install it with: pkg install python", proc.stderr)
|
||||
self.assertNotIn("sudo", proc.stderr)
|
||||
|
||||
def test_macos_is_told_xcode_select(self):
|
||||
proc = self._run({}, uname="Darwin")
|
||||
self.assertEqual(proc.returncode, 1)
|
||||
self.assertIn("Install it with: xcode-select --install", proc.stderr)
|
||||
self.assertNotIn("apt", proc.stderr)
|
||||
|
||||
|
||||
@unittest.skipUnless(shutil.which("pwsh") and shutil.which("script"), "needs PowerShell and a terminal")
|
||||
class WindowsWithoutPythonIsOfferedWinget(unittest.TestCase):
|
||||
"""A Windows machine without Python was told to install it, open a new
|
||||
window and run the command again, while winget, present on Windows 10
|
||||
and 11, could do it from the bootstrap."""
|
||||
|
||||
def _folder(self) -> tuple[Path, Path]:
|
||||
folder = Path(_tempdir(self))
|
||||
shutil.copy(REPO_ROOT / "install.ps1", folder / "install.ps1")
|
||||
(folder / "install.py").write_text("print('installer ran')\n")
|
||||
fake_bin = folder / "bin"
|
||||
fake_bin.mkdir()
|
||||
python = shutil.which("python3") or sys.executable
|
||||
(fake_bin / "winget").write_text(
|
||||
f"#!/bin/sh\necho \"winget $*\"\n{shutil.which('ln')} -s {shlex.quote(python)} "
|
||||
f"{shlex.quote(str(fake_bin / 'python3'))}\n"
|
||||
)
|
||||
(fake_bin / "winget").chmod(0o755)
|
||||
return folder, fake_bin
|
||||
|
||||
def _run(self, folder: Path, fake_bin: Path, answer: str) -> subprocess.CompletedProcess:
|
||||
pwsh = shutil.which("pwsh")
|
||||
command = (f"{shlex.quote(pwsh)} -NoProfile -File "
|
||||
f"{shlex.quote(str(folder / 'install.ps1'))}")
|
||||
return subprocess.run(
|
||||
[shutil.which("script"), "-qec", command, "/dev/null"], input=answer,
|
||||
env={**os.environ, "PATH": f"{fake_bin}{os.pathsep}/usr/bin/nonexistent"},
|
||||
capture_output=True, text=True, timeout=120, check=False,
|
||||
)
|
||||
|
||||
def test_yes_installs_and_carries_on(self):
|
||||
folder, fake_bin = self._folder()
|
||||
proc = self._run(folder, fake_bin, "y\n")
|
||||
self.assertIn("Install Python 3.12 now with winget?", proc.stdout)
|
||||
self.assertIn("winget install --id Python.Python.3.12", proc.stdout)
|
||||
self.assertIn("installer ran", proc.stdout)
|
||||
self.assertEqual(proc.returncode, 0, proc.stdout)
|
||||
|
||||
def test_no_names_the_command(self):
|
||||
folder, fake_bin = self._folder()
|
||||
proc = self._run(folder, fake_bin, "n\n")
|
||||
self.assertNotIn("installer ran", proc.stdout)
|
||||
self.assertIn("winget install Python.Python.3.12", proc.stdout)
|
||||
self.assertFalse((fake_bin / "python3").exists())
|
||||
|
||||
|
||||
class ThePowerShellWrapperPassesTheStatusOn(unittest.TestCase):
|
||||
"""Every non-zero exit became a red "installer failed" exception and
|
||||
code 1: a --check that found work read as a crash, 2 and 130 were lost."""
|
||||
@@ -2368,6 +2555,23 @@ class SudoAdviceNamesTheFolder(unittest.TestCase):
|
||||
source = (REPO_ROOT / "install.py").read_text(encoding="utf-8")
|
||||
self.assertIn("platform_of={path: plat for plat, path in platforms}", source)
|
||||
|
||||
@unittest.skipUnless(hasattr(os, "statvfs"), "statvfs reads mount flags")
|
||||
def test_a_read_only_mount_is_not_sent_to_sudo(self):
|
||||
"""A card Linux mounted read-only after a dirty unplug was answered
|
||||
with 'run it again with sudo', which fails the same way."""
|
||||
real = os.statvfs(".")
|
||||
flags = SimpleNamespace(f_flag=real.f_flag | os.ST_RDONLY)
|
||||
for plat in ("retroarch", ""):
|
||||
with self.subTest(platform=plat), \
|
||||
unittest.mock.patch.object(install.os, "statvfs", return_value=flags):
|
||||
advice = install._write_advice(Path("/media/card/bios"), False, plat)
|
||||
self.assertIn("mounted read-only", advice)
|
||||
self.assertNotIn("Run it again with sudo", advice)
|
||||
self.assertNotIn("sudo python3", advice)
|
||||
writable = SimpleNamespace(f_flag=real.f_flag & ~os.ST_RDONLY)
|
||||
with unittest.mock.patch.object(install.os, "statvfs", return_value=writable):
|
||||
self.assertIn("sudo", install._write_advice(Path("/media/card/bios"), False, "retroarch"))
|
||||
|
||||
|
||||
|
||||
class CaseVariants(unittest.TestCase):
|
||||
@@ -3372,6 +3576,19 @@ class HintsGiveTheUsersOwnCommandBack(unittest.TestCase):
|
||||
hint = "\n".join(install._manual_usage_hint("linux"))
|
||||
self.assertIn("python3 install.py --platform retroarch", hint)
|
||||
|
||||
def test_a_listing_hint_keeps_the_listing(self):
|
||||
"""--list-systems with nothing detected was answered with a full
|
||||
install command: the listing flag gone, a --dest added."""
|
||||
home = Path(_tempdir(self))
|
||||
env = {**os.environ, "HOME": str(home), "RETROBIOS_OS": "linux",
|
||||
"RETROBIOS_BOOTSTRAP": ""}
|
||||
for flag in ("--list-systems", "--list-cores", "--list-emulators", "--list-targets"):
|
||||
with self.subTest(flag=flag):
|
||||
proc = _install_run(env, flag, "--no-input")
|
||||
self.assertEqual(proc.returncode, 1, proc.stdout + proc.stderr)
|
||||
self.assertIn(f"python3 install.py --platform retroarch {flag}", proc.stdout)
|
||||
self.assertNotIn("--dest", proc.stdout)
|
||||
|
||||
def test_the_write_advice_keeps_the_narrowing(self):
|
||||
self._record("--system", "atari-lynx", "--region", "us")
|
||||
install._INVOCATION.os_type = "linux"
|
||||
@@ -3729,6 +3946,67 @@ class ACompletePartialIsHandedOffOnWindows(unittest.TestCase):
|
||||
self.assertTrue(install._write_refused(PermissionError(13, "denied")))
|
||||
|
||||
|
||||
class StandaloneFoldersAreNamedWhenThereAreAny(unittest.TestCase, _LoopbackRun):
|
||||
"""'Standalone copies skipped' closed every install, on a machine with
|
||||
no standalone emulator as on one whose DuckStation lacked its BIOS."""
|
||||
|
||||
def setUp(self):
|
||||
self.env = self.serve(self)
|
||||
|
||||
def _run(self) -> str:
|
||||
proc = _install_run(self.env, "--platform", "retroarch", "--dest",
|
||||
str(self.home / "system"), "--system", "atari-lynx",
|
||||
"--no-input")
|
||||
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
|
||||
return proc.stdout
|
||||
|
||||
def test_nothing_is_said_without_an_emulator_folder(self):
|
||||
out = self._run()
|
||||
self.assertNotIn("skipped", out)
|
||||
self.assertNotIn("--standalone-copies", out)
|
||||
|
||||
def test_a_found_folder_is_named(self):
|
||||
duckstation = self.home / ".local" / "share" / "duckstation" / "bios"
|
||||
duckstation.mkdir(parents=True)
|
||||
out = self._run()
|
||||
self.assertIn("Standalone emulator folders found here:", out)
|
||||
self.assertIn(str(duckstation), out)
|
||||
self.assertIn("--standalone-copies", out)
|
||||
|
||||
|
||||
class StandaloneEmulatorsWithoutAPlatformAreServed(unittest.TestCase, _LoopbackRun):
|
||||
"""ES-DE with DuckStation and PCSX2 and no RetroArch: the run said 'No
|
||||
supported platform detected' and offered RetroArch's folder, which no
|
||||
emulator on the machine reads."""
|
||||
|
||||
def setUp(self):
|
||||
self.env = self.serve(self)
|
||||
(self.home / "ES-DE").mkdir()
|
||||
self.duckstation = self.home / ".local" / "share" / "duckstation" / "bios"
|
||||
self.duckstation.mkdir(parents=True)
|
||||
|
||||
def test_the_emulator_folder_is_filled(self):
|
||||
proc = _install_run(self.env, "--no-input", "--dry-run")
|
||||
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
|
||||
self.assertIn("Found standalone emulator folders:", proc.stdout)
|
||||
self.assertIn(f"to {self.duckstation}:", proc.stdout)
|
||||
self.assertNotIn("--platform retroarch --dest", proc.stdout)
|
||||
proc = _install_run(self.env, "--no-input")
|
||||
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
|
||||
names = sorted(path.name for path in self.duckstation.iterdir())
|
||||
self.assertIn("scph5501.bin", names)
|
||||
self.assertTrue(all(name.startswith("scph") for name in names), names)
|
||||
proc = _install_run(self.env, "--no-input", "--check")
|
||||
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
|
||||
self.assertIn("All files up to date.", proc.stdout)
|
||||
|
||||
def test_no_emulator_folder_keeps_the_hint(self):
|
||||
self.duckstation.rmdir()
|
||||
proc = _install_run(self.env, "--no-input")
|
||||
self.assertEqual(proc.returncode, 1, proc.stdout + proc.stderr)
|
||||
self.assertIn("--platform retroarch --dest", proc.stdout)
|
||||
|
||||
|
||||
class TheEmulatorNamesHaveTheirOwnListing(unittest.TestCase, _LoopbackRun):
|
||||
"""--emulator nope said "--list-cores lists all 441" and --list-cores
|
||||
printed 189: the buildbot names --emulator accepts were listed nowhere."""
|
||||
|
||||
@@ -746,5 +746,47 @@ class AListingNeedsNoInstall(unittest.TestCase):
|
||||
self.assertIn("nintendo-gba", proc.stdout)
|
||||
|
||||
|
||||
class ThePlatformsOwnSystemNamesAreAccepted(unittest.TestCase):
|
||||
"""A Batocera user who typed `--system psx`, the folder under roms/,
|
||||
was told the name is unknown with no suggestion, while the platform
|
||||
file records psx as the native_system of sony-playstation."""
|
||||
|
||||
def test_the_names_come_from_the_platform_file(self):
|
||||
from common import load_platform_config # noqa: PLC0415
|
||||
from manifestselect import native_system_names # noqa: PLC0415
|
||||
|
||||
config = load_platform_config("batocera", str(REPO_ROOT / "platforms"))
|
||||
names = native_system_names(config, list(config["systems"]))
|
||||
self.assertEqual(names["sony-playstation"], ["psx"])
|
||||
self.assertEqual(names["nintendo-gba"], ["gba"])
|
||||
# A name spelled like the system id adds nothing.
|
||||
self.assertTrue(all(
|
||||
install._name_key(alias) != install._name_key(system)
|
||||
for system, aliases in names.items() for alias in aliases
|
||||
))
|
||||
|
||||
def test_the_builder_writes_them_into_the_manifest(self):
|
||||
source = (REPO_ROOT / "scripts" / "generate_pack.py").read_text(encoding="utf-8")
|
||||
self.assertIn('"system_names": native_system_names(config, list(pack_systems))',
|
||||
source)
|
||||
|
||||
def test_a_native_name_narrows_and_lists(self):
|
||||
with FakeRepository() as repo:
|
||||
path = repo.root / "install" / "retroarch.json"
|
||||
manifest = json.loads(path.read_text())
|
||||
manifest["system_names"] = {"sony-playstation": ["psx"],
|
||||
"nintendo-gba": ["gba"]}
|
||||
path.write_text(json.dumps(manifest))
|
||||
proc = repo.run("--system", "psx", "--no-input")
|
||||
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
|
||||
self.assertEqual(repo.installed(),
|
||||
["pcsx/extra.bin", "scph5500.bin", "scph5501.bin"])
|
||||
listing = repo.run("--list-systems", "--no-input")
|
||||
self.assertIn("nintendo-gba (gba)", listing.stdout)
|
||||
typo = repo.run("--system", "psxx", "--no-input")
|
||||
self.assertEqual(typo.returncode, 1)
|
||||
self.assertIn("Did you mean: sony-playstation", typo.stderr)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -34,13 +34,10 @@ class GuidesPointAtTheReadPath(unittest.TestCase):
|
||||
def test_unguided_platforms_name_the_registry_path(self):
|
||||
registry = yaml.safe_load((REPO_ROOT / "platforms" / "_registry.yml").read_text())
|
||||
for name, data in registry["platforms"].items():
|
||||
paths = [
|
||||
str(d.get("bios_path", ""))
|
||||
for d in (data.get("install", {}) or {}).get("detect", [])
|
||||
]
|
||||
if not any(paths):
|
||||
detect = (data.get("install", {}) or {}).get("detect", [])
|
||||
if not any(d.get("bios_path") for d in detect):
|
||||
continue
|
||||
text = _build_readme(name, name, "", 1, 1, bios_paths=paths)
|
||||
text = _build_readme(name, name, "", 1, 1, detect=detect)
|
||||
with self.subTest(platform=name):
|
||||
self.assertFalse(
|
||||
any("your BIOS directory" in line for line in _extract_lines(text))
|
||||
@@ -55,5 +52,56 @@ class GuidesNameShippedInstallers(unittest.TestCase):
|
||||
with self.subTest(installer=name):
|
||||
self.assertTrue((REPO_ROOT / name).is_file())
|
||||
|
||||
def _registry() -> dict:
|
||||
return yaml.safe_load((REPO_ROOT / "platforms" / "_registry.yml").read_text())
|
||||
|
||||
|
||||
def _guide(name: str) -> str:
|
||||
install = _registry()["platforms"][name].get("install") or {}
|
||||
return _build_readme(name, name, "", 1, 1, detect=install.get("detect", []),
|
||||
standalone_copies=install.get("standalone_copies", []))
|
||||
|
||||
|
||||
class TheOneLinerFollowsTheRegistry(unittest.TestCase):
|
||||
"""The guides were written per platform by hand: ROCKNIX and MiSTer,
|
||||
which the installer detects, had no one-line install, EmuDeck's
|
||||
Windows edition was told to open Konsole, and every RomM user was sent
|
||||
to library/bios/ whatever its firmware template."""
|
||||
|
||||
def test_every_detected_system_gets_its_command(self):
|
||||
for name, data in _registry()["platforms"].items():
|
||||
systems = {rule["os"] for rule in (data.get("install") or {}).get("detect", [])}
|
||||
if not systems:
|
||||
continue
|
||||
text = _guide(name)
|
||||
with self.subTest(platform=name):
|
||||
self.assertEqual("install.sh | sh" in text, bool(systems - {"windows"}))
|
||||
self.assertEqual("install.ps1 | iex" in text, "windows" in systems)
|
||||
|
||||
def test_rocknix_and_mister_run_it_on_the_device(self):
|
||||
for name in ("rocknix", "misterfpga"):
|
||||
with self.subTest(platform=name):
|
||||
self.assertIn(f"On the machine running {name}", _guide(name))
|
||||
|
||||
def test_emudeck_is_not_steam_deck_only(self):
|
||||
text = _guide("emudeck")
|
||||
self.assertNotIn("Steam Deck / Linux", text)
|
||||
self.assertNotIn("Konsole", text)
|
||||
self.assertIn("install.ps1 | iex", text)
|
||||
self.assertIn("C:\\Emulation\\bios", text)
|
||||
self.assertIn("--standalone-copies", text)
|
||||
|
||||
def test_romm_names_both_firmware_layouts(self):
|
||||
text = _guide("romm")
|
||||
self.assertIn("library/bios/", text)
|
||||
self.assertIn("library/psx/bios/", text)
|
||||
self.assertIn("--romm-firmware", text)
|
||||
|
||||
def test_the_commands_are_written_once(self):
|
||||
source = (REPO_ROOT / "scripts" / "packreadme.py").read_text(encoding="utf-8")
|
||||
self.assertEqual(source.count("main/install.sh"), 1)
|
||||
self.assertEqual(source.count("main/install.ps1"), 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user