OSDN Git Service

new repo
[bytom/vapor.git] / vendor / github.com / hashicorp / hcl / hcl / parser / parser_test.go
1 package parser
2
3 import (
4         "fmt"
5         "io/ioutil"
6         "path/filepath"
7         "reflect"
8         "runtime"
9         "strings"
10         "testing"
11
12         "github.com/hashicorp/hcl/hcl/ast"
13         "github.com/hashicorp/hcl/hcl/token"
14 )
15
16 func TestType(t *testing.T) {
17         var literals = []struct {
18                 typ token.Type
19                 src string
20         }{
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"},
28         }
29
30         for _, l := range literals {
31                 p := newParser([]byte(l.src))
32                 item, err := p.objectItem()
33                 if err != nil {
34                         t.Error(err)
35                 }
36
37                 lit, ok := item.Val.(*ast.LiteralType)
38                 if !ok {
39                         t.Errorf("node should be of type LiteralType, got: %T", item.Val)
40                 }
41
42                 if lit.Token.Type != l.typ {
43                         t.Errorf("want: %s, got: %s", l.typ, lit.Token.Type)
44                 }
45         }
46 }
47
48 func TestListType(t *testing.T) {
49         var literals = []struct {
50                 src    string
51                 tokens []token.Type
52         }{
53                 {
54                         `foo = ["123", 123]`,
55                         []token.Type{token.STRING, token.NUMBER},
56                 },
57                 {
58                         `foo = [123, "123",]`,
59                         []token.Type{token.NUMBER, token.STRING},
60                 },
61                 {
62                         `foo = [false]`,
63                         []token.Type{token.BOOL},
64                 },
65                 {
66                         `foo = []`,
67                         []token.Type{},
68                 },
69                 {
70                         `foo = [1,
71 "string",
72 <<EOF
73 heredoc contents
74 EOF
75 ]`,
76                         []token.Type{token.NUMBER, token.STRING, token.HEREDOC},
77                 },
78         }
79
80         for _, l := range literals {
81                 p := newParser([]byte(l.src))
82                 item, err := p.objectItem()
83                 if err != nil {
84                         t.Error(err)
85                 }
86
87                 list, ok := item.Val.(*ast.ListType)
88                 if !ok {
89                         t.Errorf("node should be of type LiteralType, got: %T", item.Val)
90                 }
91
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)
96                         }
97                 }
98
99                 equals(t, l.tokens, tokens)
100         }
101 }
102
103 func TestListOfMaps(t *testing.T) {
104         src := `foo = [
105     {key = "bar"},
106     {key = "baz", key2 = "qux"},
107   ]`
108         p := newParser([]byte(src))
109
110         file, err := p.Parse()
111         if err != nil {
112                 t.Fatalf("err: %s", err)
113         }
114
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)
129                 }
130
131         }
132         if !reflect.DeepEqual(expected, actual) {
133                 t.Fatalf("Expected: %#v, got %#v", expected, actual)
134         }
135 }
136
137 func TestListOfMaps_requiresComma(t *testing.T) {
138         src := `foo = [
139     {key = "bar"}
140     {key = "baz"}
141   ]`
142         p := newParser([]byte(src))
143
144         _, err := p.Parse()
145         if err == nil {
146                 t.Fatalf("Expected error, got none!")
147         }
148
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)
152         }
153 }
154
155 func TestListType_leadComment(t *testing.T) {
156         var literals = []struct {
157                 src     string
158                 comment []string
159         }{
160                 {
161                         `foo = [
162                         1,
163                         # bar
164                         2,
165                         3,
166                         ]`,
167                         []string{"", "# bar", ""},
168                 },
169         }
170
171         for _, l := range literals {
172                 p := newParser([]byte(l.src))
173                 item, err := p.objectItem()
174                 if err != nil {
175                         t.Fatal(err)
176                 }
177
178                 list, ok := item.Val.(*ast.ListType)
179                 if !ok {
180                         t.Fatalf("node should be of type LiteralType, got: %T", item.Val)
181                 }
182
183                 if len(list.List) != len(l.comment) {
184                         t.Fatalf("bad: %d", len(list.List))
185                 }
186
187                 for i, li := range list.List {
188                         lt := li.(*ast.LiteralType)
189                         comment := l.comment[i]
190
191                         if (lt.LeadComment == nil) != (comment == "") {
192                                 t.Fatalf("bad: %#v", lt)
193                         }
194
195                         if comment == "" {
196                                 continue
197                         }
198
199                         actual := lt.LeadComment.List[0].Text
200                         if actual != comment {
201                                 t.Fatalf("bad: %q %q", actual, comment)
202                         }
203                 }
204         }
205 }
206
207 func TestListType_lineComment(t *testing.T) {
208         var literals = []struct {
209                 src     string
210                 comment []string
211         }{
212                 {
213                         `foo = [
214                         1,
215                         2, # bar
216                         3,
217                         ]`,
218                         []string{"", "# bar", ""},
219                 },
220         }
221
222         for _, l := range literals {
223                 p := newParser([]byte(l.src))
224                 item, err := p.objectItem()
225                 if err != nil {
226                         t.Fatal(err)
227                 }
228
229                 list, ok := item.Val.(*ast.ListType)
230                 if !ok {
231                         t.Fatalf("node should be of type LiteralType, got: %T", item.Val)
232                 }
233
234                 if len(list.List) != len(l.comment) {
235                         t.Fatalf("bad: %d", len(list.List))
236                 }
237
238                 for i, li := range list.List {
239                         lt := li.(*ast.LiteralType)
240                         comment := l.comment[i]
241
242                         if (lt.LineComment == nil) != (comment == "") {
243                                 t.Fatalf("bad: %s", lt)
244                         }
245
246                         if comment == "" {
247                                 continue
248                         }
249
250                         actual := lt.LineComment.List[0].Text
251                         if actual != comment {
252                                 t.Fatalf("bad: %q %q", actual, comment)
253                         }
254                 }
255         }
256 }
257
258 func TestObjectType(t *testing.T) {
259         var literals = []struct {
260                 src      string
261                 nodeType []ast.Node
262                 itemLen  int
263         }{
264                 {
265                         `foo = {}`,
266                         nil,
267                         0,
268                 },
269                 {
270                         `foo = {
271                                 bar = "fatih"
272                          }`,
273                         []ast.Node{&ast.LiteralType{}},
274                         1,
275                 },
276                 {
277                         `foo = {
278                                 bar = "fatih"
279                                 baz = ["arslan"]
280                          }`,
281                         []ast.Node{
282                                 &ast.LiteralType{},
283                                 &ast.ListType{},
284                         },
285                         2,
286                 },
287                 {
288                         `foo = {
289                                 bar {}
290                          }`,
291                         []ast.Node{
292                                 &ast.ObjectType{},
293                         },
294                         1,
295                 },
296                 {
297                         `foo {
298                                 bar {}
299                                 foo = true
300                          }`,
301                         []ast.Node{
302                                 &ast.ObjectType{},
303                                 &ast.LiteralType{},
304                         },
305                         2,
306                 },
307         }
308
309         for _, l := range literals {
310                 t.Logf("Source: %s", l.src)
311
312                 p := newParser([]byte(l.src))
313                 // p.enableTrace = true
314                 item, err := p.objectItem()
315                 if err != nil {
316                         t.Error(err)
317                         continue
318                 }
319
320                 // we know that the ObjectKey name is foo for all cases, what matters
321                 // is the object
322                 obj, ok := item.Val.(*ast.ObjectType)
323                 if !ok {
324                         t.Errorf("node should be of type LiteralType, got: %T", item.Val)
325                         continue
326                 }
327
328                 // check if the total length of items are correct
329                 equals(t, l.itemLen, len(obj.List.Items))
330
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))
334                 }
335         }
336 }
337
338 func TestObjectKey(t *testing.T) {
339         keys := []struct {
340                 exp []token.Type
341                 src string
342         }{
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 {}`},
355         }
356
357         for _, k := range keys {
358                 p := newParser([]byte(k.src))
359                 keys, err := p.objectKey()
360                 if err != nil {
361                         t.Fatal(err)
362                 }
363
364                 tokens := []token.Type{}
365                 for _, o := range keys {
366                         tokens = append(tokens, o.Token.Type)
367                 }
368
369                 equals(t, k.exp, tokens)
370         }
371
372         errKeys := []struct {
373                 src string
374         }{
375                 {`foo 12 {}`},
376                 {`foo bar = {}`},
377                 {`foo []`},
378                 {`12 {}`},
379         }
380
381         for _, k := range errKeys {
382                 p := newParser([]byte(k.src))
383                 _, err := p.objectKey()
384                 if err == nil {
385                         t.Errorf("case '%s' should give an error", k.src)
386                 }
387         }
388 }
389
390 func TestCommentGroup(t *testing.T) {
391         var cases = []struct {
392                 src    string
393                 groups int
394         }{
395                 {"# Hello\n# World", 1},
396                 {"# Hello\r\n# Windows", 1},
397         }
398
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()
403                         if err != nil {
404                                 t.Fatalf("parse error: %s", err)
405                         }
406
407                         if len(file.Comments) != tc.groups {
408                                 t.Fatalf("bad: %#v", file.Comments)
409                         }
410                 })
411         }
412 }
413
414 // Official HCL tests
415 func TestParse(t *testing.T) {
416         cases := []struct {
417                 Name string
418                 Err  bool
419         }{
420                 {
421                         "assign_colon.hcl",
422                         true,
423                 },
424                 {
425                         "comment.hcl",
426                         false,
427                 },
428                 {
429                         "comment_crlf.hcl",
430                         false,
431                 },
432                 {
433                         "comment_lastline.hcl",
434                         false,
435                 },
436                 {
437                         "comment_single.hcl",
438                         false,
439                 },
440                 {
441                         "empty.hcl",
442                         false,
443                 },
444                 {
445                         "list_comma.hcl",
446                         false,
447                 },
448                 {
449                         "multiple.hcl",
450                         false,
451                 },
452                 {
453                         "object_list_comma.hcl",
454                         false,
455                 },
456                 {
457                         "structure.hcl",
458                         false,
459                 },
460                 {
461                         "structure_basic.hcl",
462                         false,
463                 },
464                 {
465                         "structure_empty.hcl",
466                         false,
467                 },
468                 {
469                         "complex.hcl",
470                         false,
471                 },
472                 {
473                         "complex_crlf.hcl",
474                         false,
475                 },
476                 {
477                         "types.hcl",
478                         false,
479                 },
480                 {
481                         "array_comment.hcl",
482                         false,
483                 },
484                 {
485                         "array_comment_2.hcl",
486                         true,
487                 },
488                 {
489                         "missing_braces.hcl",
490                         true,
491                 },
492                 {
493                         "unterminated_object.hcl",
494                         true,
495                 },
496                 {
497                         "unterminated_object_2.hcl",
498                         true,
499                 },
500                 {
501                         "key_without_value.hcl",
502                         true,
503                 },
504                 {
505                         "object_key_without_value.hcl",
506                         true,
507                 },
508                 {
509                         "object_key_assign_without_value.hcl",
510                         true,
511                 },
512                 {
513                         "object_key_assign_without_value2.hcl",
514                         true,
515                 },
516                 {
517                         "object_key_assign_without_value3.hcl",
518                         true,
519                 },
520                 {
521                         "git_crypt.hcl",
522                         true,
523                 },
524         }
525
526         const fixtureDir = "./test-fixtures"
527
528         for _, tc := range cases {
529                 t.Run(tc.Name, func(t *testing.T) {
530                         d, err := ioutil.ReadFile(filepath.Join(fixtureDir, tc.Name))
531                         if err != nil {
532                                 t.Fatalf("err: %s", err)
533                         }
534
535                         v, err := Parse(d)
536                         if (err != nil) != tc.Err {
537                                 t.Fatalf("Input: %s\n\nError: %s\n\nAST: %#v", tc.Name, err, v)
538                         }
539                 })
540         }
541 }
542
543 func TestParse_inline(t *testing.T) {
544         cases := []struct {
545                 Value string
546                 Err   bool
547         }{
548                 {"t t e{{}}", true},
549                 {"o{{}}", true},
550                 {"t t e d N{{}}", true},
551                 {"t t e d{{}}", true},
552                 {"N{}N{{}}", true},
553                 {"v\nN{{}}", true},
554                 {"v=/\n[,", true},
555                 {"v=10kb", true},
556                 {"v=/foo", true},
557         }
558
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)
564                 }
565         }
566 }
567
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)
573                 tb.FailNow()
574         }
575 }