1
2
3
4
5 package template
6
7
8
9 import (
10 "fmt"
11 "os"
12 "strings"
13 "testing"
14 "text/template/parse"
15 )
16
17 const (
18 noError = true
19 hasError = false
20 )
21
22 type multiParseTest struct {
23 name string
24 input string
25 ok bool
26 names []string
27 results []string
28 }
29
30 var 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
41 {"missing end", `{{define "foo"}} FOO `, hasError,
42 nil,
43 nil},
44 {"malformed name", `{{define "foo}} FOO `, hasError,
45 nil,
46 nil},
47 }
48
49 func 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
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 {
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
87 var 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
99 {"testFunc literal", `{{oneArg "joe"}}`, "oneArg=joe", tVal, true},
100 {"testFunc .", `{{oneArg .}}`, "oneArg=joe", "joe", true},
101 }
102
103
104 const multiText1 = `
105 {{define "x"}}TEXT{{end}}
106 {{define "dotV"}}{{.V}}{{end}}
107 `
108
109 const multiText2 = `
110 {{define "dot"}}{{.}}{{end}}
111 {{define "nested"}}{{template "dot" .}}{{end}}
112 `
113
114 func TestMultiExecute(t *testing.T) {
115
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
127 func 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
140 func 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
157 func 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
187
188 var templateFileExecTests = []execTest{
189 {"test", `{{template "tmpl1.tmpl"}}{{template "tmpl2.tmpl"}}`, "template1\n\ny\ntemplate2\n\nx\n", 0, true},
190 }
191
192 func 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
200 func 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
208 const (
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 cloneText5 = `{{define "e"}}{{.Foo}}{{end}}`
214 )
215
216 func TestClone(t *testing.T) {
217
218 root, err := New("root").Parse(cloneText1)
219 if err != nil {
220 t.Fatal(err)
221 }
222 _, err = root.Parse(cloneText2)
223 if err != nil {
224 t.Fatal(err)
225 }
226 root.Parse(cloneText5)
227 root.Option("missingkey=error")
228 clone := Must(root.Clone())
229
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
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
248 var b strings.Builder
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
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 b.Reset()
266 rootErr := root.ExecuteTemplate(&b, "e", map[string]any{})
267 cloneErr := clone.ExecuteTemplate(&b, "e", map[string]any{})
268 if cloneErr == nil {
269 t.Errorf("expected error from missing key in cloned template")
270 } else if got, want := cloneErr.Error(), rootErr.Error(); got != want {
271 t.Errorf("got %q, wan t %q", got, want)
272 }
273 }
274
275 func TestAddParseTree(t *testing.T) {
276
277 root, err := New("root").Parse(cloneText1)
278 if err != nil {
279 t.Fatal(err)
280 }
281 _, err = root.Parse(cloneText2)
282 if err != nil {
283 t.Fatal(err)
284 }
285
286 tree, err := parse.Parse("cloneText3", cloneText3, "", "", nil, builtins())
287 if err != nil {
288 t.Fatal(err)
289 }
290 added, err := root.AddParseTree("c", tree["c"])
291 if err != nil {
292 t.Fatal(err)
293 }
294
295 var b strings.Builder
296 err = added.ExecuteTemplate(&b, "a", 0)
297 if err != nil {
298 t.Fatal(err)
299 }
300 if b.String() != "broot" {
301 t.Errorf("expected %q got %q", "broot", b.String())
302 }
303 }
304
305
306 func TestAddParseTreeToUnparsedTemplate(t *testing.T) {
307 master := "{{define \"master\"}}{{end}}"
308 tmpl := New("master")
309 tree, err := parse.Parse("master", master, "", "", nil)
310 if err != nil {
311 t.Fatalf("unexpected parse err: %v", err)
312 }
313 masterTree := tree["master"]
314 tmpl.AddParseTree("master", masterTree)
315 }
316
317 func TestRedefinition(t *testing.T) {
318 var tmpl *Template
319 var err error
320 if tmpl, err = New("tmpl1").Parse(`{{define "test"}}foo{{end}}`); err != nil {
321 t.Fatalf("parse 1: %v", err)
322 }
323 if _, err = tmpl.Parse(`{{define "test"}}bar{{end}}`); err != nil {
324 t.Fatalf("got error %v, expected nil", err)
325 }
326 if _, err = tmpl.New("tmpl2").Parse(`{{define "test"}}bar{{end}}`); err != nil {
327 t.Fatalf("got error %v, expected nil", err)
328 }
329 }
330
331
332 func TestEmptyTemplateCloneCrash(t *testing.T) {
333 t1 := New("base")
334 t1.Clone()
335 }
336
337
338 func TestTemplateLookUp(t *testing.T) {
339 t1 := New("foo")
340 if t1.Lookup("foo") != nil {
341 t.Error("Lookup returned non-nil value for undefined template foo")
342 }
343 t1.New("bar")
344 if t1.Lookup("bar") != nil {
345 t.Error("Lookup returned non-nil value for undefined template bar")
346 }
347 t1.Parse(`{{define "foo"}}test{{end}}`)
348 if t1.Lookup("foo") == nil {
349 t.Error("Lookup returned nil value for defined template")
350 }
351 }
352
353 func TestNew(t *testing.T) {
354
355 t1, _ := New("test").Parse(`{{define "test"}}foo{{end}}`)
356 t2 := t1.New("test")
357
358 if t1.common != t2.common {
359 t.Errorf("t1 & t2 didn't share common struct; got %v != %v", t1.common, t2.common)
360 }
361 if t1.Tree == nil {
362 t.Error("defined template got nil Tree")
363 }
364 if t2.Tree != nil {
365 t.Error("undefined template got non-nil Tree")
366 }
367
368 containsT1 := false
369 for _, tmpl := range t1.Templates() {
370 if tmpl == t2 {
371 t.Error("Templates included undefined template")
372 }
373 if tmpl == t1 {
374 containsT1 = true
375 }
376 }
377 if !containsT1 {
378 t.Error("Templates didn't include defined template")
379 }
380 }
381
382 func TestParse(t *testing.T) {
383
384
385 t1 := New("test")
386 if _, err := t1.Parse(`{{define "test"}}{{end}}`); err != nil {
387 t.Fatalf("parsing test: %s", err)
388 }
389 if _, err := t1.Parse(`{{define "test"}}{{/* this is a comment */}}{{end}}`); err != nil {
390 t.Fatalf("parsing test: %s", err)
391 }
392 if _, err := t1.Parse(`{{define "test"}}foo{{end}}`); err != nil {
393 t.Fatalf("parsing test: %s", err)
394 }
395 }
396
397 func TestEmptyTemplate(t *testing.T) {
398 cases := []struct {
399 defn []string
400 in string
401 want string
402 }{
403 {[]string{"x", "y"}, "", "y"},
404 {[]string{""}, "once", ""},
405 {[]string{"", ""}, "twice", ""},
406 {[]string{"{{.}}", "{{.}}"}, "twice", "twice"},
407 {[]string{"{{/* a comment */}}", "{{/* a comment */}}"}, "comment", ""},
408 {[]string{"{{.}}", ""}, "twice", ""},
409 }
410
411 for i, c := range cases {
412 root := New("root")
413
414 var (
415 m *Template
416 err error
417 )
418 for _, d := range c.defn {
419 m, err = root.New(c.in).Parse(d)
420 if err != nil {
421 t.Fatal(err)
422 }
423 }
424 buf := &strings.Builder{}
425 if err := m.Execute(buf, c.in); err != nil {
426 t.Error(i, err)
427 continue
428 }
429 if buf.String() != c.want {
430 t.Errorf("expected string %q: got %q", c.want, buf.String())
431 }
432 }
433 }
434
435
436
437
438 func TestIssue19294(t *testing.T) {
439
440
441
442
443 var inlined = map[string]string{
444 "stylesheet": `{{define "stylesheet"}}stylesheet{{end}}`,
445 "xhtml": `{{block "stylesheet" .}}{{end}}`,
446 }
447 all := []string{"stylesheet", "xhtml"}
448 for i := 0; i < 100; i++ {
449 res, err := New("title.xhtml").Parse(`{{template "xhtml" .}}`)
450 if err != nil {
451 t.Fatal(err)
452 }
453 for _, name := range all {
454 _, err := res.New(name).Parse(inlined[name])
455 if err != nil {
456 t.Fatal(err)
457 }
458 }
459 var buf strings.Builder
460 res.Execute(&buf, 0)
461 if buf.String() != "stylesheet" {
462 t.Fatalf("iteration %d: got %q; expected %q", i, buf.String(), "stylesheet")
463 }
464 }
465 }
466
467
468 func TestAddToZeroTemplate(t *testing.T) {
469 tree, err := parse.Parse("c", cloneText3, "", "", nil, builtins())
470 if err != nil {
471 t.Fatal(err)
472 }
473 var tmpl Template
474 tmpl.AddParseTree("x", tree["c"])
475 }
476
View as plain text