mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-11 12:33:53 -06:00
RetroPie builds amiberry 5.7.2 and 6.3.3, one per board, both in $md_inst of its amiberry package. Each build recorded roms/kick.rom and whdboot/AmiQuit in the emulator's folder apart, and install/retropie.json listed both destinations twice. A file read outside the BIOS tree is now keyed by the folder the platform installs the listed name into, and one omission names every core reading it.
497 lines
24 KiB
Python
497 lines
24 KiB
Python
"""A platform's launcher moves where an emulator's file lands.
|
|
|
|
A profile's ``path`` is where the emulator's code reads a file. UAE4All2
|
|
reads kickstarts/<name> below its working directory, and RetroPie starts it
|
|
in its install directory, whose kickstarts/ it links to BIOS/amiga. The
|
|
pack, the manifest and verify all put the file in amiga/ on that platform,
|
|
and a platform with no such record keeps the profile's layout.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import tests # noqa: F401 offline, scratch under tmp/, the suite's own cache
|
|
|
|
import hashlib
|
|
import io
|
|
import shutil
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
import zipfile
|
|
from contextlib import redirect_stdout
|
|
from pathlib import Path
|
|
|
|
import yaml
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
|
|
|
from common import ( # noqa: E402
|
|
_emulator_profiles_cache,
|
|
load_emulator_profiles,
|
|
load_platform_config,
|
|
platform_layouts,
|
|
standalone_set,
|
|
)
|
|
from generate_pack import generate_manifest, generate_pack # noqa: E402
|
|
from verify import verify_platform # noqa: E402
|
|
|
|
WORKING_DIRECTORY = {
|
|
"emulator": "kick", "build": "standalone", "kind": "passes_path", "names": [],
|
|
"search_dir": "$md_inst", "passes_to": "working directory of $romdir/amiga/+Start.sh",
|
|
"source_ref": "kick.sh:90",
|
|
}
|
|
LINK = {
|
|
"emulator": "kick", "build": "standalone", "kind": "links", "names": [],
|
|
"search_dir": "$biosdir/amiga", "passes_to": "$md_inst/kickstarts is a symlink to it",
|
|
"source_ref": "kick.sh:79",
|
|
}
|
|
|
|
|
|
class ALinkMovesTheDestination(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
_emulator_profiles_cache.clear()
|
|
(REPO_ROOT / "tmp").mkdir(exist_ok=True)
|
|
self.root = Path(tempfile.mkdtemp(dir=REPO_ROOT / "tmp"))
|
|
self.addCleanup(shutil.rmtree, self.root, True)
|
|
bios = self.root / "bios" / "Commodore" / "Amiga"
|
|
bios.mkdir(parents=True)
|
|
files, by_md5, by_name = {}, {}, {}
|
|
for name, data in (("kick13.rom", b"KICKSTART 1.3"), ("kick.rom", b"CUSTOM KICKSTART")):
|
|
(bios / name).write_bytes(data)
|
|
sha1 = hashlib.sha1(data).hexdigest()
|
|
md5 = hashlib.md5(data).hexdigest()
|
|
files[sha1] = {"path": str(bios / name), "name": name, "sha1": sha1,
|
|
"md5": md5, "crc32": "", "size": len(data)}
|
|
by_md5[md5] = sha1
|
|
by_name[name] = [sha1]
|
|
self.db = {
|
|
"files": files,
|
|
"indexes": {"by_md5": by_md5, "by_name": by_name, "by_crc32": {},
|
|
"by_path_suffix": {}},
|
|
}
|
|
kick13 = by_name["kick13.rom"][0]
|
|
self.platforms = self.root / "platforms"
|
|
self.emulators = self.root / "emulators"
|
|
self.platforms.mkdir()
|
|
self.emulators.mkdir()
|
|
for name, launchers in (("linkplat", [WORKING_DIRECTORY, LINK]), ("plainplat", [])):
|
|
(self.platforms / f"{name}.yml").write_text(yaml.safe_dump({
|
|
"platform": name, "version": "1", "verification_mode": "existence",
|
|
"base_destination": "BIOS", "cores": ["kick"], "standalone_cores": ["kick"],
|
|
"launchers": launchers,
|
|
"systems": {"commodore-amiga": {"files": []}},
|
|
}))
|
|
(self.platforms / "_registry.yml").write_text(yaml.safe_dump({"platforms": {
|
|
"linkplat": {"config": "linkplat.yml"}, "plainplat": {"config": "plainplat.yml"},
|
|
}}))
|
|
(self.emulators / "kick.yml").write_text(yaml.safe_dump({
|
|
"emulator": "Kick", "type": "standalone", "cores": ["kick"],
|
|
"systems": ["commodore-amiga"],
|
|
"standalone_directories": ["getcwd"],
|
|
"files": [
|
|
{"name": "kick13.rom", "path": "kickstarts/kick13.rom",
|
|
"sha1": kick13, "required": False},
|
|
# Read in the working directory itself, which the platform
|
|
# links nowhere in the BIOS tree.
|
|
{"name": "kick.rom", "path": "kick.rom", "required": False},
|
|
],
|
|
}))
|
|
|
|
def pack_names(self, platform: str, name: str = "kick13.rom") -> list[str]:
|
|
with redirect_stdout(io.StringIO()):
|
|
zip_path = generate_pack(
|
|
platform, str(self.platforms), self.db, str(self.root / "bios"),
|
|
str(self.root / "out" / platform), emulators_dir=str(self.emulators),
|
|
offline=True,
|
|
)
|
|
with zipfile.ZipFile(zip_path) as zf:
|
|
return [n for n in zf.namelist() if n.rsplit("/", 1)[-1] == name]
|
|
|
|
def manifest(self, platform: str) -> dict:
|
|
with redirect_stdout(io.StringIO()):
|
|
return generate_manifest(
|
|
platform, str(self.platforms), self.db, str(self.root / "bios"),
|
|
str(self.platforms / "_registry.yml"),
|
|
emulators_dir=str(self.emulators), offline=True,
|
|
)
|
|
|
|
def manifest_dests(self, platform: str, name: str = "kick13.rom") -> list[str]:
|
|
return [f["dest"] for f in self.manifest(platform)["files"]
|
|
if f["dest"].rsplit("/", 1)[-1] == name]
|
|
|
|
def verify_entries(self, platform: str, name: str = "kick13.rom") -> list[dict]:
|
|
config = load_platform_config(platform, str(self.platforms))
|
|
result = verify_platform(config, self.db, str(self.emulators))
|
|
return [u for u in result["undeclared_files"] if u["name"] == name]
|
|
|
|
def verify_paths(self, platform: str) -> list[str]:
|
|
return [u["path"] for u in self.verify_entries(platform)]
|
|
|
|
def test_the_link_moves_it_in_pack_manifest_and_verify(self) -> None:
|
|
self.assertEqual(self.pack_names("linkplat"), ["amiga/kick13.rom"])
|
|
self.assertEqual(self.manifest_dests("linkplat"), ["amiga/kick13.rom"])
|
|
self.assertEqual(self.verify_paths("linkplat"), ["amiga/kick13.rom"])
|
|
|
|
def test_a_platform_without_the_record_is_unchanged(self) -> None:
|
|
self.assertEqual(self.pack_names("plainplat"), ["kickstarts/kick13.rom"])
|
|
self.assertEqual(self.manifest_dests("plainplat"), ["kickstarts/kick13.rom"])
|
|
self.assertEqual(self.verify_paths("plainplat"), ["kickstarts/kick13.rom"])
|
|
|
|
def test_a_file_outside_every_binding_is_named_never_placed(self) -> None:
|
|
self.assertEqual(self.pack_names("linkplat", "kick.rom"), [])
|
|
manifest = self.manifest("linkplat")
|
|
self.assertEqual(self.manifest_dests("linkplat", "kick.rom"), [])
|
|
omitted = [o for o in manifest["omitted_files"] if o["name"] == "kick.rom"]
|
|
self.assertEqual(len(omitted), 1)
|
|
self.assertEqual(
|
|
(omitted[0]["dest"], omitted[0]["root"], omitted[0]["reason"], omitted[0]["cores"]),
|
|
("kick.rom", "emulator", "outside_bios_folder", ["kick"]),
|
|
)
|
|
(entry,) = self.verify_entries("linkplat", "kick.rom")
|
|
self.assertFalse(entry["in_pack"])
|
|
self.assertIn("outside the BIOS tree", entry["omitted"])
|
|
|
|
def test_a_root_file_of_another_core_is_not_claimed_by_it(self) -> None:
|
|
# Another core reads kick.rom at the BIOS root: kick's own kick.rom,
|
|
# in its working directory, is a different place.
|
|
for name in ("linkplat", "plainplat"):
|
|
path = self.platforms / f"{name}.yml"
|
|
data = yaml.safe_load(path.read_text())
|
|
data["cores"] = ["kick", "other"]
|
|
path.write_text(yaml.safe_dump(data))
|
|
(self.emulators / "other.yml").write_text(yaml.safe_dump({
|
|
"emulator": "Other", "type": "libretro", "cores": ["other"],
|
|
"systems": ["commodore-amiga"],
|
|
"files": [{"name": "kick.rom", "path": "kick.rom", "required": False}],
|
|
}))
|
|
_emulator_profiles_cache.clear()
|
|
manifest = self.manifest("linkplat")
|
|
(held,) = [f for f in manifest["files"] if f["dest"] == "kick.rom"]
|
|
self.assertEqual(held["cores"], ["other"])
|
|
omitted = [o for o in manifest["omitted_files"] if o["name"] == "kick.rom"]
|
|
self.assertEqual([(o["root"], o["cores"]) for o in omitted], [("emulator", ["kick"])])
|
|
|
|
def test_two_builds_in_one_folder_are_one_omission(self) -> None:
|
|
# RetroPie builds amiberry 5.7.2 and 6.3.3, one per board, both in
|
|
# $md_inst of its amiberry package: one place, read by both.
|
|
data = yaml.safe_load((self.emulators / "kick.yml").read_text())
|
|
for key in ("kick-1", "kick-2"):
|
|
(self.emulators / f"{key}.yml").write_text(
|
|
yaml.safe_dump({**data, "built_by": ["linkplat"]}))
|
|
_emulator_profiles_cache.clear()
|
|
omitted = [o for o in self.manifest("linkplat")["omitted_files"]
|
|
if o["name"] == "kick.rom"]
|
|
self.assertEqual([(o["root"], o["cores"]) for o in omitted],
|
|
[("emulator", ["kick-1", "kick-2"])])
|
|
|
|
def test_the_installer_accepts_the_omission(self) -> None:
|
|
import install
|
|
|
|
manifest = self.manifest("linkplat")
|
|
omitted = next(o for o in manifest["omitted_files"] if o["name"] == "kick.rom")
|
|
install._check_omission(omitted, omitted["dest"])
|
|
# Two emulators reading kick.rom in their own folders read two files.
|
|
self.assertNotEqual(
|
|
install._entry_key(omitted), install._entry_key({**omitted, "cores": ["other"]})
|
|
)
|
|
self.assertNotEqual(install._entry_key(omitted), install._entry_key({"dest": "kick.rom"}))
|
|
self.assertEqual(install.OMISSION_REASONS[omitted["reason"]], "read outside the BIOS folder")
|
|
with self.assertRaises(ValueError):
|
|
install._check_root({"root": "emulator"}, "kick.rom")
|
|
install._check_root(omitted, "kick.rom", omitted=True)
|
|
|
|
def test_without_relocation_the_root_file_stays_at_the_root(self) -> None:
|
|
self.assertEqual(self.pack_names("plainplat", "kick.rom"), ["kick.rom"])
|
|
|
|
def test_the_link_alone_binds_nothing(self) -> None:
|
|
# Without the working directory the link's $md_inst is not the
|
|
# directory the profile's paths are relative to.
|
|
config = {"standalone_cores": ["kick"], "launchers": [LINK]}
|
|
profile = load_emulator_profiles(str(self.emulators))["kick"]
|
|
(view, standalone), = platform_layouts("kick", profile, standalone_set(config))
|
|
self.assertTrue(standalone)
|
|
self.assertNotIn("standalone_path", view["files"][0])
|
|
|
|
|
|
class UAE4AllOnRetroPie(unittest.TestCase):
|
|
"""The real profile against the real launcher records."""
|
|
|
|
def test_kickstarts_land_in_amiga(self) -> None:
|
|
profile = load_emulator_profiles(str(REPO_ROOT / "emulators"))["uae4all"]
|
|
config = load_platform_config("retropie", str(REPO_ROOT / "platforms"))
|
|
(view, standalone), = platform_layouts("uae4all", profile, standalone_set(config))
|
|
self.assertTrue(standalone)
|
|
for entry in view["files"]:
|
|
if entry["path"].startswith("kickstarts/"):
|
|
self.assertEqual(
|
|
entry["standalone_path"], "amiga/" + entry["path"].removeprefix("kickstarts/")
|
|
)
|
|
else:
|
|
# kick.rom and rom.key in the working directory, $md_inst
|
|
self.assertEqual(entry.get("unplaced"), "outside_bios_folder", entry["path"])
|
|
|
|
|
|
|
|
PROGRAM = b"PROGRAM BINARY"
|
|
PROGRAM_DATA = b"PROGRAM DATA"
|
|
|
|
|
|
class AProgramRunFromItsFolder(unittest.TestCase):
|
|
"""A program that reads its files beside its executable.
|
|
|
|
The profile gives the layout of the program's archive (prog/prog,
|
|
prog/prog.dat) and declares the executable directory. A platform that
|
|
runs prog/prog from its BIOS folder keeps that layout, one that runs it
|
|
from roms/prog places the program folder below its roms folder, and one
|
|
that runs it from an emulator folder names its files as not placeable.
|
|
"""
|
|
|
|
def setUp(self) -> None:
|
|
_emulator_profiles_cache.clear()
|
|
(REPO_ROOT / "tmp").mkdir(exist_ok=True)
|
|
self.root = Path(tempfile.mkdtemp(dir=REPO_ROOT / "tmp"))
|
|
self.addCleanup(shutil.rmtree, self.root, True)
|
|
bios = self.root / "bios" / "Maker" / "Prog"
|
|
bios.mkdir(parents=True)
|
|
files, by_md5, by_name = {}, {}, {}
|
|
for name, data in (("prog", PROGRAM), ("prog.dat", PROGRAM_DATA)):
|
|
(bios / name).write_bytes(data)
|
|
sha1 = hashlib.sha1(data).hexdigest()
|
|
md5 = hashlib.md5(data).hexdigest()
|
|
files[sha1] = {"path": str(bios / name), "name": name, "sha1": sha1,
|
|
"md5": md5, "crc32": "", "size": len(data)}
|
|
by_md5[md5] = sha1
|
|
by_name[name] = [sha1]
|
|
self.db = {"files": files, "indexes": {
|
|
"by_md5": by_md5, "by_name": by_name, "by_crc32": {}, "by_path_suffix": {}}}
|
|
self.platforms = self.root / "platforms"
|
|
self.emulators = self.root / "emulators"
|
|
self.platforms.mkdir()
|
|
self.emulators.mkdir()
|
|
run = {"emulator": "prog", "build": "standalone", "kind": "fixed_name",
|
|
"passes_to": "exec", "source_ref": "start_prog.sh:9"}
|
|
launchers = {
|
|
"biosplat": [{**run, "names": ["prog/prog"], "search_dir": "/userdata/bios"}],
|
|
"romsplat": [{**run, "names": ["prog"], "search_dir": "/storage/roms/prog"}],
|
|
"exeplat": [{**run, "names": ["prog"], "search_dir": "<emulator folder>"}],
|
|
"emuplat": [{**run, "names": ["prog"], "search_dir": '<GetPath("prog")>'}],
|
|
"plainplat": [],
|
|
}
|
|
for name, records in launchers.items():
|
|
(self.platforms / f"{name}.yml").write_text(yaml.safe_dump({
|
|
"platform": name, "version": "1", "verification_mode": "existence",
|
|
"base_destination": "bios", "cores": ["prog"], "standalone_cores": ["prog"],
|
|
"launchers": records,
|
|
"systems": {"prog-system": {"files": []}},
|
|
}))
|
|
(self.platforms / "_registry.yml").write_text(yaml.safe_dump({"platforms": {
|
|
**{name: {"config": f"{name}.yml"} for name in launchers},
|
|
"emuplat": {"config": "emuplat.yml", "emulators_directory": {
|
|
"launcher_spelling": '<GetPath("{key}")>', "bios_folder": "bios",
|
|
"searched": ["frontend/{key}", "emulators/{key}"],
|
|
"default": "emulators/{key}", "config": "frontend/launcher.cfg"}},
|
|
}}))
|
|
# The roms root as the OS's own source records it (romfolders).
|
|
(self.platforms / "romfolders").mkdir()
|
|
(self.platforms / "romfolders" / "romsplat.yml").write_text(yaml.safe_dump({
|
|
"setup": "romsplat", "platform": "romsplat",
|
|
"roms_root": {"path": "/storage/roms", "from_bios": "..", "source_ref": "os x.mk:1"},
|
|
"folders": [],
|
|
}))
|
|
# A list that installs into the roms root itself, its BIOS folder
|
|
# one of the folders there (AmberELEC's /storage/roms).
|
|
(self.platforms / "rootplat.yml").write_text(yaml.safe_dump({
|
|
"platform": "rootplat", "version": "1", "verification_mode": "existence",
|
|
"base_destination": "", "cores": ["prog"], "standalone_cores": ["prog"],
|
|
"launchers": [{**run, "names": ["prog"], "search_dir": "/storage/roms/prog"}],
|
|
"systems": {"prog-system": {"files": [
|
|
{"name": "own.bin", "destination": "bios/own.bin", "required": False},
|
|
]}},
|
|
}))
|
|
registry = yaml.safe_load((self.platforms / "_registry.yml").read_text())
|
|
registry["platforms"]["rootplat"] = {"config": "rootplat.yml"}
|
|
(self.platforms / "_registry.yml").write_text(yaml.safe_dump(registry))
|
|
(self.platforms / "romfolders" / "rootplat.yml").write_text(yaml.safe_dump({
|
|
"setup": "rootplat", "platform": "rootplat",
|
|
"roms_root": {"path": "/storage/roms", "from_bios": "..", "source_ref": "os x.mk:1"},
|
|
"folders": [],
|
|
}))
|
|
(self.emulators / "prog.yml").write_text(yaml.safe_dump({
|
|
"emulator": "Prog", "type": "standalone", "cores": ["prog"],
|
|
"systems": ["prog-system"],
|
|
"standalone_directories": ["executable directory"],
|
|
"files": [
|
|
{"name": "prog", "path": "prog/prog", "sha1": by_name["prog"][0],
|
|
"required": True},
|
|
{"name": "prog.dat", "path": "prog/prog.dat",
|
|
"sha1": by_name["prog.dat"][0], "required": True},
|
|
],
|
|
}))
|
|
|
|
def pack(self, platform: str) -> list[str]:
|
|
with redirect_stdout(io.StringIO()):
|
|
zip_path = generate_pack(
|
|
platform, str(self.platforms), self.db, str(self.root / "bios"),
|
|
str(self.root / "out" / platform), emulators_dir=str(self.emulators),
|
|
offline=True,
|
|
)
|
|
with zipfile.ZipFile(zip_path) as zf:
|
|
return sorted(n for n in zf.namelist() if n.split("/")[-1].startswith("prog"))
|
|
|
|
def manifest(self, platform: str) -> dict:
|
|
with redirect_stdout(io.StringIO()):
|
|
return generate_manifest(
|
|
platform, str(self.platforms), self.db, str(self.root / "bios"),
|
|
str(self.platforms / "_registry.yml"),
|
|
emulators_dir=str(self.emulators), offline=True,
|
|
)
|
|
|
|
def verify(self, platform: str) -> list[dict]:
|
|
config = load_platform_config(platform, str(self.platforms))
|
|
result = verify_platform(config, self.db, str(self.emulators))
|
|
return [u for u in result["undeclared_files"] if u["name"].startswith("prog")]
|
|
|
|
def test_run_from_the_bios_folder_keeps_the_layout(self) -> None:
|
|
for platform in ("biosplat", "plainplat"):
|
|
with self.subTest(platform=platform):
|
|
self.assertEqual(self.pack(platform), ["prog/prog", "prog/prog.dat"])
|
|
self.assertEqual(
|
|
sorted(f["dest"] for f in self.manifest(platform)["files"]),
|
|
["prog/prog", "prog/prog.dat"],
|
|
)
|
|
|
|
def test_run_from_the_roms_folder_is_installed_there(self) -> None:
|
|
self.assertEqual(self.pack("romsplat"), [])
|
|
manifest = self.manifest("romsplat")
|
|
self.assertEqual(
|
|
sorted((f["dest"], f.get("root")) for f in manifest["files"]),
|
|
[("prog/prog", "roms"), ("prog/prog.dat", "roms")],
|
|
)
|
|
self.assertEqual(manifest["roms_directory"], {"bios_folder": "bios", "from_bios": ".."})
|
|
entries = self.verify("romsplat")
|
|
self.assertEqual(sorted(u["path"] for u in entries), ["prog/prog", "prog/prog.dat"])
|
|
self.assertTrue(all(u["in_pack"] is False for u in entries))
|
|
|
|
def test_a_roms_root_inside_the_install_tree_is_a_plain_dest(self) -> None:
|
|
self.assertEqual(self.pack("rootplat"), ["prog/prog", "prog/prog.dat"])
|
|
manifest = self.manifest("rootplat")
|
|
self.assertEqual(
|
|
sorted((f["dest"], f.get("root")) for f in manifest["files"] if "prog" in f["dest"]),
|
|
[("prog/prog", None), ("prog/prog.dat", None)],
|
|
)
|
|
self.assertNotIn("roms_directory", manifest)
|
|
|
|
def test_run_from_an_emulator_folder_is_named(self) -> None:
|
|
self.assertEqual(self.pack("exeplat"), [])
|
|
manifest = self.manifest("exeplat")
|
|
self.assertEqual([f["dest"] for f in manifest["files"]], [])
|
|
self.assertEqual(
|
|
sorted((o["dest"], o["root"], o["reason"]) for o in manifest["omitted_files"]),
|
|
[("prog/prog", "emulator", "outside_bios_folder"),
|
|
("prog/prog.dat", "emulator", "outside_bios_folder")],
|
|
)
|
|
|
|
def test_the_installer_finds_the_roms_folder_above_the_bios_folder(self) -> None:
|
|
import install
|
|
|
|
manifest = self.manifest("romsplat")
|
|
install._check_relative_directories(manifest)
|
|
files = list(manifest["files"])
|
|
card = self.root / "card" / "roms" / "bios"
|
|
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]),
|
|
(card.parent / kept[0]["dest"]).resolve()
|
|
if install._entry_path(card, kept[0]).is_absolute() else card.parent / kept[0]["dest"],
|
|
)
|
|
other = self.root / "elsewhere"
|
|
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_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_relative_directories(beside)
|
|
disk = self.root / "userdata" / "bios"
|
|
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_relative_directories(
|
|
{**manifest, "roms_directory": {"bios_folder": "bios", "from_bios": climbing}}
|
|
)
|
|
|
|
def test_run_from_an_emulator_folder_the_registry_describes(self) -> None:
|
|
self.assertEqual(self.pack("emuplat"), [])
|
|
manifest = self.manifest("emuplat")
|
|
self.assertEqual(
|
|
sorted((f["dest"], f.get("root")) for f in manifest["files"]),
|
|
[("prog/prog", "emulators"), ("prog/prog.dat", "emulators")],
|
|
)
|
|
self.assertEqual(manifest["emulators_directory"]["default"], "emulators/{key}")
|
|
self.assertTrue(all(u["in_pack"] is False for u in self.verify("emuplat")))
|
|
|
|
def test_the_installer_follows_the_launcher_search(self) -> None:
|
|
import install
|
|
|
|
manifest = self.manifest("emuplat")
|
|
install._check_emulators_directory(manifest)
|
|
files = list(manifest["files"])
|
|
home = self.root / "Frontend"
|
|
bios = home / "bios"
|
|
bios.mkdir(parents=True)
|
|
# Not the platform's folder: no launcher config beside it.
|
|
kept, left = install.place_emulator_entries(files, bios, manifest, "emuplat", say=False)
|
|
self.assertEqual((kept, {o["reason"] for o in left}), ([], {"no_emulators_directory"}))
|
|
(home / "frontend").mkdir()
|
|
(home / "frontend" / "launcher.cfg").write_text("other=x\n")
|
|
kept, left = install.place_emulator_entries(files, bios, manifest, "emuplat", say=False)
|
|
self.assertEqual((len(kept), left), (2, []))
|
|
entry = next(f for f in kept if f["dest"] == "prog/prog")
|
|
self.assertEqual(install._entry_place(bios, entry), (home / "emulators" / "prog", "prog"))
|
|
# The first searched place that exists wins.
|
|
(home / "frontend" / "prog").mkdir()
|
|
install.place_emulator_entries(files, bios, manifest, "emuplat", say=False)
|
|
self.assertEqual(install._entry_place(bios, entry)[0], home / "frontend" / "prog")
|
|
# A key the launcher config sets moves the folder: left out.
|
|
(home / "frontend" / "launcher.cfg").write_text("prog=D:\\Prog\n")
|
|
kept, left = install.place_emulator_entries(files, bios, manifest, "emuplat", say=False)
|
|
self.assertEqual((kept, len(left)), ([], 2))
|
|
|
|
|
|
class EachProgramItsOwnProfile(unittest.TestCase):
|
|
"""PICO-8 and Voxatron are two programs: a platform running one gets its files only."""
|
|
|
|
def test_only_the_platforms_that_run_voxatron_get_it(self) -> None:
|
|
from common import list_registered_platforms, resolve_platform_cores
|
|
|
|
profiles = load_emulator_profiles(str(REPO_ROOT / "emulators"))
|
|
runs = {
|
|
name: {k for k in ("lexaloffle", "voxatron")
|
|
if k in resolve_platform_cores(
|
|
load_platform_config(name, str(REPO_ROOT / "platforms")), profiles)}
|
|
for name in list_registered_platforms(str(REPO_ROOT / "platforms"))
|
|
}
|
|
self.assertEqual(
|
|
{name for name, found in runs.items() if "voxatron" in found}, {"batocera", "knulli"}
|
|
)
|
|
self.assertTrue(all("lexaloffle" in runs[name] for name in (
|
|
"batocera", "knulli", "rocknix", "retrodeck", "retrobat", "recalbox")))
|
|
|
|
def test_a_standalone_emulator_is_run(self) -> None:
|
|
from common import resolve_platform_cores
|
|
|
|
profiles = {"emu": {"emulator": "Emu", "type": "standalone", "cores": ["emu"],
|
|
"systems": ["sys"], "files": []}}
|
|
config = {"cores": ["other"], "standalone_cores": ["emu"], "systems": {}}
|
|
self.assertEqual(resolve_platform_cores(config, profiles), {"emu"})
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|