package avm import "testing" func q16(x int32) Val { return Val{Ty: Q16_16, Q: avmClamp(int64(x))} } func runTest(prog []Instr, fuel int) (*State, error) { return Run(NewState(0), prog, fuel) } func TestBasicAdd(t *testing.T) { prog := []Instr{ {Op: PushQ16, Arg: 5 * Q16Scale}, {Op: PushQ16, Arg: 3 * Q16Scale}, {Op: Primitive, Arg: int32(AddSatQ16)}, {Op: Halt}, } s, err := runTest(prog, 100) if err != nil { t.Fatal(err) } if s.Stack[0].Q != 8*Q16Scale { t.Fatalf("got %d", s.Stack[0].Q) } } func TestFloorDiv(t *testing.T) { // (3 * Q16) / (5 * Q16) = floor(0.6) = 0.6 in Q16 prog := []Instr{ {Op: PushQ16, Arg: 3 * Q16Scale}, {Op: PushQ16, Arg: 5 * Q16Scale}, {Op: Primitive, Arg: int32(DivSatQ16)}, {Op: Halt}, } s, err := runTest(prog, 100) if err != nil { t.Fatal(err) } expected := int32((3 * Q16Scale) / 5) if s.Stack[0].Q != expected { t.Fatalf("got %d, expected %d", s.Stack[0].Q, expected) } } func TestSaturation(t *testing.T) { prog := []Instr{ {Op: PushQ16, Arg: AVMClampMax - 1}, {Op: PushQ16, Arg: 2}, {Op: Primitive, Arg: int32(AddSatQ16)}, {Op: Halt}, } s, err := runTest(prog, 100) if err != nil { t.Fatal(err) } if s.Stack[0].Q != AVMClampMax { t.Fatalf("got %d", s.Stack[0].Q) } } func TestV6LtQ16(t *testing.T) { cases := []struct{ a, b int32; exp bool }{ {-5 * Q16Scale, -3 * Q16Scale, true}, {-3 * Q16Scale, -5 * Q16Scale, false}, {5 * Q16Scale, 3 * Q16Scale, false}, {3 * Q16Scale, 5 * Q16Scale, true}, {-1 * Q16Scale, 2 * Q16Scale, true}, } for _, c := range cases { prog := []Instr{ {Op: PushQ16, Arg: c.a}, {Op: PushQ16, Arg: c.b}, {Op: Primitive, Arg: int32(LtQ16)}, {Op: Halt}, } s, err := runTest(prog, 100) if err != nil { t.Fatal(err) } if s.Stack[0].Bval != c.exp { t.Fatalf("ltQ16(%d,%d): got %v", c.a, c.b, s.Stack[0]) } } } func TestDivByZero(t *testing.T) { prog := []Instr{ {Op: PushQ16, Arg: Q16Scale}, {Op: PushQ16, Arg: 0}, {Op: Primitive, Arg: int32(DivSatQ16)}, } _, err := runTest(prog, 100) if err == nil { t.Fatal("expected error") } } func TestStackOverflow(t *testing.T) { prog := make([]Instr, AVMMaxStack+1) for i := range prog { prog[i] = Instr{Op: PushQ16, Arg: 0} } _, err := runTest(prog, 10000) if err == nil { t.Fatal("expected overflow") } } func TestControlFlow(t *testing.T) { prog := []Instr{ {Op: PushBool, Arg2: true}, {Op: JumpIf, Arg: 4}, {Op: PushQ16, Arg: 0}, {Op: Halt}, {Op: PushQ16, Arg: Q16Scale}, {Op: Halt}, } s, err := runTest(prog, 100) if err != nil { t.Fatal(err) } if s.Stack[0].Q != Q16Scale { t.Fatalf("got %d", s.Stack[0].Q) } } func TestLocals(t *testing.T) { s := NewState(1) prog := []Instr{ {Op: PushQ16, Arg: 42 * Q16Scale}, {Op: Store, Arg: 0}, {Op: Load, Arg: 0}, {Op: Halt}, } result, err := Run(s, prog, 100) if err != nil { t.Fatal(err) } if result.Stack[0].Q != 42*Q16Scale { t.Fatalf("got %d", result.Stack[0].Q) } }