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 )
214
215 func TestClone(t *testing.T) {
216
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
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
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
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
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
264 func TestAddParseTree(t *testing.T) {
265
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
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
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
295 func 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)
304 }
305
306 func 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
321 func TestEmptyTemplateCloneCrash(t *testing.T) {
322 t1 := New("base")
323 t1.Clone()
324 }
325
326
327 func 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
342 func TestNew(t *testing.T) {
343
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
371 func TestParse(t *testing.T) {
372
373
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
386 func 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
425
426
427 func TestIssue19294(t *testing.T) {
428
429
430
431
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
457 func 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
View as plain text