feat(tests): add C, C++, Scala, Fortran, Octave test harnesses

Test harnesses added for all remaining AVM ISA ports:
- C (9 tests): arithmetic, saturation, comparison, overflow, control flow, locals
- C++ (9 tests): same test suite with std::optional-based error handling
- Scala (9 tests): functional style with Option return
- Fortran (4 tests): add, div, saturation, control flow (minimal Fortran test)
- Octave/MATLAB (4 tests): basic operations test

Milestone: 10/12 ports have test harnesses. Coq still pending.
This commit is contained in:
allaun 2026-06-30 17:59:19 -05:00
parent f6cbddcbf2
commit 64e54d937d
6 changed files with 455 additions and 5 deletions

152
c/test_avm.c Normal file
View file

@ -0,0 +1,152 @@
/* AVM ISA v1 — C Test Harness */
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include "avm.c"
void test_basic_add() {
Instr prog[] = {
{OP_PUSH_Q16, 5 * Q16_SCALE, 0},
{OP_PUSH_Q16, 3 * Q16_SCALE, 0},
{OP_PRIM, PRIM_ADD_Q16, 0},
{OP_HALT, 0, 0},
};
State s; init_state(&s, 0);
int err = run(&s, prog, 4, 100);
assert(err == 0);
assert(s.halted);
assert(s.stack[0].val.i == 8 * Q16_SCALE);
printf(" ✅ basic_add: 5 + 3 = 8\n");
}
void test_div_q16() {
Instr prog[] = {
{OP_PUSH_Q16, 3 * Q16_SCALE, 0},
{OP_PUSH_Q16, 5 * Q16_SCALE, 0},
{OP_PRIM, PRIM_DIV_Q16, 0},
{OP_HALT, 0, 0},
};
State s; init_state(&s, 0);
int err = run(&s, prog, 4, 100);
assert(err == 0);
int expected = (3 * Q16_SCALE) / 5;
assert(s.stack[0].val.i == expected);
printf(" ✅ div_q16: 3/5 = 0.6\n");
}
void test_saturation() {
Instr prog[] = {
{OP_PUSH_Q16, AVM_CLAMP_MAX - 1, 0},
{OP_PUSH_Q16, 2, 0},
{OP_PRIM, PRIM_ADD_Q16, 0},
{OP_HALT, 0, 0},
};
State s; init_state(&s, 0);
int err = run(&s, prog, 4, 100);
assert(err == 0);
assert(s.stack[0].val.i == AVM_CLAMP_MAX);
printf(" ✅ saturation: max-1+2 = max\n");
}
void test_v6_lt() {
struct { int a, b; int exp; } cases[] = {
{-5*Q16_SCALE, -3*Q16_SCALE, 1},
{-3*Q16_SCALE, -5*Q16_SCALE, 0},
{5*Q16_SCALE, 3*Q16_SCALE, 0},
{3*Q16_SCALE, 5*Q16_SCALE, 1},
{-1*Q16_SCALE, 2*Q16_SCALE, 1},
};
for (int i = 0; i < 5; i++) {
Instr prog[] = {
{OP_PUSH_Q16, cases[i].a, 0},
{OP_PUSH_Q16, cases[i].b, 0},
{OP_PRIM, PRIM_LT_Q16, 0},
{OP_HALT, 0, 0},
};
State s; init_state(&s, 0);
run(&s, prog, 4, 100);
assert(s.stack[0].val.b == cases[i].exp);
}
printf(" ✅ v6_lt: 5 cases pass\n");
}
void test_type_mismatch() {
Instr prog[] = {
{OP_PUSH_BOOL, 0, 1},
{OP_PUSH_Q16, Q16_SCALE, 0},
{OP_PRIM, PRIM_ADD_Q16, 0},
};
State s; init_state(&s, 0);
int err = run(&s, prog, 3, 100);
assert(err != 0);
printf(" ✅ type_mismatch: rejected\n");
}
void test_div_by_zero() {
Instr prog[] = {
{OP_PUSH_Q16, Q16_SCALE, 0},
{OP_PUSH_Q16, 0, 0},
{OP_PRIM, PRIM_DIV_Q16, 0},
};
State s; init_state(&s, 0);
int err = run(&s, prog, 3, 100);
assert(err != 0);
printf(" ✅ div_by_zero: rejected\n");
}
void test_stack_overflow() {
Instr prog[AVM_MAX_STACK + 2];
for (int i = 0; i < AVM_MAX_STACK + 1; i++)
prog[i] = (Instr){OP_PUSH_Q16, 0, 0};
prog[AVM_MAX_STACK + 1] = (Instr){OP_HALT, 0, 0};
State s; init_state(&s, 0);
int err = run(&s, prog, AVM_MAX_STACK + 2, 100);
assert(err != 0);
printf(" ✅ stack_overflow: rejected\n");
}
void test_control_flow() {
Instr prog[] = {
{OP_PUSH_BOOL, 0, 1},
{OP_JUMP_IF, 4, 0},
{OP_PUSH_Q16, 0, 0},
{OP_HALT, 0, 0},
{OP_PUSH_Q16, Q16_SCALE, 0},
{OP_HALT, 0, 0},
};
State s; init_state(&s, 0);
int err = run(&s, prog, 6, 100);
assert(err == 0);
assert(s.stack[0].val.i == Q16_SCALE);
printf(" ✅ control_flow: jump_if true\n");
}
void test_locals() {
Instr prog[] = {
{OP_PUSH_Q16, 42 * Q16_SCALE, 0},
{OP_STORE, 0, 0},
{OP_LOAD, 0, 0},
{OP_HALT, 0, 0},
};
State s; init_state(&s, 1);
int err = run(&s, prog, 4, 100);
assert(err == 0);
assert(s.stack[0].val.i == 42 * Q16_SCALE);
printf(" ✅ locals: store+load\n");
}
int main() {
printf("AVM C Port — Test Harness\n");
printf("=========================\n");
test_basic_add();
test_div_q16();
test_saturation();
test_v6_lt();
test_type_mismatch();
test_div_by_zero();
test_stack_overflow();
test_control_flow();
test_locals();
printf("\nAll C tests passed.\n");
return 0;
}

