fix: truth generation uses platform config not registry

This commit is contained in:
Abdessamad Derraz
2026-03-29 13:41:49 +02:00
parent 3bf6e5c961
commit dee37c2530
3 changed files with 71 additions and 39 deletions
+42 -12
View File
@@ -1156,6 +1156,8 @@ def _determine_core_mode(
return "standalone" return "standalone"
return "libretro" return "libretro"
ptype = profile.get("type", "libretro") ptype = profile.get("type", "libretro")
if "standalone" in ptype and "libretro" in ptype:
return "both"
if "standalone" in ptype: if "standalone" in ptype:
return "standalone" return "standalone"
return "libretro" return "libretro"
@@ -1245,35 +1247,41 @@ def _merge_file_into_system(
def generate_platform_truth( def generate_platform_truth(
platform_name: str, platform_name: str,
registry: dict, config: dict,
registry_entry: dict,
profiles: dict[str, dict], profiles: dict[str, dict],
db: dict | None = None, db: dict | None = None,
target_cores: set[str] | None = None, target_cores: set[str] | None = None,
) -> dict: ) -> dict:
"""Generate ground-truth system data for a platform from emulator profiles. """Generate ground-truth system data for a platform from emulator profiles.
Args:
platform_name: platform identifier
config: loaded platform config (via load_platform_config), has cores,
systems, standalone_cores with inheritance resolved
registry_entry: registry metadata for hash_type, verification_mode, etc.
profiles: all loaded emulator profiles
db: optional database for hash enrichment
target_cores: optional hardware target core filter
Returns a dict with platform metadata, systems, and per-file details Returns a dict with platform metadata, systems, and per-file details
including which cores reference each file. including which cores reference each file.
""" """
plat_entry = registry.get(platform_name, {}) cores_config = config.get("cores")
cores_config = plat_entry.get("cores")
# Build a synthetic config dict for resolve_platform_cores
synthetic_config: dict = {"cores": cores_config}
if "systems" in plat_entry:
synthetic_config["systems"] = plat_entry["systems"]
# Resolve standalone set for mode determination # Resolve standalone set for mode determination
standalone_set: set[str] | None = None standalone_set: set[str] | None = None
standalone_cores = plat_entry.get("standalone_cores") standalone_cores = config.get("standalone_cores")
if isinstance(standalone_cores, list): if isinstance(standalone_cores, list):
standalone_set = {str(c) for c in standalone_cores} standalone_set = {str(c) for c in standalone_cores}
resolved = resolve_platform_cores(synthetic_config, profiles, target_cores) resolved = resolve_platform_cores(config, profiles, target_cores)
systems: dict[str, dict] = {} systems: dict[str, dict] = {}
cores_profiled: set[str] = set() cores_profiled: set[str] = set()
cores_unprofiled: set[str] = set() cores_unprofiled: set[str] = set()
# Track which cores contribute to each system
system_cores: dict[str, dict[str, set[str]]] = {}
for emu_name in sorted(resolved): for emu_name in sorted(resolved):
profile = profiles.get(emu_name) profile = profiles.get(emu_name)
@@ -1284,7 +1292,10 @@ def generate_platform_truth(
mode = _determine_core_mode(emu_name, profile, cores_config, standalone_set) mode = _determine_core_mode(emu_name, profile, cores_config, standalone_set)
raw_files = profile.get("files", []) raw_files = profile.get("files", [])
filtered = filter_files_by_mode(raw_files, standalone=(mode == "standalone")) if mode == "both":
filtered = raw_files
else:
filtered = filter_files_by_mode(raw_files, standalone=(mode == "standalone"))
for fe in filtered: for fe in filtered:
sys_id = fe.get("system", "") sys_id = fe.get("system", "")
@@ -1293,12 +1304,31 @@ def generate_platform_truth(
sys_id = sys_ids[0] if sys_ids else "unknown" sys_id = sys_ids[0] if sys_ids else "unknown"
system = systems.setdefault(sys_id, {}) system = systems.setdefault(sys_id, {})
_merge_file_into_system(system, fe, emu_name, db) _merge_file_into_system(system, fe, emu_name, db)
# Track core contribution per system
sys_cov = system_cores.setdefault(sys_id, {
"profiled": set(), "unprofiled": set(),
})
sys_cov["profiled"].add(emu_name)
# Track unprofiled cores per system based on profile system lists
for emu_name in cores_unprofiled:
for sys_id in systems:
sys_cov = system_cores.setdefault(sys_id, {
"profiled": set(), "unprofiled": set(),
})
sys_cov["unprofiled"].add(emu_name)
# Convert sets to sorted lists for serialization # Convert sets to sorted lists for serialization
for sys_data in systems.values(): for sys_id, sys_data in systems.items():
for fe in sys_data.get("files", []): for fe in sys_data.get("files", []):
fe["_cores"] = sorted(fe.get("_cores", set())) fe["_cores"] = sorted(fe.get("_cores", set()))
fe["_source_refs"] = sorted(fe.get("_source_refs", set())) fe["_source_refs"] = sorted(fe.get("_source_refs", set()))
# Add per-system coverage
cov = system_cores.get(sys_id, {})
sys_data["_coverage"] = {
"cores_profiled": sorted(cov.get("profiled", set())),
"cores_unprofiled": sorted(cov.get("unprofiled", set())),
}
return { return {
"platform": platform_name, "platform": platform_name,
+11 -1
View File
@@ -18,6 +18,7 @@ from common import (
list_registered_platforms, list_registered_platforms,
load_database, load_database,
load_emulator_profiles, load_emulator_profiles,
load_platform_config,
load_target_config, load_target_config,
) )
@@ -98,8 +99,17 @@ def main(argv: list[str] | None = None) -> None:
print(f" {name}: no target config, skipped") print(f" {name}: no target config, skipped")
continue continue
# Load platform config (with inheritance) and registry entry
try:
config = load_platform_config(name, args.platforms_dir)
except FileNotFoundError:
print(f" {name}: no platform config, skipped")
continue
registry_entry = registry.get(name, {})
result = generate_platform_truth( result = generate_platform_truth(
name, registry, profiles, db=db, target_cores=target_cores, name, config, registry_entry, profiles,
db=db, target_cores=target_cores,
) )
out_path = os.path.join(args.output_dir, f"{name}.yml") out_path = os.path.join(args.output_dir, f"{name}.yml")
+18 -26
View File
@@ -2526,14 +2526,10 @@ class TestE2E(unittest.TestCase):
_emulator_profiles_cache.clear() _emulator_profiles_cache.clear()
profiles = load_emulator_profiles(self.emulators_dir) profiles = load_emulator_profiles(self.emulators_dir)
registry = { config = {"cores": ["testcore"]}
"testplat": {
"cores": ["testcore"],
},
}
result = generate_platform_truth( result = generate_platform_truth(
"testplat", registry, profiles, db=None, "testplat", config, {}, profiles, db=None,
) )
self.assertEqual(result["platform"], "testplat") self.assertEqual(result["platform"], "testplat")
@@ -2573,9 +2569,9 @@ class TestE2E(unittest.TestCase):
_emulator_profiles_cache.clear() _emulator_profiles_cache.clear()
profiles = load_emulator_profiles(self.emulators_dir) profiles = load_emulator_profiles(self.emulators_dir)
registry = {"testplat": {"cores": "all_libretro"}} config = {"cores": "all_libretro"}
result = generate_platform_truth("testplat", registry, profiles) result = generate_platform_truth("testplat", config, {}, profiles)
names = {fe["name"] for fe in result["systems"]["test-system"]["files"]} names = {fe["name"] for fe in result["systems"]["test-system"]["files"]}
self.assertIn("both.bin", names) self.assertIn("both.bin", names)
@@ -2605,14 +2601,12 @@ class TestE2E(unittest.TestCase):
_emulator_profiles_cache.clear() _emulator_profiles_cache.clear()
profiles = load_emulator_profiles(self.emulators_dir) profiles = load_emulator_profiles(self.emulators_dir)
registry = { config = {
"testplat": { "cores": ["dualcore"],
"cores": ["dualcore"], "standalone_cores": ["dualcore"],
"standalone_cores": ["dualcore"],
},
} }
result = generate_platform_truth("testplat", registry, profiles) result = generate_platform_truth("testplat", config, {}, profiles)
names = {fe["name"] for fe in result["systems"]["test-system"]["files"]} names = {fe["name"] for fe in result["systems"]["test-system"]["files"]}
self.assertIn("sa_file.bin", names) self.assertIn("sa_file.bin", names)
@@ -2649,9 +2643,9 @@ class TestE2E(unittest.TestCase):
_emulator_profiles_cache.clear() _emulator_profiles_cache.clear()
profiles = load_emulator_profiles(self.emulators_dir) profiles = load_emulator_profiles(self.emulators_dir)
registry = {"testplat": {"cores": ["core_a", "core_b"]}} config = {"cores": ["core_a", "core_b"]}
result = generate_platform_truth("testplat", registry, profiles) result = generate_platform_truth("testplat", config, {}, profiles)
sys_files = result["systems"]["test-system"]["files"] sys_files = result["systems"]["test-system"]["files"]
self.assertEqual(len(sys_files), 1) self.assertEqual(len(sys_files), 1)
@@ -2682,9 +2676,9 @@ class TestE2E(unittest.TestCase):
_emulator_profiles_cache.clear() _emulator_profiles_cache.clear()
profiles = load_emulator_profiles(self.emulators_dir) profiles = load_emulator_profiles(self.emulators_dir)
registry = {"testplat": {"cores": ["profiled_core", "unprofiled_core"]}} config = {"cores": ["profiled_core", "unprofiled_core"]}
result = generate_platform_truth("testplat", registry, profiles) result = generate_platform_truth("testplat", config, {}, profiles)
cov = result["_coverage"] cov = result["_coverage"]
self.assertEqual(cov["cores_profiled"], 1) self.assertEqual(cov["cores_profiled"], 1)
@@ -3113,13 +3107,11 @@ class TestE2E(unittest.TestCase):
def test_truth_diff_integration(self): def test_truth_diff_integration(self):
"""Full chain: generate truth from profiles, diff against scraped data.""" """Full chain: generate truth from profiles, diff against scraped data."""
# Registry: one platform with two cores, only core_a has a profile # Config: platform with two cores, only core_a has a profile
registry = { config = {"cores": ["core_a", "core_b"]}
"testplat": { registry_entry = {
"cores": ["core_a", "core_b"], "hash_type": "md5",
"hash_type": "md5", "verification_mode": "md5",
"verification_mode": "md5",
},
} }
# Emulator profile for core_a with 2 files # Emulator profile for core_a with 2 files
@@ -3161,7 +3153,7 @@ class TestE2E(unittest.TestCase):
self.assertNotIn("core_b", profiles) self.assertNotIn("core_b", profiles)
# Generate truth # Generate truth
truth = generate_platform_truth("testplat", registry, profiles, db=None) truth = generate_platform_truth("testplat", config, registry_entry, profiles, db=None)
# Verify truth structure # Verify truth structure
self.assertIn("test-system", truth["systems"]) self.assertIn("test-system", truth["systems"])