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 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_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 TestEvaluate_ListVars(t *testing.T) { script := ` var strList = [ 'host', 'dc', 'service' ] f(strList) ` called := false f := func(a, b, c string) interface{} { called = true got := []string{a, b, c} exp := []string{"host", "dc", "service"} if !reflect.DeepEqual(got, exp) { t.Errorf("unexpected func args got %v exp %v", got, exp) } return nil } scope := stateful.NewScope() scope.Set("f", f) _, err := tick.Evaluate(script, scope, nil, false) if err != nil { t.Fatal(err) } if !called { t.Fatal("expected function to be called") } }
func TestEvaluate(t *testing.T) { //Run a test that evaluates the DSL against the above structures. script := ` var s2 = a|structB() .field1('f1') .field2(42) s2.field3(15m) s2|structC() .options('c', 21.5, 7h) .aggFunc(influxql.agg.sum) ` scope := stateful.NewScope() a := &structA{} scope.Set("a", a) i := &influxql{ Agg: &agg{ Sum: aggSum, }, } scope.Set("influxql", i) _, err := tick.Evaluate(script, scope, nil, false) if err != nil { t.Fatal(err) } s2I, err := scope.Get("s2") if err != nil { t.Fatal(err) } s2 := s2I.(*structB) exp := structB{ Field1: "f1", Field2: 42, Field3: time.Minute * 15, } s3 := *s2.c s2.c = nil if !reflect.DeepEqual(*s2, exp) { t.Errorf("unexpected s2 exp:%v got%v", exp, *s2) } c := structC{ field1: "c", field2: 21.5, field3: time.Hour * 7, } aggFunc := s3.AggFunc s3.AggFunc = nil if !reflect.DeepEqual(s3, c) { t.Errorf("unexpected s3 exp:%v got%v", c, s3) } if exp, got := []float64{10.0}, aggFunc([]float64{5, 5}); !reflect.DeepEqual(exp, got) { t.Errorf("unexpected s3.AggFunc exp:%v got%v", exp, got) } }
func BenchmarkEvalBool_TwoLevelDeep(b *testing.B) { scope := stateful.NewScope() scope.Set("a", float64(11)) scope.Set("b", float64(8)) benchmarkEvalBool(b, scope, &ast.BinaryNode{ Operator: ast.TokenAnd, Left: &ast.BinaryNode{ Operator: ast.TokenGreater, Left: &ast.ReferenceNode{ Reference: "a", }, Right: &ast.NumberNode{ IsFloat: true, Float64: 10, }, }, Right: &ast.BinaryNode{ Operator: ast.TokenLess, Left: &ast.ReferenceNode{ Reference: "b", }, Right: &ast.NumberNode{ IsFloat: true, Float64: 10, }, }, }) }
func ExampleEvaluate() { //Run a test that evaluates the DSL against the Process struct. script := ` //Name the parent parent.name('parent') // Spawn a first child var child1 = parent|spawn() // Name the first child child1.name('child1') //Spawn a grandchild and name it child1|spawn().name('grandchild') //Spawn a second child and name it parent|spawn().name('child2') ` scope := stateful.NewScope() parent := &Process{} scope.Set("parent", parent) _, err := Evaluate(script, scope, nil, false) if err != nil { fmt.Println(err) } fmt.Println(parent) // Output: {"parent" [{"child1" [{"grandchild" []}]} {"child2" []}]} }
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 (tm *TaskMaster) CreateTICKScope() *stateful.Scope { scope := stateful.NewScope() scope.Set("time", groupByTime) // Add dynamic methods to the scope for UDFs if tm.UDFService != nil { for _, f := range tm.UDFService.List() { f := f info, _ := tm.UDFService.Info(f) scope.SetDynamicMethod( f, stateful.DynamicMethod(func(self interface{}, args ...interface{}) (interface{}, error) { parent, ok := self.(pipeline.Node) if !ok { return nil, fmt.Errorf("cannot call %s on %T", f, self) } udf := pipeline.NewUDF( parent, f, info.Wants, info.Provides, info.Options, ) return udf, nil }), ) } } return scope }
func BenchmarkEvalBool_OneOperator_UnaryNode_BoolNode(b *testing.B) { emptyScope := stateful.NewScope() benchmarkEvalBool(b, emptyScope, &ast.UnaryNode{ Operator: ast.TokenNot, Node: &ast.BoolNode{ Bool: false, }, }) }
func BenchmarkEvalBool_OneOperator_UnaryNode_ReferenceNode(b *testing.B) { scope := stateful.NewScope() scope.Set("value", bool(false)) benchmarkEvalBool(b, scope, &ast.UnaryNode{ Operator: ast.TokenNot, Node: &ast.ReferenceNode{ Reference: "value", }, }) }
func TestEvaluate_Vars_ErrorMissingValue(t *testing.T) { script := ` var x duration ` scope := stateful.NewScope() if _, err := tick.Evaluate(script, scope, nil, false); err == nil { t.Fatal("expected error for missing var type") } if _, err := tick.Evaluate(script, scope, nil, true); err != nil { t.Fatal("uexpected error missing var should be ignored") } }
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 BenchmarkEvalBool_OneOperatorValueChanges_ReferenceNodeInt64_NumberInt64(b *testing.B) { scope := stateful.NewScope() initialValue := int64(20) scope.Set("value", initialValue) b.ReportAllocs() b.ResetTimer() se, err := stateful.NewExpression(&ast.BinaryNode{ Operator: ast.TokenGreater, Left: &ast.ReferenceNode{ Reference: "value", }, Right: &ast.NumberNode{ IsInt: true, Int64: int64(10), }, }) if err != nil { b.Fatalf("Failed to compile the expression: %v", err) } // We have maximum value because we want to limit the maximum number in // the reference node so we don't get too much big numbers and the benchmark suite will increase our iterations number (b.N) currentValue := initialValue maximumValue := int64(40) var result bool for i := 0; i < b.N; i++ { b.StopTimer() currentValue += int64(1) if currentValue > maximumValue { currentValue = initialValue } scope.Set("value", currentValue) b.StartTimer() result, err := se.EvalBool(scope) if err != nil || !result { v, _ := scope.Get("value") b.Errorf("Failed to evaluate: error=%v, result=%t, value=%v, init=%v, maximum=%v", err, result, v, initialValue, maximumValue) } } evalBoolResult = result }
func TestEvaluate_DynamicMethod(t *testing.T) { script := `var x = a@dynamicMethod(1,'str', 10s).sad(FALSE)` scope := stateful.NewScope() a := &structA{} scope.Set("a", a) dm := func(self interface{}, args ...interface{}) (interface{}, error) { a, ok := self.(*structA) if !ok { return nil, fmt.Errorf("cannot call dynamicMethod on %T", self) } o := &orphan{ parent: a, Sad: true, args: args, } return o, nil } scope.SetDynamicMethod("dynamicMethod", dm) _, err := tick.Evaluate(script, scope, nil, false) if err != nil { t.Fatal(err) } xI, err := scope.Get("x") if err != nil { t.Fatal(err) } x, ok := xI.(*orphan) if !ok { t.Fatalf("expected x to be an *orphan, got %T", xI) } if x.Sad { t.Errorf("expected x to not be sad") } if got, exp := len(x.args), 3; exp != got { t.Fatalf("unexpected number of args: got %d exp %d", got, exp) } if got, exp := x.args[0], int64(1); exp != got { t.Errorf("unexpected x.args[0]: got %v exp %d", got, exp) } if got, exp := x.args[1], "str"; exp != got { t.Errorf("unexpected x.args[1]: got %v exp %s", got, exp) } if got, exp := x.args[2], time.Second*10; exp != got { t.Errorf("unexpected x.args[1]: got %v exp %v", got, exp) } }
func BenchmarkEvalBool_OneOperator_NumberInt64_NumberInt64(b *testing.B) { emptyScope := stateful.NewScope() benchmarkEvalBool(b, emptyScope, &ast.BinaryNode{ Operator: ast.TokenGreater, Left: &ast.NumberNode{ IsInt: true, Int64: int64(20), }, Right: &ast.NumberNode{ IsInt: true, Int64: int64(10), }, }) }
func BenchmarkEvalBool_OneOperator_ReferenceNodeFloat64_ReferenceNodeFloat64(b *testing.B) { scope := stateful.NewScope() scope.Set("l", float64(20)) scope.Set("r", float64(10)) benchmarkEvalBool(b, scope, &ast.BinaryNode{ Operator: ast.TokenGreater, Left: &ast.ReferenceNode{ Reference: "l", }, Right: &ast.ReferenceNode{ Reference: "r", }, }) }
// Test that using the wrong chain operator fails func TestStrictEvaluate(t *testing.T) { script := ` var s2 = a.structB() .field1('f1') .field2(42) ` scope := stateful.NewScope() a := &structA{} scope.Set("a", a) _, err := tick.Evaluate(script, scope, nil, false) if err == nil { t.Fatal("expected error from Evaluate") } }
func BenchmarkEvalBool_OneOperator_ReferenceNodeFloat64_NumberInt64(b *testing.B) { scope := stateful.NewScope() scope.Set("value", float64(20)) benchmarkEvalBool(b, scope, &ast.BinaryNode{ Operator: ast.TokenGreater, Left: &ast.ReferenceNode{ Reference: "value", }, Right: &ast.NumberNode{ IsInt: true, Int64: int64(10), }, }) }
func TestEvaluate_Vars_TypeConversion(t *testing.T) { script := ` var d = 5m var messageDuration = '{{ .ID }} has crossed threshold: ' + string(d) var x = 5 var messageInt = '{{ .ID }} has crossed threshold: ' + string(x) var y = 1.0 / 3.0 var messageFloat = '{{ .ID }} has crossed threshold: ' + string(y) var z = FALSE var messageBool = '{{ .ID }} is: ' + string(z) ` scope := stateful.NewScope() vars, err := tick.Evaluate(script, scope, nil, true) if err != nil { t.Fatal(err) } exp := map[string]interface{}{ "d": 5 * time.Minute, "messageDuration": "{{ .ID }} has crossed threshold: 5m", "x": int64(5), "messageInt": "{{ .ID }} has crossed threshold: 5", "y": 1.0 / 3.0, "messageFloat": "{{ .ID }} has crossed threshold: 0.3333333333333333", "z": false, "messageBool": "{{ .ID }} is: false", } for name, value := range exp { if got, err := scope.Get(name); err != nil { t.Errorf("unexpected error for %s: %s", name, err) } else if !reflect.DeepEqual(got, value) { t.Errorf("unexpected %s value: got %v exp %v", name, got, value) } if got, exp := vars[name].Value, value; !reflect.DeepEqual(got, exp) { t.Errorf("unexpected %s vars value: got %v exp %v", name, got, value) } if got, exp := vars[name].Type, ast.TypeOf(value); got != exp { t.Errorf("unexpected %s vars type: got %v exp %v", name, got, exp) } } }
func TestEvaluate_Vars_ErrorWrongType(t *testing.T) { script := ` var x = 3m ` definedVars := map[string]tick.Var{ "x": { Value: "5m", Type: ast.TString, }, } scope := stateful.NewScope() _, err := tick.Evaluate(script, scope, definedVars, false) if err == nil { t.Fatal("expected error for invalid var type") } }
func TestStatefulExpression_Integration_EvalBool_SanityCallingFunction(t *testing.T) { scope := stateful.NewScope() se := mustCompileExpression(&ast.BinaryNode{ Operator: ast.TokenEqual, Left: &ast.FunctionNode{ Func: "count", }, Right: &ast.NumberNode{ IsInt: true, Int64: 1, }, }) result, err := se.EvalBool(scope) if err != nil { t.Error(err) } if !result { t.Errorf("first evaluation: unexpected result: got: %v, expected: true", result) } // Second time, to make sure that count() increases the value result, err = se.EvalBool(scope) if err != nil { t.Error(err) } if result { t.Errorf("second evaluation: unexpected result: got: %v, expected: false", result) } // reset the expression se.Reset() result, err = se.EvalBool(scope) if err != nil { t.Error(err) } if !result { t.Errorf("last evaluation after reset: unexpected result: got: %v, expected: true", result) } }
func BenchmarkEvalBool_OneOperator_UnaryNode(b *testing.B) { scope := stateful.NewScope() scope.Set("value", bool(true)) benchmarkEvalBool(b, scope, &ast.BinaryNode{ Operator: ast.TokenEqual, Left: &ast.UnaryNode{ Operator: ast.TokenNot, Node: &ast.BoolNode{ Bool: false, }, }, Right: &ast.ReferenceNode{ Reference: "value", }, }) }
func TestEvaluate_StringQuotesError(t *testing.T) { script := ` f("asdf") ` f := func(got string) (interface{}, error) { return nil, errors.New("function should not be called") } scope := stateful.NewScope() scope.Set("f", f) _, err := tick.Evaluate(script, scope, nil, false) if err == nil { t.Fatal("expected error from invalid string call") } else if got, exp := err.Error(), "line 2 char 1: cannot assign *ast.ReferenceNode to type string, did you use double quotes instead of single quotes?"; got != exp { t.Errorf("unexpected error string: \ngot\n%s\nexp\n%s\n", got, exp) } }
func TestEvaluate_Func_Expression_Parameter(t *testing.T) { script := ` f('asdf' + string(10) + 'qwerty') ` f := func(got string) (interface{}, error) { if exp := "asdf10qwerty"; got != exp { t.Errorf("unexpected arg to function: got %s exp %s", got, exp) } return nil, nil } scope := stateful.NewScope() scope.Set("f", f) if _, err := tick.Evaluate(script, scope, nil, false); err != nil { t.Fatal(err) } }
func BenchmarkEvalBool_OneOperatorWith11ScopeItem_ReferenceNodeInt64_NumberInt64(b *testing.B) { scope := stateful.NewScope() scope.Set("value", int64(20)) for i := 0; i < 10; i++ { scope.Set(fmt.Sprintf("value_%v", i), int64(i)) } benchmarkEvalBool(b, scope, &ast.BinaryNode{ Operator: ast.TokenGreater, Left: &ast.ReferenceNode{ Reference: "value", }, Right: &ast.NumberNode{ IsInt: true, Int64: int64(10), }, }) }
func TestTICK_To_Pipeline_MultiLine(t *testing.T) { var tickScript = ` var w = stream |from() |window() w.period(10s) w.every(1s) ` d := deadman{} scope := stateful.NewScope() p, err := CreatePipeline(tickScript, StreamEdge, scope, d, nil) if err != nil { t.Fatal(err) } if p == nil { t.Fatal("unexpected pipeline, got nil") } if exp, got := 1, len(p.sources); exp != got { t.Errorf("unexpected number of pipeline sources: exp %d got %d", exp, got) } if exp, got := 1, len(p.sources[0].Children()); exp != got { t.Errorf("unexpected number of source0 children: exp %d got %d", exp, got) } sn, ok := p.sources[0].Children()[0].(*FromNode) if !ok { t.Fatalf("unexpected node type: exp FromNode got %T", p.sources[0].Children()[0]) } w, ok := sn.Children()[0].(*WindowNode) if !ok { t.Fatalf("unexpected node type: exp WindowNode got %T", sn.Children()[0]) } if exp, got := time.Duration(10)*time.Second, w.Period; exp != got { t.Errorf("unexpected window period exp %v got %v", exp, got) } if exp, got := time.Duration(1)*time.Second, w.Every; exp != got { t.Errorf("unexpected window every exp %v got %v", exp, got) } }
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 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) } }