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