#!/usr/bin/env python3 """The gate that inspects contributor files before a PR is reviewed. validate.yml runs this on every added or modified file under bios/, and its output is posted to the PR. It reads paths chosen by whoever opened the PR, so it is an untrusted-input boundary with nothing behind it. """ from __future__ import annotations import os import sys import tempfile import unittest from pathlib import Path REPO_ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(REPO_ROOT / "scripts")) import validate_pr # noqa: E402 EMPTY_HASHES: dict = {"sha1": set(), "md5": set(), "names": set()} class _Fixture(unittest.TestCase): def setUp(self): self._tmp = tempfile.TemporaryDirectory() self.tmp = Path(self._tmp.name) self._cwd = os.getcwd() os.chdir(self.tmp) def tearDown(self): os.chdir(self._cwd) self._tmp.cleanup() def _bios_file(self, relative: str, data: bytes = b"BIOS") -> str: path = self.tmp / relative path.parent.mkdir(parents=True, exist_ok=True) path.write_bytes(data) return relative class FileShapeChecks(_Fixture): def test_missing_file_fails_without_hashing(self): result = validate_pr.validate_file("bios/nope.bin", None, EMPTY_HASHES) self.assertFalse(result.passed) self.assertEqual(result.sha1, "") def test_empty_file_fails(self): rel = self._bios_file("bios/Sony/PS/empty.bin", b"") result = validate_pr.validate_file(rel, None, EMPTY_HASHES) self.assertFalse(result.passed) self.assertIn("empty", result.to_markdown().lower()) def test_executable_extension_is_refused(self): rel = self._bios_file("bios/Sony/PS/payload.exe") result = validate_pr.validate_file(rel, None, EMPTY_HASHES) self.assertFalse(result.passed) self.assertIn("Blocked file extension", result.to_markdown()) def test_oversized_file_is_refused(self): rel = self._bios_file("bios/Sony/PS/big.bin") original = validate_pr.MAX_FILE_SIZE validate_pr.MAX_FILE_SIZE = 2 try: result = validate_pr.validate_file(rel, None, EMPTY_HASHES) finally: validate_pr.MAX_FILE_SIZE = original self.assertFalse(result.passed) self.assertIn("too large", result.to_markdown()) def test_ordinary_file_passes(self): rel = self._bios_file("bios/Sony/PlayStation/scph.bin") result = validate_pr.validate_file(rel, None, EMPTY_HASHES) self.assertTrue(result.passed, result.to_markdown()) self.assertEqual(len(result.sha1), 40) class AFileOnlyACoreReadsIsKnown(_Fixture): """A file only an emulator profile declares (a standalone firmware, a game engine's data) was reported "not referenced in any platform config - needs manual review".""" def test_the_profiles_that_declare_the_hash_are_named(self): import hashlib # noqa: PLC0415 emulators = self.tmp / "emulators" emulators.mkdir() data = b"engine data" (emulators / "engine.yml").write_text( "emulator: Engine\ntype: standalone\nsystems: [x]\nfiles:\n" f" - name: \"data.bin\"\n sha1: \"{hashlib.sha1(data).hexdigest()}\"\n" ) known = validate_pr.load_profile_hashes(str(emulators)) result = validate_pr.validate_file( self._bios_file("bios/Maker/Engine/data.bin", data), None, EMPTY_HASHES, known, ) self.assertIn(("PASS", "Hash matches a file the emulator profile declares: engine"), result.checks) self.assertFalse(any("not referenced" in message for _s, message in result.checks)) class SymlinksAreRefusedBeforeReading(_Fixture): """A symlink must be rejected without its target being read. The check used to run after compute_hashes, so a link to an endless device would be read until the CI job timed out, and a link outside the checkout would be hashed and reported as though it were contributed. """ def test_symlink_is_refused(self): self._bios_file("bios/Sony/PS/real.bin") link = self.tmp / "bios" / "Sony" / "PS" / "link.bin" link.symlink_to(self.tmp / "bios" / "Sony" / "PS" / "real.bin") result = validate_pr.validate_file( "bios/Sony/PS/link.bin", None, EMPTY_HASHES ) self.assertFalse(result.passed) self.assertIn("Symlink", result.to_markdown()) def test_symlink_target_is_never_hashed(self): self._bios_file("bios/Sony/PS/real.bin") link = self.tmp / "bios" / "Sony" / "PS" / "link.bin" link.symlink_to(self.tmp / "bios" / "Sony" / "PS" / "real.bin") calls = [] real = validate_pr.compute_hashes validate_pr.compute_hashes = lambda p, *a, **k: ( calls.append(p) or real(p, *a, **k) ) try: validate_pr.validate_file("bios/Sony/PS/link.bin", None, EMPTY_HASHES) finally: validate_pr.compute_hashes = real self.assertEqual(calls, [], "a symlink target must not be read") @unittest.skipUnless(os.path.exists("/dev/zero"), "needs /dev/zero") def test_a_link_to_an_endless_device_does_not_hang(self): link = self.tmp / "bios" / "Sony" / "PS" / "zero.bin" link.parent.mkdir(parents=True, exist_ok=True) link.symlink_to("/dev/zero") result = validate_pr.validate_file( "bios/Sony/PS/zero.bin", None, EMPTY_HASHES ) self.assertFalse(result.passed) class HashRecognition(_Fixture): def test_a_declared_sha1_is_recognised(self): rel = self._bios_file("bios/Sony/PS/known.bin") probe = validate_pr.validate_file(rel, None, EMPTY_HASHES) known = {"sha1": {probe.sha1}, "md5": set(), "names": set()} result = validate_pr.validate_file(rel, None, known) self.assertIn("SHA1 matches known platform requirement", result.to_markdown()) def test_a_declared_md5_is_recognised(self): rel = self._bios_file("bios/Sony/PS/known.bin") probe = validate_pr.validate_file(rel, None, EMPTY_HASHES) known = {"sha1": set(), "md5": {probe.md5}, "names": set()} self.assertIn( "MD5 matches known platform requirement", validate_pr.validate_file(rel, None, known).to_markdown(), ) def test_a_known_name_with_another_hash_is_flagged_as_a_variant(self): rel = self._bios_file("bios/Sony/PS/known.bin") known = {"sha1": set(), "md5": set(), "names": {"known.bin"}} markdown = validate_pr.validate_file(rel, None, known).to_markdown() self.assertIn("may be a variant", markdown) self.assertIn(".variants/known.bin.", markdown) def test_an_unreferenced_file_asks_for_review(self): rel = self._bios_file("bios/Sony/PS/mystery.bin") markdown = validate_pr.validate_file(rel, None, EMPTY_HASHES).to_markdown() self.assertIn("needs manual review", markdown) def test_a_duplicate_of_a_known_file_is_reported(self): rel = self._bios_file("bios/Sony/PS/dupe.bin") probe = validate_pr.validate_file(rel, None, EMPTY_HASHES) db = {"files": {probe.sha1: {"path": "bios/Sony/PS/original.bin"}}} markdown = validate_pr.validate_file(rel, db, EMPTY_HASHES).to_markdown() self.assertIn("Duplicate", markdown) self.assertIn("original.bin", markdown) # A duplicate is worth saying out loud but must not block the PR. self.assertTrue(validate_pr.validate_file(rel, db, EMPTY_HASHES).passed) def test_the_file_the_database_holds_is_not_its_own_duplicate(self): """The documented example on a tracked file warned "Duplicate: identical file already exists at" the very path being validated.""" rel = self._bios_file("bios/Sony/PS/held.bin") probe = validate_pr.validate_file(rel, None, EMPTY_HASHES) db = {"files": {probe.sha1: {"path": rel}}} markdown = validate_pr.validate_file(rel, db, EMPTY_HASHES).to_markdown() self.assertNotIn("Duplicate", markdown) self.assertIn("Already in the database at this path", markdown) class PlacementBySystem(_Fixture): """Depth alone passed a PS1 BIOS filed under bios/Nintendo/Game Boy/: a declared hash names its system, and the files of that system already held name its folder.""" def _known(self, sha1: str) -> dict: return {"sha1": {sha1}, "md5": set(), "names": set(), "folders": {sha1: {"bios/Sony/PlayStation/"}}} def test_a_file_under_another_maker_is_told_where_its_system_lives(self): rel = self._bios_file("bios/Nintendo/Game Boy/scph.bin") probe = validate_pr.validate_file(rel, None, EMPTY_HASHES) markdown = validate_pr.validate_file(rel, None, self._known(probe.sha1)).to_markdown() self.assertIn("bios/Sony/PlayStation/", markdown) self.assertNotIn("Correct placement", markdown) def test_the_folder_of_its_system_passes(self): rel = self._bios_file("bios/Sony/PlayStation/scph.bin") probe = validate_pr.validate_file(rel, None, EMPTY_HASHES) markdown = validate_pr.validate_file(rel, None, self._known(probe.sha1)).to_markdown() self.assertIn("Correct placement: bios/Sony/PlayStation/", markdown) def test_systems_spread_over_makers_name_no_home(self): platforms = self.tmp / "platforms" platforms.mkdir() (platforms / "_registry.yml").write_text( "platforms:\n p:\n config: p.yml\n status: active\n", encoding="utf-8") (platforms / "p.yml").write_text( "platform: P\nsystems:\n arcade:\n files:\n" " - {name: a.zip, sha1: aaaa}\n - {name: b.zip, sha1: bbbb}\n", encoding="utf-8", ) db = {"files": {"aaaa": {"path": "bios/Arcade/MAME/a.zip"}, "bbbb": {"path": "bios/Apple/Apple II/b.zip"}}, "indexes": {"by_md5": {}}} known = validate_pr.load_platform_hashes(str(platforms), db) self.assertEqual(known["folders"], {}) class Placement(_Fixture): def test_manufacturer_and_console_directories_are_accepted(self): rel = self._bios_file("bios/Sega/Saturn/sega_101.bin") self.assertIn( "Correct placement: bios/Sega/Saturn/", validate_pr.validate_file(rel, None, EMPTY_HASHES).to_markdown(), ) def test_a_file_at_the_bios_root_is_flagged(self): rel = self._bios_file("bios/loose.bin") self.assertIn( "bios/Manufacturer/Console/", validate_pr.validate_file(rel, None, EMPTY_HASHES).to_markdown(), ) def test_a_file_outside_bios_is_flagged(self): rel = self._bios_file("elsewhere/loose.bin") self.assertIn( "not under bios/", validate_pr.validate_file(rel, None, EMPTY_HASHES).to_markdown(), ) class Reporting(_Fixture): def test_markdown_carries_every_digest_and_the_verdict(self): rel = self._bios_file("bios/Sega/Saturn/sega.bin") result = validate_pr.validate_file(rel, None, EMPTY_HASHES) markdown = result.to_markdown() for value in (result.sha1, result.md5, result.crc32): self.assertIn(value, markdown) self.assertIn("✅", markdown) def test_a_failure_is_marked_in_the_heading(self): rel = self._bios_file("bios/Sega/Saturn/bad.exe") self.assertIn("❌", validate_pr.validate_file(rel, None, EMPTY_HASHES).to_markdown()) class PlatformHashLoading(_Fixture): def test_missing_directory_yields_empty_sets(self): known = validate_pr.load_platform_hashes(str(self.tmp / "absent")) self.assertEqual(known, {"sha1": set(), "md5": set(), "names": set(), "folders": {}}) def test_declared_hashes_and_names_are_collected(self): platforms = self.tmp / "platforms" platforms.mkdir() (platforms / "_registry.yml").write_text( "platforms:\n demo:\n status: active\n" ) (platforms / "demo.yml").write_text( "platform: Demo\n" "systems:\n" " test-sys:\n" " files:\n" " - name: boot.bin\n" f" sha1: {'a' * 40}\n" f" md5: {'b' * 32}\n" ) known = validate_pr.load_platform_hashes(str(platforms)) self.assertIn("a" * 40, known["sha1"]) self.assertIn("b" * 32, known["md5"]) self.assertIn("boot.bin", known["names"]) class FlagsAreAppliedOrRefused(unittest.TestCase): """Files named beside --changed were dropped for the git diff and the run said "No changed BIOS files detected" about files it never read; --json beside --markdown printed JSON and dropped the markdown.""" def _run(self, *argv: str): import subprocess # noqa: PLC0415 return subprocess.run( [sys.executable, "scripts/validate_pr.py", *argv], cwd=REPO_ROOT, capture_output=True, text=True, timeout=300, check=False, ) def test_files_beside_changed_are_refused(self): result = self._run("--changed", "bios/Sony/PlayStation/scph5501.bin", "--json") self.assertEqual(result.returncode, 2) self.assertIn("--changed", result.stderr) def test_two_output_formats_are_refused(self): result = self._run("--json", "--markdown", "bios/Sony/PlayStation/scph5501.bin") self.assertEqual(result.returncode, 2) self.assertIn("not allowed with", result.stderr) if __name__ == "__main__": unittest.main()