From 7531ccad8f07c16ddd5b9dccb543a73abbd30b54 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Sun, 18 Oct 2026 07:33:11 +0200 Subject: [PATCH] fix: refuse a profile_sync filter matching nothing --- scripts/profile_sync.py | 25 ++++++++++++++++++++++--- tests/test_profile_sync.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/scripts/profile_sync.py b/scripts/profile_sync.py index 76df322e..e052033c 100644 --- a/scripts/profile_sync.py +++ b/scripts/profile_sync.py @@ -31,7 +31,7 @@ import yaml sys.path.insert(0, os.path.dirname(__file__)) import upstream -from common import load_emulator_profiles +from common import load_emulator_profiles, suggest_names from upstream import CompareResult, find_renamed DEFAULT_CACHE = upstream.CACHE_DIR @@ -3305,8 +3305,10 @@ def select_profiles(profiles: dict, args: argparse.Namespace) -> dict: """Profiles matching the selection flags.""" if args.emulator: if args.emulator not in profiles: - print(f"unknown profile: {args.emulator}", file=sys.stderr) - raise SystemExit(1) + raise SystemExit( + f"ERROR: unknown profile {args.emulator}. " + + suggest_names([args.emulator], sorted(profiles), "verify.py --list-emulators") + ) return {args.emulator: profiles[args.emulator]} selected = {} @@ -3324,6 +3326,23 @@ def select_profiles(profiles: dict, args: argparse.Namespace) -> dict: selected[name] = profile if args.limit and len(selected) >= args.limit: break + if not selected: + # A misspelt filter selected nothing and the run said nothing: it + # read as "no profile of that system needs a re-read". + filters = ", ".join( + f"{flag} {value}" for flag, value in ( + ("--system", args.system), ("--type", args.type_), + ("--stale-before", args.stale_before), + ) if value + ) + hint = "" + if args.system: + systems = sorted({s for p in profiles.values() for s in p.get("systems") or []}) + hint = " " + suggest_names([args.system], systems, "verify.py --list-systems") + elif args.type_: + kinds = sorted({str(p.get("type")) for p in profiles.values() if p.get("type")}) + hint = f" Types: {', '.join(kinds)}." + raise SystemExit(f"ERROR: no profile matches {filters or 'the selection'}.{hint}") return selected diff --git a/tests/test_profile_sync.py b/tests/test_profile_sync.py index 6244f660..6e057b03 100644 --- a/tests/test_profile_sync.py +++ b/tests/test_profile_sync.py @@ -4005,5 +4005,36 @@ class OfflineCacheMissIsNotGone(TestBuildReport): build_report("test", self._profile(["a.c:2"]), self.dir, offline=True) + +class TestSelectionNamesWhatMatchedNothing(unittest.TestCase): + """`--all --system atari-lynxx` printed nothing and exited 0, which read + as "no Lynx profile needs a re-read"; `--emulator handyy` offered no + suggestion.""" + + PROFILES = { + "handy": {"type": "libretro", "systems": ["atari-lynx"]}, + "mednafen_lynx": {"type": "libretro", "systems": ["atari-lynx"]}, + } + + def _args(self, **values): + base = {"emulator": None, "system": None, "type_": None, + "stale_before": None, "limit": None} + return argparse.Namespace(**{**base, **values}) + + def test_a_misspelt_system_stops_and_suggests(self): + with self.assertRaises(SystemExit) as caught: + profile_sync.select_profiles(self.PROFILES, self._args(system="atari-lynxx")) + self.assertIn("atari-lynx", str(caught.exception.code)) + + def test_a_misspelt_emulator_suggests(self): + with self.assertRaises(SystemExit) as caught: + profile_sync.select_profiles(self.PROFILES, self._args(emulator="handyy")) + self.assertIn("Did you mean: handy", str(caught.exception.code)) + + def test_a_matching_filter_selects(self): + selected = profile_sync.select_profiles(self.PROFILES, self._args(system="atari-lynx")) + self.assertEqual(sorted(selected), ["handy", "mednafen_lynx"]) + + if __name__ == "__main__": unittest.main()