125
128
BinaryFunc: func(a, b string) string { return fmt.Sprintf("[%s=%s]", a, b) },
126
129
VariadicFunc: func(s ...string) string { return fmt.Sprint("<", strings.Join(s, "+"), ">") },
127
130
VariadicFuncInt: func(a int, s ...string) string { return fmt.Sprint(a, "=<", strings.Join(s, "+"), ">") },
131
NilOKFunc: func(s *int) bool { return s == nil },
128
132
Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X
220
224
// Trivial cases.
221
225
{"empty", "", "", nil, true},
222
226
{"text", "some text", "some text", nil, true},
227
{"nil action", "{{nil}}", "", nil, false},
224
229
// Ideal constants.
225
230
{"ideal int", "{{typeOf 3}}", "int", 0, true},
228
233
{"ideal complex", "{{typeOf 1i}}", "complex128", 0, true},
229
234
{"ideal int", "{{typeOf " + bigInt + "}}", "int", 0, true},
230
235
{"ideal too big", "{{typeOf " + bigUint + "}}", "", 0, false},
236
{"ideal nil without type", "{{nil}}", "", 0, false},
232
238
// Fields of structs.
233
239
{".X", "-{{.X}}-", "-x-", tVal, true},
234
240
{".U.V", "-{{.U.V}}-", "-v-", tVal, true},
241
{".unexported", "{{.unexported}}", "", tVal, false},
236
243
// Fields on maps.
237
244
{"map .one", "{{.MSI.one}}", "1", tVal, true},
292
299
{".Method2(3, .X)", "-{{.Method2 3 .X}}-", "-Method2: 3 x-", tVal, true},
293
300
{".Method2(.U16, `str`)", "-{{.Method2 .U16 `str`}}-", "-Method2: 16 str-", tVal, true},
294
301
{".Method2(.U16, $x)", "{{if $x := .X}}-{{.Method2 .U16 $x}}{{end}}-", "-Method2: 16 x-", tVal, true},
295
{".Method3(nil)", "-{{.Method3 .MXI.unset}}-", "-Method3: <nil>-", tVal, true},
302
{".Method3(nil constant)", "-{{.Method3 nil}}-", "-Method3: <nil>-", tVal, true},
303
{".Method3(nil value)", "-{{.Method3 .MXI.unset}}-", "-Method3: <nil>-", tVal, true},
296
304
{"method on var", "{{if $x := .}}-{{$x.Method2 .U16 $x.X}}{{end}}-", "-Method2: 16 x-", tVal, true},
297
305
{"method on chained var",
298
306
"{{range .MSIone}}{{if $.U.TrueFalse $.True}}{{$.U.TrueFalse $.True}}{{else}}WRONG{{end}}{{end}}",
303
311
{"chained method on variable",
304
312
"{{with $x := .}}{{with .SI}}{{$.GetU.TrueFalse $.True}}{{end}}{{end}}",
305
313
"true", tVal, true},
314
{".NilOKFunc not nil", "{{call .NilOKFunc .PI}}", "false", tVal, true},
315
{".NilOKFunc nil", "{{call .NilOKFunc nil}}", "true", tVal, true},
307
317
// Function call builtin.
308
318
{".BinaryFunc", "{{call .BinaryFunc `1` `2`}}", "[1=2]", tVal, true},
321
331
{".VariadicFuncBad0", "{{call .VariadicFunc 3}}", "", tVal, false},
322
332
{".VariadicFuncIntBad0", "{{call .VariadicFuncInt}}", "", tVal, false},
323
333
{".VariadicFuncIntBad`", "{{call .VariadicFuncInt `x`}}", "", tVal, false},
334
{".VariadicFuncNilBad", "{{call .VariadicFunc nil}}", "", tVal, false},
326
337
{"pipeline", "-{{.Method0 | .Method2 .U16}}-", "-Method2: 16 M0-", tVal, true},
327
338
{"pipeline func", "-{{call .VariadicFunc `llo` | call .VariadicFunc `he` }}-", "-<he+<llo>>-", tVal, true},
340
// Parenthesized expressions
341
{"parens in pipeline", "{{printf `%d %d %d` (1) (2 | add 3) (add 4 (add 5 6))}}", "1 5 15", tVal, true},
343
// Parenthesized expressions with field accesses
344
{"parens: $ in paren", "{{($).X}}", "x", tVal, true},
345
{"parens: $.GetU in paren", "{{($.GetU).V}}", "v", tVal, true},
346
{"parens: $ in paren in pipe", "{{($ | echo).X}}", "x", tVal, true},
347
{"parens: spaces and args", `{{(makemap "up" "down" "left" "right").left}}`, "right", tVal, true},
330
350
{"if true", "{{if true}}TRUE{{end}}", "TRUE", tVal, true},
331
351
{"if false", "{{if false}}TRUE{{else}}FALSE{{end}}", "FALSE", tVal, true},
352
{"if nil", "{{if nil}}TRUE{{end}}", "", tVal, false},
332
353
{"if 1", "{{if 1}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true},
333
354
{"if 0", "{{if 0}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true},
334
355
{"if 1.5", "{{if 1.5}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true},
350
371
{"print", `{{print "hello, print"}}`, "hello, print", tVal, true},
351
{"print", `{{print 1 2 3}}`, "1 2 3", tVal, true},
372
{"print 123", `{{print 1 2 3}}`, "1 2 3", tVal, true},
373
{"print nil", `{{print nil}}`, "<nil>", tVal, true},
352
374
{"println", `{{println 1 2 3}}`, "1 2 3\n", tVal, true},
353
375
{"printf int", `{{printf "%04x" 127}}`, "007f", tVal, true},
354
376
{"printf float", `{{printf "%g" 3.5}}`, "3.5", tVal, true},
387
409
{"slice[WRONG]", "{{index .SI `hello`}}", "", tVal, false},
388
410
{"map[one]", "{{index .MSI `one`}}", "1", tVal, true},
389
411
{"map[two]", "{{index .MSI `two`}}", "2", tVal, true},
390
{"map[NO]", "{{index .MSI `XXX`}}", "", tVal, true},
412
{"map[NO]", "{{index .MSI `XXX`}}", "0", tVal, true},
413
{"map[nil]", "{{index .MSI nil}}", "0", tVal, true},
391
414
{"map[WRONG]", "{{index .MSI 10}}", "", tVal, false},
392
415
{"double index", "{{index .SMSI 1 `eleven`}}", "11", tVal, true},
474
497
// Pipelined arg was not being type-checked.
475
498
{"bug8a", "{{3|oneArg}}", "", tVal, false},
476
499
{"bug8b", "{{4|dddArg 3}}", "", tVal, false},
500
// A bug was introduced that broke map lookups for lower-case names.
501
{"bug9", "{{.cause}}", "neglect", map[string]string{"cause": "neglect"}, true},
502
// Field chain starting with function did not work.
503
{"bug10", "{{mapOfThree.three}}-{{(mapOfThree).three}}", "3-3", 0, true},
479
506
func zeroArgs() string {
538
func add(args ...int) int {
540
for _, x := range args {
546
func echo(arg interface{}) interface{} {
550
func makemap(arg ...string) map[string]string {
554
m := make(map[string]string)
555
for i := 0; i < len(arg); i += 2 {
511
561
func stringer(s fmt.Stringer) string {
512
562
return s.String()
565
func mapOfThree() interface{} {
566
return map[string]int{"three": 3}
515
569
func testExecute(execTests []execTest, template *Template, t *testing.T) {
516
570
b := new(bytes.Buffer)
517
571
funcs := FuncMap{
523
"zeroArgs": zeroArgs,
524
"stringer": stringer,
577
"mapOfThree": mapOfThree,
579
"stringer": stringer,
582
"zeroArgs": zeroArgs,
526
584
for _, test := range execTests {
527
585
var tmpl *Template
685
const execErrorText = `line 1
689
{{define "one"}}{{template "two" .}}{{end}}
690
{{define "two"}}{{template "three" .}}{{end}}
691
{{define "three"}}{{index "hi" $}}{{end}}`
693
// Check that an error from a nested template contains all the relevant information.
694
func TestExecError(t *testing.T) {
695
tmpl, err := New("top").Parse(execErrorText)
697
t.Fatal("parse error:", err)
700
err = tmpl.Execute(&b, 5) // 5 is out of range indexing "hi"
702
t.Fatal("expected error")
704
const want = `template: top:7:20: executing "three" at <index "hi" $>: error calling index: index out of range: 5`
707
t.Errorf("expected\n%q\ngot\n%q", want, got)
627
711
func TestJSEscaping(t *testing.T) {
628
712
testCases := []struct {
734
818
t.Errorf("expected %q got %q", expect, result)
822
func TestExecuteOnNewTemplate(t *testing.T) {
823
// This is issue 3872.
824
_ = New("Name").Templates()
827
const testTemplates = `{{define "one"}}one{{end}}{{define "two"}}two{{end}}`
829
func TestMessageForExecuteEmpty(t *testing.T) {
830
// Test a truly empty template.
833
err := tmpl.Execute(&b, 0)
835
t.Fatal("expected initial error")
838
want := `template: empty: "empty" is an incomplete or empty template`
840
t.Errorf("expected error %s got %s", want, got)
842
// Add a non-empty template to check that the error is helpful.
843
tests, err := New("").Parse(testTemplates)
847
tmpl.AddParseTree("secondary", tests.Tree)
848
err = tmpl.Execute(&b, 0)
850
t.Fatal("expected second error")
853
want = `template: empty: "empty" is an incomplete or empty template; defined templates are: "secondary"`
855
t.Errorf("expected error %s got %s", want, got)
857
// Make sure we can execute the secondary.
858
err = tmpl.ExecuteTemplate(&b, "secondary", 0)