fix: emulator validation is informational, not a platform failure

This commit is contained in:
Abdessamad Derraz
2026-03-25 17:34:56 +01:00
parent c61d40d8ef
commit 69131f4ad1
2 changed files with 55 additions and 10 deletions

View File

@@ -80,12 +80,14 @@ def verify_entry_existence(
required = file_entry.get("required", True) required = file_entry.get("required", True)
if not local_path: if not local_path:
return {"name": name, "status": Status.MISSING, "required": required} return {"name": name, "status": Status.MISSING, "required": required}
result = {"name": name, "status": Status.OK, "required": required}
if validation_index: if validation_index:
reason = check_file_validation(local_path, name, validation_index) reason = check_file_validation(local_path, name, validation_index)
if reason: if reason:
return {"name": name, "status": Status.UNTESTED, "required": required, ventry = validation_index.get(name, {})
"path": local_path, "reason": reason} emus = ", ".join(ventry.get("emulators", []))
return {"name": name, "status": Status.OK, "required": required} result["discrepancy"] = f"file present (OK) but {emus} says {reason}"
return result
def verify_entry_md5( def verify_entry_md5(
@@ -318,6 +320,34 @@ def find_exclusion_notes(
# Platform verification # Platform verification
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
def _find_best_variant(
file_entry: dict, db: dict, current_path: str,
validation_index: dict,
) -> str | None:
"""Search for a repo file that passes both platform MD5 and emulator validation."""
fname = file_entry.get("name", "")
if not fname or fname not in validation_index:
return None
md5_expected = file_entry.get("md5", "")
md5_set = {m.strip().lower() for m in md5_expected.split(",") if m.strip()} if md5_expected else set()
by_name = db.get("indexes", {}).get("by_name", {})
files_db = db.get("files", {})
for sha1 in by_name.get(fname, []):
candidate = files_db.get(sha1, {})
path = candidate.get("path", "")
if not path or not os.path.exists(path) or os.path.realpath(path) == os.path.realpath(current_path):
continue
if md5_set and candidate.get("md5", "").lower() not in md5_set:
continue
reason = check_file_validation(path, fname, validation_index)
if reason is None:
return path
return None
def verify_platform( def verify_platform(
config: dict, db: dict, config: dict, db: dict,
emulators_dir: str = DEFAULT_EMULATORS_DIR, emulators_dir: str = DEFAULT_EMULATORS_DIR,
@@ -361,13 +391,20 @@ def verify_platform(
) )
else: else:
result = verify_entry_md5(file_entry, local_path, resolve_status) result = verify_entry_md5(file_entry, local_path, resolve_status)
# Apply emulator-level validation on top of MD5 check # Emulator-level validation: informational for platform packs.
# Platform verification (MD5) is the authority. Emulator
# mismatches are reported as discrepancies, not failures.
if result["status"] == Status.OK and local_path and validation_index: if result["status"] == Status.OK and local_path and validation_index:
fname = file_entry.get("name", "") fname = file_entry.get("name", "")
reason = check_file_validation(local_path, fname, validation_index) reason = check_file_validation(local_path, fname, validation_index)
if reason: if reason:
result["status"] = Status.UNTESTED better = _find_best_variant(
result["reason"] = reason file_entry, db, local_path, validation_index,
)
if not better:
ventry = validation_index.get(fname, {})
emus = ", ".join(ventry.get("emulators", []))
result["discrepancy"] = f"{platform} says OK but {emus} says {reason}"
result["system"] = sys_id result["system"] = sys_id
result["hle_fallback"] = hle_index.get(file_entry.get("name", ""), False) result["hle_fallback"] = hle_index.get(file_entry.get("name", ""), False)
details.append(result) details.append(result)
@@ -470,6 +507,14 @@ def print_platform_result(result: dict, group: list[str]) -> None:
req = "required" if d.get("required", True) else "optional" req = "required" if d.get("required", True) else "optional"
hle = ", HLE available" if d.get("hle_fallback") else "" hle = ", HLE available" if d.get("hle_fallback") else ""
print(f" MISSING ({req}{hle}): {key}") print(f" MISSING ({req}{hle}): {key}")
for d in result["details"]:
disc = d.get("discrepancy")
if disc:
key = f"{d['system']}/{d['name']}"
if key in seen_details:
continue
seen_details.add(key)
print(f" DISCREPANCY: {key}{disc}")
# Cross-reference: undeclared files used by cores # Cross-reference: undeclared files used by cores
undeclared = result.get("undeclared_files", []) undeclared = result.get("undeclared_files", [])

View File

@@ -763,15 +763,15 @@ class TestE2E(unittest.TestCase):
self.assertIn("crc32 mismatch", reason) self.assertIn("crc32 mismatch", reason)
def test_75_validation_applied_in_existence_mode(self): def test_75_validation_applied_in_existence_mode(self):
"""Existence platform downgrades OK to UNTESTED when validation fails.""" """Existence mode reports discrepancy when validation fails, keeps OK."""
config = load_platform_config("test_existence", self.platforms_dir) config = load_platform_config("test_existence", self.platforms_dir)
profiles = load_emulator_profiles(self.emulators_dir) profiles = load_emulator_profiles(self.emulators_dir)
result = verify_platform(config, self.db, self.emulators_dir, profiles) result = verify_platform(config, self.db, self.emulators_dir, profiles)
# present_opt.bin exists but has wrong expected size → UNTESTED # present_opt.bin exists but has wrong expected size - OK with discrepancy
for d in result["details"]: for d in result["details"]:
if d["name"] == "present_opt.bin": if d["name"] == "present_opt.bin":
self.assertEqual(d["status"], Status.UNTESTED) self.assertEqual(d["status"], Status.OK)
self.assertIn("size mismatch", d.get("reason", "")) self.assertIn("size mismatch", d.get("discrepancy", ""))
break break
else: else:
self.fail("present_opt.bin not found in details") self.fail("present_opt.bin not found in details")