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