mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-14 22:13:47 -06:00
chore: merge integ configs root and switch folders
The configs root place joins roms and emulator folders in the split handed directories, and RetroBat's portable folder table keeps both sides. Batocera, KNULLI and RetroBat records regenerated.
This commit is contained in:
commit
e93ff86bf1
27 files changed
+893
-206
No files matched your search
+32
-5
@@ -1150,7 +1150,7 @@ class OuterRootsAreUntrusted(unittest.TestCase):
|
||||
def test_unsafe_from_bios_is_refused(self):
|
||||
for value in (*self.UNSAFE, "../../../x", "../x/..", "x", "./x", "/x", "..x", None):
|
||||
with self.subTest(from_bios=value), self.assertRaises(ValueError):
|
||||
install._check_roms_directory({"roms_directory": {**self.ROMS, "from_bios": value}})
|
||||
install._check_relative_directories({"roms_directory": {**self.ROMS, "from_bios": value}})
|
||||
|
||||
def test_the_relations_the_rom_folders_files_use_are_accepted(self):
|
||||
bios = Path("/card/system/bios")
|
||||
@@ -1161,13 +1161,13 @@ class OuterRootsAreUntrusted(unittest.TestCase):
|
||||
}
|
||||
for value, root in expected.items():
|
||||
with self.subTest(from_bios=value):
|
||||
install._check_roms_directory({"roms_directory": {**self.ROMS, "from_bios": value}})
|
||||
self.assertEqual(install._roms_root(bios, value), root)
|
||||
install._check_relative_directories({"roms_directory": {**self.ROMS, "from_bios": value}})
|
||||
self.assertEqual(install._relative_root(bios, value), root)
|
||||
self.assertEqual(install.MAX_ROMS_UPS, 2)
|
||||
|
||||
def test_a_list_without_a_roms_root_leaves_the_files_out(self):
|
||||
files = [{"dest": "pico-8/pico8_64", "root": "roms", "cores": ["x"]}]
|
||||
kept, left = install.place_roms_entries(files, Path("/card/roms/bios"), {}, "x", say=False)
|
||||
kept, left = install.place_relative_entries(files, Path("/card/roms/bios"), {}, "x", say=False)
|
||||
self.assertEqual((kept, [o["reason"] for o in left]), ([], ["no_roms_directory"]))
|
||||
|
||||
def test_unsafe_emulator_folders_are_refused(self):
|
||||
@@ -1192,7 +1192,7 @@ class OuterRootsAreUntrusted(unittest.TestCase):
|
||||
with unittest.mock.patch.object(install.os, "name", host):
|
||||
for value in ("../C:x", "..\\x", "../x:", "../Roms:x"):
|
||||
with self.subTest(host=host, from_bios=value), self.assertRaises(ValueError):
|
||||
install._check_roms_directory(
|
||||
install._check_relative_directories(
|
||||
{"roms_directory": {**self.ROMS, "from_bios": value}})
|
||||
for value in ("C:x/{key}", "x:/{key}", "emu:x/{key}"):
|
||||
layout = {**self.EMULATORS, "searched": [value], "default": value}
|
||||
@@ -7721,5 +7721,32 @@ class TheListVersionIsSaidBeforeAndAfter(unittest.TestCase):
|
||||
self.assertNotIn("This list was built for", proc.stdout)
|
||||
|
||||
|
||||
class APackageHintNamesThePackageCommand(unittest.TestCase):
|
||||
"""Run from the retrobios package, a hint said `python3 install.py`,
|
||||
a file the user does not have."""
|
||||
|
||||
def test_the_environment_names_the_command(self):
|
||||
for prefix, command in (
|
||||
("/home/me/.cache/uv/archive-v0/UwL4i3xOlCEYSTnw", "uvx retrobios"),
|
||||
("C:\\Users\\me\\AppData\\Local\\uv\\cache\\archive-v0\\x1", "uvx retrobios"),
|
||||
("/home/me/.cache/pipx/539b8243c11ee7c", "pipx run retrobios"),
|
||||
("/home/me/.local/pipx/.cache/539b8243c11ee7c", "pipx run retrobios"),
|
||||
("/home/me/.local/share/pipx/venvs/retrobios", "retrobios"),
|
||||
("/home/me/.local/share/uv/tools/retrobios", "retrobios"),
|
||||
("/home/me/venv", "retrobios"),
|
||||
):
|
||||
with self.subTest(prefix=prefix):
|
||||
self.assertEqual(install.package_command(prefix), command)
|
||||
|
||||
def test_the_launcher_is_the_package_command(self):
|
||||
hint = install.Invocation()
|
||||
with unittest.mock.patch.dict(os.environ, {"RETROBIOS_BOOTSTRAP": "pypi"}), \
|
||||
unittest.mock.patch.object(install.sys, "prefix", "/home/me/.cache/uv/archive-v0/a"):
|
||||
self.assertEqual(hint.launcher(), "uvx retrobios")
|
||||
self.assertEqual(hint.launcher(elevated=True), "sudo uvx retrobios")
|
||||
self.assertTrue(hint.command("retroarch", "DIR").startswith(
|
||||
"uvx retrobios --platform retroarch --dest DIR"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
+135
-31
@@ -21,7 +21,6 @@ REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
||||
|
||||
from launchers import handed_directories, standalone_bindings # noqa: E402
|
||||
from scraper import knulli_launchers as kl # noqa: E402
|
||||
from scraper import retrobat_launchers as rl # noqa: E402
|
||||
from scraper.batocera_launchers import ( # noqa: E402
|
||||
GENERATORS,
|
||||
@@ -128,35 +127,27 @@ class AFolderTheGeneratorEnters(unittest.TestCase):
|
||||
self.assertEqual(found, {"": ("outside", "")})
|
||||
|
||||
|
||||
class ADataFolderTheEnvironmentSets(unittest.TestCase):
|
||||
SUYU = f"{kl.GENERATORS}/suyu/suyuGenerator.py"
|
||||
TEXT = textwrap.dedent("""\
|
||||
from ...batoceraPaths import CACHE, CONFIGS, SAVES
|
||||
|
||||
class SuyuGenerator:
|
||||
def generate(self, system, rom):
|
||||
commandArray = ["/usr/bin/suyu", "-f", "-g", rom]
|
||||
return Command.Command(array=commandArray, env={
|
||||
"XDG_CONFIG_HOME": CONFIGS,
|
||||
"XDG_DATA_HOME": SAVES / "switch",
|
||||
})
|
||||
""")
|
||||
|
||||
def test_the_data_folder_is_a_record_and_binds_outside(self):
|
||||
files = {f"{kl.CONFIGGEN}/batoceraPaths.py": PATHS,
|
||||
f"{kl.GENERATORS}/importer.py": IMPORTER_TEXT, self.SUYU: self.TEXT}
|
||||
(record,) = records(kl.data_homes(kl.configgen(files.__getitem__)))
|
||||
self.assertEqual(
|
||||
(record["emulator"], record["search_dir"], record["passes_to"], record["source_ref"]),
|
||||
("suyu", "/userdata/saves/switch", "environment XDG_DATA_HOME", f"{self.SUYU}:8"),
|
||||
)
|
||||
config = {"base_destination": "bios", "standalone_cores": ["suyu"], "launchers": [record]}
|
||||
class AVariableADeclaredFolderIsSpelledFrom(unittest.TestCase):
|
||||
def test_a_data_folder_outside_the_tree_binds_outside(self):
|
||||
record = {"emulator": "emu", "build": "standalone", "kind": "passes_path",
|
||||
"names": [], "search_dir": "/userdata/saves/switch",
|
||||
"passes_to": "environment XDG_DATA_HOME", "source_ref": "x.py:1"}
|
||||
config = {"base_destination": "bios", "standalone_cores": ["emu"], "launchers": [record]}
|
||||
found = standalone_bindings(
|
||||
{"suyu"}, ["$XDG_DATA_HOME/suyu", "SuyuPath::KeysDir = keys"],
|
||||
handed_directories(config),
|
||||
{"emu"}, ["$XDG_DATA_HOME/emu"], handed_directories(config)
|
||||
)
|
||||
self.assertEqual(found, {"": ("outside", "")})
|
||||
|
||||
def test_a_data_folder_in_the_tree_binds_below_it(self):
|
||||
record = {"emulator": "emu", "build": "standalone", "kind": "passes_path",
|
||||
"names": [], "search_dir": "/userdata/bios/data",
|
||||
"passes_to": "environment XDG_DATA_HOME", "source_ref": "x.py:1"}
|
||||
config = {"base_destination": "bios", "standalone_cores": ["emu"], "launchers": [record]}
|
||||
found = standalone_bindings(
|
||||
{"emu"}, ["$XDG_DATA_HOME/emu"], handed_directories(config)
|
||||
)
|
||||
self.assertEqual(found, {"": ("", "data/emu")})
|
||||
|
||||
|
||||
class APortableUserFolder(unittest.TestCase):
|
||||
CITRON = """
|
||||
@@ -185,12 +176,28 @@ class APortableUserFolder(unittest.TestCase):
|
||||
}
|
||||
"""
|
||||
|
||||
RYUJINX = """
|
||||
public override System.Diagnostics.ProcessStartInfo Generate(string system, string emulator, string core, string rom, string playersControllers, ScreenResolution resolution)
|
||||
{
|
||||
string path = AppConfig.GetFullPath("ryujinx");
|
||||
string portablePath = Path.Combine(AppConfig.GetFullPath("saves"), "switch", "ryujinx", "portable");
|
||||
string setupPath = Path.Combine(path, "portable");
|
||||
}
|
||||
"""
|
||||
|
||||
def sources(self) -> dict[str, rl.Source]:
|
||||
def source(ref: str, text: str) -> rl.Source:
|
||||
return rl.Source(ref, textwrap.dedent(text).lstrip("\n"))
|
||||
|
||||
def yuzu_like(exe: str) -> str:
|
||||
return self.CITRON.replace("citron.exe", exe)
|
||||
|
||||
return {
|
||||
f"{rl.GENERATORS}/Citron.Generator.cs": rl.Source(
|
||||
"c", textwrap.dedent(self.CITRON).lstrip("\n")),
|
||||
f"{rl.GENERATORS}/ShadPS4.Generator.cs": rl.Source(
|
||||
"s", textwrap.dedent(self.SHADPS4).lstrip("\n")),
|
||||
f"{rl.GENERATORS}/Citron.Generator.cs": source("c", self.CITRON),
|
||||
f"{rl.GENERATORS}/Eden.Generator.cs": source("e", yuzu_like("eden.exe")),
|
||||
f"{rl.GENERATORS}/Ryujinx.Generator.cs": source("r", self.RYUJINX),
|
||||
f"{rl.GENERATORS}/ShadPS4.Generator.cs": source("s", self.SHADPS4),
|
||||
f"{rl.GENERATORS}/Yuzu.Generator.cs": source("y", yuzu_like("yuzu.exe")),
|
||||
}
|
||||
|
||||
def test_each_generator_gives_its_user_folder(self):
|
||||
@@ -198,7 +205,15 @@ class APortableUserFolder(unittest.TestCase):
|
||||
self.assertEqual(
|
||||
[(r["emulator"], r["search_dir"], r["source_ref"]) for r in found],
|
||||
[("citron", '<AppConfig.GetFullPath("citron")>/user', "c:3, 7-8"),
|
||||
("shadps4", '<AppConfig.GetFullPath("shadps4")>/user', "s:3, 8-10")],
|
||||
("eden", '<AppConfig.GetFullPath("eden")>/user', "e:3, 7-8"),
|
||||
("ryujinx", '<AppConfig.GetFullPath("ryujinx")>/portable', "r:3-5"),
|
||||
("shadps4", '<AppConfig.GetFullPath("shadps4")>/user', "s:3, 8-10"),
|
||||
("yuzu", '<AppConfig.GetFullPath("yuzu")>/user', "y:3, 7-8")],
|
||||
)
|
||||
ryujinx = found[2]
|
||||
self.assertEqual(ryujinx["passes_to"], "DefaultPortableDir, the portable folder beside Ryujinx.exe")
|
||||
self.assertEqual(
|
||||
ryujinx["depends_on_option"], "unless saves/switch/ryujinx/portable exists, passed with -r"
|
||||
)
|
||||
|
||||
def test_the_user_folder_binds_below_the_emulator_folder(self):
|
||||
@@ -213,5 +228,94 @@ class APortableUserFolder(unittest.TestCase):
|
||||
self.assertEqual(found, {"": ("emulators", "citron/user")})
|
||||
|
||||
|
||||
|
||||
class ADefaultTheBuildLacksIsNotRun(unittest.TestCase):
|
||||
"""KNULLI's configgen-defaults.yml picks suyu for the Switch, which its
|
||||
es_systems.yml does not offer and no package builds."""
|
||||
|
||||
ES = textwrap.dedent("""\
|
||||
switch:
|
||||
emulators:
|
||||
citron:
|
||||
citron: { requireAnyOf: [BR2_PACKAGE_CITRON] }
|
||||
voxatron:
|
||||
emulators: {}
|
||||
""")
|
||||
DEFAULTS = textwrap.dedent("""\
|
||||
switch:
|
||||
emulator: suyu
|
||||
core: suyu
|
||||
voxatron:
|
||||
emulator: lexaloffle
|
||||
core: vox_official
|
||||
kodi:
|
||||
emulator: kodi
|
||||
core: kodi
|
||||
""")
|
||||
LAUNCHERS = [{"emulator": "lexaloffle", "kind": "fixed_name", "passes_to": "exec",
|
||||
"search_dir": "/userdata/bios"}]
|
||||
|
||||
def test_only_what_a_package_or_the_user_supplies_is_run(self):
|
||||
from scraper import batocera_scraper as bs
|
||||
|
||||
paths = ["package/batocera/emulators/citron/Config.in", "package/kodi/Config.in"]
|
||||
built = bs.packages_and_programs(bs.package_names(paths), self.LAUNCHERS)
|
||||
cores, standalone, _dual = bs.configgen_cores(self.DEFAULTS, self.ES, set(), built)
|
||||
self.assertEqual(sorted(cores), ["citron", "kodi", "vox_official"])
|
||||
self.assertIn("lexaloffle", standalone)
|
||||
self.assertNotIn("suyu", standalone)
|
||||
by_system = bs.es_systems_by_system(self.ES, self.DEFAULTS, set(), built)
|
||||
self.assertEqual(by_system["switch"], ["citron"])
|
||||
# Without the packages, every default counts, as before.
|
||||
self.assertIn("suyu", bs.configgen_cores(self.DEFAULTS, self.ES, set())[0])
|
||||
|
||||
def test_the_real_knulli_list_names_no_suyu(self):
|
||||
import yaml
|
||||
|
||||
config = yaml.safe_load((REPO_ROOT / "platforms" / "knulli.yml").read_text())
|
||||
self.assertNotIn("suyu", config["cores"])
|
||||
self.assertNotIn("suyu", config.get("standalone_cores") or [])
|
||||
|
||||
|
||||
|
||||
class AUserFolderInTheConfigsFolder(unittest.TestCase):
|
||||
"""Batocera starts shadPS4 from /userdata/system/configs/shadps4, a folder
|
||||
of the configs root its batoceraPaths builds (../system/configs)."""
|
||||
|
||||
def test_the_sys_modules_go_below_the_configs_root(self):
|
||||
from common import load_emulator_profiles, load_platform_config, platform_layouts
|
||||
from common import standalone_set
|
||||
|
||||
profile = load_emulator_profiles(str(REPO_ROOT / "emulators"))["shadps4"]
|
||||
for platform in ("batocera", "knulli"):
|
||||
config = load_platform_config(platform, str(REPO_ROOT / "platforms"))
|
||||
((view, _standalone),) = platform_layouts("shadps4", profile, standalone_set(config))
|
||||
with self.subTest(platform=platform):
|
||||
self.assertEqual(
|
||||
{(f.get("root"), f["standalone_path"].rsplit("/", 1)[0]) for f in view["files"]},
|
||||
{("configs", "shadps4/user/sys_modules"), ("configs", "shadps4/user/fonts/font")},
|
||||
)
|
||||
|
||||
def test_the_installer_follows_from_bios_to_the_configs_folder(self):
|
||||
import install
|
||||
|
||||
manifest = {"configs_directory": {"bios_folder": "bios", "from_bios": "../system/configs"}}
|
||||
install._check_relative_directories(manifest)
|
||||
files = [{"dest": "shadps4/user/sys_modules/libSceNgs2.sprx", "root": "configs"}]
|
||||
bios = Path("/userdata/bios")
|
||||
kept, left = install.place_relative_entries(files, bios, manifest, "x", say=False)
|
||||
self.assertEqual((len(kept), left), (1, []))
|
||||
self.assertEqual(
|
||||
install._entry_place(bios, kept[0])[0], Path("/userdata/system/configs")
|
||||
)
|
||||
kept, left = install.place_relative_entries(
|
||||
files, Path("/elsewhere/firmware"), manifest, "x", say=False)
|
||||
self.assertEqual((kept, [o["reason"] for o in left]), ([], ["no_configs_directory"]))
|
||||
for value in ("../C:x", "../../../x", "../system/../x"):
|
||||
with self.subTest(from_bios=value), self.assertRaises(ValueError):
|
||||
install._check_relative_directories(
|
||||
{"configs_directory": {"bios_folder": "bios", "from_bios": value}})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -383,10 +383,10 @@ class AProgramRunFromItsFolder(unittest.TestCase):
|
||||
import install
|
||||
|
||||
manifest = self.manifest("romsplat")
|
||||
install._check_roms_directory(manifest)
|
||||
install._check_relative_directories(manifest)
|
||||
files = list(manifest["files"])
|
||||
card = self.root / "card" / "roms" / "bios"
|
||||
kept, left = install.place_roms_entries(files, card, manifest, "romsplat", say=False)
|
||||
kept, left = install.place_relative_entries(files, card, manifest, "romsplat", say=False)
|
||||
self.assertEqual((len(kept), left), (2, []))
|
||||
self.assertEqual(
|
||||
install._entry_path(card, kept[0]),
|
||||
@@ -394,22 +394,22 @@ class AProgramRunFromItsFolder(unittest.TestCase):
|
||||
if install._entry_path(card, kept[0]).is_absolute() else card.parent / kept[0]["dest"],
|
||||
)
|
||||
other = self.root / "elsewhere"
|
||||
kept, left = install.place_roms_entries(files, other, manifest, "romsplat", say=False)
|
||||
kept, left = install.place_relative_entries(files, other, manifest, "romsplat", say=False)
|
||||
self.assertEqual(kept, [])
|
||||
self.assertEqual({o["reason"] for o in left}, {"no_roms_directory"})
|
||||
with self.assertRaises(ValueError):
|
||||
install._check_roms_directory({"files": files})
|
||||
install._check_relative_directories({"files": files})
|
||||
# A roms root beside the BIOS folder (Batocera's ../roms).
|
||||
beside = {**manifest, "roms_directory": {"bios_folder": "bios", "from_bios": "../roms"}}
|
||||
install._check_roms_directory(beside)
|
||||
install._check_relative_directories(beside)
|
||||
disk = self.root / "userdata" / "bios"
|
||||
install.place_roms_entries(files, disk, beside, "romsplat", say=False)
|
||||
install.place_relative_entries(files, disk, beside, "romsplat", say=False)
|
||||
self.assertEqual(install._entry_place(disk, kept_entry := files[0])[0],
|
||||
self.root / "userdata" / "roms")
|
||||
self.assertEqual(kept_entry["root"], "roms")
|
||||
for climbing in ("roms", "../roms/..", "/storage/roms"):
|
||||
with self.subTest(from_bios=climbing), self.assertRaises(ValueError):
|
||||
install._check_roms_directory(
|
||||
install._check_relative_directories(
|
||||
{**manifest, "roms_directory": {"bios_folder": "bios", "from_bios": climbing}}
|
||||
)
|
||||
|
||||
|
||||
@@ -186,8 +186,10 @@ class TheInstalledCommandIsTheInstaller(Built):
|
||||
|
||||
def _same_answers(self, python: str, name: str) -> None:
|
||||
command = self._installed(python, name)
|
||||
# `retrobios` and `python -m retrobios` both run install.py.
|
||||
# `retrobios` and `python -m retrobios` both run install.py, whose
|
||||
# examples then name the package command.
|
||||
launchers = ([str(command)], [str(command.with_name("python")), "-m", "retrobios"])
|
||||
as_package = {**os.environ, "RETROBIOS_BOOTSTRAP": "pypi"}
|
||||
for launcher in launchers:
|
||||
for args in (["--help"], ["--version"], ["--no-such-option"]):
|
||||
with self.subTest(python=name, launcher=launcher[-1], args=args):
|
||||
@@ -195,7 +197,7 @@ class TheInstalledCommandIsTheInstaller(Built):
|
||||
text=True, check=False, cwd=self.scratch)
|
||||
script = subprocess.run([python, str(REPO_ROOT / "install.py"), *args],
|
||||
capture_output=True, text=True, check=False,
|
||||
cwd=self.scratch)
|
||||
cwd=self.scratch, env=as_package)
|
||||
self.assertEqual(
|
||||
(packaged.returncode, packaged.stdout, packaged.stderr),
|
||||
(script.returncode, script.stdout, script.stderr))
|
||||
@@ -203,6 +205,20 @@ class TheInstalledCommandIsTheInstaller(Built):
|
||||
def test_this_python(self):
|
||||
self._same_answers(sys.executable, "venv-current")
|
||||
|
||||
def test_a_hint_gives_back_the_package_command(self):
|
||||
"""Nothing detected, the example said `python3 install.py`."""
|
||||
command = self._installed(sys.executable, "venv-hint")
|
||||
home = self.scratch / "empty-home"
|
||||
home.mkdir()
|
||||
env = {**os.environ, "HOME": str(home), "USERPROFILE": str(home),
|
||||
"RETROBIOS_INSTALL_OFFLINE": "1"}
|
||||
env.pop("RETROBIOS_BOOTSTRAP", None)
|
||||
run = subprocess.run([str(command), "--yes"], capture_output=True, text=True,
|
||||
check=False, cwd=self.scratch, env=env)
|
||||
out = run.stdout + run.stderr
|
||||
self.assertIn("retrobios --platform retroarch --dest", out)
|
||||
self.assertNotIn("install.py --platform", out)
|
||||
|
||||
def test_python_38(self):
|
||||
python = _python38()
|
||||
if python is None:
|
||||
|
||||
Reference in new issue
Block a user