mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-21 11:19:11 -05:00
fix: stop when a profile does not parse
generate_db wrote a database without the aliases of a profile it could not read, and the MAME and FBNeo refreshes passed without it. Each now names the file and exits non-zero.
This commit is contained in:
1 parent
9a417dde2a
commit
89ac31780b
5 files changed
+79
-7
No files matched your search
+24
-3
@@ -375,7 +375,13 @@ def main() -> int:
|
||||
if preserved:
|
||||
print(f" Preserved {preserved} large file entries from existing database")
|
||||
|
||||
platform_aliases = _collect_all_aliases(files)
|
||||
try:
|
||||
platform_aliases = _collect_all_aliases(files)
|
||||
except UnreadableSource as exc:
|
||||
for source in exc.sources:
|
||||
print(f"Error: cannot read {source}", file=sys.stderr)
|
||||
print(f"Error: {args.output} not written", file=sys.stderr)
|
||||
return 1
|
||||
for sha1, name_list in platform_aliases.items():
|
||||
for alias_entry in name_list:
|
||||
if sha1 not in aliases:
|
||||
@@ -440,6 +446,14 @@ def _own_aliases(file_entry: dict, primary_names: set[str]) -> list[str]:
|
||||
]
|
||||
|
||||
|
||||
class UnreadableSource(Exception):
|
||||
"""Platform or profile files that do not parse, named one per line."""
|
||||
|
||||
def __init__(self, sources: list[str]) -> None:
|
||||
super().__init__("\n".join(sources))
|
||||
self.sources = sources
|
||||
|
||||
|
||||
def _collect_all_aliases(files: dict) -> dict:
|
||||
"""Collect alternate filenames from platform YAMLs, core-info, and known aliases.
|
||||
|
||||
@@ -464,6 +478,10 @@ def _collect_all_aliases(files: dict) -> dict:
|
||||
if name not in existing:
|
||||
aliases[matched_sha1].append({"name": name, "path": ""})
|
||||
|
||||
# A source that does not parse is reported and the run refused: written
|
||||
# without it, the database lost every alias the file proves and the
|
||||
# command exited 0 (dosbox-x's wqy fonts answered not_found).
|
||||
unreadable: list[str] = []
|
||||
platforms_dir = Path("platforms")
|
||||
if platforms_dir.is_dir():
|
||||
for platform_name in list_registered_platforms(
|
||||
@@ -474,7 +492,7 @@ def _collect_all_aliases(files: dict) -> dict:
|
||||
with open(config_file) as f:
|
||||
config = yaml_load(f) or {}
|
||||
except (yaml.YAMLError, OSError) as e:
|
||||
print(f"Warning: {config_file.name}: {e}", file=sys.stderr)
|
||||
unreadable.append(f"{config_file}: {e}")
|
||||
continue
|
||||
|
||||
for sys_id, system in config.get("systems", {}).items():
|
||||
@@ -513,7 +531,8 @@ def _collect_all_aliases(files: dict) -> dict:
|
||||
try:
|
||||
with open(emu_file) as f:
|
||||
emu_config = yaml_load(f) or {}
|
||||
except (yaml.YAMLError, OSError):
|
||||
except (yaml.YAMLError, OSError) as e:
|
||||
unreadable.append(f"{emu_file}: {e}")
|
||||
continue
|
||||
# A profile whose entries name each other (dosbox-x opens
|
||||
# MT32_CONTROL.ROM and CM32L_CONTROL.ROM alike, so each entry
|
||||
@@ -602,6 +621,8 @@ def _collect_all_aliases(files: dict) -> dict:
|
||||
for name in group:
|
||||
_add_alias(name, matched_sha1)
|
||||
|
||||
if unreadable:
|
||||
raise UnreadableSource(unreadable)
|
||||
return aliases
|
||||
|
||||
|
||||
|
||||
@@ -204,8 +204,10 @@ def _find_fbneo_profiles() -> list[Path]:
|
||||
for path in source_yaml_paths(EMULATORS_DIR):
|
||||
try:
|
||||
data = yaml_load(path.read_text(encoding="utf-8"))
|
||||
except (yaml.YAMLError, OSError):
|
||||
continue
|
||||
except (yaml.YAMLError, OSError) as exc:
|
||||
# Skipped, a broken fbneo.yml left the list and the refresh
|
||||
# reported "no matching emulator profiles found" with exit 0.
|
||||
raise RuntimeError(f"cannot read {path}: {exc}") from exc
|
||||
if not data or not isinstance(data, dict):
|
||||
continue
|
||||
upstream = data.get("upstream", "")
|
||||
|
||||
@@ -216,8 +216,10 @@ def _find_mame_profiles() -> list[Path]:
|
||||
and source.rstrip("/") in _MAME_TREES
|
||||
):
|
||||
profiles.append(path)
|
||||
except (yaml.YAMLError, OSError):
|
||||
continue
|
||||
except (yaml.YAMLError, OSError) as exc:
|
||||
# Skipped, a broken mame.yml left the list and the refresh
|
||||
# reported success without touching it.
|
||||
raise RuntimeError(f"cannot read {path}: {exc}") from exc
|
||||
return profiles
|
||||
|
||||
|
||||
|
||||
@@ -128,5 +128,30 @@ class CrossNamingStaysInItsProfile(unittest.TestCase):
|
||||
self.assertEqual([a["name"] for a in aliases["mt"]], ["MT32_CONTROL.ROM"])
|
||||
self.assertEqual([a["name"] for a in aliases["cm"]], ["CM32L_CONTROL.ROM"])
|
||||
|
||||
class AnUnreadableSourceStopsTheRun(unittest.TestCase):
|
||||
"""A profile with a YAML error was skipped in silence: the database was
|
||||
written without its aliases and the command exited 0."""
|
||||
|
||||
def test_the_file_is_named_and_nothing_collected(self):
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
import generate_db
|
||||
|
||||
tmp_root = Path(__file__).resolve().parents[1] / "tmp"
|
||||
tmp_root.mkdir(exist_ok=True)
|
||||
root = Path(tempfile.mkdtemp(dir=tmp_root))
|
||||
self.addCleanup(shutil.rmtree, root, True)
|
||||
previous = os.getcwd()
|
||||
os.chdir(root)
|
||||
self.addCleanup(os.chdir, previous)
|
||||
Path("emulators").mkdir()
|
||||
Path("emulators/broken.yml").write_text("files: [\n - name: a\n")
|
||||
with self.assertRaises(generate_db.UnreadableSource) as caught:
|
||||
generate_db._collect_all_aliases({})
|
||||
self.assertIn("broken.yml", str(caught.exception))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -774,5 +774,27 @@ class OneCloneDirectoryPerRun(unittest.TestCase):
|
||||
self.assertTrue(clone.name.endswith(f"-{os.getpid()}"))
|
||||
|
||||
|
||||
class ABrokenProfileStopsTheRefresh(unittest.TestCase):
|
||||
"""A profile with a YAML error left the list in silence: the refresh
|
||||
printed 'no matching emulator profiles found' and exited 0."""
|
||||
|
||||
def test_both_scrapers_name_it(self):
|
||||
from unittest import mock
|
||||
|
||||
from scripts.scraper import fbneo_hash_scraper, mame_hash_scraper
|
||||
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
Path(directory, "fbneo.yml").write_text("files: [\n - name: a\n")
|
||||
for module, attr, finder in (
|
||||
(fbneo_hash_scraper, "EMULATORS_DIR", "_find_fbneo_profiles"),
|
||||
(mame_hash_scraper, "_EMULATORS_DIR", "_find_mame_profiles"),
|
||||
):
|
||||
with self.subTest(scraper=module.__name__), \
|
||||
mock.patch.object(module, attr, Path(directory)), \
|
||||
self.assertRaises(RuntimeError) as caught:
|
||||
getattr(module, finder)()
|
||||
self.assertIn("fbneo.yml", str(caught.exception))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user