1 package parser
2
3 import (
4 "fmt"
5 "io/ioutil"
6 "path/filepath"
7 "reflect"
8 "runtime"
9 "testing"
10
11 "github.com/hashicorp/hcl/hcl/ast"
12 "github.com/hashicorp/hcl/hcl/token"
13 )
14
15 func TestType(t *testing.T) {
16 var literals = []struct {
17 typ token.Type
18 src string
19 }{
20 {token.STRING, `"foo": "bar"`},
21 {token.NUMBER, `"foo": 123`},
22 {token.FLOAT, `"foo": 123.12`},
23 {token.FLOAT, `"foo": -123.12`},
24 {token.BOOL, `"foo": true`},
25 {token.STRING, `"foo": null`},
26 }
27
28 for _, l := range literals {
29 t.Logf("Testing: %s", l.src)
30
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": []`,
63 []token.Type{},
64 },
65 {
66 `"foo": ["123", 123]`,
67 []token.Type{token.STRING, token.NUMBER},
68 },
69 {
70 `"foo": ["123", {}]`,
71 []token.Type{token.STRING, token.LBRACE},
72 },
73 }
74
75 for _, l := range literals {
76 t.Logf("Testing: %s", l.src)
77
78 p := newParser([]byte(l.src))
79 item, err := p.objectItem()
80 if err != nil {
81 t.Error(err)
82 }
83
84 list, ok := item.Val.(*ast.ListType)
85 if !ok {
86 t.Errorf("node should be of type LiteralType, got: %T", item.Val)
87 }
88
89 tokens := []token.Type{}
90 for _, li := range list.List {
91 switch v := li.(type) {
92 case *ast.LiteralType:
93 tokens = append(tokens, v.Token.Type)
94 case *ast.ObjectType:
95 tokens = append(tokens, token.LBRACE)
96 }
97 }
98
99 equals(t, l.tokens, tokens)
100 }
101 }
102
103 func TestObjectType(t *testing.T) {
104 var literals = []struct {
105 src string
106 nodeType []ast.Node
107 itemLen int
108 }{
109 {
110 `"foo": {}`,
111 nil,
112 0,
113 },
114 {
115 `"foo": {
116 "bar": "fatih"
117 }`,
118 []ast.Node{&ast.LiteralType{}},
119 1,
120 },
121 {
122 `"foo": {
123 "bar": "fatih",
124 "baz": ["arslan"]
125 }`,
126 []ast.Node{
127 &ast.LiteralType{},
128 &ast.ListType{},
129 },
130 2,
131 },
132 {
133 `"foo": {
134 "bar": {}
135 }`,
136 []ast.Node{
137 &ast.ObjectType{},
138 },
139 1,
140 },
141 {
142 `"foo": {
143 "bar": {},
144 "foo": true
145 }`,
146 []ast.Node{
147 &ast.ObjectType{},
148 &ast.LiteralType{},
149 },
150 2,
151 },
152 }
153
154 for _, l := range literals {
155 t.Logf("Testing:\n%s\n", l.src)
156
157 p := newParser([]byte(l.src))
158
159 item, err := p.objectItem()
160 if err != nil {
161 t.Error(err)
162 }
163
164
165
166 obj, ok := item.Val.(*ast.ObjectType)
167 if !ok {
168 t.Errorf("node should be of type LiteralType, got: %T", item.Val)
169 }
170
171
172 equals(t, l.itemLen, len(obj.List.Items))
173
174
175 for i, item := range obj.List.Items {
176 equals(t, reflect.TypeOf(l.nodeType[i]), reflect.TypeOf(item.Val))
177 }
178 }
179 }
180
181 func TestFlattenObjects(t *testing.T) {
182 var literals = []struct {
183 src string
184 nodeType []ast.Node
185 itemLen int
186 }{
187 {
188 `{
189 "foo": [
190 {
191 "foo": "svh",
192 "bar": "fatih"
193 }
194 ]
195 }`,
196 []ast.Node{
197 &ast.ObjectType{},
198 &ast.LiteralType{},
199 &ast.LiteralType{},
200 },
201 3,
202 },
203 {
204 `{
205 "variable": {
206 "foo": {}
207 }
208 }`,
209 []ast.Node{
210 &ast.ObjectType{},
211 },
212 1,
213 },
214 {
215 `{
216 "empty": []
217 }`,
218 []ast.Node{
219 &ast.ListType{},
220 },
221 1,
222 },
223 {
224 `{
225 "basic": [1, 2, 3]
226 }`,
227 []ast.Node{
228 &ast.ListType{},
229 },
230 1,
231 },
232 }
233
234 for _, l := range literals {
235 t.Logf("Testing:\n%s\n", l.src)
236
237 f, err := Parse([]byte(l.src))
238 if err != nil {
239 t.Error(err)
240 }
241
242
243
244 obj, ok := f.Node.(*ast.ObjectList)
245 if !ok {
246 t.Errorf("node should be *ast.ObjectList, got: %T", f.Node)
247 }
248
249
250 var i int
251 for _, item := range obj.Items {
252 equals(t, reflect.TypeOf(l.nodeType[i]), reflect.TypeOf(item.Val))
253 i++
254
255 if obj, ok := item.Val.(*ast.ObjectType); ok {
256 for _, item := range obj.List.Items {
257 equals(t, reflect.TypeOf(l.nodeType[i]), reflect.TypeOf(item.Val))
258 i++
259 }
260 }
261 }
262
263
264 equals(t, l.itemLen, i)
265
266 }
267 }
268
269 func TestObjectKey(t *testing.T) {
270 keys := []struct {
271 exp []token.Type
272 src string
273 }{
274 {[]token.Type{token.STRING}, `"foo": {}`},
275 }
276
277 for _, k := range keys {
278 p := newParser([]byte(k.src))
279 keys, err := p.objectKey()
280 if err != nil {
281 t.Fatal(err)
282 }
283
284 tokens := []token.Type{}
285 for _, o := range keys {
286 tokens = append(tokens, o.Token.Type)
287 }
288
289 equals(t, k.exp, tokens)
290 }
291
292 errKeys := []struct {
293 src string
294 }{
295 {`foo 12 {}`},
296 {`foo bar = {}`},
297 {`foo []`},
298 {`12 {}`},
299 }
300
301 for _, k := range errKeys {
302 p := newParser([]byte(k.src))
303 _, err := p.objectKey()
304 if err == nil {
305 t.Errorf("case '%s' should give an error", k.src)
306 }
307 }
308 }
309
310
311 func TestParse(t *testing.T) {
312 cases := []struct {
313 Name string
314 Err bool
315 }{
316 {
317 "array.json",
318 false,
319 },
320 {
321 "basic.json",
322 false,
323 },
324 {
325 "object.json",
326 false,
327 },
328 {
329 "types.json",
330 false,
331 },
332 {
333 "bad_input_128.json",
334 true,
335 },
336 {
337 "bad_input_tf_8110.json",
338 true,
339 },
340 {
341 "good_input_tf_8110.json",
342 false,
343 },
344 }
345
346 const fixtureDir = "./test-fixtures"
347
348 for _, tc := range cases {
349 d, err := ioutil.ReadFile(filepath.Join(fixtureDir, tc.Name))
350 if err != nil {
351 t.Fatalf("err: %s", err)
352 }
353
354 _, err = Parse(d)
355 if (err != nil) != tc.Err {
356 t.Fatalf("Input: %s\n\nError: %s", tc.Name, err)
357 }
358 }
359 }
360
361 func TestParse_inline(t *testing.T) {
362 cases := []struct {
363 Value string
364 Err bool
365 }{
366 {"{:{", true},
367 }
368
369 for _, tc := range cases {
370 _, err := Parse([]byte(tc.Value))
371 if (err != nil) != tc.Err {
372 t.Fatalf("Input: %q\n\nError: %s", tc.Value, err)
373 }
374 }
375 }
376
377
378 func equals(tb testing.TB, exp, act interface{}) {
379 if !reflect.DeepEqual(exp, act) {
380 _, file, line, _ := runtime.Caller(1)
381 fmt.Printf("\033[31m%s:%d:\n\n\texp: %s\n\n\tgot: %s\033[39m\n\n", filepath.Base(file), line, exp, act)
382 tb.FailNow()
383 }
384 }
385
View as plain text