func TestEvalFunctionNode_FailedToEvaluateArgumentNodes(t *testing.T) { // bool("value"), where in our case "value" won't exist evaluator, err := stateful.NewEvalFunctionNode(&ast.FunctionNode{ Func: "bool", Args: []ast.Node{ &ast.ReferenceNode{Reference: "value"}, }, }) if err != nil { t.Fatalf("Failed to create node evaluator: %v", err) } result, err := evaluator.EvalBool(stateful.NewScope(), stateful.CreateExecutionState()) expectedError := errors.New("Failed to handle 1 argument: name \"value\" is undefined. Names in scope:") if err == nil { t.Errorf("Expected an error, but got nil error and result (%t)", result) return } if strings.TrimSpace(err.Error()) != expectedError.Error() { t.Errorf("Got unexpected error:\ngot: %v\nexpected: %v\n", err, expectedError) } }
func TestEvalFunctionNode_EvalFloat64_Sanity(t *testing.T) { evaluator, err := stateful.NewEvalFunctionNode(&ast.FunctionNode{ Func: "abs", Args: []ast.Node{ &ast.NumberNode{ IsFloat: true, Float64: float64(-1), }, }, }) if err != nil { t.Fatalf("Failed to create node evaluator: %v", err) } result, err := evaluator.EvalFloat(stateful.NewScope(), stateful.CreateExecutionState()) if err != nil { t.Errorf("Expected a result, but got error - %v", err) return } if result != float64(1) { t.Errorf("unexpected result: got: %T(%v), expected: float64(1)", result, result) } }
func TestEvalFunctionNode_EvalInt64_KeepConsistentState(t *testing.T) { evaluator, err := stateful.NewEvalFunctionNode(&ast.FunctionNode{ Func: "count", Args: []ast.Node{}, }) if err != nil { t.Fatalf("Failed to create node evaluator: %v", err) } executionState := stateful.CreateExecutionState() // first evaluation result, err := evaluator.EvalInt(stateful.NewScope(), executionState) if err != nil { t.Errorf("first evaluation: Expected a result, but got error - %v", err) return } if result != int64(1) { t.Errorf("first evaluation: unexpected result: got: %T(%v), expected: int64(1)", result, result) } // second evaluation result, err = evaluator.EvalInt(stateful.NewScope(), executionState) if err != nil { t.Errorf("second evaluation: Expected a result, but got error - %v", err) return } if result != int64(2) { t.Errorf("second evaluation: unexpected result: got: %T(%v), expected: int64(2)", result, result) } }
func TestEvalUnaryNode_EvalFloat64_FailedToEvaluateNode(t *testing.T) { evaluator, err := stateful.NewEvalUnaryNode(&ast.UnaryNode{ Operator: ast.TokenMinus, Node: &ast.ReferenceNode{ Reference: "value", }, }) if err != nil { t.Fatalf("Failed to compile unary node: %v", err) } result, err := evaluator.EvalFloat(stateful.NewScope(), stateful.CreateExecutionState()) expectedError := errors.New("name \"value\" is undefined. Names in scope:") if err == nil && result != float64(0) { t.Errorf("Expected an error, but got nil error and result: %v", result) return } if strings.TrimSpace(err.Error()) != expectedError.Error() { t.Errorf("Got unexpected error:\ngot: %v\nexpected: %v\n", err, expectedError) } }
func TestEvalUnaryNode_EvalInt64_FailedToEvaluateNode(t *testing.T) { evaluator, err := stateful.NewEvalUnaryNode(&ast.UnaryNode{ Operator: ast.TokenMinus, Node: &ast.BoolNode{ Bool: false, }, }) if err != nil { t.Fatalf("Failed to compile unary node: %v", err) } result, err := evaluator.EvalInt(stateful.NewScope(), stateful.CreateExecutionState()) expectedError := errors.New("TypeGuard: expression returned unexpected type boolean, expected int") if err == nil && result != int64(0) { t.Errorf("Expected an error, but got nil error and result: %v", result) return } if err.Error() != expectedError.Error() { t.Errorf("Got unexpected error:\ngot: %v\nexpected: %v\n", err, expectedError) } }
func TestEvalFunctionNode_EvalBool_Sanity(t *testing.T) { evaluator, err := stateful.NewEvalFunctionNode(&ast.FunctionNode{ Func: "bool", Args: []ast.Node{&ast.StringNode{Literal: "true"}}, }) if err != nil { t.Fatalf("Failed to create node evaluator: %v", err) } result, err := evaluator.EvalBool(stateful.NewScope(), stateful.CreateExecutionState()) if err != nil { t.Errorf("Expected a result, but got error - %v", err) return } if !result { t.Errorf("unexpected result: got: %v, expected: true", result) } }
func TestEvalLambdaNode_EvalRegex_Sanity(t *testing.T) { evaluator, err := stateful.NewEvalLambdaNode(&ast.LambdaNode{ Expression: &ast.RegexNode{ Regex: regexp.MustCompile("^abc.*"), }, }) if err != nil { t.Fatalf("Failed to compile lambda node: %v", err) } result, err := evaluator.EvalRegex(stateful.NewScope(), stateful.CreateExecutionState()) if err != nil { t.Errorf("Got unexpected error: %v", err) } if got, exp := result.String(), "^abc.*"; exp != got { t.Errorf("unexpected result: got: %T(%v), expected: %s", got, got, exp) } }
func TestEvalLambdaNode_EvalString_Sanity(t *testing.T) { evaluator, err := stateful.NewEvalLambdaNode(&ast.LambdaNode{ Expression: &ast.StringNode{ Literal: "string", }, }) if err != nil { t.Fatalf("Failed to compile lambda node: %v", err) } result, err := evaluator.EvalString(stateful.NewScope(), stateful.CreateExecutionState()) if err != nil { t.Errorf("Got unexpected error: %v", err) } if got, exp := result, "string"; got != exp { t.Errorf("unexpected result: got: %T(%v), expected: %s", got, got, exp) } }
func TestEvalUnaryNode_EvalBool_Sanity(t *testing.T) { evaluator, err := stateful.NewEvalUnaryNode(&ast.UnaryNode{ Operator: ast.TokenNot, Node: &ast.BoolNode{ Bool: false, }, }) if err != nil { t.Fatalf("Failed to compile unary node: %v", err) } result, err := evaluator.EvalBool(stateful.NewScope(), stateful.CreateExecutionState()) if err != nil { t.Errorf("Got unexpected error: %v", err) } if !result { t.Errorf("unexpected result: got: %t, expected: true", result) } }
func TestEvalUnaryNode_EvalDuration_Sanity(t *testing.T) { evaluator, err := stateful.NewEvalUnaryNode(&ast.UnaryNode{ Operator: ast.TokenMinus, Node: &ast.DurationNode{ Dur: 12 * time.Hour, }, }) if err != nil { t.Fatalf("Failed to compile unary node: %v", err) } result, err := evaluator.EvalDuration(stateful.NewScope(), stateful.CreateExecutionState()) if err != nil { t.Errorf("Got unexpected error: %v", err) } if got, exp := result, -12*time.Hour; got != exp { t.Errorf("unexpected result: got: %T(%v), expected: %s", got, got, exp) } }
func TestEvalFunctionNode_AryMismatch(t *testing.T) { evaluator, err := stateful.NewEvalFunctionNode(&ast.FunctionNode{ Func: "sigma", Args: []ast.Node{&ast.BoolNode{Bool: true}}, }) if err != nil { t.Fatalf("Failed to create node evaluator: %v", err) } result, err := evaluator.EvalFloat(stateful.NewScope(), stateful.CreateExecutionState()) expectedError := errors.New("error calling \"sigma\": value is not a float") if err == nil { t.Errorf("Expected an error, but got nil error and result (%v)", result) return } if strings.TrimSpace(err.Error()) != expectedError.Error() { t.Errorf("Got unexpected error:\ngot: %v\nexpected: %v\n", err, expectedError) } }
func TestEvalFunctionNode_UndefinedFunction(t *testing.T) { evaluator, err := stateful.NewEvalFunctionNode(&ast.FunctionNode{ Func: "yosi_the_king", Args: []ast.Node{}, }) if err != nil { t.Fatalf("Failed to create node evaluator: %v", err) } result, err := evaluator.EvalBool(stateful.NewScope(), stateful.CreateExecutionState()) expectedError := errors.New("undefined function: \"yosi_the_king\"") if err == nil { t.Errorf("Expected an error, but got nil error and result (%t)", result) return } if strings.TrimSpace(err.Error()) != expectedError.Error() { t.Errorf("Got unexpected error:\ngot: %v\nexpected: %v\n", err, expectedError) } }
func TestEvalFunctionNode_EvalInt64_Reset(t *testing.T) { evaluator, err := stateful.NewEvalFunctionNode(&ast.FunctionNode{ Func: "count", Args: []ast.Node{}, }) if err != nil { t.Fatalf("Failed to create node evaluator: %v", err) } executionState := stateful.CreateExecutionState() // first evaluation result, err := evaluator.EvalInt(stateful.NewScope(), executionState) if err != nil { t.Errorf("first evaluation: Expected a result, but got error - %v", err) return } if result != int64(1) { t.Errorf("first evaluation: unexpected result: got: %T(%v), expected: int64(1)", result, result) } // reset (we don't call ResetAll on ExecutionState in order to isolate the scope of this test) for _, fnc := range executionState.Funcs { fnc.Reset() } // second evaluation result, err = evaluator.EvalInt(stateful.NewScope(), executionState) if err != nil { t.Errorf("second evaluation (after reset): Expected a result, but got error - %v", err) return } if result != int64(1) { t.Errorf("second evaluation (after reset): unexpected result: got: %T(%v), expected: int64(1)", result, result) } }
func TestEvalUnaryNode_EvalInt64_Sanity(t *testing.T) { evaluator, err := stateful.NewEvalUnaryNode(&ast.UnaryNode{ Operator: ast.TokenMinus, Node: &ast.NumberNode{ IsInt: true, Int64: int64(12), }, }) if err != nil { t.Fatalf("Failed to compile unary node: %v", err) } result, err := evaluator.EvalInt(stateful.NewScope(), stateful.CreateExecutionState()) if err != nil { t.Errorf("Got unexpected error: %v", err) } if result != int64(-12) { t.Errorf("unexpected result: got: %T(%v), expected: int64(-12)", result, result) } }
func TestEvalFunctionNode_ComplexNodes(t *testing.T) { // pow("x" * 2, -"y") => pow(4, -1) evaluator, err := stateful.NewEvalFunctionNode(&ast.FunctionNode{ Func: "pow", Args: []ast.Node{ // "x" * 2 &ast.BinaryNode{ Operator: ast.TokenMult, Left: &ast.ReferenceNode{Reference: "x"}, Right: &ast.NumberNode{IsFloat: true, Float64: float64(2)}, }, // -"y" &ast.UnaryNode{ Operator: ast.TokenMinus, Node: &ast.ReferenceNode{Reference: "y"}, }, }, }) if err != nil { t.Fatalf("Failed to create node evaluator: %v", err) } scope := stateful.NewScope() scope.Set("x", float64(2)) scope.Set("y", float64(1)) result, err := evaluator.EvalFloat(scope, stateful.CreateExecutionState()) if err != nil { t.Errorf("Expected a result, but got error - %v", err) return } if result != float64(0.25) { t.Errorf("unexpected result: got: %T(%v), expected: float64(0.25)", result, result) } }
func TestEvalLambdaNode_EvalBool_SeparateState(t *testing.T) { // l1 = lambda: count() > 5 // l2 = lambda: count() > 10 // Because of short-circuiting it should take 15 calls before, // 'l1 AND l2' evaluates to true. l1 := &ast.LambdaNode{ Expression: &ast.BinaryNode{ Operator: ast.TokenGreater, Left: &ast.FunctionNode{ Func: "count", }, Right: &ast.NumberNode{ IsInt: true, Int64: 5, }, }, } l2 := &ast.LambdaNode{ Expression: &ast.BinaryNode{ Operator: ast.TokenGreater, Left: &ast.FunctionNode{ Func: "count", }, Right: &ast.NumberNode{ IsInt: true, Int64: 10, }, }, } evaluator, err := stateful.NewEvalBinaryNode(&ast.BinaryNode{ Operator: ast.TokenAnd, Left: l1, Right: l2, }) if err != nil { t.Fatalf("Failed to compile lambda node: %v", err) } count := 15 for i := 0; i < count; i++ { result, err := evaluator.EvalBool(stateful.NewScope(), stateful.CreateExecutionState()) if err != nil { t.Fatalf("Got unexpected error: %v", err) } if got, exp := result, false; got != exp { t.Fatalf("unexpected result: got: %T(%v), expected: %t", got, got, exp) } } // Final time it should be true result, err := evaluator.EvalBool(stateful.NewScope(), stateful.CreateExecutionState()) if err != nil { t.Fatalf("Got unexpected error: %v", err) } if got, exp := result, true; got != exp { t.Fatalf("unexpected result: got: %T(%v), expected: %t", got, got, exp) } }