12 "github.com/hashicorp/hcl/hcl/ast"
13 "github.com/hashicorp/hcl/hcl/token"
16 func TestType(t *testing.T) {
17 var literals = []struct {
21 {token.STRING, `foo = "foo"`},
22 {token.NUMBER, `foo = 123`},
23 {token.NUMBER, `foo = -29`},
24 {token.FLOAT, `foo = 123.12`},
25 {token.FLOAT, `foo = -123.12`},
26 {token.BOOL, `foo = true`},
27 {token.HEREDOC, "foo = <<EOF\nHello\nWorld\nEOF"},
30 for _, l := range literals {
31 p := newParser([]byte(l.src))
32 item, err := p.objectItem()
37 lit, ok := item.Val.(*ast.LiteralType)
39 t.Errorf("node should be of type LiteralType, got: %T", item.Val)
42 if lit.Token.Type != l.typ {
43 t.Errorf("want: %s, got: %s", l.typ, lit.Token.Type)
48 func TestListType(t *testing.T) {
49 var literals = []struct {
55 []token.Type{token.STRING, token.NUMBER},
58 `foo = [123, "123",]`,
59 []token.Type{token.NUMBER, token.STRING},
63 []token.Type{token.BOOL},
76 []token.Type{token.NUMBER, token.STRING, token.HEREDOC},
80 for _, l := range literals {
81 p := newParser([]byte(l.src))
82 item, err := p.objectItem()
87 list, ok := item.Val.(*ast.ListType)
89 t.Errorf("node should be of type LiteralType, got: %T", item.Val)
92 tokens := []token.Type{}
93 for _, li := range list.List {
94 if tp, ok := li.(*ast.LiteralType); ok {
95 tokens = append(tokens, tp.Token.Type)
99 equals(t, l.tokens, tokens)
103 func TestListOfMaps(t *testing.T) {
106 {key = "baz", key2 = "qux"},
108 p := newParser([]byte(src))
110 file, err := p.Parse()
112 t.Fatalf("err: %s", err)
115 // Here we make all sorts of assumptions about the input structure w/ type
116 // assertions. The intent is only for this to be a "smoke test" ensuring
117 // parsing actually performed its duty - giving this test something a bit
118 // more robust than _just_ "no error occurred".
119 expected := []string{`"bar"`, `"baz"`, `"qux"`}
120 actual := make([]string, 0, 3)
121 ol := file.Node.(*ast.ObjectList)
122 objItem := ol.Items[0]
123 list := objItem.Val.(*ast.ListType)
124 for _, node := range list.List {
125 obj := node.(*ast.ObjectType)
126 for _, item := range obj.List.Items {
127 val := item.Val.(*ast.LiteralType)
128 actual = append(actual, val.Token.Text)
132 if !reflect.DeepEqual(expected, actual) {
133 t.Fatalf("Expected: %#v, got %#v", expected, actual)
137 func TestListOfMaps_requiresComma(t *testing.T) {
142 p := newParser([]byte(src))
146 t.Fatalf("Expected error, got none!")
149 expected := "error parsing list, expected comma or list end"
150 if !strings.Contains(err.Error(), expected) {
151 t.Fatalf("Expected err:\n %s\nTo contain:\n %s\n", err, expected)
155 func TestListType_leadComment(t *testing.T) {
156 var literals = []struct {
167 []string{"", "# bar", ""},
171 for _, l := range literals {
172 p := newParser([]byte(l.src))
173 item, err := p.objectItem()
178 list, ok := item.Val.(*ast.ListType)
180 t.Fatalf("node should be of type LiteralType, got: %T", item.Val)
183 if len(list.List) != len(l.comment) {
184 t.Fatalf("bad: %d", len(list.List))
187 for i, li := range list.List {
188 lt := li.(*ast.LiteralType)
189 comment := l.comment[i]
191 if (lt.LeadComment == nil) != (comment == "") {
192 t.Fatalf("bad: %#v", lt)
199 actual := lt.LeadComment.List[0].Text
200 if actual != comment {
201 t.Fatalf("bad: %q %q", actual, comment)
207 func TestListType_lineComment(t *testing.T) {
208 var literals = []struct {
218 []string{"", "# bar", ""},
222 for _, l := range literals {
223 p := newParser([]byte(l.src))
224 item, err := p.objectItem()
229 list, ok := item.Val.(*ast.ListType)
231 t.Fatalf("node should be of type LiteralType, got: %T", item.Val)
234 if len(list.List) != len(l.comment) {
235 t.Fatalf("bad: %d", len(list.List))
238 for i, li := range list.List {
239 lt := li.(*ast.LiteralType)
240 comment := l.comment[i]
242 if (lt.LineComment == nil) != (comment == "") {
243 t.Fatalf("bad: %s", lt)
250 actual := lt.LineComment.List[0].Text
251 if actual != comment {
252 t.Fatalf("bad: %q %q", actual, comment)
258 func TestObjectType(t *testing.T) {
259 var literals = []struct {
273 []ast.Node{&ast.LiteralType{}},
309 for _, l := range literals {
310 t.Logf("Source: %s", l.src)
312 p := newParser([]byte(l.src))
313 // p.enableTrace = true
314 item, err := p.objectItem()
320 // we know that the ObjectKey name is foo for all cases, what matters
322 obj, ok := item.Val.(*ast.ObjectType)
324 t.Errorf("node should be of type LiteralType, got: %T", item.Val)
328 // check if the total length of items are correct
329 equals(t, l.itemLen, len(obj.List.Items))
331 // check if the types are correct
332 for i, item := range obj.List.Items {
333 equals(t, reflect.TypeOf(l.nodeType[i]), reflect.TypeOf(item.Val))
338 func TestObjectKey(t *testing.T) {
343 {[]token.Type{token.IDENT}, `foo {}`},
344 {[]token.Type{token.IDENT}, `foo = {}`},
345 {[]token.Type{token.IDENT}, `foo = bar`},
346 {[]token.Type{token.IDENT}, `foo = 123`},
347 {[]token.Type{token.IDENT}, `foo = "${var.bar}`},
348 {[]token.Type{token.STRING}, `"foo" {}`},
349 {[]token.Type{token.STRING}, `"foo" = {}`},
350 {[]token.Type{token.STRING}, `"foo" = "${var.bar}`},
351 {[]token.Type{token.IDENT, token.IDENT}, `foo bar {}`},
352 {[]token.Type{token.IDENT, token.STRING}, `foo "bar" {}`},
353 {[]token.Type{token.STRING, token.IDENT}, `"foo" bar {}`},
354 {[]token.Type{token.IDENT, token.IDENT, token.IDENT}, `foo bar baz {}`},
357 for _, k := range keys {
358 p := newParser([]byte(k.src))
359 keys, err := p.objectKey()
364 tokens := []token.Type{}
365 for _, o := range keys {
366 tokens = append(tokens, o.Token.Type)
369 equals(t, k.exp, tokens)
372 errKeys := []struct {
381 for _, k := range errKeys {
382 p := newParser([]byte(k.src))
383 _, err := p.objectKey()
385 t.Errorf("case '%s' should give an error", k.src)
390 func TestCommentGroup(t *testing.T) {
391 var cases = []struct {
395 {"# Hello\n# World", 1},
396 {"# Hello\r\n# Windows", 1},
399 for _, tc := range cases {
400 t.Run(tc.src, func(t *testing.T) {
401 p := newParser([]byte(tc.src))
402 file, err := p.Parse()
404 t.Fatalf("parse error: %s", err)
407 if len(file.Comments) != tc.groups {
408 t.Fatalf("bad: %#v", file.Comments)
414 // Official HCL tests
415 func TestParse(t *testing.T) {
433 "comment_lastline.hcl",
437 "comment_single.hcl",
453 "object_list_comma.hcl",
461 "structure_basic.hcl",
465 "structure_empty.hcl",
485 "array_comment_2.hcl",
489 "missing_braces.hcl",
493 "unterminated_object.hcl",
497 "unterminated_object_2.hcl",
501 "key_without_value.hcl",
505 "object_key_without_value.hcl",
509 "object_key_assign_without_value.hcl",
513 "object_key_assign_without_value2.hcl",
517 "object_key_assign_without_value3.hcl",
526 const fixtureDir = "./test-fixtures"
528 for _, tc := range cases {
529 t.Run(tc.Name, func(t *testing.T) {
530 d, err := ioutil.ReadFile(filepath.Join(fixtureDir, tc.Name))
532 t.Fatalf("err: %s", err)
536 if (err != nil) != tc.Err {
537 t.Fatalf("Input: %s\n\nError: %s\n\nAST: %#v", tc.Name, err, v)
543 func TestParse_inline(t *testing.T) {
550 {"t t e d N{{}}", true},
551 {"t t e d{{}}", true},
559 for _, tc := range cases {
560 t.Logf("Testing: %q", tc.Value)
561 ast, err := Parse([]byte(tc.Value))
562 if (err != nil) != tc.Err {
563 t.Fatalf("Input: %q\n\nError: %s\n\nAST: %#v", tc.Value, err, ast)
568 // equals fails the test if exp is not equal to act.
569 func equals(tb testing.TB, exp, act interface{}) {
570 if !reflect.DeepEqual(exp, act) {
571 _, file, line, _ := runtime.Caller(1)
572 fmt.Printf("\033[31m%s:%d:\n\n\texp: %#v\n\n\tgot: %#v\033[39m\n\n", filepath.Base(file), line, exp, act)