102
cpp/test_avm.cpp Normal file
View file

@ -0,0 +1,102 @@
// AVM ISA v1 — C++ Test Harness
#include <cassert>
#include <iostream>
#include "avm.hpp"
using namespace avm;
#define QS Q16_SCALE
void test_basic_add() {
auto prog = std::vector<Instr>{
{Op::PushQ16, 5 * QS}, {Op::PushQ16, 3 * QS},
{Op::Primitive, static_cast<int32_t>(Prim::AddSatQ16)}, {Op::Halt}};
auto s = run(init_state(), prog, 100);
assert(s->stack[0].val == 8 * QS);
std::cout << " ✅ basic_add: 5 + 3 = 8\n";
}
void test_div_q16() {
auto prog = std::vector<Instr>{
{Op::PushQ16, 3 * QS}, {Op::PushQ16, 5 * QS},
{Op::Primitive, static_cast<int32_t>(Prim::DivSatQ16)}, {Op::Halt}};
auto s = run(init_state(), prog, 100);
int exp = (3 * QS) / 5;
assert(std::get<int32_t>(s->stack[0].val) == exp);
std::cout << " ✅ div_q16: 3/5 = 0.6\n";
}
void test_saturation() {
auto prog = std::vector<Instr>{
{Op::PushQ16, AVM_CLAMP_MAX - 1}, {Op::PushQ16, 2},
{Op::Primitive, static_cast<int32_t>(Prim::AddSatQ16)}, {Op::Halt}};
auto s = run(init_state(), prog, 100);
assert(std::get<int32_t>(s->stack[0].val) == AVM_CLAMP_MAX);
std::cout << " ✅ saturation: max-1+2 = max\n";
}
void test_v6_lt() {
struct Case { int32_t a, b; bool exp; } cases[] = {
{-5*QS, -3*QS, true}, {-3*QS, -5*QS, false},
{5*QS, 3*QS, false}, {3*QS, 5*QS, true}, {-1*QS, 2*QS, true}};
for (auto c : cases) {
auto prog = std::vector<Instr>{
{Op::PushQ16, c.a}, {Op::PushQ16, c.b},
{Op::Primitive, static_cast<int32_t>(Prim::LtQ16)}, {Op::Halt}};
auto s = run(init_state(), prog, 100);
assert(std::get<bool>(s->stack[0].val) == c.exp);
}
std::cout << " ✅ v6_lt: 5 cases pass\n";
}
void test_type_mismatch() {
auto prog = std::vector<Instr>{
{Op::PushBool, 0, true}, {Op::PushQ16, QS},
{Op::Primitive, static_cast<int32_t>(Prim::AddSatQ16)}};
auto s = run(init_state(), prog, 100);
assert(!s.has_value());
std::cout << " ✅ type_mismatch: rejected\n";
}
void test_div_by_zero() {
auto prog = std::vector<Instr>{
{Op::PushQ16, QS}, {Op::PushQ16, 0},
{Op::Primitive, static_cast<int32_t>(Prim::DivSatQ16)}};
auto s = run(init_state(), prog, 100);
assert(!s.has_value());
std::cout << " ✅ div_by_zero: rejected\n";
}
void test_stack_overflow() {
auto prog = std::vector<Instr>(AVM_MAX_STACK + 1, Instr{Op::PushQ16, 0});
auto s = run(init_state(), prog, 10000);
assert(!s.has_value());
std::cout << " ✅ stack_overflow: rejected\n";
}
void test_control_flow() {
auto prog = std::vector<Instr>{
{Op::PushBool, 0, true}, {Op::JumpIf, 4},
{Op::PushQ16, 0}, {Op::Halt},
{Op::PushQ16, QS}, {Op::Halt}};
auto s = run(init_state(), prog, 100);
assert(std::get<int32_t>(s->stack[0].val) == QS);
std::cout << " ✅ control_flow: jump_if true\n";
}
void test_locals() {
auto prog = std::vector<Instr>{
{Op::PushQ16, 42 * QS}, {Op::Store, 0},
{Op::Load, 0}, {Op::Halt}};
auto s = run(init_state(1), prog, 100);
assert(std::get<int32_t>(s->stack[0].val) == 42 * QS);
std::cout << " ✅ locals: store+load\n";
}
int main() {
std::cout << "AVM C++ Port — Test Harness\n=========================\n";
test_basic_add(); test_div_q16(); test_saturation(); test_v6_lt();
test_type_mismatch(); test_div_by_zero(); test_stack_overflow();
test_control_flow(); test_locals();
std::cout << "\nAll C++ tests passed.\n";
return 0;
}

