Ocean-240.2-Emulator/gval/operator_test.go

167 lines
3.9 KiB
Go

package gval
import (
"context"
"fmt"
"reflect"
"testing"
)
func Test_Infix(t *testing.T) {
type subTest struct {
name string
a interface{}
b interface{}
wantRet interface{}
}
tests := []struct {
name string
infix
subTests []subTest
}{
{
"number operator",
infix{
number: func(a, b float64) (interface{}, error) { return a * b, nil },
},
[]subTest{
{"float64 arguments", 7., 3., 21.},
{"int arguments", 7, 3, 21.},
{"string arguments", "7", "3.", 21.},
},
},
{
"number and string operator",
infix{
number: func(a, b float64) (interface{}, error) { return a + b, nil },
text: func(a, b string) (interface{}, error) { return fmt.Sprintf("%v%v", a, b), nil },
},
[]subTest{
{"float64 arguments", 7., 3., 10.},
{"int arguments", 7, 3, 10.},
{"number string arguments", "7", "3.", "73."},
{"string arguments", "hello ", "world", "hello world"},
},
},
{
"bool operator",
infix{
shortCircuit: func(a interface{}) (interface{}, bool) { return false, a == false },
boolean: func(a, b bool) (interface{}, error) { return a && b, nil },
},
[]subTest{
{"bool arguments", false, true, false},
{"number arguments", 0, true, false},
{"lower string arguments", "false", "true", false},
{"upper string arguments", "TRUE", "FALSE", false},
{"shortCircuit", false, "not a boolean", false},
},
},
{
"bool, number, text and interface operator",
infix{
number: func(a, b float64) (interface{}, error) { return a == b, nil },
boolean: func(a, b bool) (interface{}, error) { return a == b, nil },
text: func(a, b string) (interface{}, error) { return a == b, nil },
arbitrary: func(a, b interface{}) (interface{}, error) { return a == b, nil },
},
[]subTest{
{"number string and int arguments", "7", 7, true},
{"bool string and bool arguments", "true", true, true},
{"string arguments", "hello", "hello", true},
{"upper string arguments", "TRUE", "FALSE", false},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.infix.initiate("<" + tt.name + ">")
builder := tt.infix.builder
for _, tt := range tt.subTests {
t.Run(tt.name, func(t *testing.T) {
eval, err := builder(constant(tt.a), constant(tt.b))
if err != nil {
t.Fatal(err)
}
got, err := eval(context.Background(), nil)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(got, tt.wantRet) {
t.Fatalf("binaryOperator() eval() = %v, want %v", got, tt.wantRet)
}
})
}
})
}
}
func Test_stageStack_push(t *testing.T) {
p := (*Parser)(nil)
tests := []struct {
name string
pres []operatorPrecedence
expect string
}{
{
"flat",
[]operatorPrecedence{1, 1, 1, 1},
"((((AB)C)D)E)",
},
{
"asc",
[]operatorPrecedence{1, 2, 3, 4},
"(A(B(C(DE))))",
},
{
"desc",
[]operatorPrecedence{4, 3, 2, 1},
"((((AB)C)D)E)",
},
{
"mixed",
[]operatorPrecedence{1, 2, 1, 1},
"(((A(BC))D)E)",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
X := int('A')
op := func(a, b Evaluable) (Evaluable, error) {
return func(c context.Context, o interface{}) (interface{}, error) {
aa, _ := a.EvalString(c, nil)
bb, _ := b.EvalString(c, nil)
s := "(" + aa + bb + ")"
return s, nil
}, nil
}
stack := stageStack{}
for _, pre := range tt.pres {
if err := stack.push(stage{p.Const(string(rune(X))), op, pre}); err != nil {
t.Fatal(err)
}
X++
}
if err := stack.push(stage{p.Const(string(rune(X))), nil, 0}); err != nil {
t.Fatal(err)
}
if len(stack) != 1 {
t.Fatalf("stack must hold exactly one element")
}
got, _ := stack[0].EvalString(context.Background(), nil)
if got != tt.expect {
t.Fatalf("got %s but expected %s", got, tt.expect)
}
})
}
}