From f3fe6f58b48e0583d51ba30c87c02230f8cff2fb Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Thu, 5 Nov 2026 11:41:46 +0100 Subject: [PATCH] fix: name the path of a file read at two places MyMinUI printed the same MISSING line twice for Bios/FBN/ and Bios/NG/ sfiii2a.zip (finding 78). --- scripts/verify.py | 31 +++++++++++++++++++++++-------- tests/test_tool_messages.py | 12 ++++++++++++ 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/scripts/verify.py b/scripts/verify.py index 05bc92c99..4d67492ce 100644 --- a/scripts/verify.py +++ b/scripts/verify.py @@ -1747,13 +1747,20 @@ def _print_detail_entries(details: list[dict], seen: set[str], verbose: bool) -> print(f" {line}") -def _print_undeclared_entry(u: dict, prefix: str, verbose: bool) -> None: - """Print a single undeclared file entry with its validation checks.""" +def _print_undeclared_entry( + u: dict, prefix: str, verbose: bool, repeated: frozenset[str] = frozenset() +) -> None: + """Print a single undeclared file entry with its validation checks. + + A name in ``repeated`` is read at more than one path, and the line names + the path: two identical lines told no one there were two slots to fill. + """ + name = u["path"] if u["name"] in repeated and u.get("path") else u["name"] arc_count = u.get("archive_file_count") if arc_count: - name_label = f"{u['name']} ({arc_count} file{'s' if arc_count != 1 else ''})" + name_label = f"{name} ({arc_count} file{'s' if arc_count != 1 else ''})" else: - name_label = u["name"] + name_label = name print(f" {prefix}: {u['emulator']} needs {name_label}") checks = u.get("checks", []) if checks: @@ -1801,6 +1808,12 @@ def _core_required_missing(bios_files: list[dict]) -> list[dict]: ] +def _repeated_names(entries: list[dict]) -> frozenset[str]: + """Names more than one of ``entries`` carries.""" + counts = Counter(u["name"] for u in entries) + return frozenset(name for name, count in counts.items() if count > 1) + + def _omission_reasons(entries: list[dict]) -> str: """Each reason a held file was left out, with its count when several. @@ -1855,13 +1868,14 @@ def _print_undeclared_section(result: dict, verbose: bool) -> None: ) # The missing files follow the line that counts them: printed after the # held-but-not-packed line, they read as files installed with their emulator. + repeated = _repeated_names(req_not_in_repo + req_hle_not_in_repo + opt_not_in_repo) for u in req_not_in_repo: - _print_undeclared_entry(u, "MISSING (required)", verbose) + _print_undeclared_entry(u, "MISSING (required)", verbose, repeated) for u in req_hle_not_in_repo: - _print_undeclared_entry(u, "MISSING (required, HLE fallback)", verbose) + _print_undeclared_entry(u, "MISSING (required, HLE fallback)", verbose, repeated) if verbose: for u in opt_not_in_repo: - _print_undeclared_entry(u, "MISSING (optional)", verbose) + _print_undeclared_entry(u, "MISSING (optional)", verbose, repeated) if bundled: names = ", ".join(sorted({u["name"] for u in bundled})) print(f" Bundled with their emulator, not collected: {names}") @@ -1880,8 +1894,9 @@ def _print_undeclared_section(result: dict, verbose: bool) -> None: listed = "" if verbose or not gd_missing else " (--verbose lists them)" print(f" Game data: {gd_packed} in pack, {len(gd_missing)} missing{listed}") if verbose: + repeated = _repeated_names(gd_missing) for u in gd_missing: - _print_undeclared_entry(u, "MISSING (game data)", verbose) + _print_undeclared_entry(u, "MISSING (game data)", verbose, repeated) if gd_unplaced: print( f" Game data held but not packed: {len(gd_unplaced)} " diff --git a/tests/test_tool_messages.py b/tests/test_tool_messages.py index 76a069ad8..66438844e 100644 --- a/tests/test_tool_messages.py +++ b/tests/test_tool_messages.py @@ -202,6 +202,18 @@ class HeldButNotPackedNamesEachReason(unittest.TestCase): self.assertIn("MISSING (required): FinalBurn Neo needs sfiii2a.zip", lines[1]) self.assertTrue(lines[2].startswith(" Core files held but not packed: "), lines) + def test_one_name_read_at_two_paths_prints_both_paths(self): + """MyMinUI printed 'FinalBurn Neo needs sfiii2a.zip' twice, word for + word, for Bios/FBN/ and Bios/NG/.""" + base = {"name": "sfiii2a.zip", "emulator": "FinalBurn Neo", "required": True, + "in_repo": False, "category": "bios"} + lone = dict(base, name="other.zip", path="Bios/FBN/other.zip") + text = self._printed([dict(base, path="Bios/FBN/sfiii2a.zip"), + dict(base, path="Bios/NG/sfiii2a.zip"), lone]) + self.assertIn("FinalBurn Neo needs Bios/FBN/sfiii2a.zip", text) + self.assertIn("FinalBurn Neo needs Bios/NG/sfiii2a.zip", text) + self.assertIn("FinalBurn Neo needs other.zip\n", text) + def test_a_single_reason_reads_as_before(self): text = self._printed([self._entry("a.rom", "installed with its emulator")] * 2) self.assertIn("Core files held but not packed: 2 (installed with its emulator)", text)