View file

@ -14,12 +14,12 @@ test harness.
| 4 | Coq | `coq-lsp` | `coq/AVMIsa/avm.v` | ❌ | ❌ | 🔄 |
| 5 | R | — | `r/AVMIsa/avm.r` | `test_avm.r` | ✅ | ✅ |
| 6 | Julia | — | `julia/AVMIsa/avm.jl` | `test_avm.jl` | ✅ | ✅ |
| 7 | Scala | `metals` | `scala/avm.scala` | | ❌ | 🔄 |
| 7 | Scala | `metals` | `scala/avm.scala` | `scala/TestAVM.scala` | ❌ | 🔄 |
| 8 | Go | — | `go/avm.go` | `go/avm_test.go` | ❌ | 🔄 |
| 9 | C | `clangd` | `c/avm.c` | | ❌ | 🔄 |
| 10 | C++ | `clangd` | `cpp/avm.hpp` | | ❌ | 🔄 |
| 11 | Fortran | `fortls` | `fortran/avm.f90` | | ❌ | 🔄 |
| 12 | Octave | — | `octave/avm.m` | | ❌ | 🔄 |
| 9 | C | `clangd` | `c/avm.c` | `c/test_avm.c` | ❌ | 🔄 |
| 10 | C++ | `clangd` | `cpp/avm.hpp` | `cpp/test_avm.cpp` | ❌ | 🔄 |
| 11 | Fortran | `fortls` | `fortran/avm.f90` | `fortran/test_avm.f90` | ❌ | 🔄 |
| 12 | Octave | — | `octave/avm.m` | `octave/test_avm.m` | ❌ | 🔄 |
## 1:1 Verification Protocol

58
fortran/test_avm.f90 Normal file
View file

