...

Source file src/github.com/noirbizarre/gonja/parser/parser_test.go

Documentation: github.com/noirbizarre/gonja/parser

     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  	// Literals
    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  // func parseText(text string) (*nodeDocument, *Error) {
   543  // 	tokens, err := lex("test", text)
   544  // 	if err != nil {
   545  // 		return nil, err
   546  // 	}
   547  // 	parser := newParser("test", tokens, &Template{
   548  // 		set: &TemplateSet{},
   549  // 	})
   550  // 	return parser.parseDocument()
   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  	// t.Logf("type(expected %+v, actual %+v)", reflect.TypeOf(specs.typ), value.Type())
   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  			// t.Parallel()
   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