CharlesCNorton
Smoke test for play.py
d062318
"""Smoke tests for play.py.
Drives play.main() against the smallest CPU variant (neural_computer8_small,
1 KB memory) and the smallest ALU-only variant (neural_alu8). Each demo
prints a deterministic block on stdout; we capture and grep for the success
markers. Catches the kind of drift where play.py outpaces a refactor of the
gate naming convention (cmp8bit, mod5, etc.) without anyone running it.
"""
from __future__ import annotations
import io
import os
import sys
import unittest
from contextlib import redirect_stdout
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if REPO_ROOT not in sys.path:
sys.path.insert(0, REPO_ROOT)
import play # noqa: E402
def _run(model_path: str, skip_cpu: bool) -> str:
argv_save = sys.argv
sys.argv = ["play.py", "--model", model_path]
if skip_cpu:
sys.argv.append("--skip-cpu")
buf = io.StringIO()
try:
with redirect_stdout(buf):
rc = play.main()
finally:
sys.argv = argv_save
if rc not in (0, None):
raise AssertionError(f"play.main() returned {rc}")
return buf.getvalue()
class TestPlay(unittest.TestCase):
def test_alu_variant(self) -> None:
path = os.path.join(REPO_ROOT, "variants", "neural_alu8.safetensors")
out = _run(path, skip_cpu=True)
# Booleans
for line in ["and 00->0 01->0 10->0 11->1",
"or 00->0 01->1 10->1 11->1",
"xor 00->0 01->1 10->1 11->0"]:
self.assertIn(line, out, f"missing boolean line: {line!r}")
# ALU ADD/SUB
self.assertIn("127 + 128 = 255 (carry=0) expected 255 [OK]", out)
self.assertIn("127 - 128 = 255 (no_borrow=0) expected 255 [OK]", out)
# CMP via cmp8bit cascade
self.assertIn("50 vs 30 -> GT=1 LT=0 EQ=0", out)
self.assertIn("77 vs 77 -> GT=0 LT=0 EQ=1", out)
self.assertIn("128 vs 127 -> GT=1 LT=0 EQ=0", out)
# MUL
self.assertIn("12 * 11 = 132 expected 132 [OK]", out)
# mod-5 (52 multiples of 5 in [0, 255])
self.assertIn("52 hits", out)
self.assertNotIn("FAIL", out)
def test_cpu_variant(self) -> None:
path = os.path.join(REPO_ROOT, "variants", "neural_computer8_small.safetensors")
out = _run(path, skip_cpu=False)
self.assertIn("M[0x0083] = 15 (expected 15) [OK]", out)
self.assertIn("R0=15", out)
self.assertNotIn("FAIL", out)
if __name__ == "__main__":
unittest.main()