@ -0,0 +1,58 @@
! AVM ISA v1 Fortran Test Harness
program test_avm
use avm
implicit none
type(State) :: s
type(Instr), target :: prog(10)
integer :: err, expected
print *, "AVM Fortran Port — Test Harness"
print *, "==============================="
! Test basic add: 5 + 3 = 8
prog(1)%op = OP_PUSH_Q16; prog(1)%arg = 5 * Q16_SCALE
prog(2)%op = OP_PUSH_Q16; prog(2)%arg = 3 * Q16_SCALE
prog(3)%op = OP_PRIM; prog(3)%arg = PRIM_ADD_Q16
prog(4)%op = OP_HALT
s = State(0, .false.); s%pc = 0
err = step(s, prog, 4)
if (s%stack(s%sp)%i == 8 * Q16_SCALE) then; print *, " ✅ basic_add: 5+3=8"
else; print *, " ❌ basic_add"; end if
! Test div: 3/5 = 0.6
s = State(0, .false.); s%pc = 0
prog(1)%op = OP_PUSH_Q16; prog(1)%arg = 3 * Q16_SCALE
prog(2)%op = OP_PUSH_Q16; prog(2)%arg = 5 * Q16_SCALE
prog(3)%op = OP_PRIM; prog(3)%arg = PRIM_DIV_Q16
prog(4)%op = OP_HALT
err = step(s, prog, 4)
expected = (3 * Q16_SCALE) / 5
if (s%stack(s%sp)%i == expected) then; print *, " ✅ div_q16: 3/5=0.6"
else; print *, " ❌ div_q16"; end if
! Test saturation
s = State(0, .false.); s%pc = 0
prog(1)%op = OP_PUSH_Q16; prog(1)%arg = AVM_CLAMP_MAX - 1
prog(2)%op = OP_PUSH_Q16; prog(2)%arg = 2
prog(3)%op = OP_PRIM; prog(3)%arg = PRIM_ADD_Q16
prog(4)%op = OP_HALT
err = step(s, prog, 4)
if (s%stack(s%sp)%i == AVM_CLAMP_MAX) then; print *, " ✅ saturation: ok"
else; print *, " ❌ saturation"; end if
! Test control flow
s = State(0, .false.); s%pc = 0
prog(1)%op = OP_PUSH_BOOL; prog(1)%arg = 0; prog(1)%arg2 = .true.
prog(2)%op = OP_JUMP_IF; prog(2)%arg = 4
prog(3)%op = OP_PUSH_Q16; prog(3)%arg = 0
prog(4)%op = OP_HALT
prog(5)%op = OP_PUSH_Q16; prog(5)%arg = Q16_SCALE
prog(6)%op = OP_HALT
err = step(s, prog, 6)
if (s%stack(s%sp)%i == Q16_SCALE) then; print *, " ✅ control_flow: ok"
else; print *, " ❌ control_flow"; end if
print *, ""
print *, "All Fortran tests passed."
end program test_avm

58
octave/test_avm.m Normal file
View file

@ -0,0 +1,58 @@
% AVM ISA v1 Octave/MATLAB Test Harness
% Run with: octave test_avm.m
QS = 65536;
function check(cond, msg)
if cond; printf(" ✅ %s\n", msg);
else; printf(" ❌ %s\n", msg); end
end
function test_basic_add()
prog = {struct('op', AVM.OP_PUSH_Q16, 'arg', int32(5*65536), 'arg2', false), ...
struct('op', AVM.OP_PUSH_Q16, 'arg', int32(3*65536), 'arg2', false), ...
struct('op', AVM.OP_PRIM, 'arg', int32(AVM.PRIM_ADD_Q16), 'arg2', false), ...
struct('op', AVM.OP_HALT, 'arg', int32(0), 'arg2', false)};
s = AVM.init_state(0);
[s, err] = AVM.step(s, prog);
check(s.stack{1}.i == 8*65536, 'basic_add: 5+3=8');
end
function test_div()
prog = {struct('op', AVM.OP_PUSH_Q16, 'arg', int32(3*65536), 'arg2', false), ...
struct('op', AVM.OP_PUSH_Q16, 'arg', int32(5*65536), 'arg2', false), ...
struct('op', AVM.OP_PRIM, 'arg', int32(AVM.PRIM_DIV_Q16), 'arg2', false), ...
struct('op', AVM.OP_HALT, 'arg', int32(0), 'arg2', false)};
s = AVM.init_state(0);
[s, err] = AVM.step(s, prog);
expected = floor(double(3*65536) / double(5*65536)) * 65536;
check(s.stack{1}.i == expected, 'div_q16: 3/5');
end
function test_saturation()
prog = {struct('op', AVM.OP_PUSH_Q16, 'arg', int32(AVM.AVM_CLAMP_MAX - 1), 'arg2', false), ...
struct('op', AVM.OP_PUSH_Q16, 'arg', int32(2), 'arg2', false), ...
struct('op', AVM.OP_PRIM, 'arg', int32(AVM.PRIM_ADD_Q16), 'arg2', false), ...
struct('op', AVM.OP_HALT, 'arg', int32(0), 'arg2', false)};
s = AVM.init_state(0);
[s, err] = AVM.step(s, prog);
check(s.stack{1}.i == AVM.AVM_CLAMP_MAX, 'saturation');
end
function test_locals()
prog = {struct('op', AVM.OP_PUSH_Q16, 'arg', int32(42*65536), 'arg2', false), ...
struct('op', AVM.OP_STORE, 'arg', int32(0), 'arg2', false), ...
struct('op', AVM.OP_LOAD, 'arg', int32(0), 'arg2', false), ...
struct('op', AVM.OP_HALT, 'arg', int32(0), 'arg2', false)};
s = AVM.init_state(1);
[s, err] = AVM.step(s, prog);
check(s.stack{1}.i == 42*65536, 'locals: store+load');
end
printf("AVM Octave Port — Test Harness\n");
printf("===============================\n");
test_basic_add();
test_div();
test_saturation();
test_locals();
printf("\nAll Octave tests passed.\n");

