fix: compare narrowed emulator packs in step 5

This commit is contained in:
Abdessamad Derraz committed 2026-11-06 03:51:52 +01:00
1 parent 7f6ba6952c
commit cead17429b
3 files changed
+99 -47

No files matched your search

+72 -46
View File
@@ -523,15 +523,43 @@ def check_pack_destinations(
return all_ok
# The narrowings an emulator or system pack takes, compared by
# check_emulator_packs. A test holds this to the builder's parameters.
EMULATOR_NARROWINGS_COMPARED = frozenset({"standalone", "regions", "required_only"})
def emulator_variants(profiles: list[dict], full: bool) -> list[tuple[str, dict]]:
"""(label, builder arguments) of each narrowed emulator pack compared.
--required-only always, and each region the profiles' files declare,
sampled as check_narrowed_packs samples them unless *full*. A profile
without a regional file has nothing --region could withdraw.
"""
import region as region_mod # noqa: PLC0415
regions = sorted({
r for profile in profiles for fe in profile.get("files") or []
for r in region_mod.normalize_declared(fe.get("region"))
})
variants: list[tuple[str, dict]] = [("", {}), (" --required-only", {"required_only": True})]
variants += [
(f" --region {regions[i]}", {"regions": [regions[i]]})
for i in _matrix_coverage(full, len(regions))
]
return variants
def check_emulator_packs(
emulators_dir: str = "emulators", platforms_dir: str = "platforms",
db_path: str = "database.json",
db_path: str = "database.json", full: bool = False,
) -> bool:
"""Build every emulator and system pack in memory and compare it with verify.
Each profile in each build it has (libretro, standalone): the paths the
pack fills with the profile's files against the ones verify credits. No
archive is written and no file read, so all of them take seconds.
Each profile in each build it has (libretro, standalone), full and under
each narrowing of emulator_variants: the paths the pack fills with the
profile's files against the ones verify credits. The region and
required-only rules are written twice, once per side, and only this
comparison holds them together. No archive is written and no file read.
"""
import generate_pack # noqa: PLC0415
import verify # noqa: PLC0415
@@ -549,6 +577,26 @@ def check_emulator_packs(
db = load_database(db_path)
zip_contents = build_zip_contents_index(db)
profiles = load_emulator_profiles(emulators_dir, skip_aliases=False)
def compare(names: list[str], standalone: bool, label: str, **narrowing) -> bool:
record = generate_pack.PackRecord()
with contextlib.redirect_stdout(io.StringIO()), \
contextlib.redirect_stderr(io.StringIO()):
generate_pack.generate_emulator_pack(
names, emulators_dir, db, "bios", "", standalone, zip_contents,
offline=True, platforms_dir=platforms_dir, record=record, **narrowing,
)
report = verify.verify_emulator(
names, emulators_dir, db, standalone, platforms_dir=platforms_dir,
**narrowing,
)
lines = _set_difference_lines(
label, set(report["pack_destinations"]), set(record.files)
)
if lines:
print("\n".join(lines))
return not lines
all_ok = True
compared = 0
for name in sorted(profiles):
@@ -559,23 +607,10 @@ def check_emulator_packs(
select_emulator_profiles([name], profiles, standalone)
except ProfileSelectionError:
continue
record = generate_pack.PackRecord()
with contextlib.redirect_stdout(io.StringIO()):
generate_pack.generate_emulator_pack(
[name], emulators_dir, db, "bios", "", standalone, zip_contents,
offline=True, platforms_dir=platforms_dir, record=record,
)
report = verify.verify_emulator(
[name], emulators_dir, db, standalone, platforms_dir=platforms_dir,
)
compared += 1
label = f"{name}{' --standalone' if standalone else ''}"
lines = _set_difference_lines(
label, set(report["pack_destinations"]), set(record.files)
)
if lines:
print("\n".join(lines))
all_ok = False
for tag, narrowing in emulator_variants([profiles[name]], full):
compared += 1
label = f"{name}{' --standalone' if standalone else ''}{tag}"
all_ok = compare([name], standalone, label, **narrowing) and all_ok
print(f" {compared} emulator packs compared, {'all equal' if all_ok else 'differences above'}")
# A system pack builds every profile of the system together: a placement
# rule that only goes wrong when two profiles share a path (the bluemsx
@@ -593,34 +628,24 @@ def check_emulator_packs(
matching = profiles_for_systems(profiles, [sid], standalone)
if len(matching) < 2:
continue
record = generate_pack.PackRecord()
with contextlib.redirect_stdout(io.StringIO()), \
contextlib.redirect_stderr(io.StringIO()):
generate_pack.generate_emulator_pack(
matching, emulators_dir, db, "bios", "", standalone, zip_contents,
offline=True, platforms_dir=platforms_dir, record=record,
system_ids=[sid],
)
report = verify.verify_emulator(
matching, emulators_dir, db, standalone, platforms_dir=platforms_dir,
system_ids=[sid],
)
compared += 1
label = f"--system {sid}{' --standalone' if standalone else ''}"
lines = _set_difference_lines(
label, set(report["pack_destinations"]), set(record.files)
)
if lines:
print("\n".join(lines))
systems_ok = False
for tag, narrowing in emulator_variants(
[profiles[n] for n in matching if n in profiles], full
):
compared += 1
label = f"--system {sid}{' --standalone' if standalone else ''}{tag}"
systems_ok = compare(
matching, standalone, label, system_ids=[sid], **narrowing
) and systems_ok
print(f" {compared} system packs compared, "
f"{'all equal' if systems_ok else 'differences above'}")
return all_ok and systems_ok
# How each _narrowings() dimension is compared by check_narrowed_packs, or why
# it is not. A test reads the builder's signature against these two tables, so
# a new narrowing fails it until it is compared or a reason is written here.
# How each _narrowings() dimension of a platform pack is compared by
# check_narrowed_packs, or why it is not. A test reads the builder's
# signature against these two tables, so a new narrowing fails it until it
# is compared or a reason is written here. Emulator packs have their own
# table, EMULATOR_NARROWINGS_COMPARED.
NARROWINGS_COMPARED = frozenset({"regions", "target_name", "required_only", "no_game_data"})
NARROWINGS_NOT_COMPARED = {
"standalone": "an emulator pack mode, compared by check_emulator_packs",
@@ -964,7 +989,8 @@ def main() -> None:
parser.add_argument(
"--full-narrowings", action="store_true",
help="Step 5 compares every region, target and system of each "
"platform, not a sample of three (about 25 minutes)",
"platform, and every region of each emulator and system pack, not a "
"sample of three (about 25 minutes)",
)
parser.add_argument(
"--check-buildbot",
@@ -1335,7 +1361,7 @@ def main() -> None:
)
# Counts can agree while the paths differ, both ways at once.
ok = check_pack_destinations(destinations_file, pack_output) and ok
ok = check_emulator_packs() and ok
ok = check_emulator_packs(full=args.full_narrowings) and ok
ok = check_narrowed_packs(
include_archived=args.include_archived, full=args.full_narrowings,
) and ok
+23 -1
View File
@@ -182,6 +182,28 @@ class EveryNarrowingIsCompared(unittest.TestCase):
"compare the new narrowing in check_narrowed_packs, "
"or write in NARROWINGS_NOT_COMPARED why it is not")
def test_each_emulator_pack_narrowing_is_compared(self):
"""check_emulator_packs built every pack without --region or
--required-only, while the table above counted both as compared:
it describes the platform packs only."""
import inspect
import pipeline
from generate_pack import _narrowings, generate_emulator_pack
taken = (set(inspect.signature(_narrowings).parameters)
& set(inspect.signature(generate_emulator_pack).parameters))
self.assertEqual(taken, set(pipeline.EMULATOR_NARROWINGS_COMPARED))
profile = {"files": [{"name": "a.bin", "region": "japan"},
{"name": "b.bin", "region": ["north-america", "europe"]}]}
used = {key for _label, narrowing in pipeline.emulator_variants([profile], False)
for key in narrowing}
# standalone is the loop around the variants, both builds of each profile.
self.assertEqual(used, pipeline.EMULATOR_NARROWINGS_COMPARED - {"standalone"})
regions = [n["regions"] for _l, n in pipeline.emulator_variants([profile], True)
if "regions" in n]
self.assertEqual(len(regions), 3)
def test_the_variants_reach_every_compared_dimension(self):
import pipeline
from common import load_emulator_profiles, load_platform_config
@@ -246,7 +268,7 @@ class ThePipelineComparesPaths(unittest.TestCase):
def test_the_run_compares_every_emulator_pack(self):
source = (REPO_ROOT / "scripts" / "pipeline.py").read_text(encoding="utf-8")
self.assertIn("ok = check_pack_destinations(destinations_file, pack_output) and ok", source)
self.assertIn("ok = check_emulator_packs() and ok", source)
self.assertIn("ok = check_emulator_packs(full=args.full_narrowings) and ok", source)
self.assertIn('"--destinations", destinations_file', source)
+4
View File
@@ -95,6 +95,10 @@ alone, run `python scripts/check_buildbot_system.py`.
ones verify credits (`--destinations`), every emulator pack, each
profile in each build, built in memory against `verify --emulator`, and
every system pack that gathers several profiles against `verify --system`.
Each of them is also built with `--required-only` and with the regions
its files declare (first, middle and last, all of them with
`--full-narrowings`). 2,112 emulator and 1,493 system packs took about
two minutes on 2026-11-06.
Equal counts once hid four RetroArch paths credited and never written.
- **Each narrowed platform pack is compared too.** Step 5 builds in memory
the `--required-only` and `--no-game-data` pack of every platform, and its