fix: one system scope for verify and the packs

A system the target leaves no core for was verified 0/0 with exit 0,
and generate_pack blamed the platform for lacking it. Both read
platform_scope_systems and system_scope_error now (findings 13, 14).
This commit is contained in:
Abdessamad Derraz committed 2026-11-05 11:47:03 +01:00
1 parent 9ae0ede21d
commit c7df790744
4 files changed
+106 -36

No files matched your search

+40
View File
@@ -2993,6 +2993,46 @@ def filter_systems_by_target(
}
def platform_scope_systems(
config: dict,
profiles: dict[str, dict],
target_cores: set[str] | None,
system_filter: list[str] | None = None,
) -> dict[str, dict]:
"""The systems a pack or a report of a platform covers.
Those a core of the target runs, then the ones a --system list keeps.
verify.py and generate_pack.py read this one function.
"""
plat_cores = resolve_platform_cores(config, profiles) if target_cores else None
systems = filter_systems_by_target(
config.get("systems", {}), profiles, target_cores, platform_cores=plat_cores
)
return select_systems(systems, system_filter) if system_filter else systems
def system_scope_error(
config: dict, platform_name: str, system_filter: list[str], target: str | None
) -> str:
"""Why a --system list leaves a platform no system.
A system the platform declares but no core of the target runs was
reported as a system the platform lacks, with a lookalike to try and a
listing that refuses --target.
"""
systems = config.get("systems", {})
declared = select_systems(systems, system_filter)
if declared and target:
return (
f"no core {platform_name} runs on target {target} emulates "
f"{', '.join(sorted(declared))}"
)
hint = suggest_names(
system_filter, sorted(systems), f"--platform {platform_name} --list-systems"
)
return f"{platform_name} has no system matching {', '.join(system_filter)}. {hint}"
def platform_declarations(
config: dict, db: dict, *, enrich: bool = True
) -> dict[str, list[tuple[int | None, str]]]:
+8 -25
View File
@@ -53,7 +53,8 @@ from common import (
list_platform_system_ids,
platform_system_names,
resolve_system_arg,
select_systems,
platform_scope_systems,
system_scope_error,
canonical_target_name,
platform_files_target,
list_registered_platforms,
@@ -753,31 +754,13 @@ def generate_pack(
target_cores=target_cores, source=source,
)
# Filter systems by target if specified
plat_cores = (
resolve_platform_cores(config, emu_profiles or {}) if target_cores else None
pack_systems = platform_scope_systems(
config, emu_profiles or {}, target_cores, system_filter
)
pack_systems = filter_systems_by_target(
config.get("systems", {}),
emu_profiles or {},
target_cores,
platform_cores=plat_cores,
)
if system_filter:
filtered = select_systems(pack_systems, system_filter)
if not filtered:
hint = suggest_names(
system_filter,
sorted(pack_systems),
f"--platform {platform_name} --list-systems",
)
print(
f" WARNING: {platform_name} has no system matching "
f"{', '.join(system_filter)}. {hint}"
)
return None
pack_systems = filtered
if system_filter and not pack_systems:
reason = system_scope_error(config, platform_name, system_filter, target_name)
print(f" WARNING: {reason}")
return None
if extras_only:
# The part of a split that carries the core extras no platform system
# owns: the platform's own files and trees went to the other parts.
+15 -11
View File
@@ -55,7 +55,6 @@ from common import (
compute_hashes,
expand_directory_entries,
platform_declarations,
filter_systems_by_target,
group_identical_platforms,
list_emulator_profiles,
list_system_ids,
@@ -65,6 +64,8 @@ from common import (
load_platform_config,
resolve_system_arg,
select_systems,
platform_scope_systems,
system_scope_error,
md5sum,
require_yaml,
resolve_local_file,
@@ -1218,16 +1219,7 @@ def verify_platform(
validation_index = _build_validation_index(platform_profiles)
validation_owners = destination_owners(platform_profiles)
if not target_cores:
plat_cores = None
verify_systems = filter_systems_by_target(
config.get("systems", {}),
profiles,
target_cores,
platform_cores=plat_cores,
)
if system_filter:
verify_systems = select_systems(verify_systems, system_filter)
verify_systems = platform_scope_systems(config, profiles, target_cores, system_filter)
details = []
file_status: dict[str, str] = {}
@@ -2884,6 +2876,18 @@ def _main(held: contextlib.ExitStack) -> None:
except (FileNotFoundError, ValueError) as e:
print(f"ERROR: {e}", file=sys.stderr)
sys.exit(1)
# A system the target leaves no core for was reported 0/0 with exit 0,
# where generate_pack refuses the same request.
if platform_systems:
config = load_platform_config(args.platform, args.platforms_dir)
if not platform_scope_systems(
config, emu_profiles, target_cores_cache.get(args.platform), platform_systems
):
reason = system_scope_error(
config, args.platform, platform_systems, args.target
)
print(f"ERROR: {reason}", file=sys.stderr)
sys.exit(1)
# Group identical platforms (same function as generate_pack)
groups = group_identical_platforms(
+43
View File
@@ -85,5 +85,48 @@ class AMistypedTargetIsRefusedFirst(unittest.TestCase):
self.assertIn("Unknown target 'swich'", err.getvalue())
class ASystemTheTargetDropsIsNamedAsSuch(unittest.TestCase):
"""`--platform retroarch --system sony-playstation-2 --target switch`:
verify printed 0/0 with exit 0 under --strict, and generate_pack said
RetroArch has no such system and suggested sony-playstation."""
PLATFORMS = str(REPO_ROOT / "platforms")
def test_both_tools_read_one_scope(self):
from common import (
load_emulator_profiles, load_platform_config, platform_scope_systems,
)
config = load_platform_config("retroarch", self.PLATFORMS)
profiles = load_emulator_profiles(str(REPO_ROOT / "emulators"))
switch = _target_cores_for("retroarch", "switch", self.PLATFORMS)
wanted = ["sony-playstation-2"]
self.assertEqual(platform_scope_systems(config, profiles, switch, wanted), {})
self.assertEqual(list(platform_scope_systems(config, profiles, None, wanted)), wanted)
def test_the_message_names_the_target(self):
from common import system_scope_error
config = {"systems": {"sony-playstation-2": {}, "sony-playstation": {}}}
self.assertEqual(
system_scope_error(config, "retroarch", ["sony-playstation-2"], "switch"),
"no core retroarch runs on target switch emulates sony-playstation-2",
)
undeclared = system_scope_error(config, "retroarch", ["nec-pc-98"], "switch")
self.assertTrue(undeclared.startswith("retroarch has no system matching nec-pc-98."))
def test_verify_refuses_it(self):
import subprocess
proc = subprocess.run(
[sys.executable, "scripts/verify.py", "--platform", "retroarch",
"--system", "sony-playstation-2", "--target", "switch", "--strict"],
capture_output=True, text=True, cwd=str(REPO_ROOT), timeout=600, check=False,
)
self.assertEqual(proc.returncode, 1, proc.stdout[-300:])
self.assertIn("no core retroarch runs on target switch emulates sony-playstation-2",
proc.stderr)
if __name__ == "__main__":
unittest.main()