diff --git a/scripts/verify.py b/scripts/verify.py index 0211d8dc..5cf00018 100644 --- a/scripts/verify.py +++ b/scripts/verify.py @@ -1345,6 +1345,15 @@ def _split_undeclared(undeclared: list[dict]) -> tuple[list[dict], list[dict], l return game_data, placed, unplaced +def _core_required_missing(bios_files: list[dict]) -> list[dict]: + """Core files a core cannot run without, absent from the collection.""" + return [ + u + for u in bios_files + if u["required"] and not u["in_repo"] and not u.get("hle_fallback") + ] + + def _print_undeclared_section(result: dict, verbose: bool) -> None: """Print cross-reference section for undeclared files used by cores.""" undeclared = result.get("undeclared_files", []) @@ -1353,11 +1362,7 @@ def _print_undeclared_section(result: dict, verbose: bool) -> None: game_data, bios_files, unplaced = _split_undeclared(undeclared) - req_not_in_repo = [ - u - for u in bios_files - if u["required"] and not u["in_repo"] and not u.get("hle_fallback") - ] + req_not_in_repo = _core_required_missing(bios_files) req_hle_not_in_repo = [ u for u in bios_files @@ -2314,16 +2319,43 @@ def _exit_if_strict_emulator(strict: bool, result: dict) -> None: def _overall_summary(group_results: list[tuple[dict, list[str]]]) -> str: - """One closing line for a multi-platform run, worst severity first.""" + """The closing lines of a multi-platform run, worst severity first. + + Each count names the platforms carrying it, and the core files a core + cannot run without are counted too: a reader of the summary alone used + to conclude nothing required was missing, and could not tell where the + one warning was without reading the whole output. + """ platforms = sum(len(group) for _, group in group_results) - totals = { - sev: sum(r["severity_counts"].get(sev, 0) for r, _ in group_results) - for sev in (Severity.CRITICAL, Severity.WARNING, Severity.INFO) + + def where(counts: dict[str, int]) -> str: + named = [f"{name} {count}" if count > 1 else name + for name, count in counts.items() if count] + return f" ({', '.join(named)})" if named else "" + + parts = [] + for severity, label in ((Severity.CRITICAL, "critical"), (Severity.WARNING, "warning"), + (Severity.INFO, "info")): + counts = { + " / ".join(group): r["severity_counts"].get(severity, 0) + for r, group in group_results + } + parts.append(f"{sum(counts.values())} {label}{where(counts)}") + lines = [f"{platforms} platforms: {', '.join(parts)}"] + + missing = { + " / ".join(group): sorted({ + u["name"] for u in _core_required_missing(_split_undeclared( + r.get("undeclared_files", []))[1]) + }) + for r, group in group_results } - return ( - f"{platforms} platforms: {totals[Severity.CRITICAL]} critical, " - f"{totals[Severity.WARNING]} warning, {totals[Severity.INFO]} info" - ) + total = sum(len(names) for names in missing.values()) + if total: + detail = "; ".join(f"{group}: {', '.join(names)}" + for group, names in missing.items() if names) + lines.append(f"{total} core file(s) a core cannot run without are missing ({detail})") + return "\n".join(lines) if __name__ == "__main__": diff --git a/tests/test_audit_regressions.py b/tests/test_audit_regressions.py index dd019b4e..44274d58 100644 --- a/tests/test_audit_regressions.py +++ b/tests/test_audit_regressions.py @@ -4374,5 +4374,32 @@ class PipelineDocstringNamesThePrintedSteps(unittest.TestCase): self.assertEqual(listed, printed) + +class VerifyAllSummaryNamesWhatIsWrong(unittest.TestCase): + """The closing line counted platform severities only: a required core + file missing on ROCKNIX was absent from it, and the one warning did not + say which platform carried it.""" + + def test_counts_carry_platforms_and_core_misses(self): + sys.path.insert(0, str(ROOT / "scripts")) + import verify + + clean = {"severity_counts": {}, "undeclared_files": []} + warned = {"severity_counts": {verify.Severity.WARNING: 1}, "undeclared_files": []} + core_miss = { + "severity_counts": {}, + "undeclared_files": [{ + "name": "n64.psh", "required": True, "in_repo": False, + "category": "bios", "in_pack": True, + }], + } + summary = verify._overall_summary( + [(clean, ["RetroArch"]), (warned, ["RetroDECK"]), (core_miss, ["ROCKNIX"])] + ) + self.assertIn("1 warning (RetroDECK)", summary) + self.assertIn("ROCKNIX: n64.psh", summary) + self.assertIn("0 critical,", summary) + + if __name__ == "__main__": unittest.main()