diff --git a/emulators/ares.yml b/emulators/ares.yml index 170c3351..f0d9ac96 100644 --- a/emulators/ares.yml +++ b/emulators/ares.yml @@ -112,12 +112,12 @@ files: source_ref: "desktop-ui/emulator/colecovision.cpp:12" # --- Famicom Disk System --- - - name: disksys.rom + - name: rp2c33-01.bin system: famicom-disk-system required: true sha256: "fdc1a76e654feea993fcb38366e05ee5f4eb641f86fe6bebaeefd412e112dd72" validation: [sha256] - description: "Famicom Disk System BIOS" + description: "Famicom Disk System BIOS, RP2C33-01 revision" source_ref: "desktop-ui/emulator/famicom-disk-system.cpp:19" # --- Game Boy Advance --- @@ -202,7 +202,6 @@ files: # --- Mega LD (LaserActive SEGA PAC) --- - name: mega_ld_bios_U.bin - aliases: ["[BIOS] LaserActive PAC-S10 (US) (v1.04).bin"] system: mega-ld region: [north-america] required: true @@ -220,14 +219,6 @@ files: description: "LaserActive SEGA PAC BIOS (Japan), v1.02" source_ref: "desktop-ui/emulator/mega-ld.cpp:17" - - name: "[BIOS] LaserActive PAC-S1 (Japan) (v1.05).bin" - system: mega-ld - region: [japan] - required: false - description: "LaserActive SEGA PAC BIOS (Japan), v1.05" - note: "Absent from the firmware table, so the scan by hash never assigns it. A path written into the Japanese BIOS slot of settings.bml is read as is, with no hash check. RetroBat writes this name there first and falls back to v1.02." - source_ref: "desktop-ui/emulator/mega-ld.cpp:17, desktop-ui/emulator/mega-ld.cpp:54, desktop-ui/settings/firmware.cpp:109, desktop-ui/emulator/emulator.cpp:188-197" - # --- MSX --- - name: msx-japanese.rom system: msx @@ -270,7 +261,6 @@ files: # --- Neo Geo Pocket --- - name: ngp_bios.rom - aliases: ["[BIOS] SNK Neo Geo Pocket (Japan, Europe) (En,Ja).ngp"] system: neo-geo-pocket required: true sha256: "0293555b21c4fac516d25199df7809b26beeae150e1d4504a050db32264a6ad7" @@ -280,7 +270,6 @@ files: # --- Neo Geo Pocket Color --- - name: ngpc_bios.rom - aliases: ["[BIOS] SNK Neo Geo Pocket (Japan, Europe) (En,Ja).ngp"] system: neo-geo-pocket-color required: true sha256: "8fb845a2f71514cec20728e2f0fecfade69444f8d50898b92c2259f1ba63e10d" @@ -350,7 +339,6 @@ files: # --- PC Engine LD (LaserActive NEC PAC) --- - name: pac-n10.pce - aliases: ["[BIOS] LaserActive PAC-N10 (US) (v1.02).bin"] system: pc-engine-ld required: true sha256: "0e87a3385a27b3a4cac51934819b7eefa5b3d690768d2495633838488cd0e2e4" @@ -359,7 +347,6 @@ files: source_ref: "desktop-ui/emulator/pc-engine-ld.cpp:18" - name: pac-n1.pce - aliases: ["[BIOS] LaserActive PAC-N1 (Japan) (v1.02).bin"] system: pc-engine-ld required: true sha256: "459325690a458baebd77495c91e37c4dddfdd542ba13a821ce954e5bb245627f" @@ -368,7 +355,6 @@ files: source_ref: "desktop-ui/emulator/pc-engine-ld.cpp:19" - name: pce-lp1.pce - aliases: ["[BIOS] LaserActive PCE-LP1 (Japan) (v1.02).bin"] system: pc-engine-ld required: true sha256: "3f43b3b577117d84002e99cb0baeb97b0d65b1d70b4adadc68817185c6a687f0" diff --git a/emulators/basiliskii.yml b/emulators/basiliskii.yml index f23856f4..23cf2b01 100644 --- a/emulators/basiliskii.yml +++ b/emulators/basiliskii.yml @@ -65,7 +65,6 @@ notes: | files: - name: ROM - aliases: [mac.rom] required: true min_size: 524288 max_size: 1048576 diff --git a/emulators/bsnes_cplusplus98.yml b/emulators/bsnes_cplusplus98.yml index c62ab2fd..02b427ca 100644 --- a/emulators/bsnes_cplusplus98.yml +++ b/emulators/bsnes_cplusplus98.yml @@ -29,7 +29,7 @@ notes: | Core continues without DSP firmware but coprocessor games won't function. exclusion_note: > - firmware_count=0 in .info is correct. DSP firmware (NEC uPD7725/uPD96050, + The .info declares firmware_count=0. DSP firmware (NEC uPD7725/uPD96050, Hitachi CX4) is loaded from the ROM directory via game XML markup, not from system_dir. RetroArch cannot pre-populate firmware in the ROM directory. Upstream byuu.org is defunct, and this libretro port is the preserved v085 code. diff --git a/emulators/fbalpha2012_neogeo.yml b/emulators/fbalpha2012_neogeo.yml index 1a8ab1ef..e760d3c0 100644 --- a/emulators/fbalpha2012_neogeo.yml +++ b/emulators/fbalpha2012_neogeo.yml @@ -52,8 +52,8 @@ notes: | neogeo.zip is a parent ROM set, loaded from g_rom_dir alongside game ZIPs. g_system_dir is set (src/burner/libretro/libretro.cpp:3016) but never used for file loading. - The .info note "BIOS files must be inside the ROM directory" is accurate here: - neogeo.zip must be in the same directory as game ROM ZIPs. + neogeo.zip is read from the same directory as the game ROM ZIPs, as the .info + note "BIOS files must be inside the ROM directory" states. files: [] diff --git a/emulators/kega-fusion.yml b/emulators/kega-fusion.yml index b24f4358..458bd4d8 100644 --- a/emulators/kega-fusion.yml +++ b/emulators/kega-fusion.yml @@ -51,7 +51,6 @@ notes: | files: - name: us_scd1_9210.bin - aliases: [bios_CD_U.bin] system: sega-megacd region: [north-america, latin-america] required: true @@ -60,7 +59,6 @@ files: source_ref: "Fusion.exe 0x44b662-0x44b670 (default path), 0x478877-0x4788d6 (open, message, fread 0x20000)" - name: jp_mcd1_9111.bin - aliases: [bios_CD_J.bin] system: sega-megacd region: [japan] required: true @@ -69,7 +67,6 @@ files: source_ref: "Fusion.exe 0x44b676 (default path), 0x4788f9-0x478959 (open, message, fread 0x20000)" - name: eu_mcd1_9210.bin - aliases: [bios_CD_E.bin] system: sega-megacd region: [europe, oceania] required: true diff --git a/emulators/mame2000.yml b/emulators/mame2000.yml index b840fe1e..a29ae3bc 100644 --- a/emulators/mame2000.yml +++ b/emulators/mame2000.yml @@ -34,15 +34,16 @@ notes: | files: - name: hiscore.dat + load_from: content_dir required: false category: game_data system: arcade sha1: "21ddda0c61bd4f1cc04fb9d09e16d403d620f9e8" md5: "9b262149ec70cbda741a8ba55784f8e1" size: 36182 - source_ref: "src/hiscore.c:18,132-146,252-254, src/cpuintrf.c:664-666" + source_ref: "src/hiscore.c:18,132-146,252-254, src/cpuintrf.c:664-666, src/libretro/fileio.c:1002-1014, src/libretro/libretro.c:1044-1052" note: >- - high score definitions, loaded from ROM directory. metadata/hiscore.dat + high score definitions, opened from IMAMEBASEPATH, the directory of the loaded content. metadata/hiscore.dat of the core repository. is_mem_range reads only "cpu:addr:len:start:end" lines under a "gamename:" header. @@ -55,18 +56,20 @@ files: note: "cheat definitions, loaded from system_dir/mame2000/cheat/" - name: history.dat + load_from: content_dir required: false category: game_data system: arcade - source_ref: "src/datafile.c:52,281,541" - note: "game history database, loaded from ROM directory" + source_ref: "src/datafile.c:52,281,541, src/libretro/fileio.c:1002-1014" + note: "game history database, opened from IMAMEBASEPATH, the directory of the loaded content" - name: mameinfo.dat + load_from: content_dir required: false category: game_data system: arcade - source_ref: "src/datafile.c:53,281,568" - note: "game technical info database, loaded from ROM directory" + source_ref: "src/datafile.c:53,281,568, src/libretro/fileio.c:1002-1014" + note: "game technical info database, opened from IMAMEBASEPATH, the directory of the loaded content" - name: neogeo.zip required: true diff --git a/emulators/mame2014.yml b/emulators/mame2014.yml index 835cee5b..4fd821d0 100644 --- a/emulators/mame2014.yml +++ b/emulators/mame2014.yml @@ -102,6 +102,7 @@ notes: | files: # Data files - name: hiscore.dat + load_from: working_directory required: false category: game_data system: arcade diff --git a/emulators/mame2015.yml b/emulators/mame2015.yml index 816735dc..2c45a9cf 100644 --- a/emulators/mame2015.yml +++ b/emulators/mame2015.yml @@ -91,6 +91,7 @@ notes: | files: # Data files - name: hiscore.dat + load_from: working_directory installed_on: [recalbox] required: false category: game_data diff --git a/emulators/mupen64plus_next.yml b/emulators/mupen64plus_next.yml index 8eabe068..d9495e3f 100644 --- a/emulators/mupen64plus_next.yml +++ b/emulators/mupen64plus_next.yml @@ -13,8 +13,6 @@ cores: [mupen64plus_next, mupen64plus_next_develop, mupen64plus_next_gles3, mupe files: - name: "IPL.n64" path: "Mupen64plus/IPL.n64" - standalone_path: "64DD_IPL.bin" - aliases: ["64DD_IPL.bin"] size: 4194304 required: false description: "64DD IPL ROM" diff --git a/emulators/nanoboyadvance.yml b/emulators/nanoboyadvance.yml index c6edfe09..92546cc9 100644 --- a/emulators/nanoboyadvance.yml +++ b/emulators/nanoboyadvance.yml @@ -30,18 +30,14 @@ notes: | ref: src/platform/core/src/loader/bios.cpp:17-48, src/platform/sdl/main.cpp:177-192, src/nba/src/core.cpp:42-44 - ROCKNIX patches config.toml and keymap.toml to /storage/.config/nanoboyadvance/ - and its config.toml sets bios_path = /storage/roms/bios/gba_bios.bin. - files: - name: "bios.bin" path: "bios.bin" - aliases: ["gba_bios.bin"] system: nintendo-gba required: true size: 16384 validation: [size] config_key: "general.bios_path" description: "Game Boy Advance BIOS" - note: "Any file name through general.bios_path or --bios, or bios.bin beside the executable when neither is set. ROCKNIX points the key at gba_bios.bin in its BIOS directory." + note: "Any file name through general.bios_path or --bios, or bios.bin beside the executable when neither is set." source_ref: "src/platform/core/src/loader/bios.cpp:17-48, src/platform/core/src/config.cpp:36, src/platform/sdl/main.cpp:177-192" diff --git a/emulators/openlara.yml b/emulators/openlara.yml index 7946ae9f..85d1b26e 100644 --- a/emulators/openlara.yml +++ b/emulators/openlara.yml @@ -26,6 +26,6 @@ notes: > subdirectories as .ogg/.mp3/.wav files or cdaudio.wad (src/gameflow.h:1272-1356). System directory used only for shader cache at {system_dir}/openlara/cache/ (src/platform/libretro/main.cpp:177-197). No BIOS, firmware, or engine data files required in - system directory. .info omits sat from supported_extensions (code declares it). + system directory. The code declares the sat extension, which the .info supported_extensions does not list. files: [] diff --git a/emulators/prosystem.yml b/emulators/prosystem.yml index 5f4ee4c6..20095165 100644 --- a/emulators/prosystem.yml +++ b/emulators/prosystem.yml @@ -29,7 +29,8 @@ notes: | directory, it is loaded automatically. No hash or size validation is performed on the BIOS file. - The .info declares firmware_count=1 (U only), missing the PAL BIOS. + The .info declares one firmware, the U BIOS. The code also opens the E BIOS + for PAL cartridges (core/libretro.c:581-582). files: - name: "7800 BIOS (U).rom" diff --git a/emulators/retrodream.yml b/emulators/retrodream.yml index 3ac394cf..3154bfe2 100644 --- a/emulators/retrodream.yml +++ b/emulators/retrodream.yml @@ -20,8 +20,8 @@ notes: | The fork sets appdir to the RetroArch system directory root (retro_host.c:216-217), unlike the upstream which appends /dc - (upstream retro_host.c:196-198). The .info still declares dc/ prefix paths - matching the upstream, not this fork. + (upstream retro_host.c:196-198). The .info declares dc/ prefix paths, the + layout the upstream reads. boot.bin is validated against four known MD5 hashes (boot.c:25-30). Validation failure logs a warning but the core continues via HLE. diff --git a/emulators/rvvm.yml b/emulators/rvvm.yml index 43fa07c6..465326f3 100644 --- a/emulators/rvvm.yml +++ b/emulators/rvvm.yml @@ -76,5 +76,5 @@ notes: OpenSBI: https://github.com/riscv-software-src/opensbi/releases. Pre-built images for RVVM: https://github.com/LekKit/RVVM/wiki/Running info_firmware_count: > - .info declares 0 firmware. Correct: the core loads firmware via + .info declares 0 firmware. The core loads firmware via user-specified paths in the .rvvm config, not from system_dir. diff --git a/emulators/skyemu.yml b/emulators/skyemu.yml index 4b4bd378..a6e0e972 100644 --- a/emulators/skyemu.yml +++ b/emulators/skyemu.yml @@ -23,7 +23,7 @@ notes: | stub firmware (NDS), and HLE boot skip (GB/GBC). BIOS loading controlled by core options (default: on). Files loaded from system_dir/SkyEmu/ subdirectory. Standalone validates exact BIOS file size, while libretro only rejects files exceeding buffer. - .info missing gb_bios.bin (gb.h:1603). + The .info does not list gb_bios.bin, which the code reads (gb.h:1603). files: - name: cgb_boot.bin diff --git a/emulators/speccy.yml b/emulators/speccy.yml index 93cb291d..72b6e64c 100644 --- a/emulators/speccy.yml +++ b/emulators/speccy.yml @@ -144,7 +144,6 @@ files: source_ref: "libmain.so LoadBIOS 0x5b2e4-0x5b2f4, Speccy59-Ubuntu-x86-bin speccy 0x3dcbe-0x3dcd1" - name: ZXS16.ROM - aliases: ["ZXS48.ROM", "48.rom"] system: sinclair-zx-spectrum required: true max_size: 16384 diff --git a/emulators/squirreljme.yml b/emulators/squirreljme.yml index 6afddb4f..3cb49e82 100644 --- a/emulators/squirreljme.yml +++ b/emulators/squirreljme.yml @@ -17,7 +17,7 @@ notes: | All entries are optional: without a class library, the core runs a built-in modeless stars demo but cannot execute J2ME software. The .info declares firmware_count=8 but the code defines 10 search names - (squirreljme-{version}-test.jar and squirreljme-test.jar are missing from .info). + (the two not in the .info are squirreljme-{version}-test.jar and squirreljme-test.jar). files: - name: "squirreljme-0.3.0-fast.jar" diff --git a/emulators/uw8.yml b/emulators/uw8.yml index e7ad6e8c..fec80a14 100644 --- a/emulators/uw8.yml +++ b/emulators/uw8.yml @@ -14,4 +14,4 @@ notes: > WebAssembly-based fantasy console. 320x240 screen, 32-color palette, 256KB memory. The WASM runtime (platform + loader modules) is compiled to C via wasm2c and embedded in the binary. No external files required. - firmware_count=0 in .info is correct. + The .info declares firmware_count=0. diff --git a/emulators/vice_x64dtv.yml b/emulators/vice_x64dtv.yml index 9c8453d9..6df7689e 100644 --- a/emulators/vice_x64dtv.yml +++ b/emulators/vice_x64dtv.yml @@ -17,7 +17,7 @@ notes: > DTV has its own color system (viciidtv-color.c), and external palette loading is disabled. JiffyDOS is not supported (excluded from build conditionals). The .info declares 4 JiffyDOS firmware (firmware_count=4) but the code - never loads them for this core. The .info also omits dtvrom.bin. + never loads them for this core. The .info does not list dtvrom.bin. files: - name: "dtvrom.bin" diff --git a/scripts/verify.py b/scripts/verify.py index 09947723..d9c5db0d 100644 --- a/scripts/verify.py +++ b/scripts/verify.py @@ -666,6 +666,17 @@ def find_undeclared_files( path_base, by_name, by_path_suffix, data_names, by_name_lower, ) + if in_repo and not any( + f.get(h) for h in ("sha1", "md5", "crc32", "sha256") + ): + # A name alone answers nothing when every copy of that + # name fails the size the code checks: the resolver + # rejects them all. NanoBoyAdvance's 16 KB bios.bin met + # only PCem's and QEMU's PC BIOS images of that name. + # The fallback below then decides as for any mismatch. + _lp, _st = resolve_local_file(f, db, dest_hint=dest) + if _st == "hash_mismatch": + in_repo = False if not in_repo: # Hash fallback: the repo may hold the content under a # different filename (exos21.rom vs exos21.bin). diff --git a/tests/test_audit_regressions.py b/tests/test_audit_regressions.py index 3fe7785d..1ca902ae 100644 --- a/tests/test_audit_regressions.py +++ b/tests/test_audit_regressions.py @@ -5453,6 +5453,109 @@ class AHashScanningCoreContradictsNoPlatform(unittest.TestCase): self.assertEqual(claimed, []) +class AHashScanLabelNamesNoOtherDump(unittest.TestCase): + """A core that finds its firmware by content names no file, so each + entry name is a label. ares labelled its Famicom Disk System revision + (RP2C33-01, crc32 1c7ae5d5) disksys.rom, the name six NES cores read for + the RP2C33A-01A dump: on ROCKNIX the manifest put ares's revision at + disksys.rom for all of them. A label that another profile gives a + different dump is refused, the two dumps compared through the + collection's hash indexes.""" + + # Names the core's own code gives the image, not labels. + CODE_NAMED = { + ("bizhawk", "MSX.rom"): + "FirmwareDatabase.cs:362 names the image MSX.rom", + ("swanstation", "ps1_rom.bin"): + "the whole PS3 image is the file the PS3 itself names ps1_rom.bin, " + "beetle_psx reads its first 512 KB under the same name", + } + + @staticmethod + def _identity(entry: dict, db: dict) -> tuple[set, set]: + raw: set = set() + sha1s: set = set() + indexes = db["indexes"] + for field, index in (("sha1", None), ("md5", "by_md5"), + ("crc32", "by_crc32"), ("sha256", "by_sha256")): + value = entry.get(field) + values = value if isinstance(value, list) else [value] if value else [] + for item in values: + for digest in str(item).lower().split(","): + digest = digest.strip() + if not digest: + continue + raw.add((field, digest)) + sha1 = digest if index is None else indexes[index].get(digest) + if sha1 in db["files"]: + sha1s.add(sha1) + return raw, sha1s + + def test_labels_are_not_other_dumps_names(self): + import json + + from scripts.common import load_emulator_profiles + + if not (ROOT / "database.json").exists(): + self.skipTest("database.json is not built") + db = json.loads((ROOT / "database.json").read_text(encoding="utf-8")) + profiles = load_emulator_profiles(str(ROOT / "emulators")) + by_name: dict[str, list[tuple[str, dict]]] = {} + for emulator, profile in profiles.items(): + for entry in profile.get("files") or []: + for name in [entry.get("name"), *(entry.get("aliases") or [])]: + if name: + by_name.setdefault(str(name).casefold(), []).append((emulator, entry)) + clashes = [] + for emulator, profile in profiles.items(): + if profile.get("found_by") != "content": + continue + for entry in profile.get("files") or []: + raw, sha1s = self._identity(entry, db) + if not sha1s: + continue + for name in [entry.get("name"), *(entry.get("aliases") or [])]: + if (emulator, name) in self.CODE_NAMED: + continue + for other, other_entry in by_name.get(str(name).casefold(), []): + if other == emulator: + continue + other_raw, other_sha1s = self._identity(other_entry, db) + if other_sha1s and not raw & other_raw and not sha1s & other_sha1s: + clashes.append(f"{emulator} {name} is {other}'s name for another dump") + self.assertEqual(sorted(set(clashes)), []) + + +class ANoteOutsideTheSystemDirCarriesLoadFrom(unittest.TestCase): + """mame2000 opens hiscore.dat, history.dat and mameinfo.dat from the + directory of the loaded content (src/libretro/fileio.c:1002-1014), and + mame2014 and mame2015 open hiscore.dat from the working directory + (src/emu/hiscore.c:330-337). Their notes said so, but no load_from did: + the entries claimed system/hiscore.dat and the RetroArch pack shipped a + file neither core opens there. A libretro entry whose note places the + read outside the system directory declares where.""" + + ELSEWHERE = re.compile( + r"\b(?:opened|read|loaded)\b[^.]*?\bfrom (?:the )?(?:working directory|" + r"ROM directory|content directory|directory of the loaded content)" + r"|IMAMEBASEPATH", + re.IGNORECASE, + ) + + def test_prose_and_load_from_agree(self): + offenders = [] + for path in sorted((ROOT / "emulators").glob("*.yml")): + profile = yaml.safe_load(path.read_text(encoding="utf-8")) or {} + if profile.get("type") != "libretro": + continue + for entry in profile.get("files") or []: + if entry.get("path") or entry.get("load_from") or entry.get("mode") == "standalone": + continue + if self.ELSEWHERE.search(str(entry.get("note") or "")): + offenders.append(f"{path.name}: {entry.get('name')}") + self.assertEqual(offenders, []) + + class ReportsHoldTheDataTrees(unittest.TestCase): """Run on their own, the README, site and slot reports resolved against data/ without the shared hold, so a concurrent refresh could swap a tree diff --git a/tests/test_launcher_layer.py b/tests/test_launcher_layer.py index dd029a6d..4231a0c8 100644 --- a/tests/test_launcher_layer.py +++ b/tests/test_launcher_layer.py @@ -231,6 +231,25 @@ class LauncherLayerTest(unittest.TestCase): self.assertIn("v103.bin", report) self.assertIn("boot.bin", report) + def test_a_name_whose_copies_fail_the_size_is_not_held(self) -> None: + """NanoBoyAdvance reads a 16 KB bios.bin, and the collection's + bios.bin files are PCem's and QEMU's PC BIOS images. Counted held by + name, the entry escaped the launcher layer, which settles only what + the collection lacks, and the ROCKNIX pack carried PCem's 64 KB image + beside the gba_bios.bin its launcher passes.""" + profile = { + "emulator": "FixEmu", "type": "standalone", "systems": ["test-sys"], + "files": [{"name": "core.bin", "required": True, + "size": len(CONTENT["plain.bin"]), "validation": ["size"]}], + } + with open(os.path.join(self.emulators_dir, "fixemu.yml"), "w") as fh: + yaml.dump(profile, fh) + self.config["launchers"][2]["names"] = ["plain.bin"] + _emulator_profiles_cache.clear() + self.profiles = load_emulator_profiles(self.emulators_dir) + self.assertNotIn("core.bin", {u["name"] for u in self._report()}) + self.assertNotIn("core.bin", self._pack_names()) + def test_no_place_is_filled_twice(self) -> None: added = [u["path"] for u in self._report() if u.get("launcher")] self.assertEqual(sorted(added), ["e.bin", "sub/fix.bin"]) diff --git a/tests/test_prose_style.py b/tests/test_prose_style.py index 6893f5fb..1451ba62 100644 --- a/tests/test_prose_style.py +++ b/tests/test_prose_style.py @@ -225,6 +225,18 @@ class ProfilesStateFactsAboutOtherProjects(unittest.TestCase): re.IGNORECASE, ) + # A line that names another project's file grades it when it calls the + # file correct or accurate, says it still declares something, or that an + # entry is missing from it or omitted. prosystem, retrodream, + # fbalpha2012_neogeo and seven more profiles wrote it that way. + OTHER_FILE = re.compile(r"(\.info\b|core-info|System\.dat)", re.IGNORECASE) + GRADES = re.compile( + r"\b(?:is (?:correct|accurate|inaccurate|outdated|stale)|accurate here|" + r"correct:|still (?:declares|lists)|omits|missing (?:the|from))|" + r"\.info missing", + re.IGNORECASE, + ) + def test_profile_prose_and_comments(self): offenders = [ f"{path.relative_to(ROOT)}: {line.strip()[:100]}" @@ -234,6 +246,15 @@ class ProfilesStateFactsAboutOtherProjects(unittest.TestCase): ] self.assertEqual(offenders, [], "\n".join(offenders[:40])) + def test_no_grade_on_another_projects_file(self): + offenders = [ + f"{path.relative_to(ROOT)}: {line.strip()[:100]}" + for path in sorted((ROOT / "emulators").glob("*.yml")) + for line in path.read_text(encoding="utf-8").splitlines() + if self.OTHER_FILE.search(line) and self.GRADES.search(line) + ] + self.assertEqual(offenders, [], "\n".join(offenders[:40])) + class NoClauseSemicolons(unittest.TestCase): def _check(self, files: list[Path], extract) -> None: diff --git a/wiki/testing-guide.md b/wiki/testing-guide.md index da47320f..8376d164 100644 --- a/wiki/testing-guide.md +++ b/wiki/testing-guide.md @@ -266,8 +266,10 @@ to the way the project writes: number with its criterion says more. - Headings in sentence case, never repeating the site name. - In emulator profiles, no verdict on another project's file: "phantom", - "misleading", "bogus", "typo", "has errors", "incorrectly says". The note - states what that file declares and what the code reads. + "misleading", "bogus", "typo", "has errors", "incorrectly says", and on a + line naming a .info, core-info or System.dat no "is correct", "is + accurate", "still declares", "omits" or "missing". The note states what + that file declares and what the code reads. Formatting stays: bold labels, lists, tables and admonitions make a page easier to use. When markup shows raw on the site, the rendering is fixed and