1 package parser_test
2
3 import (
4 "flag"
5 "fmt"
6 "os"
7 "reflect"
8 "testing"
9
10 "github.com/stretchr/testify/assert"
11
12 "github.com/noirbizarre/gonja/nodes"
13 "github.com/noirbizarre/gonja/parser"
14 "github.com/noirbizarre/gonja/tokens"
15 log "github.com/sirupsen/logrus"
16 prefixed "github.com/x-cray/logrus-prefixed-formatter"
17 )
18
19 var logLevel = flag.String("log.level", "", "Log Level")
20
21 func TestMain(m *testing.M) {
22 flag.Parse()
23
24 log.SetFormatter(&prefixed.TextFormatter{
25 ForceColors: true,
26 DisableTimestamp: true,
27 ForceFormatting: true,
28 })
29
30 switch *logLevel {
31 case "error":
32 log.SetLevel(log.ErrorLevel)
33 case "warning", "warn":
34 log.SetLevel(log.WarnLevel)
35 case "info":
36 log.SetLevel(log.InfoLevel)
37 case "debug":
38 log.SetLevel(log.DebugLevel)
39 case "trace":
40 log.SetLevel(log.TraceLevel)
41 default:
42 log.SetLevel(log.PanicLevel)
43 }
44 os.Exit(m.Run())
45 }
46
47 var testCases = []struct {
48 name string
49 text string
50 expected specs
51 }{
52 {"comment", "{# My comment #}", specs{nodes.Comment{}, attrs{
53 "Text": val{" My comment "},
54 }}},
55 {"multiline comment", "{# My\nmultiline\ncomment #}", specs{nodes.Comment{}, attrs{
56 "Text": val{" My\nmultiline\ncomment "},
57 }}},
58 {"empty comment", "{##}", specs{nodes.Comment{}, attrs{
59 "Text": val{""},
60 }}},
61 {"raw text", "raw text", specs{nodes.Data{}, attrs{
62 "Data": _token("raw text"),
63 }}},
64
65 {"single quotes string", "{{ 'test' }}", specs{nodes.Output{}, attrs{
66 "Expression": _literal(nodes.String{}, "test"),
67 }}},
68 {"single quotes string with whitespace chars", "{{ ' \n\ttest' }}", specs{nodes.Output{}, attrs{
69 "Expression": _literal(nodes.String{}, " \n\ttest"),
70 }}},
71 {"single quotes string with raw whitespace chars", `{{ ' \n\ttest' }}`, specs{nodes.Output{}, attrs{
72 "Expression": _literal(nodes.String{}, " \n\ttest"),
73 }}},
74 {"double quotes string", `{{ "test" }}`, specs{nodes.Output{}, attrs{
75 "Expression": _literal(nodes.String{}, "test"),
76 }}},
77 {"double quotes string with whitespace chars", "{{ \" \n\ttest\" }}", specs{nodes.Output{}, attrs{
78 "Expression": _literal(nodes.String{}, " \n\ttest"),
79 }}},
80 {"double quotes string with raw whitespace chars", `{{ " \n\ttest" }}`, specs{nodes.Output{}, attrs{
81 "Expression": _literal(nodes.String{}, " \n\ttest"),
82 }}},
83 {"single quotes inside double quotes string", `{{ "'quoted' test" }}`, specs{nodes.Output{}, attrs{
84 "Expression": _literal(nodes.String{}, "'quoted' test"),
85 }}},
86 {"integer", "{{ 42 }}", specs{nodes.Output{}, attrs{
87 "Expression": _literal(nodes.Integer{}, int64(42)),
88 }}},
89 {"negative-integer", "{{ -42 }}", specs{nodes.Output{}, attrs{
90 "Expression": specs{nodes.UnaryExpression{}, attrs{
91 "Negative": val{true},
92 "Term": _literal(nodes.Integer{}, int64(42)),
93 }},
94 }}},
95 {"float", "{{ 42.0 }}", specs{nodes.Output{}, attrs{
96 "Expression": _literal(nodes.Float{}, float64(42)),
97 }}},
98 {"negative-float", "{{ -42.0 }}", specs{nodes.Output{}, attrs{
99 "Expression": specs{nodes.UnaryExpression{}, attrs{
100 "Negative": val{true},
101 "Term": _literal(nodes.Float{}, float64(42)),
102 }},
103 }}},
104 {"bool-true", "{{ true }}", specs{nodes.Output{}, attrs{
105 "Expression": _literal(nodes.Bool{}, true),
106 }}},
107 {"bool-True", "{{ True }}", specs{nodes.Output{}, attrs{
108 "Expression": _literal(nodes.Bool{}, true),
109 }}},
110 {"bool-false", "{{ false }}", specs{nodes.Output{}, attrs{
111 "Expression": _literal(nodes.Bool{}, false),
112 }}},
113 {"bool-False", "{{ False }}", specs{nodes.Output{}, attrs{
114 "Expression": _literal(nodes.Bool{}, false),
115 }}},
116 {"list", "{{ ['list', \"of\", 'objects'] }}", specs{nodes.Output{}, attrs{
117 "Expression": _literal(nodes.List{}, slice{
118 _literal(nodes.String{}, "list"),
119 _literal(nodes.String{}, "of"),
120 _literal(nodes.String{}, "objects"),
121 }),
122 }}},
123 {"list with trailing coma", "{{ ['list', \"of\", 'objects',] }}", specs{nodes.Output{}, attrs{
124 "Expression": _literal(nodes.List{}, slice{
125 _literal(nodes.String{}, "list"),
126 _literal(nodes.String{}, "of"),
127 _literal(nodes.String{}, "objects"),
128 }),
129 }}},
130 {"single entry list", "{{ ['list'] }}", specs{nodes.Output{}, attrs{
131 "Expression": _literal(nodes.List{}, slice{
132 _literal(nodes.String{}, "list"),
133 }),
134 }}},
135 {"empty list", "{{ [] }}", specs{nodes.Output{}, attrs{
136 "Expression": _literal(nodes.List{}, slice{}),
137 }}},
138 {"tuple", "{{ ('tuple', \"of\", 'objects') }}", specs{nodes.Output{}, attrs{
139 "Expression": _literal(nodes.Tuple{}, slice{
140 _literal(nodes.String{}, "tuple"),
141 _literal(nodes.String{}, "of"),
142 _literal(nodes.String{}, "objects"),
143 }),
144 }}},
145 {"tuple with trailing coma", "{{ ('tuple', \"of\", 'objects',) }}", specs{nodes.Output{}, attrs{
146 "Expression": _literal(nodes.Tuple{}, slice{
147 _literal(nodes.String{}, "tuple"),
148 _literal(nodes.String{}, "of"),
149 _literal(nodes.String{}, "objects"),
150 }),
151 }}},
152 {"single entry tuple", "{{ ('tuple',) }}", specs{nodes.Output{}, attrs{
153 "Expression": _literal(nodes.Tuple{}, slice{
154 _literal(nodes.String{}, "tuple"),
155 }),
156 }}},
157 {"empty dict", "{{ {} }}", specs{nodes.Output{}, attrs{
158 "Expression": specs{nodes.Dict{}, attrs{}},
159 }}},
160 {"dict string", "{{ {'dict': 'of', 'key': 'and', 'value': 'pairs'} }}", specs{nodes.Output{}, attrs{
161 "Expression": specs{nodes.Dict{}, attrs{
162 "Pairs": slice{
163 specs{nodes.Pair{}, attrs{
164 "Key": _literal(nodes.String{}, "dict"),
165 "Value": _literal(nodes.String{}, "of"),
166 }},
167 specs{nodes.Pair{}, attrs{
168 "Key": _literal(nodes.String{}, "key"),
169 "Value": _literal(nodes.String{}, "and"),
170 }},
171 specs{nodes.Pair{}, attrs{
172 "Key": _literal(nodes.String{}, "value"),
173 "Value": _literal(nodes.String{}, "pairs"),
174 }},
175 },
176 }},
177 }}},
178 {"dict int", "{{ {1: 'one', 2: 'two', 3: 'three'} }}", specs{nodes.Output{}, attrs{
179 "Expression": specs{nodes.Dict{}, attrs{
180 "Pairs": slice{
181 specs{nodes.Pair{}, attrs{
182 "Key": _literal(nodes.Integer{}, int64(1)),
183 "Value": _literal(nodes.String{}, "one"),
184 }},
185 specs{nodes.Pair{}, attrs{
186 "Key": _literal(nodes.Integer{}, int64(2)),
187 "Value": _literal(nodes.String{}, "two"),
188 }},
189 specs{nodes.Pair{}, attrs{
190 "Key": _literal(nodes.Integer{}, int64(3)),
191 "Value": _literal(nodes.String{}, "three"),
192 }},
193 },
194 }},
195 }}},
196 {"addition", "{{ 40 + 2 }}", specs{nodes.Output{}, attrs{
197 "Expression": specs{nodes.BinaryExpression{}, attrs{
198 "Left": _literal(nodes.Integer{}, int64(40)),
199 "Right": _literal(nodes.Integer{}, int64(2)),
200 "Operator": _binOp("+"),
201 }},
202 }}},
203 {"multiple additions", "{{ 40 + 1 + 1 }}", specs{nodes.Output{}, attrs{
204 "Expression": specs{nodes.BinaryExpression{}, attrs{
205 "Left": specs{nodes.BinaryExpression{}, attrs{
206 "Left": _literal(nodes.Integer{}, int64(40)),
207 "Right": _literal(nodes.Integer{}, int64(1)),
208 "Operator": _binOp("+"),
209 }},
210 "Right": _literal(nodes.Integer{}, int64(1)),
211 "Operator": _binOp("+"),
212 }},
213 }}},
214 {"multiple additions with power", "{{ 40 + 2 ** 1 + 0 }}", specs{nodes.Output{}, attrs{
215 "Expression": specs{nodes.BinaryExpression{}, attrs{
216 "Left": specs{nodes.BinaryExpression{}, attrs{
217 "Left": _literal(nodes.Integer{}, int64(40)),
218 "Right": specs{nodes.BinaryExpression{}, attrs{
219 "Left": _literal(nodes.Integer{}, int64(2)),
220 "Right": _literal(nodes.Integer{}, int64(1)),
221 "Operator": _binOp("**"),
222 }},
223 "Operator": _binOp("+"),
224 }},
225 "Right": _literal(nodes.Integer{}, int64(0)),
226 "Operator": _binOp("+"),
227 }},
228 }}},
229 {"substract", "{{ 40 - 2 }}", specs{nodes.Output{}, attrs{
230 "Expression": specs{nodes.BinaryExpression{}, attrs{
231 "Left": _literal(nodes.Integer{}, int64(40)),
232 "Right": _literal(nodes.Integer{}, int64(2)),
233 "Operator": _binOp("-"),
234 }},
235 }}},
236 {"complex math", "{{ -1 * (-(-(10-100)) ** 2) ** 3 + 3 * (5 - 17) + 1 + 2 }}", specs{nodes.Output{}, attrs{
237 "Expression": specs{nodes.BinaryExpression{}, attrs{
238 "Left": specs{nodes.BinaryExpression{}, attrs{
239 "Left": specs{nodes.BinaryExpression{}, attrs{
240 "Left": specs{nodes.BinaryExpression{}, attrs{
241 "Left": specs{nodes.UnaryExpression{}, attrs{
242 "Negative": val{true},
243 "Term": _literal(nodes.Integer{}, int64(1)),
244 }},
245 "Right": specs{nodes.BinaryExpression{}, attrs{
246 "Left": specs{nodes.UnaryExpression{}, attrs{
247 "Negative": val{true},
248 "Term": specs{nodes.BinaryExpression{}, attrs{
249 "Left": specs{nodes.UnaryExpression{}, attrs{
250 "Negative": val{true},
251 "Term": specs{nodes.BinaryExpression{}, attrs{
252 "Left": _literal(nodes.Integer{}, int64(10)),
253 "Right": _literal(nodes.Integer{}, int64(100)),
254 "Operator": _binOp("-"),
255 }},
256 }},
257 "Right": _literal(nodes.Integer{}, int64(2)),
258 "Operator": _binOp("**"),
259 }},
260 }},
261 "Right": _literal(nodes.Integer{}, int64(3)),
262 "Operator": _binOp("**"),
263 }},
264 "Operator": _binOp("*"),
265 }},
266 "Right": specs{nodes.BinaryExpression{}, attrs{
267 "Left": _literal(nodes.Integer{}, int64(3)),
268 "Right": specs{nodes.BinaryExpression{}, attrs{
269 "Left": _literal(nodes.Integer{}, int64(5)),
270 "Right": _literal(nodes.Integer{}, int64(17)),
271 "Operator": _binOp("-"),
272 }},
273 "Operator": _binOp("*"),
274 }},
275 "Operator": _binOp("+"),
276 }},
277 "Right": _literal(nodes.Integer{}, int64(1)),
278 "Operator": _binOp("+"),
279 }},
280 "Right": _literal(nodes.Integer{}, int64(2)),
281 "Operator": _binOp("+"),
282 }},
283 }}},
284 {"negative-expression", "{{ -(40 + 2) }}", specs{nodes.Output{}, attrs{
285 "Expression": specs{nodes.UnaryExpression{}, attrs{
286 "Negative": val{true},
287 "Term": specs{nodes.BinaryExpression{}, attrs{
288 "Left": _literal(nodes.Integer{}, int64(40)),
289 "Right": _literal(nodes.Integer{}, int64(2)),
290 "Operator": _binOp("+"),
291 }},
292 }},
293 }}},
294 {"Operators precedence", "{{ 2 * 3 + 4 % 2 + 1 - 2 }}", specs{nodes.Output{}, attrs{
295 "Expression": specs{nodes.BinaryExpression{}, attrs{
296 "Left": specs{nodes.BinaryExpression{}, attrs{
297 "Left": specs{nodes.BinaryExpression{}, attrs{
298 "Left": specs{nodes.BinaryExpression{}, attrs{
299 "Left": _literal(nodes.Integer{}, int64(2)),
300 "Right": _literal(nodes.Integer{}, int64(3)),
301 "Operator": _binOp("*"),
302 }},
303 "Right": specs{nodes.BinaryExpression{}, attrs{
304 "Left": _literal(nodes.Integer{}, int64(4)),
305 "Right": _literal(nodes.Integer{}, int64(2)),
306 "Operator": _binOp("%"),
307 }},
308 "Operator": _binOp("+"),
309 }},
310 "Right": _literal(nodes.Integer{}, int64(1)),
311 "Operator": _binOp("+"),
312 }},
313 "Right": _literal(nodes.Integer{}, int64(2)),
314 "Operator": _binOp("-"),
315 }},
316 }}},
317 {"Operators precedence with parenthesis", "{{ 2 * (3 + 4) % 2 + (1 - 2) }}", specs{nodes.Output{}, attrs{
318 "Expression": specs{nodes.BinaryExpression{}, attrs{
319 "Left": specs{nodes.BinaryExpression{}, attrs{
320 "Left": specs{nodes.BinaryExpression{}, attrs{
321 "Left": _literal(nodes.Integer{}, int64(2)),
322 "Right": specs{nodes.BinaryExpression{}, attrs{
323 "Left": _literal(nodes.Integer{}, int64(3)),
324 "Right": _literal(nodes.Integer{}, int64(4)),
325 "Operator": _binOp("+"),
326 }},
327 "Operator": _binOp("*"),
328 }},
329 "Right": _literal(nodes.Integer{}, int64(2)),
330 "Operator": _binOp("%"),
331 }},
332 "Right": specs{nodes.BinaryExpression{}, attrs{
333 "Left": _literal(nodes.Integer{}, int64(1)),
334 "Right": _literal(nodes.Integer{}, int64(2)),
335 "Operator": _binOp("-"),
336 }},
337 "Operator": _binOp("+"),
338 }},
339 }}},
340 {"variable", "{{ a_var }}", specs{nodes.Output{}, attrs{
341 "Expression": specs{nodes.Name{}, attrs{
342 "Name": _token("a_var"),
343 }},
344 }}},
345 {"variable attribute", "{{ a_var.attr }}", specs{nodes.Output{}, attrs{
346 "Expression": specs{nodes.Getattr{}, attrs{
347 "Node": specs{nodes.Name{}, attrs{
348 "Name": _token("a_var"),
349 }},
350 "Attr": val{"attr"},
351 }},
352 }}},
353 {"variable and filter", "{{ a_var|safe }}", specs{nodes.Output{}, attrs{
354 "Expression": specs{nodes.FilteredExpression{}, attrs{
355 "Expression": specs{nodes.Name{}, attrs{
356 "Name": _token("a_var"),
357 }},
358 "Filters": slice{
359 filter{"safe", slice{}, attrs{}},
360 },
361 }},
362 }}},
363 {"integer and filter", "{{ 42|safe }}", specs{nodes.Output{}, attrs{
364 "Expression": specs{nodes.FilteredExpression{}, attrs{
365 "Expression": _literal(nodes.Integer{}, int64(42)),
366 "Filters": slice{
367 filter{"safe", slice{}, attrs{}},
368 },
369 }},
370 }}},
371 {"negative integer and filter", "{{ -42|safe }}", specs{nodes.Output{}, attrs{
372 "Expression": specs{nodes.FilteredExpression{}, attrs{
373 "Expression": specs{nodes.UnaryExpression{}, attrs{
374 "Negative": val{true},
375 "Term": _literal(nodes.Integer{}, int64(42)),
376 }},
377 "Filters": slice{
378 filter{"safe", slice{}, attrs{}},
379 },
380 }},
381 }}},
382 {"logical expressions", "{{ true and false }}", specs{nodes.Output{}, attrs{
383 "Expression": specs{nodes.BinaryExpression{}, attrs{
384 "Left": _literal(nodes.Bool{}, true),
385 "Right": _literal(nodes.Bool{}, false),
386 "Operator": _binOp("and"),
387 }},
388 }}},
389 {"negated boolean", "{{ not true }}", specs{nodes.Output{}, attrs{
390 "Expression": specs{nodes.Negation{}, attrs{
391 "Term": _literal(nodes.Bool{}, true),
392 }},
393 }}},
394 {"negated logical expression", "{{ not false and true }}", specs{nodes.Output{}, attrs{
395 "Expression": specs{nodes.BinaryExpression{}, attrs{
396 "Left": specs{nodes.Negation{}, attrs{
397 "Term": _literal(nodes.Bool{}, false),
398 }},
399 "Right": _literal(nodes.Bool{}, true),
400 "Operator": _binOp("and"),
401 }},
402 }}},
403 {"negated logical expression with parenthesis", "{{ not (false and true) }}", specs{nodes.Output{}, attrs{
404 "Expression": specs{nodes.Negation{}, attrs{
405 "Term": specs{nodes.BinaryExpression{}, attrs{
406 "Left": _literal(nodes.Bool{}, false),
407 "Right": _literal(nodes.Bool{}, true),
408 "Operator": _binOp("and"),
409 }},
410 }},
411 }}},
412 {"logical expression with math comparison", "{{ 40 + 2 > 5 }}", specs{nodes.Output{}, attrs{
413 "Expression": specs{nodes.BinaryExpression{}, attrs{
414 "Left": specs{nodes.BinaryExpression{}, attrs{
415 "Left": _literal(nodes.Integer{}, int64(40)),
416 "Right": _literal(nodes.Integer{}, int64(2)),
417 "Operator": _binOp("+"),
418 }},
419 "Right": _literal(nodes.Integer{}, int64(5)),
420 "Operator": _binOp(">"),
421 }},
422 }}},
423 {"logical expression with filter", "{{ false and true|safe }}", specs{nodes.Output{}, attrs{
424 "Expression": specs{nodes.BinaryExpression{}, attrs{
425 "Left": _literal(nodes.Bool{}, false),
426 "Right": specs{nodes.FilteredExpression{}, attrs{
427 "Expression": _literal(nodes.Bool{}, true),
428 "Filters": slice{
429 filter{"safe", slice{}, attrs{}},
430 },
431 }},
432 "Operator": _binOp("and"),
433 }},
434 }}},
435 {"logical expression with parenthesis and filter", "{{ (false and true)|safe }}", specs{nodes.Output{}, attrs{
436 "Expression": specs{nodes.FilteredExpression{}, attrs{
437 "Expression": specs{nodes.BinaryExpression{}, attrs{
438 "Left": _literal(nodes.Bool{}, false),
439 "Right": _literal(nodes.Bool{}, true),
440 "Operator": _binOp("and"),
441 }},
442 "Filters": slice{
443 filter{"safe", slice{}, attrs{}},
444 },
445 }},
446 }}},
447 {"function", "{{ a_func(42) }}", specs{nodes.Output{}, attrs{
448 "Expression": specs{nodes.Call{}, attrs{
449 "Func": specs{nodes.Name{}, attrs{"Name": _token("a_func")}},
450 "Args": slice{_literal(nodes.Integer{}, int64(42))},
451 }},
452 }}},
453 {"method", "{{ an_obj.a_method(42) }}", specs{nodes.Output{}, attrs{
454 "Expression": specs{nodes.Call{}, attrs{
455 "Func": specs{nodes.Getattr{}, attrs{
456 "Node": specs{nodes.Name{}, attrs{"Name": _token("an_obj")}},
457 "Attr": val{"a_method"},
458 }},
459 "Args": slice{_literal(nodes.Integer{}, int64(42))},
460 }},
461 }}},
462 {"function with filtered args", "{{ a_func(42|safe) }}", specs{nodes.Output{}, attrs{
463 "Expression": specs{nodes.Call{}, attrs{
464 "Func": specs{nodes.Name{}, attrs{"Name": _token("a_func")}},
465 "Args": slice{
466 specs{nodes.FilteredExpression{}, attrs{
467 "Expression": _literal(nodes.Integer{}, int64(42)),
468 "Filters": slice{
469 filter{"safe", slice{}, attrs{}},
470 },
471 }},
472 },
473 }},
474 }}},
475 {"variable and multiple filters", "{{ a_var|add(42)|safe }}", specs{nodes.Output{}, attrs{
476 "Expression": specs{nodes.FilteredExpression{}, attrs{
477 "Expression": specs{nodes.Name{}, attrs{"Name": _token("a_var")}},
478 "Filters": slice{
479 filter{"add", slice{_literal(nodes.Integer{}, int64(42))}, attrs{}},
480 filter{"safe", slice{}, attrs{}},
481 },
482 }},
483 }}},
484 {"variable and expression filters", "{{ a_var|add(40 + 2) }}", specs{nodes.Output{}, attrs{
485 "Expression": specs{nodes.FilteredExpression{}, attrs{
486 "Expression": specs{nodes.Name{}, attrs{"Name": _token("a_var")}},
487 "Filters": slice{
488 filter{"add", slice{
489 specs{nodes.BinaryExpression{}, attrs{
490 "Left": _literal(nodes.Integer{}, int64(40)),
491 "Right": _literal(nodes.Integer{}, int64(2)),
492 "Operator": _binOp("+"),
493 }},
494 }, attrs{}},
495 },
496 }},
497 }}},
498 {"variable and nested filters", "{{ a_var|add( 42|add(2) ) }}", specs{nodes.Output{}, attrs{
499 "Expression": specs{nodes.FilteredExpression{}, attrs{
500 "Expression": specs{nodes.Name{}, attrs{"Name": _token("a_var")}},
501 "Filters": slice{
502 filter{"add", slice{
503 specs{nodes.FilteredExpression{}, attrs{
504 "Expression": _literal(nodes.Integer{}, int64(42)),
505 "Filters": slice{
506 filter{"add", slice{_literal(nodes.Integer{}, int64(2))}, attrs{}},
507 },
508 }},
509 }, attrs{}},
510 },
511 }},
512 }}},
513 {"Test equal", "{{ 3 is equal 3 }}", specs{nodes.Output{}, attrs{
514 "Expression": specs{nodes.TestExpression{}, attrs{
515 "Expression": _literal(nodes.Integer{}, int64(3)),
516 "Test": specs{nodes.TestCall{}, attrs{
517 "Name": val{"equal"},
518 "Args": slice{_literal(nodes.Integer{}, int64(3))},
519 }},
520 }},
521 }}},
522 {"Test equal parenthesis", "{{ 3 is equal(3) }}", specs{nodes.Output{}, attrs{
523 "Expression": specs{nodes.TestExpression{}, attrs{
524 "Expression": _literal(nodes.Integer{}, int64(3)),
525 "Test": specs{nodes.TestCall{}, attrs{
526 "Name": val{"equal"},
527 "Args": slice{_literal(nodes.Integer{}, int64(3))},
528 }},
529 }},
530 }}},
531 {"Test ==", "{{ 3 is == 3 }}", specs{nodes.Output{}, attrs{
532 "Expression": specs{nodes.TestExpression{}, attrs{
533 "Expression": _literal(nodes.Integer{}, int64(3)),
534 "Test": specs{nodes.TestCall{}, attrs{
535 "Name": val{"=="},
536 "Args": slice{_literal(nodes.Integer{}, int64(3))},
537 }},
538 }},
539 }}},
540 }
541
542
543
544
545
546
547
548
549
550
551
552
553 func _deref(value reflect.Value) reflect.Value {
554 for (value.Kind() == reflect.Interface || value.Kind() == reflect.Ptr) && !value.IsNil() {
555 value = value.Elem()
556 }
557 return value
558 }
559
560 type asserter interface {
561 assert(t *testing.T, value reflect.Value)
562 }
563
564 type specs struct {
565 typ interface{}
566 attrs attrs
567 }
568
569 func (specs specs) assert(t *testing.T, value reflect.Value) {
570 assert := assert.New(t)
571 value = _deref(value)
572
573 if !assert.Equal(reflect.TypeOf(specs.typ), value.Type()) {
574 return
575 }
576 if specs.attrs != nil {
577 specs.attrs.assert(t, value)
578 }
579 }
580
581 type val struct {
582 value interface{}
583 }
584
585 func (val val) assert(t *testing.T, value reflect.Value) {
586 assert := assert.New(t)
587 value = _deref(value)
588 switch value.Kind() {
589 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
590 assert.Equal(val.value, value.Int())
591 case reflect.Float32, reflect.Float64:
592 assert.Equal(val.value, value.Float())
593 case reflect.String:
594 assert.Equal(val.value, value.String())
595 case reflect.Bool:
596 assert.Equal(val.value, value.Bool())
597 case reflect.Slice:
598 current, ok := val.value.(asserter)
599 if assert.True(ok) {
600 current.assert(t, value)
601 }
602 case reflect.Map:
603 assert.Len(val.value, value.Len())
604 v2 := reflect.ValueOf(val.value)
605
606 iter := value.MapRange()
607 for iter.Next() {
608 assert.Equal(iter.Value(), v2.MapIndex(iter.Key()))
609 }
610 case reflect.Func:
611 assert.Equal(value, reflect.ValueOf(val.value))
612 default:
613 assert.Failf("Unknown value", "Unknown value kind '%s'", value.Kind())
614 }
615 }
616
617 func _literal(typ interface{}, value interface{}) asserter {
618 return specs{typ, attrs{
619 "Val": val{value},
620 }}
621 }
622
623 func _token(value string) asserter {
624 return specs{tokens.Token{}, attrs{
625 "Val": val{value},
626 }}
627 }
628
629 func _binOp(value string) asserter {
630 return specs{nodes.BinOperator{}, attrs{
631 "Token": _token(value),
632 }}
633 }
634
635 type attrs map[string]asserter
636
637 func (attrs attrs) assert(t *testing.T, value reflect.Value) {
638 assert := assert.New(t)
639 for attr, specs := range attrs {
640 field := value.FieldByName(attr)
641 if assert.True(field.IsValid(), fmt.Sprintf("No field named '%s' found", attr)) {
642 specs.assert(t, field)
643 }
644 }
645 }
646
647 type slice []asserter
648
649 func (slice slice) assert(t *testing.T, value reflect.Value) {
650 if assert.Equal(t, reflect.Slice, value.Kind()) {
651 if assert.Equal(t, len(slice), value.Len()) {
652 for idx, specs := range slice {
653 specs.assert(t, value.Index(idx))
654 }
655 }
656 }
657 }
658
659 type filter struct {
660 name string
661 args slice
662 kwargs attrs
663 }
664
665 func (filter filter) assert(t *testing.T, value reflect.Value) {
666 value = _deref(value)
667 assert := assert.New(t)
668 assert.Equal(reflect.TypeOf(nodes.FilterCall{}), value.Type())
669 assert.Equal(filter.name, value.FieldByName("Name").String())
670 args := value.FieldByName("Args")
671 kwargs := value.FieldByName("Kwargs")
672 if assert.Equal(len(filter.args), args.Len()) {
673 for idx, specs := range filter.args {
674 specs.assert(t, args.Index(idx))
675 }
676 }
677 if assert.Equal(len(filter.kwargs), kwargs.Len()) {
678 for key, specs := range filter.kwargs {
679 specs.assert(t, args.MapIndex(reflect.ValueOf(key)))
680 }
681 }
682 }
683
684 func TestParser(t *testing.T) {
685 for _, tc := range testCases {
686 test := tc
687 t.Run(test.name, func(t *testing.T) {
688 defer func() {
689 if err := recover(); err != nil {
690 t.Error(err)
691 }
692 }()
693
694 assert := assert.New(t)
695 tpl, err := parser.Parse(test.text)
696 if assert.Nil(err, "Unable to parse template: %s", err) {
697 if assert.Equal(1, len(tpl.Nodes), "Expected one node") {
698 test.expected.assert(t, reflect.ValueOf(tpl.Nodes[0]))
699 } else {
700 t.Logf("Nodes %+v", tpl.Nodes)
701 }
702 }
703 })
704 }
705 }
706
View as plain text