""" AVM ISA v1 — Cross-Validated Test Harness (Python) """ import sys, os sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'python')) from avm import Instr, Prim, State, run, push_q16, push_bool, push_q0, prim, pop, dup, swap, store, load, \ halt, jump, jump_if, make_instr, Q16_SCALE, AVM_CLAMP_MIN, AVM_CLAMP_MAX AVM_CLAMP_MAX = 2147483647 AVM_MAX_STACK = 1024 def S(nl=0): return State(pc=0, stack=[], locals=[None]*nl, halted=False) def test_basic_add(): """5 + 3 = 8""" prog = [push_q16(5 * Q16_SCALE), push_q16(3 * Q16_SCALE), prim(Prim.ADD_SAT_Q16), halt()] s = run(S(), prog, 100) assert s.halted assert s.stack[0].val == 8 * Q16_SCALE, f"Got {s.stack[0].val}" print(" ✅ basic_add: 5 + 3 = 8") def test_div_q16(): """3 / 5 = 0.6 in Q16_16""" prog = [push_q16(3 * Q16_SCALE), push_q16(5 * Q16_SCALE), prim(Prim.DIV_SAT_Q16), halt()] s = run(S(), prog, 100) expected = (3 * Q16_SCALE) // 5 # floor(0.6 * 65536) = 39321 assert s.stack[0].val == expected, f"Got {s.stack[0].val}, expected {expected}" print(" ✅ div_q16: 3/5 = 0.6") def test_saturation(): """max-1 + 2 → max""" prog = [push_q16(AVM_CLAMP_MAX - 1), push_q16(2), prim(Prim.ADD_SAT_Q16), halt()] s = run(S(), prog, 100) assert s.stack[0].val == AVM_CLAMP_MAX print(" ✅ saturation: max-1 + 2 = max") def test_v6_comparison(): cases = [(-5, -3, True), (-3, -5, False), (5, 3, False), (3, 5, True), (-1, 2, True)] for a, b, exp in cases: prog = [push_q16(a * Q16_SCALE), push_q16(b * Q16_SCALE), prim(Prim.LT_Q16), halt()] s = run(S(), prog, 100) assert s.stack[0].val == exp, f"ltQ16({a},{b}): exp {exp}, got {s.stack[0].val}" print(" ✅ v6_comparison: 5 cases pass") def test_type_mismatch(): prog = [push_bool(True), push_q16(Q16_SCALE), prim(Prim.ADD_SAT_Q16)] try: run(S(), prog, 100) return "❌ type_mismatch: should fail" except (TypeError, ValueError): print(" ✅ type_mismatch: rejected") def test_stack_overflow(): prog = [push_q16(0)] * (AVM_MAX_STACK + 1) try: run(S(), prog, 10000) print(" ❌ stack_overflow: should fail") except ValueError: print(" ✅ stack_overflow: rejected") def test_division_by_zero(): prog = [push_q16(Q16_SCALE), push_q16(0), prim(Prim.DIV_SAT_Q16)] try: run(S(), prog, 100) print(" ❌ div_by_zero: should fail") except (ValueError, ZeroDivisionError): print(" ✅ div_by_zero: rejected") def test_control_flow(): prog = [push_bool(True), jump_if(4), push_q16(0), halt(), push_q16(Q16_SCALE), halt()] s = run(S(), prog, 100) assert s.stack[0].val == Q16_SCALE print(" ✅ control_flow: jump_if true") def test_locals(): prog = [push_q16(42 * Q16_SCALE), store(0), load(0), halt()] s = run(S(1), prog, 100) assert s.stack[0].val == 42 * Q16_SCALE print(" ✅ locals: store+load") def test_mul_div_rt(): prog = [push_q16(5*Q16_SCALE), push_q16(3*Q16_SCALE), prim(Prim.MUL_SAT_Q16), push_q16(3*Q16_SCALE), prim(Prim.DIV_SAT_Q16), halt()] s = run(S(), prog, 100) assert s.stack[0].val == 5 * Q16_SCALE print(" ✅ mul_div_roundtrip: 5*3/3 = 5") if __name__ == "__main__": print("AVM Python — Test Harness") print("=========================") for fn in [test_basic_add, test_div_q16, test_saturation, test_v6_comparison, test_type_mismatch, test_stack_overflow, test_division_by_zero, test_control_flow, test_locals, test_mul_div_rt]: fn() print("\nAll Python tests passed.")