mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-26 05:37:20 -05:00
feat: check standalone paths under data_root
This commit is contained in:
1 parent
64e559d2c3
commit
fe34484408
1 file changed
+77
@@ -0,0 +1,77 @@
|
||||
"""A standalone app's paths are the folders its code opens below data_root.
|
||||
|
||||
The site prints data_root joined to each entry's standalone destination, so a
|
||||
frontend's layout written there (Batocera's bios/switch/) sends the reader to a
|
||||
folder the app never reads. A frontend folder name is allowed only where the
|
||||
app's own code joins that folder to its data root.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
FRONTEND_FOLDERS = ("bios/", "switch/", "system/")
|
||||
|
||||
# Profiles whose code reads one of those folders below its data root.
|
||||
CODE_READS = {
|
||||
# BIOS folder of the data directory, the PCSX2 layout it carries
|
||||
# (aethersx2 profile notes, AetherSX2-release MacOS/AetherSX2).
|
||||
"aethersx2": {"bios/"},
|
||||
# BIOS_FOLDER under gui_data_folder() (src/core/fds.c:305, src/core/cheat.c:105).
|
||||
"punes": {"bios/"},
|
||||
# KeysDir "system" under BaseDirPath
|
||||
# (src/Ryujinx.Common/Configuration/AppDataManager.cs:17, 229).
|
||||
"ryujinx": {"system/"},
|
||||
"kenji-nx": {"system/"},
|
||||
# PathSystem is the system folder beside DOSBoxPure.cfg (main.cpp:2032-2039).
|
||||
"dosbox-pure-unleashed": {"system/"},
|
||||
}
|
||||
|
||||
|
||||
def _standalone_profiles() -> dict[str, dict]:
|
||||
profiles = {}
|
||||
for path in sorted((REPO_ROOT / "emulators").glob("*.yml")):
|
||||
with path.open(encoding="utf-8") as fh:
|
||||
profile = yaml.safe_load(fh)
|
||||
if isinstance(profile, dict) and profile.get("data_root") \
|
||||
and profile.get("type") == "standalone":
|
||||
profiles[path.stem] = profile
|
||||
return profiles
|
||||
|
||||
|
||||
def _destination(entry: dict) -> str:
|
||||
return str(entry.get("standalone_path") or entry.get("path") or "")
|
||||
|
||||
|
||||
class StandaloneDestinations(unittest.TestCase):
|
||||
def test_no_frontend_folder_below_data_root(self):
|
||||
wrong = []
|
||||
for name, profile in _standalone_profiles().items():
|
||||
allowed = CODE_READS.get(name, set())
|
||||
for entry in profile.get("files") or []:
|
||||
if not isinstance(entry, dict):
|
||||
continue
|
||||
dest = _destination(entry)
|
||||
for folder in FRONTEND_FOLDERS:
|
||||
if dest.startswith(folder) and folder not in allowed:
|
||||
wrong.append(f"{name}: {entry.get('name')} at {dest}")
|
||||
self.assertEqual(wrong, [])
|
||||
|
||||
def test_every_allowed_folder_is_read(self):
|
||||
profiles = _standalone_profiles()
|
||||
unused = []
|
||||
for name, folders in CODE_READS.items():
|
||||
dests = [_destination(e) for e in profiles.get(name, {}).get("files") or []
|
||||
if isinstance(e, dict)]
|
||||
unused += [f"{name}: {folder}" for folder in sorted(folders)
|
||||
if not any(d.startswith(folder) for d in dests)]
|
||||
self.assertEqual(unused, [])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user