80
scala/TestAVM.scala Normal file
View file

@ -0,0 +1,80 @@
// AVM ISA v1 Scala Test Harness
import silversight.avm.AVM._
import silversight.avm.AVM
object TestAVM {
val QS = Q16Scale
def check(cond: Boolean, msg: String): Unit = {
if (cond) println(s"$msg")
else println(s"$msg")
}
def test_basic_add(): Unit = {
val prog = Vector(PushQ16(5 * QS), PushQ16(3 * QS), Primitive(AddSatQ16), Halt)
val s = run(initState(), prog, 100).get
check(s.stack.head.i == 8 * QS, "basic_add: 5 + 3 = 8")
}
def test_div_q16(): Unit = {
val prog = Vector(PushQ16(3 * QS), PushQ16(5 * QS), Primitive(DivSatQ16), Halt)
val s = run(initState(), prog, 100).get
val exp = (3 * QS) / 5
check(s.stack.head.i == exp, "div_q16: 3/5 = 0.6")
}
def test_saturation(): Unit = {
val prog = Vector(PushQ16(AVMClampMax - 1), PushQ16(2), Primitive(AddSatQ16), Halt)
val s = run(initState(), prog, 100).get
check(s.stack.head.i == AVMClampMax, "saturation: max-1+2 = max")
}
def test_v6_lt(): Unit = {
val cases = Seq((-5*QS, -3*QS, true), (-3*QS, -5*QS, false),
(5*QS, 3*QS, false), (3*QS, 5*QS, true), (-1*QS, 2*QS, true))
cases.foreach { case (a, b, exp) =>
val prog = Vector(PushQ16(a), PushQ16(b), Primitive(LtQ16), Halt)
val s = run(initState(), prog, 100).get
check(s.stack.head.b == exp, s"v6_lt($a,$b) = $exp")
}
}
def test_type_mismatch(): Unit = {
val prog = Vector(PushBool(true), PushQ16(QS), Primitive(AddSatQ16))
val s = run(initState(), prog, 100)
check(s.isEmpty, "type_mismatch: rejected")
}
def test_div_by_zero(): Unit = {
val prog = Vector(PushQ16(QS), PushQ16(0), Primitive(DivSatQ16))
val s = run(initState(), prog, 100)
check(s.isEmpty, "div_by_zero: rejected")
}
def test_stack_overflow(): Unit = {
val prog = Vector.fill(AVMMaxStack + 1)(PushQ16(0: Int))
val s = run(initState(), prog, 10000)
check(s.isEmpty, "stack_overflow: rejected")
}
def test_control_flow(): Unit = {
val prog = Vector(PushBool(true), JumpIf(4), PushQ16(0), Halt, PushQ16(QS), Halt)
val s = run(initState(), prog, 100).get
check(s.stack.head.i == QS, "control_flow: jump_if true")
}
def test_locals(): Unit = {
val prog = Vector(PushQ16(42 * QS), Store(0), Load(0), Halt)
val s = run(initState(1), prog, 100).get
check(s.stack.head.i == 42 * QS, "locals: store+load")
}
def main(args: Array[String]): Unit = {
println("AVM Scala Port — Test Harness")
println("==============================")
test_basic_add(); test_div_q16(); test_saturation(); test_v6_lt()
test_type_mismatch(); test_div_by_zero(); test_stack_overflow()
test_control_flow(); test_locals()
println("\nAll Scala tests passed.")
}
}