CharlesCNorton commited on
Commit
d062318
·
1 Parent(s): df99f2e

Smoke test for play.py

Browse files

Drives play.main() against neural_alu8 (Demos 1-3) and
neural_computer8_small (Demo 4 CPU loop), captures stdout, and asserts
the deterministic success markers from the cmp8bit cascade, the mod-5
top-level OR, and the CPU sum 5+4+3+2+1 = 15. Catches the kind of drift
where play.py outpaces a refactor of the gate naming convention.
Runs in ~8 s on CPU.

Files changed (2) hide show
  1. tests/__init__.py +0 -0
  2. tests/test_play.py +72 -0
tests/__init__.py ADDED
File without changes
tests/test_play.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Smoke tests for play.py.
2
+
3
+ Drives play.main() against the smallest CPU variant (neural_computer8_small,
4
+ 1 KB memory) and the smallest ALU-only variant (neural_alu8). Each demo
5
+ prints a deterministic block on stdout; we capture and grep for the success
6
+ markers. Catches the kind of drift where play.py outpaces a refactor of the
7
+ gate naming convention (cmp8bit, mod5, etc.) without anyone running it.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import io
12
+ import os
13
+ import sys
14
+ import unittest
15
+ from contextlib import redirect_stdout
16
+
17
+ REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
18
+ if REPO_ROOT not in sys.path:
19
+ sys.path.insert(0, REPO_ROOT)
20
+
21
+ import play # noqa: E402
22
+
23
+
24
+ def _run(model_path: str, skip_cpu: bool) -> str:
25
+ argv_save = sys.argv
26
+ sys.argv = ["play.py", "--model", model_path]
27
+ if skip_cpu:
28
+ sys.argv.append("--skip-cpu")
29
+ buf = io.StringIO()
30
+ try:
31
+ with redirect_stdout(buf):
32
+ rc = play.main()
33
+ finally:
34
+ sys.argv = argv_save
35
+ if rc not in (0, None):
36
+ raise AssertionError(f"play.main() returned {rc}")
37
+ return buf.getvalue()
38
+
39
+
40
+ class TestPlay(unittest.TestCase):
41
+
42
+ def test_alu_variant(self) -> None:
43
+ path = os.path.join(REPO_ROOT, "variants", "neural_alu8.safetensors")
44
+ out = _run(path, skip_cpu=True)
45
+ # Booleans
46
+ for line in ["and 00->0 01->0 10->0 11->1",
47
+ "or 00->0 01->1 10->1 11->1",
48
+ "xor 00->0 01->1 10->1 11->0"]:
49
+ self.assertIn(line, out, f"missing boolean line: {line!r}")
50
+ # ALU ADD/SUB
51
+ self.assertIn("127 + 128 = 255 (carry=0) expected 255 [OK]", out)
52
+ self.assertIn("127 - 128 = 255 (no_borrow=0) expected 255 [OK]", out)
53
+ # CMP via cmp8bit cascade
54
+ self.assertIn("50 vs 30 -> GT=1 LT=0 EQ=0", out)
55
+ self.assertIn("77 vs 77 -> GT=0 LT=0 EQ=1", out)
56
+ self.assertIn("128 vs 127 -> GT=1 LT=0 EQ=0", out)
57
+ # MUL
58
+ self.assertIn("12 * 11 = 132 expected 132 [OK]", out)
59
+ # mod-5 (52 multiples of 5 in [0, 255])
60
+ self.assertIn("52 hits", out)
61
+ self.assertNotIn("FAIL", out)
62
+
63
+ def test_cpu_variant(self) -> None:
64
+ path = os.path.join(REPO_ROOT, "variants", "neural_computer8_small.safetensors")
65
+ out = _run(path, skip_cpu=False)
66
+ self.assertIn("M[0x0083] = 15 (expected 15) [OK]", out)
67
+ self.assertIn("R0=15", out)
68
+ self.assertNotIn("FAIL", out)
69
+
70
+
71
+ if __name__ == "__main__":
72
+ unittest.main()