github.com/varialus/godfly@v0.0.0-20130904042352-1934f9f095ab/src/pkg/text/template/exec_test.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package template 6 7 import ( 8 "bytes" 9 "errors" 10 "flag" 11 "fmt" 12 "reflect" 13 "strings" 14 "testing" 15 ) 16 17 var debug = flag.Bool("debug", false, "show the errors produced by the tests") 18 19 // T has lots of interesting pieces to use to test execution. 20 type T struct { 21 // Basics 22 True bool 23 I int 24 U16 uint16 25 X string 26 FloatZero float64 27 ComplexZero complex128 28 // Nested structs. 29 U *U 30 // Struct with String method. 31 V0 V 32 V1, V2 *V 33 // Struct with Error method. 34 W0 W 35 W1, W2 *W 36 // Slices 37 SI []int 38 SIEmpty []int 39 SB []bool 40 // Maps 41 MSI map[string]int 42 MSIone map[string]int // one element, for deterministic output 43 MSIEmpty map[string]int 44 MXI map[interface{}]int 45 MII map[int]int 46 SMSI []map[string]int 47 // Empty interfaces; used to see if we can dig inside one. 48 Empty0 interface{} // nil 49 Empty1 interface{} 50 Empty2 interface{} 51 Empty3 interface{} 52 Empty4 interface{} 53 // Non-empty interface. 54 NonEmptyInterface I 55 // Stringer. 56 Str fmt.Stringer 57 Err error 58 // Pointers 59 PI *int 60 PS *string 61 PSI *[]int 62 NIL *int 63 // Function (not method) 64 BinaryFunc func(string, string) string 65 VariadicFunc func(...string) string 66 VariadicFuncInt func(int, ...string) string 67 NilOKFunc func(*int) bool 68 ErrFunc func() (string, error) 69 // Template to test evaluation of templates. 70 Tmpl *Template 71 // Unexported field; cannot be accessed by template. 72 unexported int 73 } 74 75 type U struct { 76 V string 77 } 78 79 type V struct { 80 j int 81 } 82 83 func (v *V) String() string { 84 if v == nil { 85 return "nilV" 86 } 87 return fmt.Sprintf("<%d>", v.j) 88 } 89 90 type W struct { 91 k int 92 } 93 94 func (w *W) Error() string { 95 if w == nil { 96 return "nilW" 97 } 98 return fmt.Sprintf("[%d]", w.k) 99 } 100 101 var tVal = &T{ 102 True: true, 103 I: 17, 104 U16: 16, 105 X: "x", 106 U: &U{"v"}, 107 V0: V{6666}, 108 V1: &V{7777}, // leave V2 as nil 109 W0: W{888}, 110 W1: &W{999}, // leave W2 as nil 111 SI: []int{3, 4, 5}, 112 SB: []bool{true, false}, 113 MSI: map[string]int{"one": 1, "two": 2, "three": 3}, 114 MSIone: map[string]int{"one": 1}, 115 MXI: map[interface{}]int{"one": 1}, 116 MII: map[int]int{1: 1}, 117 SMSI: []map[string]int{ 118 {"one": 1, "two": 2}, 119 {"eleven": 11, "twelve": 12}, 120 }, 121 Empty1: 3, 122 Empty2: "empty2", 123 Empty3: []int{7, 8}, 124 Empty4: &U{"UinEmpty"}, 125 NonEmptyInterface: new(T), 126 Str: bytes.NewBuffer([]byte("foozle")), 127 Err: errors.New("erroozle"), 128 PI: newInt(23), 129 PS: newString("a string"), 130 PSI: newIntSlice(21, 22, 23), 131 BinaryFunc: func(a, b string) string { return fmt.Sprintf("[%s=%s]", a, b) }, 132 VariadicFunc: func(s ...string) string { return fmt.Sprint("<", strings.Join(s, "+"), ">") }, 133 VariadicFuncInt: func(a int, s ...string) string { return fmt.Sprint(a, "=<", strings.Join(s, "+"), ">") }, 134 NilOKFunc: func(s *int) bool { return s == nil }, 135 ErrFunc: func() (string, error) { return "bla", nil }, 136 Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X 137 } 138 139 // A non-empty interface. 140 type I interface { 141 Method0() string 142 } 143 144 var iVal I = tVal 145 146 // Helpers for creation. 147 func newInt(n int) *int { 148 return &n 149 } 150 151 func newString(s string) *string { 152 return &s 153 } 154 155 func newIntSlice(n ...int) *[]int { 156 p := new([]int) 157 *p = make([]int, len(n)) 158 copy(*p, n) 159 return p 160 } 161 162 // Simple methods with and without arguments. 163 func (t *T) Method0() string { 164 return "M0" 165 } 166 167 func (t *T) Method1(a int) int { 168 return a 169 } 170 171 func (t *T) Method2(a uint16, b string) string { 172 return fmt.Sprintf("Method2: %d %s", a, b) 173 } 174 175 func (t *T) Method3(v interface{}) string { 176 return fmt.Sprintf("Method3: %v", v) 177 } 178 179 func (t *T) MAdd(a int, b []int) []int { 180 v := make([]int, len(b)) 181 for i, x := range b { 182 v[i] = x + a 183 } 184 return v 185 } 186 187 var myError = errors.New("my error") 188 189 // MyError returns a value and an error according to its argument. 190 func (t *T) MyError(error bool) (bool, error) { 191 if error { 192 return true, myError 193 } 194 return false, nil 195 } 196 197 // A few methods to test chaining. 198 func (t *T) GetU() *U { 199 return t.U 200 } 201 202 func (u *U) TrueFalse(b bool) string { 203 if b { 204 return "true" 205 } 206 return "" 207 } 208 209 func typeOf(arg interface{}) string { 210 return fmt.Sprintf("%T", arg) 211 } 212 213 type execTest struct { 214 name string 215 input string 216 output string 217 data interface{} 218 ok bool 219 } 220 221 // bigInt and bigUint are hex string representing numbers either side 222 // of the max int boundary. 223 // We do it this way so the test doesn't depend on ints being 32 bits. 224 var ( 225 bigInt = fmt.Sprintf("0x%x", int(1<<uint(reflect.TypeOf(0).Bits()-1)-1)) 226 bigUint = fmt.Sprintf("0x%x", uint(1<<uint(reflect.TypeOf(0).Bits()-1))) 227 ) 228 229 var execTests = []execTest{ 230 // Trivial cases. 231 {"empty", "", "", nil, true}, 232 {"text", "some text", "some text", nil, true}, 233 {"nil action", "{{nil}}", "", nil, false}, 234 235 // Ideal constants. 236 {"ideal int", "{{typeOf 3}}", "int", 0, true}, 237 {"ideal float", "{{typeOf 1.0}}", "float64", 0, true}, 238 {"ideal exp float", "{{typeOf 1e1}}", "float64", 0, true}, 239 {"ideal complex", "{{typeOf 1i}}", "complex128", 0, true}, 240 {"ideal int", "{{typeOf " + bigInt + "}}", "int", 0, true}, 241 {"ideal too big", "{{typeOf " + bigUint + "}}", "", 0, false}, 242 {"ideal nil without type", "{{nil}}", "", 0, false}, 243 244 // Fields of structs. 245 {".X", "-{{.X}}-", "-x-", tVal, true}, 246 {".U.V", "-{{.U.V}}-", "-v-", tVal, true}, 247 {".unexported", "{{.unexported}}", "", tVal, false}, 248 249 // Fields on maps. 250 {"map .one", "{{.MSI.one}}", "1", tVal, true}, 251 {"map .two", "{{.MSI.two}}", "2", tVal, true}, 252 {"map .NO", "{{.MSI.NO}}", "<no value>", tVal, true}, 253 {"map .one interface", "{{.MXI.one}}", "1", tVal, true}, 254 {"map .WRONG args", "{{.MSI.one 1}}", "", tVal, false}, 255 {"map .WRONG type", "{{.MII.one}}", "", tVal, false}, 256 257 // Dots of all kinds to test basic evaluation. 258 {"dot int", "<{{.}}>", "<13>", 13, true}, 259 {"dot uint", "<{{.}}>", "<14>", uint(14), true}, 260 {"dot float", "<{{.}}>", "<15.1>", 15.1, true}, 261 {"dot bool", "<{{.}}>", "<true>", true, true}, 262 {"dot complex", "<{{.}}>", "<(16.2-17i)>", 16.2 - 17i, true}, 263 {"dot string", "<{{.}}>", "<hello>", "hello", true}, 264 {"dot slice", "<{{.}}>", "<[-1 -2 -3]>", []int{-1, -2, -3}, true}, 265 {"dot map", "<{{.}}>", "<map[two:22]>", map[string]int{"two": 22}, true}, 266 {"dot struct", "<{{.}}>", "<{7 seven}>", struct { 267 a int 268 b string 269 }{7, "seven"}, true}, 270 271 // Variables. 272 {"$ int", "{{$}}", "123", 123, true}, 273 {"$.I", "{{$.I}}", "17", tVal, true}, 274 {"$.U.V", "{{$.U.V}}", "v", tVal, true}, 275 {"declare in action", "{{$x := $.U.V}}{{$x}}", "v", tVal, true}, 276 277 // Type with String method. 278 {"V{6666}.String()", "-{{.V0}}-", "-<6666>-", tVal, true}, 279 {"&V{7777}.String()", "-{{.V1}}-", "-<7777>-", tVal, true}, 280 {"(*V)(nil).String()", "-{{.V2}}-", "-nilV-", tVal, true}, 281 282 // Type with Error method. 283 {"W{888}.Error()", "-{{.W0}}-", "-[888]-", tVal, true}, 284 {"&W{999}.Error()", "-{{.W1}}-", "-[999]-", tVal, true}, 285 {"(*W)(nil).Error()", "-{{.W2}}-", "-nilW-", tVal, true}, 286 287 // Pointers. 288 {"*int", "{{.PI}}", "23", tVal, true}, 289 {"*string", "{{.PS}}", "a string", tVal, true}, 290 {"*[]int", "{{.PSI}}", "[21 22 23]", tVal, true}, 291 {"*[]int[1]", "{{index .PSI 1}}", "22", tVal, true}, 292 {"NIL", "{{.NIL}}", "<nil>", tVal, true}, 293 294 // Empty interfaces holding values. 295 {"empty nil", "{{.Empty0}}", "<no value>", tVal, true}, 296 {"empty with int", "{{.Empty1}}", "3", tVal, true}, 297 {"empty with string", "{{.Empty2}}", "empty2", tVal, true}, 298 {"empty with slice", "{{.Empty3}}", "[7 8]", tVal, true}, 299 {"empty with struct", "{{.Empty4}}", "{UinEmpty}", tVal, true}, 300 {"empty with struct, field", "{{.Empty4.V}}", "UinEmpty", tVal, true}, 301 302 // Method calls. 303 {".Method0", "-{{.Method0}}-", "-M0-", tVal, true}, 304 {".Method1(1234)", "-{{.Method1 1234}}-", "-1234-", tVal, true}, 305 {".Method1(.I)", "-{{.Method1 .I}}-", "-17-", tVal, true}, 306 {".Method2(3, .X)", "-{{.Method2 3 .X}}-", "-Method2: 3 x-", tVal, true}, 307 {".Method2(.U16, `str`)", "-{{.Method2 .U16 `str`}}-", "-Method2: 16 str-", tVal, true}, 308 {".Method2(.U16, $x)", "{{if $x := .X}}-{{.Method2 .U16 $x}}{{end}}-", "-Method2: 16 x-", tVal, true}, 309 {".Method3(nil constant)", "-{{.Method3 nil}}-", "-Method3: <nil>-", tVal, true}, 310 {".Method3(nil value)", "-{{.Method3 .MXI.unset}}-", "-Method3: <nil>-", tVal, true}, 311 {"method on var", "{{if $x := .}}-{{$x.Method2 .U16 $x.X}}{{end}}-", "-Method2: 16 x-", tVal, true}, 312 {"method on chained var", 313 "{{range .MSIone}}{{if $.U.TrueFalse $.True}}{{$.U.TrueFalse $.True}}{{else}}WRONG{{end}}{{end}}", 314 "true", tVal, true}, 315 {"chained method", 316 "{{range .MSIone}}{{if $.GetU.TrueFalse $.True}}{{$.U.TrueFalse $.True}}{{else}}WRONG{{end}}{{end}}", 317 "true", tVal, true}, 318 {"chained method on variable", 319 "{{with $x := .}}{{with .SI}}{{$.GetU.TrueFalse $.True}}{{end}}{{end}}", 320 "true", tVal, true}, 321 {".NilOKFunc not nil", "{{call .NilOKFunc .PI}}", "false", tVal, true}, 322 {".NilOKFunc nil", "{{call .NilOKFunc nil}}", "true", tVal, true}, 323 324 // Function call builtin. 325 {".BinaryFunc", "{{call .BinaryFunc `1` `2`}}", "[1=2]", tVal, true}, 326 {".VariadicFunc0", "{{call .VariadicFunc}}", "<>", tVal, true}, 327 {".VariadicFunc2", "{{call .VariadicFunc `he` `llo`}}", "<he+llo>", tVal, true}, 328 {".VariadicFuncInt", "{{call .VariadicFuncInt 33 `he` `llo`}}", "33=<he+llo>", tVal, true}, 329 {"if .BinaryFunc call", "{{ if .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{end}}", "[1=2]", tVal, true}, 330 {"if not .BinaryFunc call", "{{ if not .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{else}}No{{end}}", "No", tVal, true}, 331 {"Interface Call", `{{stringer .S}}`, "foozle", map[string]interface{}{"S": bytes.NewBufferString("foozle")}, true}, 332 {".ErrFunc", "{{call .ErrFunc}}", "bla", tVal, true}, 333 334 // Erroneous function calls (check args). 335 {".BinaryFuncTooFew", "{{call .BinaryFunc `1`}}", "", tVal, false}, 336 {".BinaryFuncTooMany", "{{call .BinaryFunc `1` `2` `3`}}", "", tVal, false}, 337 {".BinaryFuncBad0", "{{call .BinaryFunc 1 3}}", "", tVal, false}, 338 {".BinaryFuncBad1", "{{call .BinaryFunc `1` 3}}", "", tVal, false}, 339 {".VariadicFuncBad0", "{{call .VariadicFunc 3}}", "", tVal, false}, 340 {".VariadicFuncIntBad0", "{{call .VariadicFuncInt}}", "", tVal, false}, 341 {".VariadicFuncIntBad`", "{{call .VariadicFuncInt `x`}}", "", tVal, false}, 342 {".VariadicFuncNilBad", "{{call .VariadicFunc nil}}", "", tVal, false}, 343 344 // Pipelines. 345 {"pipeline", "-{{.Method0 | .Method2 .U16}}-", "-Method2: 16 M0-", tVal, true}, 346 {"pipeline func", "-{{call .VariadicFunc `llo` | call .VariadicFunc `he` }}-", "-<he+<llo>>-", tVal, true}, 347 348 // Parenthesized expressions 349 {"parens in pipeline", "{{printf `%d %d %d` (1) (2 | add 3) (add 4 (add 5 6))}}", "1 5 15", tVal, true}, 350 351 // Parenthesized expressions with field accesses 352 {"parens: $ in paren", "{{($).X}}", "x", tVal, true}, 353 {"parens: $.GetU in paren", "{{($.GetU).V}}", "v", tVal, true}, 354 {"parens: $ in paren in pipe", "{{($ | echo).X}}", "x", tVal, true}, 355 {"parens: spaces and args", `{{(makemap "up" "down" "left" "right").left}}`, "right", tVal, true}, 356 357 // If. 358 {"if true", "{{if true}}TRUE{{end}}", "TRUE", tVal, true}, 359 {"if false", "{{if false}}TRUE{{else}}FALSE{{end}}", "FALSE", tVal, true}, 360 {"if nil", "{{if nil}}TRUE{{end}}", "", tVal, false}, 361 {"if 1", "{{if 1}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true}, 362 {"if 0", "{{if 0}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, 363 {"if 1.5", "{{if 1.5}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true}, 364 {"if 0.0", "{{if .FloatZero}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, 365 {"if 1.5i", "{{if 1.5i}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true}, 366 {"if 0.0i", "{{if .ComplexZero}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, 367 {"if emptystring", "{{if ``}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, 368 {"if string", "{{if `notempty`}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true}, 369 {"if emptyslice", "{{if .SIEmpty}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, 370 {"if slice", "{{if .SI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true}, 371 {"if emptymap", "{{if .MSIEmpty}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, 372 {"if map", "{{if .MSI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true}, 373 {"if map unset", "{{if .MXI.none}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, 374 {"if map not unset", "{{if not .MXI.none}}ZERO{{else}}NON-ZERO{{end}}", "ZERO", tVal, true}, 375 {"if $x with $y int", "{{if $x := true}}{{with $y := .I}}{{$x}},{{$y}}{{end}}{{end}}", "true,17", tVal, true}, 376 {"if $x with $x int", "{{if $x := true}}{{with $x := .I}}{{$x}},{{end}}{{$x}}{{end}}", "17,true", tVal, true}, 377 {"if else if", "{{if false}}FALSE{{else if true}}TRUE{{end}}", "TRUE", tVal, true}, 378 {"if else chain", "{{if eq 1 3}}1{{else if eq 2 3}}2{{else if eq 3 3}}3{{end}}", "3", tVal, true}, 379 380 // Print etc. 381 {"print", `{{print "hello, print"}}`, "hello, print", tVal, true}, 382 {"print 123", `{{print 1 2 3}}`, "1 2 3", tVal, true}, 383 {"print nil", `{{print nil}}`, "<nil>", tVal, true}, 384 {"println", `{{println 1 2 3}}`, "1 2 3\n", tVal, true}, 385 {"printf int", `{{printf "%04x" 127}}`, "007f", tVal, true}, 386 {"printf float", `{{printf "%g" 3.5}}`, "3.5", tVal, true}, 387 {"printf complex", `{{printf "%g" 1+7i}}`, "(1+7i)", tVal, true}, 388 {"printf string", `{{printf "%s" "hello"}}`, "hello", tVal, true}, 389 {"printf function", `{{printf "%#q" zeroArgs}}`, "`zeroArgs`", tVal, true}, 390 {"printf field", `{{printf "%s" .U.V}}`, "v", tVal, true}, 391 {"printf method", `{{printf "%s" .Method0}}`, "M0", tVal, true}, 392 {"printf dot", `{{with .I}}{{printf "%d" .}}{{end}}`, "17", tVal, true}, 393 {"printf var", `{{with $x := .I}}{{printf "%d" $x}}{{end}}`, "17", tVal, true}, 394 {"printf lots", `{{printf "%d %s %g %s" 127 "hello" 7-3i .Method0}}`, "127 hello (7-3i) M0", tVal, true}, 395 396 // HTML. 397 {"html", `{{html "<script>alert(\"XSS\");</script>"}}`, 398 "<script>alert("XSS");</script>", nil, true}, 399 {"html pipeline", `{{printf "<script>alert(\"XSS\");</script>" | html}}`, 400 "<script>alert("XSS");</script>", nil, true}, 401 {"html", `{{html .PS}}`, "a string", tVal, true}, 402 403 // JavaScript. 404 {"js", `{{js .}}`, `It\'d be nice.`, `It'd be nice.`, true}, 405 406 // URL query. 407 {"urlquery", `{{"http://www.example.org/"|urlquery}}`, "http%3A%2F%2Fwww.example.org%2F", nil, true}, 408 409 // Booleans 410 {"not", "{{not true}} {{not false}}", "false true", nil, true}, 411 {"and", "{{and false 0}} {{and 1 0}} {{and 0 true}} {{and 1 1}}", "false 0 0 1", nil, true}, 412 {"or", "{{or 0 0}} {{or 1 0}} {{or 0 true}} {{or 1 1}}", "0 1 true 1", nil, true}, 413 {"boolean if", "{{if and true 1 `hi`}}TRUE{{else}}FALSE{{end}}", "TRUE", tVal, true}, 414 {"boolean if not", "{{if and true 1 `hi` | not}}TRUE{{else}}FALSE{{end}}", "FALSE", nil, true}, 415 416 // Indexing. 417 {"slice[0]", "{{index .SI 0}}", "3", tVal, true}, 418 {"slice[1]", "{{index .SI 1}}", "4", tVal, true}, 419 {"slice[HUGE]", "{{index .SI 10}}", "", tVal, false}, 420 {"slice[WRONG]", "{{index .SI `hello`}}", "", tVal, false}, 421 {"map[one]", "{{index .MSI `one`}}", "1", tVal, true}, 422 {"map[two]", "{{index .MSI `two`}}", "2", tVal, true}, 423 {"map[NO]", "{{index .MSI `XXX`}}", "0", tVal, true}, 424 {"map[nil]", "{{index .MSI nil}}", "0", tVal, true}, 425 {"map[WRONG]", "{{index .MSI 10}}", "", tVal, false}, 426 {"double index", "{{index .SMSI 1 `eleven`}}", "11", tVal, true}, 427 428 // Len. 429 {"slice", "{{len .SI}}", "3", tVal, true}, 430 {"map", "{{len .MSI }}", "3", tVal, true}, 431 {"len of int", "{{len 3}}", "", tVal, false}, 432 {"len of nothing", "{{len .Empty0}}", "", tVal, false}, 433 434 // With. 435 {"with true", "{{with true}}{{.}}{{end}}", "true", tVal, true}, 436 {"with false", "{{with false}}{{.}}{{else}}FALSE{{end}}", "FALSE", tVal, true}, 437 {"with 1", "{{with 1}}{{.}}{{else}}ZERO{{end}}", "1", tVal, true}, 438 {"with 0", "{{with 0}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true}, 439 {"with 1.5", "{{with 1.5}}{{.}}{{else}}ZERO{{end}}", "1.5", tVal, true}, 440 {"with 0.0", "{{with .FloatZero}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true}, 441 {"with 1.5i", "{{with 1.5i}}{{.}}{{else}}ZERO{{end}}", "(0+1.5i)", tVal, true}, 442 {"with 0.0i", "{{with .ComplexZero}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true}, 443 {"with emptystring", "{{with ``}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, 444 {"with string", "{{with `notempty`}}{{.}}{{else}}EMPTY{{end}}", "notempty", tVal, true}, 445 {"with emptyslice", "{{with .SIEmpty}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, 446 {"with slice", "{{with .SI}}{{.}}{{else}}EMPTY{{end}}", "[3 4 5]", tVal, true}, 447 {"with emptymap", "{{with .MSIEmpty}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, 448 {"with map", "{{with .MSIone}}{{.}}{{else}}EMPTY{{end}}", "map[one:1]", tVal, true}, 449 {"with empty interface, struct field", "{{with .Empty4}}{{.V}}{{end}}", "UinEmpty", tVal, true}, 450 {"with $x int", "{{with $x := .I}}{{$x}}{{end}}", "17", tVal, true}, 451 {"with $x struct.U.V", "{{with $x := $}}{{$x.U.V}}{{end}}", "v", tVal, true}, 452 {"with variable and action", "{{with $x := $}}{{$y := $.U.V}}{{$y}}{{end}}", "v", tVal, true}, 453 454 // Range. 455 {"range []int", "{{range .SI}}-{{.}}-{{end}}", "-3--4--5-", tVal, true}, 456 {"range empty no else", "{{range .SIEmpty}}-{{.}}-{{end}}", "", tVal, true}, 457 {"range []int else", "{{range .SI}}-{{.}}-{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true}, 458 {"range empty else", "{{range .SIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, 459 {"range []bool", "{{range .SB}}-{{.}}-{{end}}", "-true--false-", tVal, true}, 460 {"range []int method", "{{range .SI | .MAdd .I}}-{{.}}-{{end}}", "-20--21--22-", tVal, true}, 461 {"range map", "{{range .MSI}}-{{.}}-{{end}}", "-1--3--2-", tVal, true}, 462 {"range empty map no else", "{{range .MSIEmpty}}-{{.}}-{{end}}", "", tVal, true}, 463 {"range map else", "{{range .MSI}}-{{.}}-{{else}}EMPTY{{end}}", "-1--3--2-", tVal, true}, 464 {"range empty map else", "{{range .MSIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, 465 {"range empty interface", "{{range .Empty3}}-{{.}}-{{else}}EMPTY{{end}}", "-7--8-", tVal, true}, 466 {"range empty nil", "{{range .Empty0}}-{{.}}-{{end}}", "", tVal, true}, 467 {"range $x SI", "{{range $x := .SI}}<{{$x}}>{{end}}", "<3><4><5>", tVal, true}, 468 {"range $x $y SI", "{{range $x, $y := .SI}}<{{$x}}={{$y}}>{{end}}", "<0=3><1=4><2=5>", tVal, true}, 469 {"range $x MSIone", "{{range $x := .MSIone}}<{{$x}}>{{end}}", "<1>", tVal, true}, 470 {"range $x $y MSIone", "{{range $x, $y := .MSIone}}<{{$x}}={{$y}}>{{end}}", "<one=1>", tVal, true}, 471 {"range $x PSI", "{{range $x := .PSI}}<{{$x}}>{{end}}", "<21><22><23>", tVal, true}, 472 {"declare in range", "{{range $x := .PSI}}<{{$foo:=$x}}{{$x}}>{{end}}", "<21><22><23>", tVal, true}, 473 {"range count", `{{range $i, $x := count 5}}[{{$i}}]{{$x}}{{end}}`, "[0]a[1]b[2]c[3]d[4]e", tVal, true}, 474 {"range nil count", `{{range $i, $x := count 0}}{{else}}empty{{end}}`, "empty", tVal, true}, 475 476 // Cute examples. 477 {"or as if true", `{{or .SI "slice is empty"}}`, "[3 4 5]", tVal, true}, 478 {"or as if false", `{{or .SIEmpty "slice is empty"}}`, "slice is empty", tVal, true}, 479 480 // Error handling. 481 {"error method, error", "{{.MyError true}}", "", tVal, false}, 482 {"error method, no error", "{{.MyError false}}", "false", tVal, true}, 483 484 // Fixed bugs. 485 // Must separate dot and receiver; otherwise args are evaluated with dot set to variable. 486 {"bug0", "{{range .MSIone}}{{if $.Method1 .}}X{{end}}{{end}}", "X", tVal, true}, 487 // Do not loop endlessly in indirect for non-empty interfaces. 488 // The bug appears with *interface only; looped forever. 489 {"bug1", "{{.Method0}}", "M0", &iVal, true}, 490 // Was taking address of interface field, so method set was empty. 491 {"bug2", "{{$.NonEmptyInterface.Method0}}", "M0", tVal, true}, 492 // Struct values were not legal in with - mere oversight. 493 {"bug3", "{{with $}}{{.Method0}}{{end}}", "M0", tVal, true}, 494 // Nil interface values in if. 495 {"bug4", "{{if .Empty0}}non-nil{{else}}nil{{end}}", "nil", tVal, true}, 496 // Stringer. 497 {"bug5", "{{.Str}}", "foozle", tVal, true}, 498 {"bug5a", "{{.Err}}", "erroozle", tVal, true}, 499 // Args need to be indirected and dereferenced sometimes. 500 {"bug6a", "{{vfunc .V0 .V1}}", "vfunc", tVal, true}, 501 {"bug6b", "{{vfunc .V0 .V0}}", "vfunc", tVal, true}, 502 {"bug6c", "{{vfunc .V1 .V0}}", "vfunc", tVal, true}, 503 {"bug6d", "{{vfunc .V1 .V1}}", "vfunc", tVal, true}, 504 // Legal parse but illegal execution: non-function should have no arguments. 505 {"bug7a", "{{3 2}}", "", tVal, false}, 506 {"bug7b", "{{$x := 1}}{{$x 2}}", "", tVal, false}, 507 {"bug7c", "{{$x := 1}}{{3 | $x}}", "", tVal, false}, 508 // Pipelined arg was not being type-checked. 509 {"bug8a", "{{3|oneArg}}", "", tVal, false}, 510 {"bug8b", "{{4|dddArg 3}}", "", tVal, false}, 511 // A bug was introduced that broke map lookups for lower-case names. 512 {"bug9", "{{.cause}}", "neglect", map[string]string{"cause": "neglect"}, true}, 513 // Field chain starting with function did not work. 514 {"bug10", "{{mapOfThree.three}}-{{(mapOfThree).three}}", "3-3", 0, true}, 515 } 516 517 func zeroArgs() string { 518 return "zeroArgs" 519 } 520 521 func oneArg(a string) string { 522 return "oneArg=" + a 523 } 524 525 func dddArg(a int, b ...string) string { 526 return fmt.Sprintln(a, b) 527 } 528 529 // count returns a channel that will deliver n sequential 1-letter strings starting at "a" 530 func count(n int) chan string { 531 if n == 0 { 532 return nil 533 } 534 c := make(chan string) 535 go func() { 536 for i := 0; i < n; i++ { 537 c <- "abcdefghijklmnop"[i : i+1] 538 } 539 close(c) 540 }() 541 return c 542 } 543 544 // vfunc takes a *V and a V 545 func vfunc(V, *V) string { 546 return "vfunc" 547 } 548 549 func add(args ...int) int { 550 sum := 0 551 for _, x := range args { 552 sum += x 553 } 554 return sum 555 } 556 557 func echo(arg interface{}) interface{} { 558 return arg 559 } 560 561 func makemap(arg ...string) map[string]string { 562 if len(arg)%2 != 0 { 563 panic("bad makemap") 564 } 565 m := make(map[string]string) 566 for i := 0; i < len(arg); i += 2 { 567 m[arg[i]] = arg[i+1] 568 } 569 return m 570 } 571 572 func stringer(s fmt.Stringer) string { 573 return s.String() 574 } 575 576 func mapOfThree() interface{} { 577 return map[string]int{"three": 3} 578 } 579 580 func testExecute(execTests []execTest, template *Template, t *testing.T) { 581 b := new(bytes.Buffer) 582 funcs := FuncMap{ 583 "add": add, 584 "count": count, 585 "dddArg": dddArg, 586 "echo": echo, 587 "makemap": makemap, 588 "mapOfThree": mapOfThree, 589 "oneArg": oneArg, 590 "stringer": stringer, 591 "typeOf": typeOf, 592 "vfunc": vfunc, 593 "zeroArgs": zeroArgs, 594 } 595 for _, test := range execTests { 596 var tmpl *Template 597 var err error 598 if template == nil { 599 tmpl, err = New(test.name).Funcs(funcs).Parse(test.input) 600 } else { 601 tmpl, err = template.New(test.name).Funcs(funcs).Parse(test.input) 602 } 603 if err != nil { 604 t.Errorf("%s: parse error: %s", test.name, err) 605 continue 606 } 607 b.Reset() 608 err = tmpl.Execute(b, test.data) 609 switch { 610 case !test.ok && err == nil: 611 t.Errorf("%s: expected error; got none", test.name) 612 continue 613 case test.ok && err != nil: 614 t.Errorf("%s: unexpected execute error: %s", test.name, err) 615 continue 616 case !test.ok && err != nil: 617 // expected error, got one 618 if *debug { 619 fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err) 620 } 621 } 622 result := b.String() 623 if result != test.output { 624 t.Errorf("%s: expected\n\t%q\ngot\n\t%q", test.name, test.output, result) 625 } 626 } 627 } 628 629 func TestExecute(t *testing.T) { 630 testExecute(execTests, nil, t) 631 } 632 633 var delimPairs = []string{ 634 "", "", // default 635 "{{", "}}", // same as default 636 "<<", ">>", // distinct 637 "|", "|", // same 638 "(日)", "(本)", // peculiar 639 } 640 641 func TestDelims(t *testing.T) { 642 const hello = "Hello, world" 643 var value = struct{ Str string }{hello} 644 for i := 0; i < len(delimPairs); i += 2 { 645 text := ".Str" 646 left := delimPairs[i+0] 647 trueLeft := left 648 right := delimPairs[i+1] 649 trueRight := right 650 if left == "" { // default case 651 trueLeft = "{{" 652 } 653 if right == "" { // default case 654 trueRight = "}}" 655 } 656 text = trueLeft + text + trueRight 657 // Now add a comment 658 text += trueLeft + "/*comment*/" + trueRight 659 // Now add an action containing a string. 660 text += trueLeft + `"` + trueLeft + `"` + trueRight 661 // At this point text looks like `{{.Str}}{{/*comment*/}}{{"{{"}}`. 662 tmpl, err := New("delims").Delims(left, right).Parse(text) 663 if err != nil { 664 t.Fatalf("delim %q text %q parse err %s", left, text, err) 665 } 666 var b = new(bytes.Buffer) 667 err = tmpl.Execute(b, value) 668 if err != nil { 669 t.Fatalf("delim %q exec err %s", left, err) 670 } 671 if b.String() != hello+trueLeft { 672 t.Errorf("expected %q got %q", hello+trueLeft, b.String()) 673 } 674 } 675 } 676 677 // Check that an error from a method flows back to the top. 678 func TestExecuteError(t *testing.T) { 679 b := new(bytes.Buffer) 680 tmpl := New("error") 681 _, err := tmpl.Parse("{{.MyError true}}") 682 if err != nil { 683 t.Fatalf("parse error: %s", err) 684 } 685 err = tmpl.Execute(b, tVal) 686 if err == nil { 687 t.Errorf("expected error; got none") 688 } else if !strings.Contains(err.Error(), myError.Error()) { 689 if *debug { 690 fmt.Printf("test execute error: %s\n", err) 691 } 692 t.Errorf("expected myError; got %s", err) 693 } 694 } 695 696 const execErrorText = `line 1 697 line 2 698 line 3 699 {{template "one" .}} 700 {{define "one"}}{{template "two" .}}{{end}} 701 {{define "two"}}{{template "three" .}}{{end}} 702 {{define "three"}}{{index "hi" $}}{{end}}` 703 704 // Check that an error from a nested template contains all the relevant information. 705 func TestExecError(t *testing.T) { 706 tmpl, err := New("top").Parse(execErrorText) 707 if err != nil { 708 t.Fatal("parse error:", err) 709 } 710 var b bytes.Buffer 711 err = tmpl.Execute(&b, 5) // 5 is out of range indexing "hi" 712 if err == nil { 713 t.Fatal("expected error") 714 } 715 const want = `template: top:7:20: executing "three" at <index "hi" $>: error calling index: index out of range: 5` 716 got := err.Error() 717 if got != want { 718 t.Errorf("expected\n%q\ngot\n%q", want, got) 719 } 720 } 721 722 func TestJSEscaping(t *testing.T) { 723 testCases := []struct { 724 in, exp string 725 }{ 726 {`a`, `a`}, 727 {`'foo`, `\'foo`}, 728 {`Go "jump" \`, `Go \"jump\" \\`}, 729 {`Yukihiro says "今日は世界"`, `Yukihiro says \"今日は世界\"`}, 730 {"unprintable \uFDFF", `unprintable \uFDFF`}, 731 {`<html>`, `\x3Chtml\x3E`}, 732 } 733 for _, tc := range testCases { 734 s := JSEscapeString(tc.in) 735 if s != tc.exp { 736 t.Errorf("JS escaping [%s] got [%s] want [%s]", tc.in, s, tc.exp) 737 } 738 } 739 } 740 741 // A nice example: walk a binary tree. 742 743 type Tree struct { 744 Val int 745 Left, Right *Tree 746 } 747 748 // Use different delimiters to test Set.Delims. 749 const treeTemplate = ` 750 (define "tree") 751 [ 752 (.Val) 753 (with .Left) 754 (template "tree" .) 755 (end) 756 (with .Right) 757 (template "tree" .) 758 (end) 759 ] 760 (end) 761 ` 762 763 func TestTree(t *testing.T) { 764 var tree = &Tree{ 765 1, 766 &Tree{ 767 2, &Tree{ 768 3, 769 &Tree{ 770 4, nil, nil, 771 }, 772 nil, 773 }, 774 &Tree{ 775 5, 776 &Tree{ 777 6, nil, nil, 778 }, 779 nil, 780 }, 781 }, 782 &Tree{ 783 7, 784 &Tree{ 785 8, 786 &Tree{ 787 9, nil, nil, 788 }, 789 nil, 790 }, 791 &Tree{ 792 10, 793 &Tree{ 794 11, nil, nil, 795 }, 796 nil, 797 }, 798 }, 799 } 800 tmpl, err := New("root").Delims("(", ")").Parse(treeTemplate) 801 if err != nil { 802 t.Fatal("parse error:", err) 803 } 804 var b bytes.Buffer 805 stripSpace := func(r rune) rune { 806 if r == '\t' || r == '\n' { 807 return -1 808 } 809 return r 810 } 811 const expect = "[1[2[3[4]][5[6]]][7[8[9]][10[11]]]]" 812 // First by looking up the template. 813 err = tmpl.Lookup("tree").Execute(&b, tree) 814 if err != nil { 815 t.Fatal("exec error:", err) 816 } 817 result := strings.Map(stripSpace, b.String()) 818 if result != expect { 819 t.Errorf("expected %q got %q", expect, result) 820 } 821 // Then direct to execution. 822 b.Reset() 823 err = tmpl.ExecuteTemplate(&b, "tree", tree) 824 if err != nil { 825 t.Fatal("exec error:", err) 826 } 827 result = strings.Map(stripSpace, b.String()) 828 if result != expect { 829 t.Errorf("expected %q got %q", expect, result) 830 } 831 } 832 833 func TestExecuteOnNewTemplate(t *testing.T) { 834 // This is issue 3872. 835 _ = New("Name").Templates() 836 } 837 838 const testTemplates = `{{define "one"}}one{{end}}{{define "two"}}two{{end}}` 839 840 func TestMessageForExecuteEmpty(t *testing.T) { 841 // Test a truly empty template. 842 tmpl := New("empty") 843 var b bytes.Buffer 844 err := tmpl.Execute(&b, 0) 845 if err == nil { 846 t.Fatal("expected initial error") 847 } 848 got := err.Error() 849 want := `template: empty: "empty" is an incomplete or empty template` 850 if got != want { 851 t.Errorf("expected error %s got %s", want, got) 852 } 853 // Add a non-empty template to check that the error is helpful. 854 tests, err := New("").Parse(testTemplates) 855 if err != nil { 856 t.Fatal(err) 857 } 858 tmpl.AddParseTree("secondary", tests.Tree) 859 err = tmpl.Execute(&b, 0) 860 if err == nil { 861 t.Fatal("expected second error") 862 } 863 got = err.Error() 864 want = `template: empty: "empty" is an incomplete or empty template; defined templates are: "secondary"` 865 if got != want { 866 t.Errorf("expected error %s got %s", want, got) 867 } 868 // Make sure we can execute the secondary. 869 err = tmpl.ExecuteTemplate(&b, "secondary", 0) 870 if err != nil { 871 t.Fatal(err) 872 } 873 } 874 875 type cmpTest struct { 876 expr string 877 truth string 878 ok bool 879 } 880 881 var cmpTests = []cmpTest{ 882 {"eq true true", "true", true}, 883 {"eq true false", "false", true}, 884 {"eq 1+2i 1+2i", "true", true}, 885 {"eq 1+2i 1+3i", "false", true}, 886 {"eq 1.5 1.5", "true", true}, 887 {"eq 1.5 2.5", "false", true}, 888 {"eq 1 1", "true", true}, 889 {"eq 1 2", "false", true}, 890 {"eq `xy` `xy`", "true", true}, 891 {"eq `xy` `xyz`", "false", true}, 892 {"eq .Xuint .Xuint", "true", true}, 893 {"eq .Xuint .Yuint", "false", true}, 894 {"eq 3 4 5 6 3", "true", true}, 895 {"eq 3 4 5 6 7", "false", true}, 896 {"ne true true", "false", true}, 897 {"ne true false", "true", true}, 898 {"ne 1+2i 1+2i", "false", true}, 899 {"ne 1+2i 1+3i", "true", true}, 900 {"ne 1.5 1.5", "false", true}, 901 {"ne 1.5 2.5", "true", true}, 902 {"ne 1 1", "false", true}, 903 {"ne 1 2", "true", true}, 904 {"ne `xy` `xy`", "false", true}, 905 {"ne `xy` `xyz`", "true", true}, 906 {"ne .Xuint .Xuint", "false", true}, 907 {"ne .Xuint .Yuint", "true", true}, 908 {"lt 1.5 1.5", "false", true}, 909 {"lt 1.5 2.5", "true", true}, 910 {"lt 1 1", "false", true}, 911 {"lt 1 2", "true", true}, 912 {"lt `xy` `xy`", "false", true}, 913 {"lt `xy` `xyz`", "true", true}, 914 {"lt .Xuint .Xuint", "false", true}, 915 {"lt .Xuint .Yuint", "true", true}, 916 {"le 1.5 1.5", "true", true}, 917 {"le 1.5 2.5", "true", true}, 918 {"le 2.5 1.5", "false", true}, 919 {"le 1 1", "true", true}, 920 {"le 1 2", "true", true}, 921 {"le 2 1", "false", true}, 922 {"le `xy` `xy`", "true", true}, 923 {"le `xy` `xyz`", "true", true}, 924 {"le `xyz` `xy`", "false", true}, 925 {"le .Xuint .Xuint", "true", true}, 926 {"le .Xuint .Yuint", "true", true}, 927 {"le .Yuint .Xuint", "false", true}, 928 {"gt 1.5 1.5", "false", true}, 929 {"gt 1.5 2.5", "false", true}, 930 {"gt 1 1", "false", true}, 931 {"gt 2 1", "true", true}, 932 {"gt 1 2", "false", true}, 933 {"gt `xy` `xy`", "false", true}, 934 {"gt `xy` `xyz`", "false", true}, 935 {"gt .Xuint .Xuint", "false", true}, 936 {"gt .Xuint .Yuint", "false", true}, 937 {"gt .Yuint .Xuint", "true", true}, 938 {"ge 1.5 1.5", "true", true}, 939 {"ge 1.5 2.5", "false", true}, 940 {"ge 2.5 1.5", "true", true}, 941 {"ge 1 1", "true", true}, 942 {"ge 1 2", "false", true}, 943 {"ge 2 1", "true", true}, 944 {"ge `xy` `xy`", "true", true}, 945 {"ge `xy` `xyz`", "false", true}, 946 {"ge `xyz` `xy`", "true", true}, 947 {"ge .Xuint .Xuint", "true", true}, 948 {"ge .Xuint .Yuint", "false", true}, 949 {"ge .Yuint .Xuint", "true", true}, 950 // Errors 951 {"eq `xy` 1", "", false}, // Different types. 952 {"lt true true", "", false}, // Unordered types. 953 {"lt 1+0i 1+0i", "", false}, // Unordered types. 954 } 955 956 func TestComparison(t *testing.T) { 957 b := new(bytes.Buffer) 958 var cmpStruct = struct { 959 Xuint, Yuint uint 960 }{3, 4} 961 for _, test := range cmpTests { 962 text := fmt.Sprintf("{{if %s}}true{{else}}false{{end}}", test.expr) 963 tmpl, err := New("empty").Parse(text) 964 if err != nil { 965 t.Fatal(err) 966 } 967 b.Reset() 968 err = tmpl.Execute(b, &cmpStruct) 969 if test.ok && err != nil { 970 t.Errorf("%s errored incorrectly: %s", test.expr, err) 971 continue 972 } 973 if !test.ok && err == nil { 974 t.Errorf("%s did not error", test.expr) 975 continue 976 } 977 if b.String() != test.truth { 978 t.Errorf("%s: want %s; got %s", test.expr, test.truth, b.String()) 979 } 980 } 981 }