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