blob: 9200199a6a074d7e9a00bf43fdfcbeb4235b6b1f [file] [log] [blame] [edit]
package hclsyntax
import (
"fmt"
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
"github.com/go-test/deep"
"github.com/hashicorp/hcl/v2"
)
func TestWalk(t *testing.T) {
tests := []struct {
src string
want []testWalkCall
}{
{
`1`,
[]testWalkCall{
{testWalkEnter, "*hclsyntax.LiteralValueExpr"},
{testWalkExit, "*hclsyntax.LiteralValueExpr"},
},
},
{
`foo`,
[]testWalkCall{
{testWalkEnter, "*hclsyntax.ScopeTraversalExpr"},
{testWalkExit, "*hclsyntax.ScopeTraversalExpr"},
},
},
{
`1 + 1`,
[]testWalkCall{
{testWalkEnter, "*hclsyntax.BinaryOpExpr"},
{testWalkEnter, "*hclsyntax.LiteralValueExpr"},
{testWalkExit, "*hclsyntax.LiteralValueExpr"},
{testWalkEnter, "*hclsyntax.LiteralValueExpr"},
{testWalkExit, "*hclsyntax.LiteralValueExpr"},
{testWalkExit, "*hclsyntax.BinaryOpExpr"},
},
},
{
`(1 + 1)`,
[]testWalkCall{
{testWalkEnter, "*hclsyntax.ParenthesesExpr"},
{testWalkEnter, "*hclsyntax.BinaryOpExpr"},
{testWalkEnter, "*hclsyntax.LiteralValueExpr"},
{testWalkExit, "*hclsyntax.LiteralValueExpr"},
{testWalkEnter, "*hclsyntax.LiteralValueExpr"},
{testWalkExit, "*hclsyntax.LiteralValueExpr"},
{testWalkExit, "*hclsyntax.BinaryOpExpr"},
{testWalkExit, "*hclsyntax.ParenthesesExpr"},
},
},
{
`a[0]`,
[]testWalkCall{
// because the index is constant here, the index is absorbed into the traversal
{testWalkEnter, "*hclsyntax.ScopeTraversalExpr"},
{testWalkExit, "*hclsyntax.ScopeTraversalExpr"},
},
},
{
`0[foo]`, // semantically incorrect, but should still parse and be walkable
[]testWalkCall{
{testWalkEnter, "*hclsyntax.IndexExpr"},
{testWalkEnter, "*hclsyntax.LiteralValueExpr"},
{testWalkExit, "*hclsyntax.LiteralValueExpr"},
{testWalkEnter, "*hclsyntax.ScopeTraversalExpr"},
{testWalkExit, "*hclsyntax.ScopeTraversalExpr"},
{testWalkExit, "*hclsyntax.IndexExpr"},
},
},
{
`bar()`,
[]testWalkCall{
{testWalkEnter, "*hclsyntax.FunctionCallExpr"},
{testWalkExit, "*hclsyntax.FunctionCallExpr"},
},
},
{
`bar(1, a)`,
[]testWalkCall{
{testWalkEnter, "*hclsyntax.FunctionCallExpr"},
{testWalkEnter, "*hclsyntax.LiteralValueExpr"},
{testWalkExit, "*hclsyntax.LiteralValueExpr"},
{testWalkEnter, "*hclsyntax.ScopeTraversalExpr"},
{testWalkExit, "*hclsyntax.ScopeTraversalExpr"},
{testWalkExit, "*hclsyntax.FunctionCallExpr"},
},
},
{
`bar(1, a)[0]`,
[]testWalkCall{
{testWalkEnter, "*hclsyntax.RelativeTraversalExpr"},
{testWalkEnter, "*hclsyntax.FunctionCallExpr"},
{testWalkEnter, "*hclsyntax.LiteralValueExpr"},
{testWalkExit, "*hclsyntax.LiteralValueExpr"},
{testWalkEnter, "*hclsyntax.ScopeTraversalExpr"},
{testWalkExit, "*hclsyntax.ScopeTraversalExpr"},
{testWalkExit, "*hclsyntax.FunctionCallExpr"},
{testWalkExit, "*hclsyntax.RelativeTraversalExpr"},
},
},
{
`[for x in foo: x + 1 if x < 10]`,
[]testWalkCall{
{testWalkEnter, "*hclsyntax.ForExpr"},
{testWalkEnter, "*hclsyntax.ScopeTraversalExpr"},
{testWalkExit, "*hclsyntax.ScopeTraversalExpr"},
{testWalkEnter, "hclsyntax.ChildScope"},
{testWalkEnter, "*hclsyntax.BinaryOpExpr"},
{testWalkEnter, "*hclsyntax.ScopeTraversalExpr"},
{testWalkExit, "*hclsyntax.ScopeTraversalExpr"},
{testWalkEnter, "*hclsyntax.LiteralValueExpr"},
{testWalkExit, "*hclsyntax.LiteralValueExpr"},
{testWalkExit, "*hclsyntax.BinaryOpExpr"},
{testWalkExit, "hclsyntax.ChildScope"},
{testWalkEnter, "hclsyntax.ChildScope"},
{testWalkEnter, "*hclsyntax.BinaryOpExpr"},
{testWalkEnter, "*hclsyntax.ScopeTraversalExpr"},
{testWalkExit, "*hclsyntax.ScopeTraversalExpr"},
{testWalkEnter, "*hclsyntax.LiteralValueExpr"},
{testWalkExit, "*hclsyntax.LiteralValueExpr"},
{testWalkExit, "*hclsyntax.BinaryOpExpr"},
{testWalkExit, "hclsyntax.ChildScope"},
{testWalkExit, "*hclsyntax.ForExpr"},
},
},
}
for _, test := range tests {
t.Run(test.src, func(t *testing.T) {
expr, diags := ParseExpression([]byte(test.src), "", hcl.Pos{Line: 1, Column: 1})
if diags.HasErrors() {
t.Fatalf("failed to parse expression: %s", diags.Error())
}
w := testWalker{}
diags = Walk(expr, &w)
if diags.HasErrors() {
t.Fatalf("failed to walk: %s", diags.Error())
}
got := w.Calls
if !reflect.DeepEqual(got, test.want) {
t.Errorf("wrong calls\ngot: %swant: %s", spew.Sdump(got), spew.Sdump(test.want))
for _, problem := range deep.Equal(got, test.want) {
t.Errorf(problem)
}
}
})
}
}
type testWalkMethod int
const testWalkEnter testWalkMethod = 1
const testWalkExit testWalkMethod = 2
type testWalkCall struct {
Method testWalkMethod
NodeType string
}
type testWalker struct {
Calls []testWalkCall
}
func (w *testWalker) Enter(node Node) hcl.Diagnostics {
w.Calls = append(w.Calls, testWalkCall{testWalkEnter, fmt.Sprintf("%T", node)})
return nil
}
func (w *testWalker) Exit(node Node) hcl.Diagnostics {
w.Calls = append(w.Calls, testWalkCall{testWalkExit, fmt.Sprintf("%T", node)})
return nil
}