Files
elm/elm_test.go
2024-01-23 17:33:55 +07:00

468 lines
14 KiB
Go

package main
import (
"testing"
"github.com/stretchr/testify/assert"
)
func All[T any](ts []T, pred func(T) bool) bool {
for _, t := range ts {
if !pred(t) {
return false
}
}
return true
}
func IsZero(b int32) bool { return b == int32(0) }
func TestHaltingDoesNothing(t *testing.T) {
m := Constructor()
m.Push(Inst{Operation: HALT})
m.Execute()
assert.Equal(t, int32(0), m.ip, "Instruction pointer should not be incremented");
assert.True(t, m.isHalted, "Virtual Machine should be Halted");
assert.True(t, All(m.stack, IsZero), "Stack should be empty");
}
func TestPush(t* testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 69})
m.Push(Inst{Operation: HALT})
m.Execute()
assert.Equal(t, int32(1), m.ip, "Instruction pointer should be incremented");
assert.False(t, m.isHalted, "Virtual Machine should not be Halted");
assert.Equal(t, int32(69), m.stack[0], "Stack should contain value");
}
func TestPushingSequence(t *testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 69})
m.Push(Inst{Operation: PUSH, Operand: 42})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.Nil(t, err, "Should be successfull")
assert.Equal(t, int32(2), m.ip, "Instruction pointer should be incremented");
assert.True(t, m.isHalted, "Virtual Machine should be Halted");
assert.Equal(t, int32(69), m.stack[0], "Stack should contain value");
assert.Equal(t, int32(42), m.stack[1], "Stack should contain value");
assert.Equal(t, int32(0), m.stack[3], "Stack should be zero after");
}
func TestAdd(t *testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 1})
m.Push(Inst{Operation: PUSH, Operand: 2})
m.Push(Inst{Operation: ADD})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.Nil(t, err, "Should be successfull")
assert.Equal(t, int32(3), m.ip, "Instruction pointer should be incremented");
assert.True(t, m.isHalted, "Virtual Machine should be Halted");
assert.Equal(t, int32(3), m.stack[0], "Stack should contain value");
// todo: should pass that test?
//assert.Equal(t, int32(0), m.stack[1], "Stack should be zero after");
}
func TestMul(t *testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 3})
m.Push(Inst{Operation: PUSH, Operand: 2})
m.Push(Inst{Operation: MUL})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.Nil(t, err, "Should be successfull")
assert.Equal(t, int32(3), m.ip, "Instruction pointer should be incremented");
assert.True(t, m.isHalted, "Virtual Machine should be Halted");
assert.Equal(t, int32(6), m.stack[0], "Stack should contain value");
// todo: should pass that test?
//assert.Equal(t, int32(0), m.stack[1], "Stack should be zero after");
}
func TestSub(t *testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 3})
m.Push(Inst{Operation: PUSH, Operand: 2})
m.Push(Inst{Operation: SUB})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.Nil(t, err, "Should be successfull")
assert.Equal(t, int32(3), m.ip, "Instruction pointer should be incremented");
assert.True(t, m.isHalted, "Virtual Machine should be Halted");
assert.Equal(t, int32(1), m.stack[0], "Stack should contain value");
// todo: should pass that test?
//assert.Equal(t, int32(0), m.stack[1], "Stack should be zero after");
}
func TestDiv(t *testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: PUSH, Operand: 2})
m.Push(Inst{Operation: DIV})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.Nil(t, err, "Should be successfull")
assert.Equal(t, int32(3), m.ip, "Instruction pointer should be incremented");
assert.True(t, m.isHalted, "Virtual Machine should be Halted");
assert.Equal(t, int32(3), m.stack[0], "Stack should contain value");
// todo: should pass that test?
//assert.Equal(t, int32(0), m.stack[1], "Stack should be zero after");
}
func TestAddNeedTwoItems(t * testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: ADD})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.NotNil(t, err, "Should be errored");
}
func TestSubNeedTwoItems(t * testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: SUB})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.NotNil(t, err, "Should be errored");
}
func TestMulNeedTwoItems(t * testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: MUL})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.NotNil(t, err, "Should be errored");
}
func TestDivNeedTwoItems(t * testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: DIV})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.NotNil(t, err, "Should be errored");
}
func TestDivNeedNonZeroValues(t * testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: PUSH, Operand: 0})
m.Push(Inst{Operation: DIV})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.NotNil(t, err, "Should be errored");
}
func TestDivNeedNonZeroValues2(t * testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 0})
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: DIV})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.NotNil(t, err, "Should be errored");
}
func TestPop(t *testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: POP})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.Nil(t, err, "Should be successfull")
assert.Equal(t, int32(2), m.ip, "Instruction pointer should be incremented");
assert.True(t, m.isHalted, "Virtual Machine should be Halted");
assert.True(t, All(m.stack, IsZero), "Stack should be empty");
}
func TestPopNeedsNonEmptyStack(t *testing.T) {
m := Constructor()
m.Push(Inst{Operation: POP})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.NotNil(t, err, "Should be errored");
}
func TestDup(t *testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: DUP})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.Nil(t, err, "Should be successfull")
assert.Equal(t, int32(2), m.ip, "Instruction pointer should be incremented");
assert.True(t, m.isHalted, "Virtual Machine should be Halted");
assert.Equal(t, int32(6), m.stack[0], "Stack should contain initial value");
assert.Equal(t, int32(6), m.stack[1], "Stack should contain duplicated value");
assert.Equal(t, int32(0), m.stack[2], "Stack should be zero after");
}
func TestDupByPointer(t *testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: PUSH, Operand: 2})
m.Push(Inst{Operation: DUP, Operand: 1})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.Nil(t, err, "Should be successfull")
assert.Equal(t, int32(3), m.ip, "Instruction pointer should be incremented");
assert.True(t, m.isHalted, "Virtual Machine should be Halted");
assert.Equal(t, int32(6), m.stack[0], "Stack should contain initial value");
assert.Equal(t, int32(2), m.stack[1], "Stack should contain initial value");
assert.Equal(t, int32(6), m.stack[2], "Stack should contain right duplicated value");
assert.Equal(t, int32(0), m.stack[3], "Stack should be zero after");
}
func TestEquals(t *testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 2})
m.Push(Inst{Operation: PUSH, Operand: 2})
m.Push(Inst{Operation: EQ})
m.Push(Inst{Operation: PUSH, Operand: 2})
m.Push(Inst{Operation: PUSH, Operand: 1})
m.Push(Inst{Operation: EQ})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.Nil(t, err, "Should be successfull")
assert.Equal(t, int32(6), m.ip, "Instruction pointer should be incremented");
assert.True(t, m.isHalted, "Virtual Machine should be Halted");
assert.Equal(t, int32(1), m.stack[0], "First quality should be true");
assert.Equal(t, int32(0), m.stack[1], "Second quality should not be true");
assert.Equal(t, int32(0), m.stack[3], "Stack should be zero after");
}
func TestEqualsNeedTwoItems(t * testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: DIV})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.NotNil(t, err, "Should be errored");
}
func TestJump(t *testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: JMP, Operand: 3})
m.Push(Inst{Operation: PUSH, Operand: 5})
m.Push(Inst{Operation: PUSH, Operand: 3})
m.Push(Inst{Operation: HALT})
err := m.Run()
notEqualToFive := func (x int32) bool { return x != int32(5) }
assert.Nil(t, err, "Should be successfull")
assert.Equal(t, int32(4), m.ip, "Instruction pointer should be incremented");
assert.True(t, m.isHalted, "Virtual Machine should be Halted");
assert.True(t, All(m.stack, notEqualToFive), "Stack should not contain skipped value");
assert.Equal(t, int32(6), m.stack[0], "Stack should contain pushed value");
assert.Equal(t, int32(3), m.stack[1], "Stack should contain pushed value");
assert.Equal(t, int32(0), m.stack[2], "Stack should be zero afterall");
}
func TestJumpDontPassIllegalAccess(t * testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: JMP, Operand: 3})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.NotNil(t, err, "Should be errored");
}
func TestJumpCanPassLastInstruction(t * testing.T) {
m := Constructor()
m.Push(Inst{Operation: JMP, Operand: 2})
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.Nil(t, err, "Should be successfull")
assert.Equal(t, int32(2), m.ip, "Instruction pointer should be incremented");
assert.True(t, m.isHalted, "Virtual Machine should be Halted");
}
func TestJumpDontPassIllegalAccess2(t * testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: JMP, Operand: -1})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.NotNil(t, err, "Should be errored");
}
func TestJumpIfDontPassWithoutStackValue(t * testing.T) {
m := Constructor()
m.Push(Inst{Operation: JMPIF, Operand: 2})
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.NotNil(t, err, "Should be errored");
}
func TestJumpIfDontPassIfNotTrue(t * testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 0})
m.Push(Inst{Operation: JMPIF, Operand: 3})
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.Nil(t, err, "Should be successfull")
assert.Equal(t, int32(3), m.ip, "Instruction pointer should be incremented");
assert.True(t, m.isHalted, "Virtual Machine should be Halted");
assert.Equal(t, int32(0), m.stack[0], "")
assert.Equal(t, int32(6), m.stack[1], "Should not skip the pushed value")
}
func TestJumpIfPassIfTrue(t * testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 1})
m.Push(Inst{Operation: JMPIF, Operand: 3})
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: PUSH, Operand: 69})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.Nil(t, err, "Should be successfull")
assert.Equal(t, int32(4), m.ip, "Instruction pointer should be incremented");
assert.True(t, m.isHalted, "Virtual Machine should be Halted");
assert.Equal(t, int32(69), m.stack[0], "Should skip the pushed value")
assert.Equal(t, int32(0), m.stack[1], "Be zero afterall")
}
func TestJumpIfDontPassIllegalAccess2(t * testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: JMPIF, Operand: -1})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.NotNil(t, err, "Should be errored");
}
func TestJumpIfDontPassIllegalAccess(t * testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: JMPIF, Operand: 3})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.NotNil(t, err, "Should be errored");
}
func TestCallDontPassIllegallAccess(t *testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: CALL, Operand: 3})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.NotNil(t, err, "Should be errored");
}
func TestCallDontPassIllegallAccess2(t *testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: CALL, Operand: -3})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.NotNil(t, err, "Should be errored");
}
func TestCallPass(t * testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 1})
m.Push(Inst{Operation: CALL, Operand: 3})
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: PUSH, Operand: 69})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.Nil(t, err, "Should be successfull")
assert.Equal(t, int32(4), m.ip, "Instruction pointer should be incremented");
assert.True(t, m.isHalted, "Virtual Machine should be Halted");
assert.Equal(t, int32(1), m.stack[0], "Should skip the pushed value")
assert.Equal(t, int32(69), m.stack[1], "Should skip the pushed value")
assert.Equal(t, int32(0), m.stack[2], "Should be zero afterall")
}
func TestRetPass(t * testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 1})
m.Push(Inst{Operation: CALL, Operand: 4})
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: HALT})
m.Push(Inst{Operation: PUSH, Operand: 69})
m.Push(Inst{Operation: RET})
err := m.Run()
assert.Nil(t, err, "Should be successfull")
assert.Equal(t, int32(3), m.ip, "Instruction pointer should be incremented");
assert.True(t, m.isHalted, "Virtual Machine should be Halted");
assert.Equal(t, int32(1), m.stack[0], "Should skip the pushed value")
assert.Equal(t, int32(69), m.stack[1], "Should skip the pushed value")
assert.Equal(t, int32(6), m.stack[2], "Should skip the pushed value")
assert.Equal(t, int32(0), m.stack[3], "Should be zero afterall")
}