github.com/hashicorp/hcl/v2@v2.20.0/hclsyntax/walk_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package hclsyntax
     5  
     6  import (
     7  	"fmt"
     8  	"reflect"
     9  	"testing"
    10  
    11  	"github.com/davecgh/go-spew/spew"
    12  	"github.com/go-test/deep"
    13  
    14  	"github.com/hashicorp/hcl/v2"
    15  )
    16  
    17  func TestWalk(t *testing.T) {
    18  
    19  	tests := []struct {
    20  		src  string
    21  		want []testWalkCall
    22  	}{
    23  		{
    24  			`1`,
    25  			[]testWalkCall{
    26  				{testWalkEnter, "*hclsyntax.LiteralValueExpr"},
    27  				{testWalkExit, "*hclsyntax.LiteralValueExpr"},
    28  			},
    29  		},
    30  		{
    31  			`foo`,
    32  			[]testWalkCall{
    33  				{testWalkEnter, "*hclsyntax.ScopeTraversalExpr"},
    34  				{testWalkExit, "*hclsyntax.ScopeTraversalExpr"},
    35  			},
    36  		},
    37  		{
    38  			`1 + 1`,
    39  			[]testWalkCall{
    40  				{testWalkEnter, "*hclsyntax.BinaryOpExpr"},
    41  				{testWalkEnter, "*hclsyntax.LiteralValueExpr"},
    42  				{testWalkExit, "*hclsyntax.LiteralValueExpr"},
    43  				{testWalkEnter, "*hclsyntax.LiteralValueExpr"},
    44  				{testWalkExit, "*hclsyntax.LiteralValueExpr"},
    45  				{testWalkExit, "*hclsyntax.BinaryOpExpr"},
    46  			},
    47  		},
    48  		{
    49  			`(1 + 1)`,
    50  			[]testWalkCall{
    51  				{testWalkEnter, "*hclsyntax.ParenthesesExpr"},
    52  				{testWalkEnter, "*hclsyntax.BinaryOpExpr"},
    53  				{testWalkEnter, "*hclsyntax.LiteralValueExpr"},
    54  				{testWalkExit, "*hclsyntax.LiteralValueExpr"},
    55  				{testWalkEnter, "*hclsyntax.LiteralValueExpr"},
    56  				{testWalkExit, "*hclsyntax.LiteralValueExpr"},
    57  				{testWalkExit, "*hclsyntax.BinaryOpExpr"},
    58  				{testWalkExit, "*hclsyntax.ParenthesesExpr"},
    59  			},
    60  		},
    61  		{
    62  			`a[0]`,
    63  			[]testWalkCall{
    64  				// because the index is constant here, the index is absorbed into the traversal
    65  				{testWalkEnter, "*hclsyntax.ScopeTraversalExpr"},
    66  				{testWalkExit, "*hclsyntax.ScopeTraversalExpr"},
    67  			},
    68  		},
    69  		{
    70  			`0[foo]`, // semantically incorrect, but should still parse and be walkable
    71  			[]testWalkCall{
    72  				{testWalkEnter, "*hclsyntax.IndexExpr"},
    73  				{testWalkEnter, "*hclsyntax.LiteralValueExpr"},
    74  				{testWalkExit, "*hclsyntax.LiteralValueExpr"},
    75  				{testWalkEnter, "*hclsyntax.ScopeTraversalExpr"},
    76  				{testWalkExit, "*hclsyntax.ScopeTraversalExpr"},
    77  				{testWalkExit, "*hclsyntax.IndexExpr"},
    78  			},
    79  		},
    80  		{
    81  			`bar()`,
    82  			[]testWalkCall{
    83  				{testWalkEnter, "*hclsyntax.FunctionCallExpr"},
    84  				{testWalkExit, "*hclsyntax.FunctionCallExpr"},
    85  			},
    86  		},
    87  		{
    88  			`bar(1, a)`,
    89  			[]testWalkCall{
    90  				{testWalkEnter, "*hclsyntax.FunctionCallExpr"},
    91  				{testWalkEnter, "*hclsyntax.LiteralValueExpr"},
    92  				{testWalkExit, "*hclsyntax.LiteralValueExpr"},
    93  				{testWalkEnter, "*hclsyntax.ScopeTraversalExpr"},
    94  				{testWalkExit, "*hclsyntax.ScopeTraversalExpr"},
    95  				{testWalkExit, "*hclsyntax.FunctionCallExpr"},
    96  			},
    97  		},
    98  		{
    99  			`bar(1, a)[0]`,
   100  			[]testWalkCall{
   101  				{testWalkEnter, "*hclsyntax.RelativeTraversalExpr"},
   102  				{testWalkEnter, "*hclsyntax.FunctionCallExpr"},
   103  				{testWalkEnter, "*hclsyntax.LiteralValueExpr"},
   104  				{testWalkExit, "*hclsyntax.LiteralValueExpr"},
   105  				{testWalkEnter, "*hclsyntax.ScopeTraversalExpr"},
   106  				{testWalkExit, "*hclsyntax.ScopeTraversalExpr"},
   107  				{testWalkExit, "*hclsyntax.FunctionCallExpr"},
   108  				{testWalkExit, "*hclsyntax.RelativeTraversalExpr"},
   109  			},
   110  		},
   111  		{
   112  			`[for x in foo: x + 1 if x < 10]`,
   113  			[]testWalkCall{
   114  				{testWalkEnter, "*hclsyntax.ForExpr"},
   115  				{testWalkEnter, "*hclsyntax.ScopeTraversalExpr"},
   116  				{testWalkExit, "*hclsyntax.ScopeTraversalExpr"},
   117  				{testWalkEnter, "hclsyntax.ChildScope"},
   118  				{testWalkEnter, "*hclsyntax.BinaryOpExpr"},
   119  				{testWalkEnter, "*hclsyntax.ScopeTraversalExpr"},
   120  				{testWalkExit, "*hclsyntax.ScopeTraversalExpr"},
   121  				{testWalkEnter, "*hclsyntax.LiteralValueExpr"},
   122  				{testWalkExit, "*hclsyntax.LiteralValueExpr"},
   123  				{testWalkExit, "*hclsyntax.BinaryOpExpr"},
   124  				{testWalkExit, "hclsyntax.ChildScope"},
   125  				{testWalkEnter, "hclsyntax.ChildScope"},
   126  				{testWalkEnter, "*hclsyntax.BinaryOpExpr"},
   127  				{testWalkEnter, "*hclsyntax.ScopeTraversalExpr"},
   128  				{testWalkExit, "*hclsyntax.ScopeTraversalExpr"},
   129  				{testWalkEnter, "*hclsyntax.LiteralValueExpr"},
   130  				{testWalkExit, "*hclsyntax.LiteralValueExpr"},
   131  				{testWalkExit, "*hclsyntax.BinaryOpExpr"},
   132  				{testWalkExit, "hclsyntax.ChildScope"},
   133  				{testWalkExit, "*hclsyntax.ForExpr"},
   134  			},
   135  		},
   136  	}
   137  
   138  	for _, test := range tests {
   139  		t.Run(test.src, func(t *testing.T) {
   140  			expr, diags := ParseExpression([]byte(test.src), "", hcl.Pos{Line: 1, Column: 1})
   141  			if diags.HasErrors() {
   142  				t.Fatalf("failed to parse expression: %s", diags.Error())
   143  			}
   144  
   145  			w := testWalker{}
   146  			diags = Walk(expr, &w)
   147  			if diags.HasErrors() {
   148  				t.Fatalf("failed to walk: %s", diags.Error())
   149  			}
   150  
   151  			got := w.Calls
   152  			if !reflect.DeepEqual(got, test.want) {
   153  				t.Errorf("wrong calls\ngot: %swant: %s", spew.Sdump(got), spew.Sdump(test.want))
   154  				for _, problem := range deep.Equal(got, test.want) {
   155  					t.Errorf(problem)
   156  				}
   157  			}
   158  		})
   159  	}
   160  }
   161  
   162  type testWalkMethod int
   163  
   164  const testWalkEnter testWalkMethod = 1
   165  const testWalkExit testWalkMethod = 2
   166  
   167  type testWalkCall struct {
   168  	Method   testWalkMethod
   169  	NodeType string
   170  }
   171  
   172  type testWalker struct {
   173  	Calls []testWalkCall
   174  }
   175  
   176  func (w *testWalker) Enter(node Node) hcl.Diagnostics {
   177  	w.Calls = append(w.Calls, testWalkCall{testWalkEnter, fmt.Sprintf("%T", node)})
   178  	return nil
   179  }
   180  
   181  func (w *testWalker) Exit(node Node) hcl.Diagnostics {
   182  	w.Calls = append(w.Calls, testWalkCall{testWalkExit, fmt.Sprintf("%T", node)})
   183  	return nil
   184  }