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 5package template 6 7// Tests for multiple-template parsing and execution. 8 9import ( 10 "fmt" 11 "os" 12 "strings" 13 "testing" 14 "text/template/parse" 15) 16 17const ( 18 noError = true 19 hasError = false 20) 21 22type multiParseTest struct { 23 name string 24 input string 25 ok bool 26 names []string 27 results []string 28} 29 30var multiParseTests = []multiParseTest{ 31 {"empty", "", noError, 32 nil, 33 nil}, 34 {"one", `{{define "foo"}} FOO {{end}}`, noError, 35 []string{"foo"}, 36 []string{" FOO "}}, 37 {"two", `{{define "foo"}} FOO {{end}}{{define "bar"}} BAR {{end}}`, noError, 38 []string{"foo", "bar"}, 39 []string{" FOO ", " BAR "}}, 40 // errors 41 {"missing end", `{{define "foo"}} FOO `, hasError, 42 nil, 43 nil}, 44 {"malformed name", `{{define "foo}} FOO `, hasError, 45 nil, 46 nil}, 47} 48 49func TestMultiParse(t *testing.T) { 50 for _, test := range multiParseTests { 51 template, err := New("root").Parse(test.input) 52 switch { 53 case err == nil && !test.ok: 54 t.Errorf("%q: expected error; got none", test.name) 55 continue 56 case err != nil && test.ok: 57 t.Errorf("%q: unexpected error: %v", test.name, err) 58 continue 59 case err != nil && !test.ok: 60 // expected error, got one 61 if *debug { 62 fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err) 63 } 64 continue 65 } 66 if template == nil { 67 continue 68 } 69 if len(template.tmpl) != len(test.names)+1 { // +1 for root 70 t.Errorf("%s: wrong number of templates; wanted %d got %d", test.name, len(test.names), len(template.tmpl)) 71 continue 72 } 73 for i, name := range test.names { 74 tmpl, ok := template.tmpl[name] 75 if !ok { 76 t.Errorf("%s: can't find template %q", test.name, name) 77 continue 78 } 79 result := tmpl.Root.String() 80 if result != test.results[i] { 81 t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.results[i]) 82 } 83 } 84 } 85} 86 87var multiExecTests = []execTest{ 88 {"empty", "", "", nil, true}, 89 {"text", "some text", "some text", nil, true}, 90 {"invoke x", `{{template "x" .SI}}`, "TEXT", tVal, true}, 91 {"invoke x no args", `{{template "x"}}`, "TEXT", tVal, true}, 92 {"invoke dot int", `{{template "dot" .I}}`, "17", tVal, true}, 93 {"invoke dot []int", `{{template "dot" .SI}}`, "[3 4 5]", tVal, true}, 94 {"invoke dotV", `{{template "dotV" .U}}`, "v", tVal, true}, 95 {"invoke nested int", `{{template "nested" .I}}`, "17", tVal, true}, 96 {"variable declared by template", `{{template "nested" $x:=.SI}},{{index $x 1}}`, "[3 4 5],4", tVal, true}, 97 98 // User-defined function: test argument evaluator. 99 {"testFunc literal", `{{oneArg "joe"}}`, "oneArg=joe", tVal, true}, 100 {"testFunc .", `{{oneArg .}}`, "oneArg=joe", "joe", true}, 101} 102 103// These strings are also in testdata/*. 104const multiText1 = ` 105 {{define "x"}}TEXT{{end}} 106 {{define "dotV"}}{{.V}}{{end}} 107` 108 109const multiText2 = ` 110 {{define "dot"}}{{.}}{{end}} 111 {{define "nested"}}{{template "dot" .}}{{end}} 112` 113 114func TestMultiExecute(t *testing.T) { 115 // Declare a couple of templates first. 116 template, err := New("root").Parse(multiText1) 117 if err != nil { 118 t.Fatalf("parse error for 1: %s", err) 119 } 120 _, err = template.Parse(multiText2) 121 if err != nil { 122 t.Fatalf("parse error for 2: %s", err) 123 } 124 testExecute(multiExecTests, template, t) 125} 126 127func TestParseFiles(t *testing.T) { 128 _, err := ParseFiles("DOES NOT EXIST") 129 if err == nil { 130 t.Error("expected error for non-existent file; got none") 131 } 132 template := New("root") 133 _, err = template.ParseFiles("testdata/file1.tmpl", "testdata/file2.tmpl") 134 if err != nil { 135 t.Fatalf("error parsing files: %v", err) 136 } 137 testExecute(multiExecTests, template, t) 138} 139 140func TestParseGlob(t *testing.T) { 141 _, err := ParseGlob("DOES NOT EXIST") 142 if err == nil { 143 t.Error("expected error for non-existent file; got none") 144 } 145 _, err = New("error").ParseGlob("[x") 146 if err == nil { 147 t.Error("expected error for bad pattern; got none") 148 } 149 template := New("root") 150 _, err = template.ParseGlob("testdata/file*.tmpl") 151 if err != nil { 152 t.Fatalf("error parsing files: %v", err) 153 } 154 testExecute(multiExecTests, template, t) 155} 156 157func TestParseFS(t *testing.T) { 158 fs := os.DirFS("testdata") 159 160 { 161 _, err := ParseFS(fs, "DOES NOT EXIST") 162 if err == nil { 163 t.Error("expected error for non-existent file; got none") 164 } 165 } 166 167 { 168 template := New("root") 169 _, err := template.ParseFS(fs, "file1.tmpl", "file2.tmpl") 170 if err != nil { 171 t.Fatalf("error parsing files: %v", err) 172 } 173 testExecute(multiExecTests, template, t) 174 } 175 176 { 177 template := New("root") 178 _, err := template.ParseFS(fs, "file*.tmpl") 179 if err != nil { 180 t.Fatalf("error parsing files: %v", err) 181 } 182 testExecute(multiExecTests, template, t) 183 } 184} 185 186// In these tests, actual content (not just template definitions) comes from the parsed files. 187 188var templateFileExecTests = []execTest{ 189 {"test", `{{template "tmpl1.tmpl"}}{{template "tmpl2.tmpl"}}`, "template1\n\ny\ntemplate2\n\nx\n", 0, true}, 190} 191 192func TestParseFilesWithData(t *testing.T) { 193 template, err := New("root").ParseFiles("testdata/tmpl1.tmpl", "testdata/tmpl2.tmpl") 194 if err != nil { 195 t.Fatalf("error parsing files: %v", err) 196 } 197 testExecute(templateFileExecTests, template, t) 198} 199 200func TestParseGlobWithData(t *testing.T) { 201 template, err := New("root").ParseGlob("testdata/tmpl*.tmpl") 202 if err != nil { 203 t.Fatalf("error parsing files: %v", err) 204 } 205 testExecute(templateFileExecTests, template, t) 206} 207 208const ( 209 cloneText1 = `{{define "a"}}{{template "b"}}{{template "c"}}{{end}}` 210 cloneText2 = `{{define "b"}}b{{end}}` 211 cloneText3 = `{{define "c"}}root{{end}}` 212 cloneText4 = `{{define "c"}}clone{{end}}` 213) 214 215func TestClone(t *testing.T) { 216 // Create some templates and clone the root. 217 root, err := New("root").Parse(cloneText1) 218 if err != nil { 219 t.Fatal(err) 220 } 221 _, err = root.Parse(cloneText2) 222 if err != nil { 223 t.Fatal(err) 224 } 225 clone := Must(root.Clone()) 226 // Add variants to both. 227 _, err = root.Parse(cloneText3) 228 if err != nil { 229 t.Fatal(err) 230 } 231 _, err = clone.Parse(cloneText4) 232 if err != nil { 233 t.Fatal(err) 234 } 235 // Verify that the clone is self-consistent. 236 for k, v := range clone.tmpl { 237 if k == clone.name && v.tmpl[k] != clone { 238 t.Error("clone does not contain root") 239 } 240 if v != v.tmpl[v.name] { 241 t.Errorf("clone does not contain self for %q", k) 242 } 243 } 244 // Execute root. 245 var b strings.Builder 246 err = root.ExecuteTemplate(&b, "a", 0) 247 if err != nil { 248 t.Fatal(err) 249 } 250 if b.String() != "broot" { 251 t.Errorf("expected %q got %q", "broot", b.String()) 252 } 253 // Execute copy. 254 b.Reset() 255 err = clone.ExecuteTemplate(&b, "a", 0) 256 if err != nil { 257 t.Fatal(err) 258 } 259 if b.String() != "bclone" { 260 t.Errorf("expected %q got %q", "bclone", b.String()) 261 } 262} 263 264func TestAddParseTree(t *testing.T) { 265 // Create some templates. 266 root, err := New("root").Parse(cloneText1) 267 if err != nil { 268 t.Fatal(err) 269 } 270 _, err = root.Parse(cloneText2) 271 if err != nil { 272 t.Fatal(err) 273 } 274 // Add a new parse tree. 275 tree, err := parse.Parse("cloneText3", cloneText3, "", "", nil, builtins()) 276 if err != nil { 277 t.Fatal(err) 278 } 279 added, err := root.AddParseTree("c", tree["c"]) 280 if err != nil { 281 t.Fatal(err) 282 } 283 // Execute. 284 var b strings.Builder 285 err = added.ExecuteTemplate(&b, "a", 0) 286 if err != nil { 287 t.Fatal(err) 288 } 289 if b.String() != "broot" { 290 t.Errorf("expected %q got %q", "broot", b.String()) 291 } 292} 293 294// Issue 7032 295func TestAddParseTreeToUnparsedTemplate(t *testing.T) { 296 master := "{{define \"master\"}}{{end}}" 297 tmpl := New("master") 298 tree, err := parse.Parse("master", master, "", "", nil) 299 if err != nil { 300 t.Fatalf("unexpected parse err: %v", err) 301 } 302 masterTree := tree["master"] 303 tmpl.AddParseTree("master", masterTree) // used to panic 304} 305 306func TestRedefinition(t *testing.T) { 307 var tmpl *Template 308 var err error 309 if tmpl, err = New("tmpl1").Parse(`{{define "test"}}foo{{end}}`); err != nil { 310 t.Fatalf("parse 1: %v", err) 311 } 312 if _, err = tmpl.Parse(`{{define "test"}}bar{{end}}`); err != nil { 313 t.Fatalf("got error %v, expected nil", err) 314 } 315 if _, err = tmpl.New("tmpl2").Parse(`{{define "test"}}bar{{end}}`); err != nil { 316 t.Fatalf("got error %v, expected nil", err) 317 } 318} 319 320// Issue 10879 321func TestEmptyTemplateCloneCrash(t *testing.T) { 322 t1 := New("base") 323 t1.Clone() // used to panic 324} 325 326// Issue 10910, 10926 327func TestTemplateLookUp(t *testing.T) { 328 t1 := New("foo") 329 if t1.Lookup("foo") != nil { 330 t.Error("Lookup returned non-nil value for undefined template foo") 331 } 332 t1.New("bar") 333 if t1.Lookup("bar") != nil { 334 t.Error("Lookup returned non-nil value for undefined template bar") 335 } 336 t1.Parse(`{{define "foo"}}test{{end}}`) 337 if t1.Lookup("foo") == nil { 338 t.Error("Lookup returned nil value for defined template") 339 } 340} 341 342func TestNew(t *testing.T) { 343 // template with same name already exists 344 t1, _ := New("test").Parse(`{{define "test"}}foo{{end}}`) 345 t2 := t1.New("test") 346 347 if t1.common != t2.common { 348 t.Errorf("t1 & t2 didn't share common struct; got %v != %v", t1.common, t2.common) 349 } 350 if t1.Tree == nil { 351 t.Error("defined template got nil Tree") 352 } 353 if t2.Tree != nil { 354 t.Error("undefined template got non-nil Tree") 355 } 356 357 containsT1 := false 358 for _, tmpl := range t1.Templates() { 359 if tmpl == t2 { 360 t.Error("Templates included undefined template") 361 } 362 if tmpl == t1 { 363 containsT1 = true 364 } 365 } 366 if !containsT1 { 367 t.Error("Templates didn't include defined template") 368 } 369} 370 371func TestParse(t *testing.T) { 372 // In multiple calls to Parse with the same receiver template, only one call 373 // can contain text other than space, comments, and template definitions 374 t1 := New("test") 375 if _, err := t1.Parse(`{{define "test"}}{{end}}`); err != nil { 376 t.Fatalf("parsing test: %s", err) 377 } 378 if _, err := t1.Parse(`{{define "test"}}{{/* this is a comment */}}{{end}}`); err != nil { 379 t.Fatalf("parsing test: %s", err) 380 } 381 if _, err := t1.Parse(`{{define "test"}}foo{{end}}`); err != nil { 382 t.Fatalf("parsing test: %s", err) 383 } 384} 385 386func TestEmptyTemplate(t *testing.T) { 387 cases := []struct { 388 defn []string 389 in string 390 want string 391 }{ 392 {[]string{"x", "y"}, "", "y"}, 393 {[]string{""}, "once", ""}, 394 {[]string{"", ""}, "twice", ""}, 395 {[]string{"{{.}}", "{{.}}"}, "twice", "twice"}, 396 {[]string{"{{/* a comment */}}", "{{/* a comment */}}"}, "comment", ""}, 397 {[]string{"{{.}}", ""}, "twice", ""}, 398 } 399 400 for i, c := range cases { 401 root := New("root") 402 403 var ( 404 m *Template 405 err error 406 ) 407 for _, d := range c.defn { 408 m, err = root.New(c.in).Parse(d) 409 if err != nil { 410 t.Fatal(err) 411 } 412 } 413 buf := &strings.Builder{} 414 if err := m.Execute(buf, c.in); err != nil { 415 t.Error(i, err) 416 continue 417 } 418 if buf.String() != c.want { 419 t.Errorf("expected string %q: got %q", c.want, buf.String()) 420 } 421 } 422} 423 424// Issue 19249 was a regression in 1.8 caused by the handling of empty 425// templates added in that release, which got different answers depending 426// on the order templates appeared in the internal map. 427func TestIssue19294(t *testing.T) { 428 // The empty block in "xhtml" should be replaced during execution 429 // by the contents of "stylesheet", but if the internal map associating 430 // names with templates is built in the wrong order, the empty block 431 // looks non-empty and this doesn't happen. 432 var inlined = map[string]string{ 433 "stylesheet": `{{define "stylesheet"}}stylesheet{{end}}`, 434 "xhtml": `{{block "stylesheet" .}}{{end}}`, 435 } 436 all := []string{"stylesheet", "xhtml"} 437 for i := 0; i < 100; i++ { 438 res, err := New("title.xhtml").Parse(`{{template "xhtml" .}}`) 439 if err != nil { 440 t.Fatal(err) 441 } 442 for _, name := range all { 443 _, err := res.New(name).Parse(inlined[name]) 444 if err != nil { 445 t.Fatal(err) 446 } 447 } 448 var buf strings.Builder 449 res.Execute(&buf, 0) 450 if buf.String() != "stylesheet" { 451 t.Fatalf("iteration %d: got %q; expected %q", i, buf.String(), "stylesheet") 452 } 453 } 454} 455 456// Issue 48436 457func TestAddToZeroTemplate(t *testing.T) { 458 tree, err := parse.Parse("c", cloneText3, "", "", nil, builtins()) 459 if err != nil { 460 t.Fatal(err) 461 } 462 var tmpl Template 463 tmpl.AddParseTree("x", tree["c"]) 464} 465