Beispiel #1
0
func Test_TypeOf(t *testing.T) {
	type expectation struct {
		value     interface{}
		valueType ast.ValueType
	}

	expectations := []expectation{
		{value: float64(0), valueType: ast.TFloat},
		{value: int64(0), valueType: ast.TInt},
		{value: "Kapacitor Rulz", valueType: ast.TString},
		{value: true, valueType: ast.TBool},
		{value: regexp.MustCompile("\\d"), valueType: ast.TRegex},
		{value: time.Duration(5), valueType: ast.TDuration},
		{value: time.Time{}, valueType: ast.TTime},
		{value: t, valueType: ast.InvalidType},
	}

	for _, expect := range expectations {
		result := ast.TypeOf(expect.value)

		if result != expect.valueType {
			t.Errorf("Got unexpected result for valueTypeOf(%T):\ngot: %s\nexpected: %s", expect.value, result, expect.valueType)
		}

	}
}
func (n *EvalReferenceNode) Type(scope ReadOnlyScope, executionState ExecutionState) (ast.ValueType, error) {
	value, err := n.getReferenceValue(scope.(*Scope), executionState)
	if err != nil {
		return ast.InvalidType, err
	}

	return ast.TypeOf(value), nil
}
func (n *EvalFunctionNode) Type(scope ReadOnlyScope, executionState ExecutionState) (ast.ValueType, error) {
	// PERF: today we are evaluating the function, it will be much faster if will type info the function it self
	result, err := n.callFunction(scope.(*Scope), executionState)
	if err != nil {
		return ast.InvalidType, err
	}

	// We can't cache here the result (although it's very tempting ;))
	// because can't trust function to return always the same consistent type
	return ast.TypeOf(result), nil
}
func (n *EvalReferenceNode) EvalFloat(scope *Scope, executionState ExecutionState) (float64, error) {
	refValue, err := n.getReferenceValue(scope, executionState)
	if err != nil {
		return float64(0), err
	}

	if float64Value, isFloat64 := refValue.(float64); isFloat64 {
		return float64Value, nil
	}

	return float64(0), ErrTypeGuardFailed{RequestedType: ast.TFloat, ActualType: ast.TypeOf(refValue)}
}
func (n *EvalReferenceNode) EvalString(scope *Scope, executionState ExecutionState) (string, error) {
	refValue, err := n.getReferenceValue(scope, executionState)
	if err != nil {
		return "", err
	}

	if stringValue, isString := refValue.(string); isString {
		return stringValue, nil
	}

	return "", ErrTypeGuardFailed{RequestedType: ast.TString, ActualType: ast.TypeOf(refValue)}
}
func (n *EvalReferenceNode) EvalDuration(scope *Scope, executionState ExecutionState) (time.Duration, error) {
	refValue, err := n.getReferenceValue(scope, executionState)
	if err != nil {
		return 0, err
	}

	if durValue, isDuration := refValue.(time.Duration); isDuration {
		return durValue, nil
	}

	return 0, ErrTypeGuardFailed{RequestedType: ast.TDuration, ActualType: ast.TypeOf(refValue)}
}
func (n *EvalReferenceNode) EvalTime(scope *Scope, executionState ExecutionState) (time.Time, error) {
	refValue, err := n.getReferenceValue(scope, executionState)
	if err != nil {
		return time.Time{}, err
	}

	if timeValue, isTime := refValue.(time.Time); isTime {
		return timeValue, nil
	}

	return time.Time{}, ErrTypeGuardFailed{RequestedType: ast.TTime, ActualType: ast.TypeOf(refValue)}
}
func (n *EvalReferenceNode) EvalRegex(scope *Scope, executionState ExecutionState) (*regexp.Regexp, error) {
	refValue, err := n.getReferenceValue(scope, executionState)
	if err != nil {
		return nil, err
	}

	if regexValue, isRegex := refValue.(*regexp.Regexp); isRegex {
		return regexValue, nil
	}

	return nil, ErrTypeGuardFailed{RequestedType: ast.TRegex, ActualType: ast.TypeOf(refValue)}
}
func (n *EvalReferenceNode) EvalBool(scope *Scope, executionState ExecutionState) (bool, error) {
	refValue, err := n.getReferenceValue(scope, executionState)
	if err != nil {
		return false, err
	}

	if boolValue, isBool := refValue.(bool); isBool {
		return boolValue, nil
	}

	return false, ErrTypeGuardFailed{RequestedType: ast.TBool, ActualType: ast.TypeOf(refValue)}
}
func (n *EvalFunctionNode) EvalInt(scope *Scope, executionState ExecutionState) (int64, error) {
	refValue, err := n.callFunction(scope, executionState)
	if err != nil {
		return int64(0), err
	}

	if int64Value, isInt64 := refValue.(int64); isInt64 {
		return int64Value, nil
	}

	return int64(0), ErrTypeGuardFailed{RequestedType: ast.TInt, ActualType: ast.TypeOf(refValue)}
}
Beispiel #11
0
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)
		}
	}
}
Beispiel #12
0
func evalDeclaration(node *ast.DeclarationNode, scope *stateful.Scope, stck *stack, predefinedVars, defaultVars map[string]Var) error {
	name := node.Left.Ident
	if v, _ := scope.Get(name); v != nil {
		return fmt.Errorf("attempted to redefine %s, vars are immutable", name)
	}
	value := stck.Pop()
	if i, ok := value.(*ast.IdentifierNode); ok {
		// Resolve identifier
		v, err := scope.Get(i.Ident)
		if err != nil {
			return err
		}
		value = v
	}
	actualType := ast.TypeOf(value)
	// Populate set of default vars
	if actualType != ast.InvalidType {
		desc := ""
		if node.Comment != nil {
			desc = node.Comment.CommentString()
		}

		v, err := convertValueToVar(value, actualType, desc)
		if err != nil {
			return err
		}
		defaultVars[name] = v
	}
	// Populate scope, first check for predefined var
	if predefinedValue, ok := predefinedVars[name]; ok {
		if predefinedValue.Type != actualType {
			return fmt.Errorf("invalid type supplied for %s, got %v exp %v", name, predefinedValue.Type, actualType)
		}
		v, err := convertVarToValue(Var{Value: predefinedValue.Value, Type: actualType})
		if err != nil {
			return err
		}
		value = v
	}
	scope.Set(name, value)
	return nil
}
Beispiel #13
0
func convertValueToVar(value interface{}, typ ast.ValueType, desc string) (Var, error) {
	varValue := value
	if typ == ast.TList {
		values, ok := value.([]interface{})
		if !ok {
			return Var{}, fmt.Errorf("var has type list but value is type %T", value)
		}

		list := make([]Var, len(values))
		for i := range values {
			typ := ast.TypeOf(values[i])
			list[i] = Var{
				Type:  typ,
				Value: values[i],
			}
		}
		varValue = list
	}
	return Var{
		Type:        typ,
		Value:       varValue,
		Description: desc,
	}, nil
}
Beispiel #14
0
func TestEvaluate_Vars_AllTypes(t *testing.T) {
	script := `
var str = 'this is a string'
var strA = str + ' concat'
var strZero string
var strCopy = str

var strList = [ 'string list', str, strA, strCopy, *]
var strListZero list
var strListCopy = strList

var star = *
var starZero star
var starCopy = star

var integer = 42
var intergerMath = (integer * 2) - ( 6 * 9 ) + (36 / 3)
var intZero int
var integerCopy= integer

var float = 3.14
var tastyPie = float * 42.0
var floatZero float
var floatCopy = float

var intFloat = int(sqrt(float)) * 3

var duration = 5m
var later = duration + 1m
var durationZero duration
var durationCopy = duration

var regex = /.*/
var regexZero regex
var regexCopy = regex

var boolean = TRUE
var f = boolean AND FALSE
var booleanZero bool
var booleanCopy = boolean

var lambda = lambda: "value" > 0
var l = lambda: lambda OR "value" < -100
var lambdaZero lambda
var lambdaCopy = lambda
`

	scope := stateful.NewScope()
	vars, err := tick.Evaluate(script, scope, nil, true)
	if err != nil {
		t.Fatal(err)
	}
	expLambda := &ast.LambdaNode{
		Expression: &ast.BinaryNode{
			Operator: ast.TokenGreater,
			Left: &ast.ReferenceNode{
				Reference: "value",
			},
			Right: &ast.NumberNode{
				IsInt: true,
				Int64: 0,
			},
		},
	}
	expNestedLambda := &ast.LambdaNode{
		Expression: &ast.BinaryNode{
			Operator: ast.TokenOr,
			Left:     expLambda,
			Right: &ast.BinaryNode{
				Operator: ast.TokenLess,
				Left: &ast.ReferenceNode{
					Reference: "value",
				},
				Right: &ast.UnaryNode{
					Operator: ast.TokenMinus,
					Node: &ast.NumberNode{
						IsInt: true,
						Int64: 100,
					},
				},
			},
		},
	}

	expStrList := []interface{}{
		"string list",
		"this is a string",
		"this is a string concat",
		"this is a string",
		&ast.StarNode{},
	}
	expStrVarList := tick.Var{
		Value: []tick.Var{
			{Value: "string list", Type: ast.TString},
			{Value: "this is a string", Type: ast.TString},
			{Value: "this is a string concat", Type: ast.TString},
			{Value: "this is a string", Type: ast.TString},
			{Value: &ast.StarNode{}, Type: ast.TStar},
		},
		Type: ast.TList,
	}

	expScope := map[string]interface{}{
		"str":          "this is a string",
		"strCopy":      "this is a string",
		"strA":         "this is a string concat",
		"strZero":      "",
		"strList":      expStrList,
		"strListCopy":  expStrList,
		"strListZero":  []interface{}(nil),
		"star":         &ast.StarNode{},
		"starZero":     (*ast.StarNode)(nil),
		"starCopy":     &ast.StarNode{},
		"integer":      int64(42),
		"integerCopy":  int64(42),
		"intergerMath": int64(42),
		"intZero":      int64(0),
		"float":        3.14,
		"floatCopy":    3.14,
		"tastyPie":     3.14 * 42.0,
		"intFloat":     int64(3),
		"floatZero":    float64(0),
		"duration":     5 * time.Minute,
		"durationCopy": 5 * time.Minute,
		"later":        6 * time.Minute,
		"durationZero": time.Duration(0),
		"regex":        regexp.MustCompile(".*"),
		"regexCopy":    regexp.MustCompile(".*"),
		"regexZero":    (*regexp.Regexp)(nil),
		"boolean":      true,
		"booleanCopy":  true,
		"f":            false,
		"booleanZero":  false,
		"lambda":       expLambda,
		"lambdaCopy":   expLambda,
		"l":            expNestedLambda,
		"lambdaZero":   (*ast.LambdaNode)(nil),
	}
	expVars := map[string]tick.Var{
		"strList":     expStrVarList,
		"strListCopy": expStrVarList,
	}

	for name, value := range expScope {
		if got, err := scope.Get(name); err != nil {
			t.Errorf("unexpected error for %s: %s", name, err)
		} else if !equal(got, value) {
			t.Errorf("unexpected %s value: \ngot\n%v\ngot type: %T\nexp\n%v\nexp type: %T", name, got, got, value, value)
		}
		expVar, ok := expVars[name]
		if !ok {
			typ := ast.TypeOf(value)
			if strings.Contains(name, "Zero") {
				value = interface{}(nil)
			}
			expVar = tick.Var{
				Type:  typ,
				Value: value,
			}
		}
		if got, exp := vars[name].Value, expVar.Value; !equal(exp, got) {
			t.Errorf("unexpected %s var value:\ngot\n%v\nexp\n%v\n", name, got, exp)
		}
		if got, exp := vars[name].Type, expVar.Type; got != exp {
			t.Errorf("unexpected %s var type: got %v exp %v", name, got, exp)
		}
	}
}