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 }