...

Source file src/text/template/multi_test.go

Documentation: text/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  	"os"
		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  	// errors
		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  			// expected error, got one
		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 { // +1 for root
		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  	// User-defined function: test argument evaluator.
		99  	{"testFunc literal", `{{oneArg "joe"}}`, "oneArg=joe", tVal, true},
	 100  	{"testFunc .", `{{oneArg .}}`, "oneArg=joe", "joe", true},
	 101  }
	 102  
	 103  // These strings are also in testdata/*.
	 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  	// Declare a couple of templates first.
	 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  // In these tests, actual content (not just template definitions) comes from the parsed files.
	 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  	// Create some templates and clone the root.
	 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  	// Add variants to both.
	 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  	// Verify that the clone is self-consistent.
	 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  	// Execute root.
	 245  	var b bytes.Buffer
	 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  	// Execute copy.
	 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  	// Create some templates.
	 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  	// Add a new parse tree.
	 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  	// Execute.
	 284  	var b bytes.Buffer
	 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  // Issue 7032
	 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) // used to panic
	 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  // Issue 10879
	 321  func TestEmptyTemplateCloneCrash(t *testing.T) {
	 322  	t1 := New("base")
	 323  	t1.Clone() // used to panic
	 324  }
	 325  
	 326  // Issue 10910, 10926
	 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  	// template with same name already exists
	 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  	// In multiple calls to Parse with the same receiver template, only one call
	 373  	// can contain text other than space, comments, and template definitions
	 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 := &bytes.Buffer{}
	 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  // Issue 19249 was a regression in 1.8 caused by the handling of empty
	 425  // templates added in that release, which got different answers depending
	 426  // on the order templates appeared in the internal map.
	 427  func TestIssue19294(t *testing.T) {
	 428  	// The empty block in "xhtml" should be replaced during execution
	 429  	// by the contents of "stylesheet", but if the internal map associating
	 430  	// names with templates is built in the wrong order, the empty block
	 431  	// looks non-empty and this doesn't happen.
	 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 bytes.Buffer
	 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  // Issue 48436
	 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