test(compat): pin independent Pong gameplay checks

This commit is contained in:
0xrsydn 2026-10-06 22:39:40 +07:00
commit 9bea613674
10 changed files with 348 additions and 0 deletions

View file

@ -0,0 +1,114 @@
"""Offline adapter tests. All fixture bytes are original synthetic data."""
import hashlib
import json
from pathlib import Path
import tempfile
import unittest
from unittest.mock import patch
import prepare_homebrew_pong as adapter
from prepare_gba_tests import ROOT, MAX_FILE_BYTES
class PongPreparation(unittest.TestCase):
def setUp(self):
self.temp = tempfile.TemporaryDirectory()
self.addCleanup(self.temp.cleanup)
self.base = Path(self.temp.name)
self.output = self.base / "prepared"
self.source = self.base / "source"
self.source.mkdir()
real = json.loads(adapter.LOCK.read_text())
self.files = {name: f"original fixture: {name}\n".encode() for name in real["files"]}
self.lock = dict(real, files={name: {"bytes": len(data), "sha256": hashlib.sha256(data).hexdigest()}
for name, data in self.files.items()})
self.lock_path = self.base / "lock.json"
self.lock_path.write_text(json.dumps(self.lock))
self.addCleanup(patch.stopall)
patch.object(adapter, "LOCK", self.lock_path).start()
for name, data in self.files.items():
path = self.source / name
path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(data)
def test_offline_preparation_retains_license_and_is_repeatable(self):
first = adapter.prepare(self.output, self.source)
second = adapter.prepare(self.base / "second", self.source)
self.assertEqual(first.read_bytes(), second.read_bytes())
self.assertEqual(json.loads(first.read_text()), adapter.manifest(self.lock["rom"]))
self.assertEqual(json.loads((self.output / "source-lock.json").read_text()), self.lock)
for name, data in self.files.items():
self.assertEqual((self.output / name).read_bytes(), data)
with self.assertRaisesRegex(ValueError, "already exists"):
adapter.prepare(self.output, self.source)
self.assertEqual((self.output / "LICENSE").read_bytes(), self.files["LICENSE"])
def test_download_uses_only_pinned_revision_urls(self):
base = f"https://raw.githubusercontent.com/{self.lock['repository']}/{self.lock['revision']}/"
calls = []
def download(url):
self.assertTrue(url.startswith(base))
calls.append(url.removeprefix(base))
return self.files[calls[-1]]
adapter.prepare(self.output, download=download)
self.assertEqual(calls, list(self.files))
def test_bad_hash_size_missing_and_oversized_files_leave_no_output(self):
rom = self.source / self.lock["rom"]
original = rom.read_bytes()
for replacement in [bytes(len(original)), original + b"x", b"x" * (MAX_FILE_BYTES + 1)]:
rom.write_bytes(replacement)
with self.assertRaises(ValueError):
adapter.prepare(self.output, self.source)
self.assertFalse(self.output.exists())
rom.unlink()
with self.assertRaises(ValueError):
adapter.prepare(self.output, self.source)
self.assertFalse(self.output.exists())
def test_network_failure_leaves_no_output(self):
def fail(_):
raise OSError("offline")
with self.assertRaises(OSError):
adapter.prepare(self.output, download=fail)
self.assertFalse(self.output.exists())
def test_tracked_and_symlinked_destinations_are_rejected_before_fetch(self):
def fail(_):
self.fail("must not fetch for unsafe destination")
with self.assertRaises(ValueError):
adapter.prepare(ROOT / "tracked-pong-test", download=fail)
link = self.base / "linked"
link.symlink_to(ROOT, target_is_directory=True)
with self.assertRaises(ValueError):
adapter.prepare(link / "tracked-pong-test", download=fail)
with self.assertRaises(ValueError):
adapter.prepare(self.base / "missing" / "out", download=fail)
def test_manifest_has_bounded_distinct_scenarios_and_complete_button_snapshots(self):
manifest = adapter.manifest("test.gba")
self.assertEqual(manifest["version"], 2)
cases = manifest["cases"]
self.assertEqual(len({case["name"] for case in cases}), len(cases))
self.assertLessEqual(sum(case["step_limit"] for case in cases), 100000000)
for case in cases:
self.assertEqual(case["rom"], "test.gba")
frames = [event["vblank"] for event in case["inputs"]]
self.assertEqual(frames, sorted(set(frames)))
self.assertTrue(all(frame < case["completion"]["vblanks"] for frame in frames))
for event in case["inputs"]:
self.assertLessEqual(event["buttons"], 1023)
if case["inputs"]:
self.assertEqual(case["inputs"][-1]["buttons"], 0)
self.assertTrue(case["checks"])
for check in case["checks"]:
self.assertEqual(check["kind"], "pixel")
self.assertLess(check["x"], 240)
self.assertLess(check["y"], 160)
self.assertLessEqual(check["equals"], 0xffffff)
if __name__ == "__main__":
unittest.main()