Ejemplo n.º 1
0
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")
	}
}
Ejemplo n.º 2
0
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")
	}
}
Ejemplo n.º 3
0
// Create a pipeline from a given script.
// tick:ignore
func CreatePipeline(script string, sourceEdge EdgeType, scope *tick.Scope, deadman DeadmanService) (*Pipeline, error) {
	p := &Pipeline{
		deadman: deadman,
	}
	var src Node
	switch sourceEdge {
	case StreamEdge:
		src = newSourceStreamNode()
		scope.Set("stream", src)
	case BatchEdge:
		src = newSourceBatchNode()
		scope.Set("batch", src)
	default:
		return nil, fmt.Errorf("source edge type must be either Stream or Batch not %s", sourceEdge)
	}
	p.addSource(src)

	err := tick.Evaluate(script, scope)
	if err != nil {
		return nil, err
	}
	if sourceEdge == StreamEdge && deadman.Global() {
		src.(*SourceStreamNode).Deadman(deadman.Threshold(), deadman.Interval())
	}
	return p, nil

}
Ejemplo n.º 4
0
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 := tick.NewScope()
	parent := &Process{}
	scope.Set("parent", parent)

	err := tick.Evaluate(script, scope)
	if err != nil {
		fmt.Println(err)
	}

	fmt.Println(parent)
	// Output: {"parent" [{"child1" [{"grandchild" []}]} {"child2" []}]}
}
Ejemplo n.º 5
0
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)
	}
}
Ejemplo n.º 6
0
func TestEvaluate(t *testing.T) {
	assert := assert.New(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 := tick.NewScope()
	a := &structA{}
	scope.Set("a", a)

	i := &influxql{
		Agg: &agg{
			Sum: aggSum,
		},
	}
	scope.Set("influxql", i)

	err := tick.Evaluate(script, scope)
	if err != nil {
		t.Fatal(err)
	}

	s2I, err := scope.Get("s2")
	if err != nil {
		t.Fatal(err)
	}
	s2 := s2I.(*structB)
	assert.NotNil(s2)
	assert.Equal("f1", s2.Field1)
	assert.Equal(int64(42), s2.Field2)
	assert.Equal(time.Minute*15, s2.Field3)

	s3 := s2.c
	if assert.NotNil(s3) {
		assert.Equal("c", s3.field1)
		assert.Equal(21.5, s3.field2)
		assert.Equal(time.Hour*7, s3.field3)
		if assert.NotNil(s3.AggFunc) {
			assert.Equal([]float64{10.0}, s3.AggFunc([]float64{5, 5}))
		}
	}
}
Ejemplo n.º 7
0
func TestEvaluate_DynamicMethod(t *testing.T) {
	script := `var x = a.dynamicMethod(1,'str', 10s).sad(FALSE)`

	scope := tick.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)
	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)
	}
}
Ejemplo n.º 8
0
// 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")
	}
}
Ejemplo n.º 9
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)
		}
	}
}
Ejemplo n.º 10
0
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")
	}
}
Ejemplo n.º 11
0
func createPipelineAndVars(
	script string,
	sourceEdge EdgeType,
	scope *stateful.Scope,
	deadman DeadmanService,
	predefinedVars map[string]tick.Var,
	ignoreMissingVars bool,
) (*Pipeline, map[string]tick.Var, error) {
	p := &Pipeline{
		deadman: deadman,
	}
	var src Node
	switch sourceEdge {
	case StreamEdge:
		src = newStreamNode()
		scope.Set("stream", src)
	case BatchEdge:
		src = newBatchNode()
		scope.Set("batch", src)
	default:
		return nil, nil, fmt.Errorf("source edge type must be either Stream or Batch not %s", sourceEdge)
	}
	p.addSource(src)

	vars, err := tick.Evaluate(script, scope, predefinedVars, ignoreMissingVars)
	if err != nil {
		return nil, nil, err
	}
	if deadman.Global() {
		switch s := src.(type) {
		case *StreamNode:
			s.Deadman(deadman.Threshold(), deadman.Interval())
		case *BatchNode:
			s.Deadman(deadman.Threshold(), deadman.Interval())
		default:
			return nil, nil, fmt.Errorf("source edge type must be either Stream or Batch not %s", sourceEdge)
		}
	}
	if err = p.Walk(
		func(n Node) error {
			return n.validate()
		}); err != nil {
		return nil, nil, err
	}
	return p, vars, nil
}
Ejemplo n.º 12
0
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)
	}
}
Ejemplo n.º 13
0
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)
	}
}
Ejemplo n.º 14
0
// Create a pipeline from a given script.
// tick:ignore
func CreatePipeline(script string, sourceEdge EdgeType, scope *tick.Scope) (*Pipeline, error) {
	var src Node
	switch sourceEdge {
	case StreamEdge:
		src = newStreamNode()
		scope.Set("stream", src)
	case BatchEdge:
		src = newSourceBatchNode()
		scope.Set("batch", src)
	default:
		return nil, fmt.Errorf("source edge type must be either Stream or Batch not %s", sourceEdge)
	}
	err := tick.Evaluate(script, scope)
	if err != nil {
		return nil, err
	}
	p := &Pipeline{Source: src}
	p.Walk(p.setID)
	return p, nil

}
Ejemplo n.º 15
0
func TestEvaluate_Vars_Immutable(t *testing.T) {
	script := `
var x = 3m
var x = 2m
`

	scope := stateful.NewScope()
	_, err := tick.Evaluate(script, scope, nil, false)
	if exp, got := "attempted to redefine x, vars are immutable", err.Error(); exp != got {
		t.Errorf("unexpected error message: got %s exp %s", got, exp)
	}

	x, err := scope.Get("x")
	if err != nil {
		t.Fatal(err)
	}
	if value, ok := x.(time.Duration); ok {
		if exp, got := time.Minute*3, value; exp != got {
			t.Errorf("unexpected x value: exp %v got %v", exp, got)
		}
	} else {
		t.Errorf("unexpected x value type: exp time.Duration got %T", x)
	}
}
Ejemplo n.º 16
0
func TestEvaluate_Vars(t *testing.T) {
	script := `
var x = 3m
var y = -x

var n = TRUE 
var m = !n 
`

	scope := tick.NewScope()
	err := tick.Evaluate(script, scope)
	if err != nil {
		t.Fatal(err)
	}

	x, err := scope.Get("x")
	if err != nil {
		t.Fatal(err)
	}
	if value, ok := x.(time.Duration); ok {
		if exp, got := time.Minute*3, value; exp != got {
			t.Errorf("unexpected x value: exp %v got %v", exp, got)
		}
	} else {
		t.Errorf("unexpected x value type: exp time.Duration got %T", x)
	}

	y, err := scope.Get("y")
	if err != nil {
		t.Fatal(err)
	}
	if value, ok := y.(time.Duration); ok {
		if exp, got := time.Minute*-3, value; exp != got {
			t.Errorf("unexpected y value: exp %v got %v", exp, got)
		}
	} else {
		t.Errorf("unexpected y value type: exp time.Duration got %T", x)
	}

	n, err := scope.Get("n")
	if err != nil {
		t.Fatal(err)
	}
	if value, ok := n.(bool); ok {
		if exp, got := true, value; exp != got {
			t.Errorf("unexpected n value: exp %v got %v", exp, got)
		}
	} else {
		t.Errorf("unexpected m value type: exp bool got %T", x)
	}

	m, err := scope.Get("m")
	if err != nil {
		t.Fatal(err)
	}
	if value, ok := m.(bool); ok {
		if exp, got := false, value; exp != got {
			t.Errorf("unexpected m value: exp %v got %v", exp, got)
		}
	} else {
		t.Errorf("unexpected m value type: exp bool got %T", x)
	}

}
Ejemplo n.º 17
0
func TestValidateTemplate_Vars(t *testing.T) {
	script := `
var x = 3m
var y = -x

var n = TRUE
var m = !n

var z = x + y

var a string
`

	scope := stateful.NewScope()
	vars, err := tick.Evaluate(script, scope, nil, true)
	if err != nil {
		t.Fatal(err)
	}

	x, err := scope.Get("x")
	if err != nil {
		t.Fatal(err)
	}
	if value, ok := x.(time.Duration); ok {
		if exp, got := time.Minute*3, value; exp != got {
			t.Errorf("unexpected x value: exp %v got %v", exp, got)
		}
	} else {
		t.Errorf("unexpected x value type: exp time.Duration got %T", x)
	}

	y, err := scope.Get("y")
	if err != nil {
		t.Fatal(err)
	}
	if value, ok := y.(time.Duration); ok {
		if exp, got := time.Minute*-3, value; exp != got {
			t.Errorf("unexpected y value: exp %v got %v", exp, got)
		}
	} else {
		t.Errorf("unexpected y value type: exp time.Duration got %T", y)
	}

	n, err := scope.Get("n")
	if err != nil {
		t.Fatal(err)
	}
	if value, ok := n.(bool); ok {
		if exp, got := true, value; exp != got {
			t.Errorf("unexpected n value: exp %v got %v", exp, got)
		}
	} else {
		t.Errorf("unexpected m value type: exp bool got %T", x)
	}

	m, err := scope.Get("m")
	if err != nil {
		t.Fatal(err)
	}
	if value, ok := m.(bool); ok {
		if exp, got := false, value; exp != got {
			t.Errorf("unexpected m value: exp %v got %v", exp, got)
		}
	} else {
		t.Errorf("unexpected m value type: exp bool got %T", x)
	}

	z, err := scope.Get("z")
	if err != nil {
		t.Fatal(err)
	}
	if value, ok := z.(time.Duration); ok {
		if exp, got := time.Duration(0), value; exp != got {
			t.Errorf("unexpected z value: exp %v got %v", exp, got)
		}
	} else {
		t.Errorf("unexpected z value type: exp time.Duration got %T", z)
	}

	expVars := map[string]tick.Var{
		"a": {
			Value: nil,
			Type:  ast.TString,
		},
		"x": {
			Value: 3 * time.Minute,
			Type:  ast.TDuration,
		},
		"y": {
			Value: -3 * time.Minute,
			Type:  ast.TDuration,
		},
		"z": {
			Value: time.Duration(0),
			Type:  ast.TDuration,
		},
		"n": {
			Value: true,
			Type:  ast.TBool,
		},
		"m": {
			Value: false,
			Type:  ast.TBool,
		},
	}
	if !reflect.DeepEqual(expVars, vars) {
		t.Errorf("unexpected vars: got %v exp %v", vars, expVars)
	}
}
Ejemplo n.º 18
0
func TestEvaluate_Vars_PreDefined(t *testing.T) {
	script := `
var str = 'this is a string'
var strZero string

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

var star = *
var starZero star

var int = 8
var intZero int

var float = 3.14
var floatZero float

var duration = 5m
var durationZero duration

var regex = /.*/
var regexZero regex

var boolean = TRUE
var booleanZero bool

var lambda = lambda: "value" > 0
var lambdaZero lambda
`
	lambda := &ast.LambdaNode{
		Expression: &ast.BinaryNode{
			Operator: ast.TokenGreater,
			Left: &ast.ReferenceNode{
				Reference: "value",
			},
			Right: &ast.NumberNode{
				IsInt: true,
				Int64: 0,
			},
		},
	}
	definedVars := map[string]tick.Var{
		"str": {
			Value: "asdf",
			Type:  ast.TString,
		},
		"strZero": {
			Value: "qwerty",
			Type:  ast.TString,
		},
		"strList": {
			Value: []tick.Var{
				{
					Value: "a",
					Type:  ast.TString,
				},
				{
					Value: "b",
					Type:  ast.TString,
				},
				{
					Value: "c",
					Type:  ast.TString,
				},
			},
			Type: ast.TList,
		},
		"strListZero": {
			Value: []tick.Var{
				{
					Value: &ast.StarNode{},
					Type:  ast.TStar,
				},
			},
			Type: ast.TList,
		},
		"star": {
			Value: &ast.StarNode{},
			Type:  ast.TStar,
		},
		"starZero": {
			Value: &ast.StarNode{},
			Type:  ast.TStar,
		},
		"int": {
			Value: int64(42),
			Type:  ast.TInt,
		},
		"intZero": {
			Value: int64(6 * 9),
			Type:  ast.TInt,
		},
		"float": {
			Value: float64(42),
			Type:  ast.TFloat,
		},
		"floatZero": {
			Value: float64(6 * 9),
			Type:  ast.TFloat,
		},
		"duration": {
			Value: 5 * time.Hour,
			Type:  ast.TDuration,
		},
		"durationZero": {
			Value: time.Minute,
			Type:  ast.TDuration,
		},
		"regex": {
			Value: regexp.MustCompile(`^asdf.*qwerty$`),
			Type:  ast.TRegex,
		},
		"regexZero": {
			Value: regexp.MustCompile(`^.*$`),
			Type:  ast.TRegex,
		},
		"boolean": {
			Value: false,
			Type:  ast.TBool,
		},
		"booleanZero": {
			Value: true,
			Type:  ast.TBool,
		},
		"lambda": {
			Value: lambda,
			Type:  ast.TLambda,
		},
		"lambdaZero": {
			Value: lambda,
			Type:  ast.TLambda,
		},
	}

	scope := stateful.NewScope()
	_, err := tick.Evaluate(script, scope, definedVars, false)
	if err != nil {
		t.Fatal(err)
	}

	expScope := map[string]interface{}{
		"str":          "asdf",
		"strZero":      "qwerty",
		"strList":      []interface{}{"a", "b", "c"},
		"strListZero":  []interface{}{&ast.StarNode{}},
		"star":         &ast.StarNode{},
		"starZero":     &ast.StarNode{},
		"int":          int64(42),
		"intZero":      int64(6 * 9),
		"float":        float64(42),
		"floatZero":    float64(6 * 9),
		"duration":     5 * time.Hour,
		"durationZero": time.Minute,
		"regex":        regexp.MustCompile(`^asdf.*qwerty$`),
		"regexZero":    regexp.MustCompile(`^.*$`),
		"boolean":      false,
		"booleanZero":  true,
		"lambda":       lambda,
		"lambdaZero":   lambda,
	}

	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)
		}
	}

}
Ejemplo n.º 19
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)
		}
	}
}