github.com/hashicorp/hcl/v2@v2.20.0/gohcl/decode_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package gohcl
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"reflect"
    10  	"testing"
    11  
    12  	"github.com/davecgh/go-spew/spew"
    13  	"github.com/hashicorp/hcl/v2"
    14  	hclJSON "github.com/hashicorp/hcl/v2/json"
    15  	"github.com/zclconf/go-cty/cty"
    16  )
    17  
    18  func TestDecodeBody(t *testing.T) {
    19  	deepEquals := func(other interface{}) func(v interface{}) bool {
    20  		return func(v interface{}) bool {
    21  			return reflect.DeepEqual(v, other)
    22  		}
    23  	}
    24  
    25  	type withNameExpression struct {
    26  		Name hcl.Expression `hcl:"name"`
    27  	}
    28  
    29  	type withTwoAttributes struct {
    30  		A string `hcl:"a,optional"`
    31  		B string `hcl:"b,optional"`
    32  	}
    33  
    34  	type withNestedBlock struct {
    35  		Plain  string             `hcl:"plain,optional"`
    36  		Nested *withTwoAttributes `hcl:"nested,block"`
    37  	}
    38  
    39  	type withListofNestedBlocks struct {
    40  		Nested []*withTwoAttributes `hcl:"nested,block"`
    41  	}
    42  
    43  	type withListofNestedBlocksNoPointers struct {
    44  		Nested []withTwoAttributes `hcl:"nested,block"`
    45  	}
    46  
    47  	tests := []struct {
    48  		Body      map[string]interface{}
    49  		Target    func() interface{}
    50  		Check     func(v interface{}) bool
    51  		DiagCount int
    52  	}{
    53  		{
    54  			map[string]interface{}{},
    55  			makeInstantiateType(struct{}{}),
    56  			deepEquals(struct{}{}),
    57  			0,
    58  		},
    59  		{
    60  			map[string]interface{}{},
    61  			makeInstantiateType(struct {
    62  				Name string `hcl:"name"`
    63  			}{}),
    64  			deepEquals(struct {
    65  				Name string `hcl:"name"`
    66  			}{}),
    67  			1, // name is required
    68  		},
    69  		{
    70  			map[string]interface{}{},
    71  			makeInstantiateType(struct {
    72  				Name *string `hcl:"name"`
    73  			}{}),
    74  			deepEquals(struct {
    75  				Name *string `hcl:"name"`
    76  			}{}),
    77  			0,
    78  		}, // name nil
    79  		{
    80  			map[string]interface{}{},
    81  			makeInstantiateType(struct {
    82  				Name string `hcl:"name,optional"`
    83  			}{}),
    84  			deepEquals(struct {
    85  				Name string `hcl:"name,optional"`
    86  			}{}),
    87  			0,
    88  		}, // name optional
    89  		{
    90  			map[string]interface{}{},
    91  			makeInstantiateType(withNameExpression{}),
    92  			func(v interface{}) bool {
    93  				if v == nil {
    94  					return false
    95  				}
    96  
    97  				wne, valid := v.(withNameExpression)
    98  				if !valid {
    99  					return false
   100  				}
   101  
   102  				if wne.Name == nil {
   103  					return false
   104  				}
   105  
   106  				nameVal, _ := wne.Name.Value(nil)
   107  				if !nameVal.IsNull() {
   108  					return false
   109  				}
   110  
   111  				return true
   112  			},
   113  			0,
   114  		},
   115  		{
   116  			map[string]interface{}{
   117  				"name": "Ermintrude",
   118  			},
   119  			makeInstantiateType(withNameExpression{}),
   120  			func(v interface{}) bool {
   121  				if v == nil {
   122  					return false
   123  				}
   124  
   125  				wne, valid := v.(withNameExpression)
   126  				if !valid {
   127  					return false
   128  				}
   129  
   130  				if wne.Name == nil {
   131  					return false
   132  				}
   133  
   134  				nameVal, _ := wne.Name.Value(nil)
   135  				if !nameVal.Equals(cty.StringVal("Ermintrude")).True() {
   136  					return false
   137  				}
   138  
   139  				return true
   140  			},
   141  			0,
   142  		},
   143  		{
   144  			map[string]interface{}{
   145  				"name": "Ermintrude",
   146  			},
   147  			makeInstantiateType(struct {
   148  				Name string `hcl:"name"`
   149  			}{}),
   150  			deepEquals(struct {
   151  				Name string `hcl:"name"`
   152  			}{"Ermintrude"}),
   153  			0,
   154  		},
   155  		{
   156  			map[string]interface{}{
   157  				"name": "Ermintrude",
   158  				"age":  23,
   159  			},
   160  			makeInstantiateType(struct {
   161  				Name string `hcl:"name"`
   162  			}{}),
   163  			deepEquals(struct {
   164  				Name string `hcl:"name"`
   165  			}{"Ermintrude"}),
   166  			1, // Extraneous "age" property
   167  		},
   168  		{
   169  			map[string]interface{}{
   170  				"name": "Ermintrude",
   171  				"age":  50,
   172  			},
   173  			makeInstantiateType(struct {
   174  				Name  string         `hcl:"name"`
   175  				Attrs hcl.Attributes `hcl:",remain"`
   176  			}{}),
   177  			func(gotI interface{}) bool {
   178  				got := gotI.(struct {
   179  					Name  string         `hcl:"name"`
   180  					Attrs hcl.Attributes `hcl:",remain"`
   181  				})
   182  				return got.Name == "Ermintrude" && len(got.Attrs) == 1 && got.Attrs["age"] != nil
   183  			},
   184  			0,
   185  		},
   186  		{
   187  			map[string]interface{}{
   188  				"name": "Ermintrude",
   189  				"age":  50,
   190  			},
   191  			makeInstantiateType(struct {
   192  				Name   string   `hcl:"name"`
   193  				Remain hcl.Body `hcl:",remain"`
   194  			}{}),
   195  			func(gotI interface{}) bool {
   196  				got := gotI.(struct {
   197  					Name   string   `hcl:"name"`
   198  					Remain hcl.Body `hcl:",remain"`
   199  				})
   200  
   201  				attrs, _ := got.Remain.JustAttributes()
   202  
   203  				return got.Name == "Ermintrude" && len(attrs) == 1 && attrs["age"] != nil
   204  			},
   205  			0,
   206  		},
   207  		{
   208  			map[string]interface{}{
   209  				"name":   "Ermintrude",
   210  				"living": true,
   211  			},
   212  			makeInstantiateType(struct {
   213  				Name   string               `hcl:"name"`
   214  				Remain map[string]cty.Value `hcl:",remain"`
   215  			}{}),
   216  			deepEquals(struct {
   217  				Name   string               `hcl:"name"`
   218  				Remain map[string]cty.Value `hcl:",remain"`
   219  			}{
   220  				Name: "Ermintrude",
   221  				Remain: map[string]cty.Value{
   222  					"living": cty.True,
   223  				},
   224  			}),
   225  			0,
   226  		},
   227  		{
   228  			map[string]interface{}{
   229  				"name": "Ermintrude",
   230  				"age":  50,
   231  			},
   232  			makeInstantiateType(struct {
   233  				Name   string   `hcl:"name"`
   234  				Body   hcl.Body `hcl:",body"`
   235  				Remain hcl.Body `hcl:",remain"`
   236  			}{}),
   237  			func(gotI interface{}) bool {
   238  				got := gotI.(struct {
   239  					Name   string   `hcl:"name"`
   240  					Body   hcl.Body `hcl:",body"`
   241  					Remain hcl.Body `hcl:",remain"`
   242  				})
   243  
   244  				attrs, _ := got.Body.JustAttributes()
   245  
   246  				return got.Name == "Ermintrude" && len(attrs) == 2 &&
   247  					attrs["name"] != nil && attrs["age"] != nil
   248  			},
   249  			0,
   250  		},
   251  		{
   252  			map[string]interface{}{
   253  				"noodle": map[string]interface{}{},
   254  			},
   255  			makeInstantiateType(struct {
   256  				Noodle struct{} `hcl:"noodle,block"`
   257  			}{}),
   258  			func(gotI interface{}) bool {
   259  				// Generating no diagnostics is good enough for this one.
   260  				return true
   261  			},
   262  			0,
   263  		},
   264  		{
   265  			map[string]interface{}{
   266  				"noodle": []map[string]interface{}{{}},
   267  			},
   268  			makeInstantiateType(struct {
   269  				Noodle struct{} `hcl:"noodle,block"`
   270  			}{}),
   271  			func(gotI interface{}) bool {
   272  				// Generating no diagnostics is good enough for this one.
   273  				return true
   274  			},
   275  			0,
   276  		},
   277  		{
   278  			map[string]interface{}{
   279  				"noodle": []map[string]interface{}{{}, {}},
   280  			},
   281  			makeInstantiateType(struct {
   282  				Noodle struct{} `hcl:"noodle,block"`
   283  			}{}),
   284  			func(gotI interface{}) bool {
   285  				// Generating one diagnostic is good enough for this one.
   286  				return true
   287  			},
   288  			1,
   289  		},
   290  		{
   291  			map[string]interface{}{},
   292  			makeInstantiateType(struct {
   293  				Noodle struct{} `hcl:"noodle,block"`
   294  			}{}),
   295  			func(gotI interface{}) bool {
   296  				// Generating one diagnostic is good enough for this one.
   297  				return true
   298  			},
   299  			1,
   300  		},
   301  		{
   302  			map[string]interface{}{
   303  				"noodle": []map[string]interface{}{},
   304  			},
   305  			makeInstantiateType(struct {
   306  				Noodle struct{} `hcl:"noodle,block"`
   307  			}{}),
   308  			func(gotI interface{}) bool {
   309  				// Generating one diagnostic is good enough for this one.
   310  				return true
   311  			},
   312  			1,
   313  		},
   314  		{
   315  			map[string]interface{}{
   316  				"noodle": map[string]interface{}{},
   317  			},
   318  			makeInstantiateType(struct {
   319  				Noodle *struct{} `hcl:"noodle,block"`
   320  			}{}),
   321  			func(gotI interface{}) bool {
   322  				return gotI.(struct {
   323  					Noodle *struct{} `hcl:"noodle,block"`
   324  				}).Noodle != nil
   325  			},
   326  			0,
   327  		},
   328  		{
   329  			map[string]interface{}{
   330  				"noodle": []map[string]interface{}{{}},
   331  			},
   332  			makeInstantiateType(struct {
   333  				Noodle *struct{} `hcl:"noodle,block"`
   334  			}{}),
   335  			func(gotI interface{}) bool {
   336  				return gotI.(struct {
   337  					Noodle *struct{} `hcl:"noodle,block"`
   338  				}).Noodle != nil
   339  			},
   340  			0,
   341  		},
   342  		{
   343  			map[string]interface{}{
   344  				"noodle": []map[string]interface{}{},
   345  			},
   346  			makeInstantiateType(struct {
   347  				Noodle *struct{} `hcl:"noodle,block"`
   348  			}{}),
   349  			func(gotI interface{}) bool {
   350  				return gotI.(struct {
   351  					Noodle *struct{} `hcl:"noodle,block"`
   352  				}).Noodle == nil
   353  			},
   354  			0,
   355  		},
   356  		{
   357  			map[string]interface{}{
   358  				"noodle": []map[string]interface{}{{}, {}},
   359  			},
   360  			makeInstantiateType(struct {
   361  				Noodle *struct{} `hcl:"noodle,block"`
   362  			}{}),
   363  			func(gotI interface{}) bool {
   364  				// Generating one diagnostic is good enough for this one.
   365  				return true
   366  			},
   367  			1,
   368  		},
   369  		{
   370  			map[string]interface{}{
   371  				"noodle": []map[string]interface{}{},
   372  			},
   373  			makeInstantiateType(struct {
   374  				Noodle []struct{} `hcl:"noodle,block"`
   375  			}{}),
   376  			func(gotI interface{}) bool {
   377  				noodle := gotI.(struct {
   378  					Noodle []struct{} `hcl:"noodle,block"`
   379  				}).Noodle
   380  				return len(noodle) == 0
   381  			},
   382  			0,
   383  		},
   384  		{
   385  			map[string]interface{}{
   386  				"noodle": []map[string]interface{}{{}},
   387  			},
   388  			makeInstantiateType(struct {
   389  				Noodle []struct{} `hcl:"noodle,block"`
   390  			}{}),
   391  			func(gotI interface{}) bool {
   392  				noodle := gotI.(struct {
   393  					Noodle []struct{} `hcl:"noodle,block"`
   394  				}).Noodle
   395  				return len(noodle) == 1
   396  			},
   397  			0,
   398  		},
   399  		{
   400  			map[string]interface{}{
   401  				"noodle": []map[string]interface{}{{}, {}},
   402  			},
   403  			makeInstantiateType(struct {
   404  				Noodle []struct{} `hcl:"noodle,block"`
   405  			}{}),
   406  			func(gotI interface{}) bool {
   407  				noodle := gotI.(struct {
   408  					Noodle []struct{} `hcl:"noodle,block"`
   409  				}).Noodle
   410  				return len(noodle) == 2
   411  			},
   412  			0,
   413  		},
   414  		{
   415  			map[string]interface{}{
   416  				"noodle": map[string]interface{}{},
   417  			},
   418  			makeInstantiateType(struct {
   419  				Noodle struct {
   420  					Name string `hcl:"name,label"`
   421  				} `hcl:"noodle,block"`
   422  			}{}),
   423  			func(gotI interface{}) bool {
   424  				// Generating two diagnostics is good enough for this one.
   425  				// (one for the missing noodle block and the other for
   426  				// the JSON serialization detecting the missing level of
   427  				// heirarchy for the label.)
   428  				return true
   429  			},
   430  			2,
   431  		},
   432  		{
   433  			map[string]interface{}{
   434  				"noodle": map[string]interface{}{
   435  					"foo_foo": map[string]interface{}{},
   436  				},
   437  			},
   438  			makeInstantiateType(struct {
   439  				Noodle struct {
   440  					Name string `hcl:"name,label"`
   441  				} `hcl:"noodle,block"`
   442  			}{}),
   443  			func(gotI interface{}) bool {
   444  				noodle := gotI.(struct {
   445  					Noodle struct {
   446  						Name string `hcl:"name,label"`
   447  					} `hcl:"noodle,block"`
   448  				}).Noodle
   449  				return noodle.Name == "foo_foo"
   450  			},
   451  			0,
   452  		},
   453  		{
   454  			map[string]interface{}{
   455  				"noodle": map[string]interface{}{
   456  					"foo_foo": map[string]interface{}{},
   457  					"bar_baz": map[string]interface{}{},
   458  				},
   459  			},
   460  			makeInstantiateType(struct {
   461  				Noodle struct {
   462  					Name string `hcl:"name,label"`
   463  				} `hcl:"noodle,block"`
   464  			}{}),
   465  			func(gotI interface{}) bool {
   466  				// One diagnostic is enough for this one.
   467  				return true
   468  			},
   469  			1,
   470  		},
   471  		{
   472  			map[string]interface{}{
   473  				"noodle": map[string]interface{}{
   474  					"foo_foo": map[string]interface{}{},
   475  					"bar_baz": map[string]interface{}{},
   476  				},
   477  			},
   478  			makeInstantiateType(struct {
   479  				Noodles []struct {
   480  					Name string `hcl:"name,label"`
   481  				} `hcl:"noodle,block"`
   482  			}{}),
   483  			func(gotI interface{}) bool {
   484  				noodles := gotI.(struct {
   485  					Noodles []struct {
   486  						Name string `hcl:"name,label"`
   487  					} `hcl:"noodle,block"`
   488  				}).Noodles
   489  				return len(noodles) == 2 && (noodles[0].Name == "foo_foo" || noodles[0].Name == "bar_baz") && (noodles[1].Name == "foo_foo" || noodles[1].Name == "bar_baz") && noodles[0].Name != noodles[1].Name
   490  			},
   491  			0,
   492  		},
   493  		{
   494  			map[string]interface{}{
   495  				"noodle": map[string]interface{}{
   496  					"foo_foo": map[string]interface{}{
   497  						"type": "rice",
   498  					},
   499  				},
   500  			},
   501  			makeInstantiateType(struct {
   502  				Noodle struct {
   503  					Name string `hcl:"name,label"`
   504  					Type string `hcl:"type"`
   505  				} `hcl:"noodle,block"`
   506  			}{}),
   507  			func(gotI interface{}) bool {
   508  				noodle := gotI.(struct {
   509  					Noodle struct {
   510  						Name string `hcl:"name,label"`
   511  						Type string `hcl:"type"`
   512  					} `hcl:"noodle,block"`
   513  				}).Noodle
   514  				return noodle.Name == "foo_foo" && noodle.Type == "rice"
   515  			},
   516  			0,
   517  		},
   518  
   519  		{
   520  			map[string]interface{}{
   521  				"name": "Ermintrude",
   522  				"age":  34,
   523  			},
   524  			makeInstantiateType(map[string]string(nil)),
   525  			deepEquals(map[string]string{
   526  				"name": "Ermintrude",
   527  				"age":  "34",
   528  			}),
   529  			0,
   530  		},
   531  		{
   532  			map[string]interface{}{
   533  				"name": "Ermintrude",
   534  				"age":  89,
   535  			},
   536  			makeInstantiateType(map[string]*hcl.Attribute(nil)),
   537  			func(gotI interface{}) bool {
   538  				got := gotI.(map[string]*hcl.Attribute)
   539  				return len(got) == 2 && got["name"] != nil && got["age"] != nil
   540  			},
   541  			0,
   542  		},
   543  		{
   544  			map[string]interface{}{
   545  				"name": "Ermintrude",
   546  				"age":  13,
   547  			},
   548  			makeInstantiateType(map[string]hcl.Expression(nil)),
   549  			func(gotI interface{}) bool {
   550  				got := gotI.(map[string]hcl.Expression)
   551  				return len(got) == 2 && got["name"] != nil && got["age"] != nil
   552  			},
   553  			0,
   554  		},
   555  		{
   556  			map[string]interface{}{
   557  				"name":   "Ermintrude",
   558  				"living": true,
   559  			},
   560  			makeInstantiateType(map[string]cty.Value(nil)),
   561  			deepEquals(map[string]cty.Value{
   562  				"name":   cty.StringVal("Ermintrude"),
   563  				"living": cty.True,
   564  			}),
   565  			0,
   566  		},
   567  		{
   568  			// Retain "nested" block while decoding
   569  			map[string]interface{}{
   570  				"plain": "foo",
   571  			},
   572  			func() interface{} {
   573  				return &withNestedBlock{
   574  					Plain: "bar",
   575  					Nested: &withTwoAttributes{
   576  						A: "bar",
   577  					},
   578  				}
   579  			},
   580  			func(gotI interface{}) bool {
   581  				foo := gotI.(withNestedBlock)
   582  				return foo.Plain == "foo" && foo.Nested != nil && foo.Nested.A == "bar"
   583  			},
   584  			0,
   585  		},
   586  		{
   587  			// Retain values in "nested" block while decoding
   588  			map[string]interface{}{
   589  				"nested": map[string]interface{}{
   590  					"a": "foo",
   591  				},
   592  			},
   593  			func() interface{} {
   594  				return &withNestedBlock{
   595  					Nested: &withTwoAttributes{
   596  						B: "bar",
   597  					},
   598  				}
   599  			},
   600  			func(gotI interface{}) bool {
   601  				foo := gotI.(withNestedBlock)
   602  				return foo.Nested.A == "foo" && foo.Nested.B == "bar"
   603  			},
   604  			0,
   605  		},
   606  		{
   607  			// Retain values in "nested" block list while decoding
   608  			map[string]interface{}{
   609  				"nested": []map[string]interface{}{
   610  					{
   611  						"a": "foo",
   612  					},
   613  				},
   614  			},
   615  			func() interface{} {
   616  				return &withListofNestedBlocks{
   617  					Nested: []*withTwoAttributes{
   618  						&withTwoAttributes{
   619  							B: "bar",
   620  						},
   621  					},
   622  				}
   623  			},
   624  			func(gotI interface{}) bool {
   625  				n := gotI.(withListofNestedBlocks)
   626  				return n.Nested[0].A == "foo" && n.Nested[0].B == "bar"
   627  			},
   628  			0,
   629  		},
   630  		{
   631  			// Remove additional elements from the list while decoding nested blocks
   632  			map[string]interface{}{
   633  				"nested": []map[string]interface{}{
   634  					{
   635  						"a": "foo",
   636  					},
   637  				},
   638  			},
   639  			func() interface{} {
   640  				return &withListofNestedBlocks{
   641  					Nested: []*withTwoAttributes{
   642  						&withTwoAttributes{
   643  							B: "bar",
   644  						},
   645  						&withTwoAttributes{
   646  							B: "bar",
   647  						},
   648  					},
   649  				}
   650  			},
   651  			func(gotI interface{}) bool {
   652  				n := gotI.(withListofNestedBlocks)
   653  				return len(n.Nested) == 1
   654  			},
   655  			0,
   656  		},
   657  		{
   658  			// Make sure decoding value slices works the same as pointer slices.
   659  			map[string]interface{}{
   660  				"nested": []map[string]interface{}{
   661  					{
   662  						"b": "bar",
   663  					},
   664  					{
   665  						"b": "baz",
   666  					},
   667  				},
   668  			},
   669  			func() interface{} {
   670  				return &withListofNestedBlocksNoPointers{
   671  					Nested: []withTwoAttributes{
   672  						{
   673  							B: "foo",
   674  						},
   675  					},
   676  				}
   677  			},
   678  			func(gotI interface{}) bool {
   679  				n := gotI.(withListofNestedBlocksNoPointers)
   680  				return n.Nested[0].B == "bar" && len(n.Nested) == 2
   681  			},
   682  			0,
   683  		},
   684  	}
   685  
   686  	for i, test := range tests {
   687  		// For convenience here we're going to use the JSON parser
   688  		// to process the given body.
   689  		buf, err := json.Marshal(test.Body)
   690  		if err != nil {
   691  			t.Fatalf("error JSON-encoding body for test %d: %s", i, err)
   692  		}
   693  
   694  		t.Run(string(buf), func(t *testing.T) {
   695  			file, diags := hclJSON.Parse(buf, "test.json")
   696  			if len(diags) != 0 {
   697  				t.Fatalf("diagnostics while parsing: %s", diags.Error())
   698  			}
   699  
   700  			targetVal := reflect.ValueOf(test.Target())
   701  
   702  			diags = DecodeBody(file.Body, nil, targetVal.Interface())
   703  			if len(diags) != test.DiagCount {
   704  				t.Errorf("wrong number of diagnostics %d; want %d", len(diags), test.DiagCount)
   705  				for _, diag := range diags {
   706  					t.Logf(" - %s", diag.Error())
   707  				}
   708  			}
   709  			got := targetVal.Elem().Interface()
   710  			if !test.Check(got) {
   711  				t.Errorf("wrong result\ngot:  %s", spew.Sdump(got))
   712  			}
   713  		})
   714  	}
   715  
   716  }
   717  
   718  func TestDecodeExpression(t *testing.T) {
   719  	tests := []struct {
   720  		Value     cty.Value
   721  		Target    interface{}
   722  		Want      interface{}
   723  		DiagCount int
   724  	}{
   725  		{
   726  			cty.StringVal("hello"),
   727  			"",
   728  			"hello",
   729  			0,
   730  		},
   731  		{
   732  			cty.StringVal("hello"),
   733  			cty.NilVal,
   734  			cty.StringVal("hello"),
   735  			0,
   736  		},
   737  		{
   738  			cty.NumberIntVal(2),
   739  			"",
   740  			"2",
   741  			0,
   742  		},
   743  		{
   744  			cty.StringVal("true"),
   745  			false,
   746  			true,
   747  			0,
   748  		},
   749  		{
   750  			cty.NullVal(cty.String),
   751  			"",
   752  			"",
   753  			1, // null value is not allowed
   754  		},
   755  		{
   756  			cty.UnknownVal(cty.String),
   757  			"",
   758  			"",
   759  			1, // value must be known
   760  		},
   761  		{
   762  			cty.ListVal([]cty.Value{cty.True}),
   763  			false,
   764  			false,
   765  			1, // bool required
   766  		},
   767  	}
   768  
   769  	for i, test := range tests {
   770  		t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) {
   771  			expr := &fixedExpression{test.Value}
   772  
   773  			targetVal := reflect.New(reflect.TypeOf(test.Target))
   774  
   775  			diags := DecodeExpression(expr, nil, targetVal.Interface())
   776  			if len(diags) != test.DiagCount {
   777  				t.Errorf("wrong number of diagnostics %d; want %d", len(diags), test.DiagCount)
   778  				for _, diag := range diags {
   779  					t.Logf(" - %s", diag.Error())
   780  				}
   781  			}
   782  			got := targetVal.Elem().Interface()
   783  			if !reflect.DeepEqual(got, test.Want) {
   784  				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
   785  			}
   786  		})
   787  	}
   788  }
   789  
   790  type fixedExpression struct {
   791  	val cty.Value
   792  }
   793  
   794  func (e *fixedExpression) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
   795  	return e.val, nil
   796  }
   797  
   798  func (e *fixedExpression) Range() (r hcl.Range) {
   799  	return
   800  }
   801  func (e *fixedExpression) StartRange() (r hcl.Range) {
   802  	return
   803  }
   804  
   805  func (e *fixedExpression) Variables() []hcl.Traversal {
   806  	return nil
   807  }
   808  
   809  func makeInstantiateType(target interface{}) func() interface{} {
   810  	return func() interface{} {
   811  		return reflect.New(reflect.TypeOf(target)).Interface()
   812  	}
   813  }