Skip to content

Commit

Permalink
Adding tests for:
Browse files Browse the repository at this point in the history
* EvalNum: "-4"
* EvalBool: "!true"
* Sanity function calling - count()
* EvalNum for number node
  • Loading branch information
yosiat committed Apr 14, 2016
1 parent 40d8117 commit 19bb61f
Showing 1 changed file with 234 additions and 27 deletions.
261 changes: 234 additions & 27 deletions tick/stateful_expr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestStatefulExpression_EvalBool_BoolNode(t *testing.T) {
}
}

runEvalBoolTests(t, createBoolNode, leftValues, rightValues, operators, map[keyStruct]bool{
runEvalBoolTests(t, createBoolNode, leftValues, rightValues, operators, map[keyStruct]interface{}{
// Left: True, Right: True
keyStruct{true, true, tick.TokenEqual}: true,
keyStruct{true, true, tick.TokenNotEqual}: false,
Expand Down Expand Up @@ -127,7 +127,7 @@ func TestStatefulExpression_EvalBool_NumberNode(t *testing.T) {
}
}

runEvalBoolTests(t, createNumberNode, leftValues, rightValues, operators, map[keyStruct]bool{
runEvalBoolTests(t, createNumberNode, leftValues, rightValues, operators, map[keyStruct]interface{}{
// Left is float64(5), Right is float64(5)
keyStruct{float64(5), float64(5), tick.TokenEqual}: true,
keyStruct{float64(5), float64(5), tick.TokenNotEqual}: false,
Expand Down Expand Up @@ -272,7 +272,7 @@ func TestStatefulExpression_EvalBool_StringNode(t *testing.T) {
}
}

runEvalBoolTests(t, createStringNode, leftValues, rightValues, operators, map[keyStruct]bool{
runEvalBoolTests(t, createStringNode, leftValues, rightValues, operators, map[keyStruct]interface{}{
// Left is "a", Right is "a"
keyStruct{"a", "a", tick.TokenEqual}: true,
keyStruct{"a", "a", tick.TokenNotEqual}: false,
Expand Down Expand Up @@ -354,7 +354,7 @@ func TestStatefulExpression_EvalBool_RegexNode(t *testing.T) {

}

runEvalBoolTests(t, createStringOrRegexNode, leftValues, rightValues, operators, map[keyStruct]bool{
runEvalBoolTests(t, createStringOrRegexNode, leftValues, rightValues, operators, map[keyStruct]interface{}{
// Left is "abc", Right is regex "(.*)c"
keyStruct{"abc", "R!^(.*)c$", tick.TokenRegexEqual}: true,
keyStruct{"abc", "R!^(.*)c$", tick.TokenRegexNotEqual}: false,
Expand Down Expand Up @@ -533,60 +533,269 @@ func TestStatefulExpression_EvalBool_UnexpectedTypeResult(t *testing.T) {
}
}

func TestStatefulExpression_EvalNum_UnaryExpression(t *testing.T) {

scope := tick.NewScope()

se := tick.NewStatefulExpr(&tick.UnaryNode{
Node: &tick.NumberNode{
IsInt: true,
Int64: 4,
},
Operator: tick.TokenMinus,
})

result, err := se.EvalNum(scope)
if err != nil {
t.Error(err)
}

if result != int64(-4) {
t.Errorf("unexpected result: got: %t, expected: -4", result)
}

}

func TestStatefulExpression_EvalBool_UnaryExpression(t *testing.T) {

scope := tick.NewScope()

se := tick.NewStatefulExpr(&tick.UnaryNode{
Node: &tick.BoolNode{
Bool: true,
},
Operator: tick.TokenNot,
})

result, err := se.EvalBool(scope)
if err != nil {
t.Error(err)
}

if result {
t.Errorf("unexpected result: got: %t, expected: false", result)
}
}

func TestStatefulExpression_EvalNum_SanityCallingFunction(t *testing.T) {

scope := tick.NewScope()

se := tick.NewStatefulExpr(&tick.FunctionNode{
Func: "count",
})

result, err := se.EvalNum(scope)
if err != nil {
t.Error(err)
}

if result != int64(1) {
t.Errorf("unexpected result: got: %t, expected: 1", result)
}

// Second time, to make sure that count() increases the value
result, err = se.EvalNum(scope)
if err != nil {
t.Error(err)
}

if result != int64(2) {
t.Errorf("unexpected result: got: %t, expected: 2", result)
}
}

func TestStatefulExpression_EvalNum_NumberNode(t *testing.T) {
leftValues := []interface{}{float64(5), float64(10), int64(5)}
rightValues := []interface{}{float64(5), float64(10), int64(5), "NON_INT_VALUE"}

operators := []tick.TokenType{
tick.TokenPlus,
tick.TokenMinus,
tick.TokenMult,
tick.TokenDiv,
tick.TokenMod,
}

createNumberNode := func(v interface{}) tick.Node {
switch value := v.(type) {
case float64:
return &tick.NumberNode{
IsFloat: true,
Float64: value,
}
case int64:
return &tick.NumberNode{
IsInt: true,
Int64: value,
}
// For the error case
case string:
return &tick.StringNode{
Literal: value,
}
default:
t.Fatalf("value supplied to createNumberNode is not string/int64/float64: %t", v)
return nil
}
}

runEvalNumericTests(t, createNumberNode, leftValues, rightValues, operators, map[keyStruct]interface{}{
// Left is float64(5), Right is float64(5)
keyStruct{float64(5), float64(5), tick.TokenPlus}: float64(10),
keyStruct{float64(5), float64(5), tick.TokenMinus}: float64(0),
keyStruct{float64(5), float64(5), tick.TokenMult}: float64(25),
keyStruct{float64(5), float64(5), tick.TokenDiv}: float64(1),

// Left is int64(5), Right is int64(5)
keyStruct{int64(5), int64(5), tick.TokenPlus}: int64(10),
keyStruct{int64(5), int64(5), tick.TokenMinus}: int64(0),
keyStruct{int64(5), int64(5), tick.TokenMult}: int64(25),
keyStruct{int64(5), int64(5), tick.TokenDiv}: int64(1),
keyStruct{int64(5), int64(5), tick.TokenMod}: int64(0),

// Left is float64(5), Right is float64(10)
keyStruct{float64(5), float64(10), tick.TokenPlus}: float64(15),
keyStruct{float64(5), float64(10), tick.TokenMinus}: float64(-5),
keyStruct{float64(5), float64(10), tick.TokenMult}: float64(50),
keyStruct{float64(5), float64(10), tick.TokenDiv}: float64(0.5),

// Left is float64(10), Right is float64(5)
keyStruct{float64(10), float64(5), tick.TokenPlus}: float64(15),
keyStruct{float64(10), float64(5), tick.TokenMinus}: float64(5),
keyStruct{float64(10), float64(5), tick.TokenMult}: float64(50),
keyStruct{float64(10), float64(5), tick.TokenDiv}: float64(2),

// Left is float64(10), Right is float64(10)
keyStruct{float64(10), float64(10), tick.TokenPlus}: float64(20),
keyStruct{float64(10), float64(10), tick.TokenMinus}: float64(0),
keyStruct{float64(10), float64(10), tick.TokenMult}: float64(100),
keyStruct{float64(10), float64(10), tick.TokenDiv}: float64(1),
}, map[keyStruct]error{
// Modulo token where left is float
keyStruct{float64(5), float64(5), tick.TokenMod}: errors.New("invalid float math operator %"),
keyStruct{float64(5), float64(10), tick.TokenMod}: errors.New("invalid float math operator %"),
keyStruct{float64(10), float64(5), tick.TokenMod}: errors.New("invalid float math operator %"),
keyStruct{float64(10), float64(10), tick.TokenMod}: errors.New("invalid float math operator %"),

// Left is int, right is float
keyStruct{int64(5), float64(5), tick.TokenPlus}: errors.New("mismatched type to binary operator. got int64 + float64. see bool(), int(), float()"),
keyStruct{int64(5), float64(5), tick.TokenMinus}: errors.New("mismatched type to binary operator. got int64 - float64. see bool(), int(), float()"),
keyStruct{int64(5), float64(5), tick.TokenMult}: errors.New("mismatched type to binary operator. got int64 * float64. see bool(), int(), float()"),
keyStruct{int64(5), float64(5), tick.TokenDiv}: errors.New("mismatched type to binary operator. got int64 / float64. see bool(), int(), float()"),
keyStruct{int64(5), float64(5), tick.TokenMod}: errors.New("mismatched type to binary operator. got int64 % float64. see bool(), int(), float()"),
keyStruct{int64(5), float64(10), tick.TokenPlus}: errors.New("mismatched type to binary operator. got int64 + float64. see bool(), int(), float()"),
keyStruct{int64(5), float64(10), tick.TokenMinus}: errors.New("mismatched type to binary operator. got int64 - float64. see bool(), int(), float()"),
keyStruct{int64(5), float64(10), tick.TokenMult}: errors.New("mismatched type to binary operator. got int64 * float64. see bool(), int(), float()"),
keyStruct{int64(5), float64(10), tick.TokenDiv}: errors.New("mismatched type to binary operator. got int64 / float64. see bool(), int(), float()"),
keyStruct{int64(5), float64(10), tick.TokenMod}: errors.New("mismatched type to binary operator. got int64 % float64. see bool(), int(), float()"),

// Left is float, right is int
keyStruct{float64(5), int64(5), tick.TokenPlus}: errors.New("mismatched type to binary operator. got float64 + int64. see bool(), int(), float()"),
keyStruct{float64(5), int64(5), tick.TokenMinus}: errors.New("mismatched type to binary operator. got float64 - int64. see bool(), int(), float()"),
keyStruct{float64(5), int64(5), tick.TokenMult}: errors.New("mismatched type to binary operator. got float64 * int64. see bool(), int(), float()"),
keyStruct{float64(5), int64(5), tick.TokenDiv}: errors.New("mismatched type to binary operator. got float64 / int64. see bool(), int(), float()"),
keyStruct{float64(5), int64(5), tick.TokenMod}: errors.New("mismatched type to binary operator. got float64 % int64. see bool(), int(), float()"),

keyStruct{float64(10), int64(5), tick.TokenPlus}: errors.New("mismatched type to binary operator. got float64 + int64. see bool(), int(), float()"),
keyStruct{float64(10), int64(5), tick.TokenMinus}: errors.New("mismatched type to binary operator. got float64 - int64. see bool(), int(), float()"),
keyStruct{float64(10), int64(5), tick.TokenMult}: errors.New("mismatched type to binary operator. got float64 * int64. see bool(), int(), float()"),
keyStruct{float64(10), int64(5), tick.TokenDiv}: errors.New("mismatched type to binary operator. got float64 / int64. see bool(), int(), float()"),
keyStruct{float64(10), int64(5), tick.TokenMod}: errors.New("mismatched type to binary operator. got float64 % int64. see bool(), int(), float()"),

// Left is int64, Right is "NON_INT_VALUE"
keyStruct{int64(5), "NON_INT_VALUE", tick.TokenPlus}: errors.New("mismatched type to binary operator. got int64 + string. see bool(), int(), float()"),
keyStruct{int64(5), "NON_INT_VALUE", tick.TokenMinus}: errors.New("mismatched type to binary operator. got int64 - string. see bool(), int(), float()"),
keyStruct{int64(5), "NON_INT_VALUE", tick.TokenMult}: errors.New("mismatched type to binary operator. got int64 * string. see bool(), int(), float()"),
keyStruct{int64(5), "NON_INT_VALUE", tick.TokenDiv}: errors.New("mismatched type to binary operator. got int64 / string. see bool(), int(), float()"),
keyStruct{int64(5), "NON_INT_VALUE", tick.TokenMod}: errors.New("mismatched type to binary operator. got int64 % string. see bool(), int(), float()"),

// Left is float64, Right is "NON_INT_VALUE"
keyStruct{float64(5), "NON_INT_VALUE", tick.TokenPlus}: errors.New("mismatched type to binary operator. got float64 + string. see bool(), int(), float()"),
keyStruct{float64(5), "NON_INT_VALUE", tick.TokenMinus}: errors.New("mismatched type to binary operator. got float64 - string. see bool(), int(), float()"),
keyStruct{float64(5), "NON_INT_VALUE", tick.TokenMult}: errors.New("mismatched type to binary operator. got float64 * string. see bool(), int(), float()"),
keyStruct{float64(5), "NON_INT_VALUE", tick.TokenDiv}: errors.New("mismatched type to binary operator. got float64 / string. see bool(), int(), float()"),
keyStruct{float64(5), "NON_INT_VALUE", tick.TokenMod}: errors.New("mismatched type to binary operator. got float64 % string. see bool(), int(), float()"),
keyStruct{float64(10), "NON_INT_VALUE", tick.TokenPlus}: errors.New("mismatched type to binary operator. got float64 + string. see bool(), int(), float()"),
keyStruct{float64(10), "NON_INT_VALUE", tick.TokenMinus}: errors.New("mismatched type to binary operator. got float64 - string. see bool(), int(), float()"),
keyStruct{float64(10), "NON_INT_VALUE", tick.TokenMult}: errors.New("mismatched type to binary operator. got float64 * string. see bool(), int(), float()"),
keyStruct{float64(10), "NON_INT_VALUE", tick.TokenDiv}: errors.New("mismatched type to binary operator. got float64 / string. see bool(), int(), float()"),
keyStruct{float64(10), "NON_INT_VALUE", tick.TokenMod}: errors.New("mismatched type to binary operator. got float64 % string. see bool(), int(), float()"),
})
}

func runEvalNumericTests(
t *testing.T,
createNodeFn func(v interface{}) tick.Node,
leftValues []interface{},
rightValues []interface{},
operators []tick.TokenType,
expected map[keyStruct]interface{},
errorExpectations map[keyStruct]error) {

runEvalTests(t, func(scope *tick.Scope, n tick.Node) (interface{}, error) {
se := tick.NewStatefulExpr(n)
return se.EvalNum(scope)
}, createNodeFn, leftValues, rightValues, operators, expected, errorExpectations)
}

func runEvalBoolTests(
t *testing.T,
createNodeFn func(v interface{}) tick.Node,
leftValues []interface{},
rightValues []interface{},
operators []tick.TokenType,
expected map[keyStruct]bool,
expected map[keyStruct]interface{},
errorExpectations map[keyStruct]error) {

runEvalTests(t, evalBoolWithScope, createNodeFn, leftValues, rightValues, operators, expected, errorExpectations);
runEvalTests(t, evalBoolWithScope, createNodeFn, leftValues, rightValues, operators, expected, errorExpectations)
}

func evalBoolWithScope(scope *tick.Scope, n tick.Node) (bool, error) {
se := tick.NewStatefulExpr(n)
return se.EvalBool(scope)
func evalBoolWithScope(scope *tick.Scope, n tick.Node) (interface{}, error) {
se := tick.NewStatefulExpr(n)
return se.EvalBool(scope)
}



func runEvalTests(
t *testing.T,
evalNodeFn func(scope *tick.Scope, n tick.Node) (bool, error),
evalNodeFn func(scope *tick.Scope, n tick.Node) (interface{}, error),
createNodeFn func(v interface{}) tick.Node,
leftValues []interface{},
rightValues []interface{},
operators []tick.TokenType,
expected map[keyStruct]bool,
expected map[keyStruct]interface{},
errorExpectations map[keyStruct]error) {
for _, lhs := range leftValues {

for _, lhs := range leftValues {
for _, rhs := range rightValues {
for _, op := range operators {

key := keyStruct{lhs, rhs, op}
exp, isBoolOk := expected[key]
exp, isExpectedResultOk := expected[key]
errorExpected, isErrorOk := errorExpectations[key]
if !isBoolOk && !isErrorOk {
if !isExpectedResultOk && !isErrorOk {
t.Fatalf("Couldn't find an expected result/error for: lhs: %t, rhs: %t, op: %v", lhs, rhs, op)
}

// Test simple const values compares
emptyScope := tick.NewScope()
emptyScope := tick.NewScope()
result, err := evalNodeFn(emptyScope, &tick.BinaryNode{
Operator: op,
Left: createNodeFn(lhs),
Right: createNodeFn(rhs),
})

// Expect value can be error or bool
if isErrorOk && errorExpected.Error() != err.Error() {
t.Errorf("unexpected error result: %t %v %t\ngot: %v\nexp: %v", lhs, op, rhs, err, errorExpected)
} else if isBoolOk && exp != result {
t.Errorf("unexpected bool result: %t %v %t\ngot: %v\nexp: %v", lhs, op, rhs, result, exp)
// This is bool matching, but not error matching..
if isExpectedResultOk && !isErrorOk && err != nil {
t.Errorf("Got an error while evaluating: %t %v %t - %v\n", lhs, op, rhs, err)
} else {

// Expect value can be error or bool
if isErrorOk && errorExpected.Error() != err.Error() {
t.Errorf("unexpected error result: %t %v %t\ngot: %v\nexp: %v", lhs, op, rhs, err, errorExpected)
} else if isExpectedResultOk && exp != result {
t.Errorf("unexpected result: %t %v %t\ngot: %v\nexp: %v", lhs, op, rhs, result, exp)
}
}

// Test left is reference while the right is const
Expand All @@ -602,14 +811,12 @@ func runEvalTests(

if isErrorOk && errorExpected.Error() != err.Error() {
t.Errorf("unexpected error result: %t %v %t\ngot: %v\nexp: %v", lhs, op, rhs, err, errorExpected)
} else if isBoolOk && exp != result {
t.Errorf("unexpected bool result: %t %v %t\ngot: %v\nexp: %v", lhs, op, rhs, result, exp)
} else if isExpectedResultOk && exp != result {
t.Errorf("unexpected result: %t %v %t\ngot: %v\nexp: %v", lhs, op, rhs, result, exp)
}

}

}
}
}


0 comments on commit 19bb61f

Please sign in to comment.