github.com/linchen2chris/hugo@v0.0.0-20230307053224-cec209389705/tpl/internal/go_templates/htmltemplate/multi_test.go (about)

     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  // Tests for multiple-template execution, copied from text/template.
     6  
     7  //go:build go1.13 && !windows
     8  // +build go1.13,!windows
     9  
    10  package template
    11  
    12  import (
    13  	"archive/zip"
    14  	"os"
    15  	"strings"
    16  	"testing"
    17  
    18  	"github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse"
    19  )
    20  
    21  var multiExecTests = []execTest{
    22  	{"empty", "", "", nil, true},
    23  	{"text", "some text", "some text", nil, true},
    24  	{"invoke x", `{{template "x" .SI}}`, "TEXT", tVal, true},
    25  	{"invoke x no args", `{{template "x"}}`, "TEXT", tVal, true},
    26  	{"invoke dot int", `{{template "dot" .I}}`, "17", tVal, true},
    27  	{"invoke dot []int", `{{template "dot" .SI}}`, "[3 4 5]", tVal, true},
    28  	{"invoke dotV", `{{template "dotV" .U}}`, "v", tVal, true},
    29  	{"invoke nested int", `{{template "nested" .I}}`, "17", tVal, true},
    30  	{"variable declared by template", `{{template "nested" $x:=.SI}},{{index $x 1}}`, "[3 4 5],4", tVal, true},
    31  
    32  	// User-defined function: test argument evaluator.
    33  	{"testFunc literal", `{{oneArg "joe"}}`, "oneArg=joe", tVal, true},
    34  	{"testFunc .", `{{oneArg .}}`, "oneArg=joe", "joe", true},
    35  }
    36  
    37  // These strings are also in testdata/*.
    38  const multiText1 = `
    39  	{{define "x"}}TEXT{{end}}
    40  	{{define "dotV"}}{{.V}}{{end}}
    41  `
    42  
    43  const multiText2 = `
    44  	{{define "dot"}}{{.}}{{end}}
    45  	{{define "nested"}}{{template "dot" .}}{{end}}
    46  `
    47  
    48  func TestMultiExecute(t *testing.T) {
    49  	// Declare a couple of templates first.
    50  	template, err := New("root").Parse(multiText1)
    51  	if err != nil {
    52  		t.Fatalf("parse error for 1: %s", err)
    53  	}
    54  	_, err = template.Parse(multiText2)
    55  	if err != nil {
    56  		t.Fatalf("parse error for 2: %s", err)
    57  	}
    58  	testExecute(multiExecTests, template, t)
    59  }
    60  
    61  func TestParseFiles(t *testing.T) {
    62  	_, err := ParseFiles("DOES NOT EXIST")
    63  	if err == nil {
    64  		t.Error("expected error for non-existent file; got none")
    65  	}
    66  	template := New("root")
    67  	_, err = template.ParseFiles("testdata/file1.tmpl", "testdata/file2.tmpl")
    68  	if err != nil {
    69  		t.Fatalf("error parsing files: %v", err)
    70  	}
    71  	testExecute(multiExecTests, template, t)
    72  }
    73  
    74  func TestParseGlob(t *testing.T) {
    75  	_, err := ParseGlob("DOES NOT EXIST")
    76  	if err == nil {
    77  		t.Error("expected error for non-existent file; got none")
    78  	}
    79  	_, err = New("error").ParseGlob("[x")
    80  	if err == nil {
    81  		t.Error("expected error for bad pattern; got none")
    82  	}
    83  	template := New("root")
    84  	_, err = template.ParseGlob("testdata/file*.tmpl")
    85  	if err != nil {
    86  		t.Fatalf("error parsing files: %v", err)
    87  	}
    88  	testExecute(multiExecTests, template, t)
    89  }
    90  
    91  func TestParseFS(t *testing.T) {
    92  	fs := os.DirFS("testdata")
    93  
    94  	{
    95  		_, err := ParseFS(fs, "DOES NOT EXIST")
    96  		if err == nil {
    97  			t.Error("expected error for non-existent file; got none")
    98  		}
    99  	}
   100  
   101  	{
   102  		template := New("root")
   103  		_, err := template.ParseFS(fs, "file1.tmpl", "file2.tmpl")
   104  		if err != nil {
   105  			t.Fatalf("error parsing files: %v", err)
   106  		}
   107  		testExecute(multiExecTests, template, t)
   108  	}
   109  
   110  	{
   111  		template := New("root")
   112  		_, err := template.ParseFS(fs, "file*.tmpl")
   113  		if err != nil {
   114  			t.Fatalf("error parsing files: %v", err)
   115  		}
   116  		testExecute(multiExecTests, template, t)
   117  	}
   118  }
   119  
   120  // In these tests, actual content (not just template definitions) comes from the parsed files.
   121  
   122  var templateFileExecTests = []execTest{
   123  	{"test", `{{template "tmpl1.tmpl"}}{{template "tmpl2.tmpl"}}`, "template1\n\ny\ntemplate2\n\nx\n", 0, true},
   124  }
   125  
   126  func TestParseFilesWithData(t *testing.T) {
   127  	template, err := New("root").ParseFiles("testdata/tmpl1.tmpl", "testdata/tmpl2.tmpl")
   128  	if err != nil {
   129  		t.Fatalf("error parsing files: %v", err)
   130  	}
   131  	testExecute(templateFileExecTests, template, t)
   132  }
   133  
   134  func TestParseGlobWithData(t *testing.T) {
   135  	template, err := New("root").ParseGlob("testdata/tmpl*.tmpl")
   136  	if err != nil {
   137  		t.Fatalf("error parsing files: %v", err)
   138  	}
   139  	testExecute(templateFileExecTests, template, t)
   140  }
   141  
   142  func TestParseZipFS(t *testing.T) {
   143  	z, err := zip.OpenReader("testdata/fs.zip")
   144  	if err != nil {
   145  		t.Fatalf("error parsing zip: %v", err)
   146  	}
   147  	template, err := New("root").ParseFS(z, "tmpl*.tmpl")
   148  	if err != nil {
   149  		t.Fatalf("error parsing files: %v", err)
   150  	}
   151  	testExecute(templateFileExecTests, template, t)
   152  }
   153  
   154  const (
   155  	cloneText1 = `{{define "a"}}{{template "b"}}{{template "c"}}{{end}}`
   156  	cloneText2 = `{{define "b"}}b{{end}}`
   157  	cloneText3 = `{{define "c"}}root{{end}}`
   158  	cloneText4 = `{{define "c"}}clone{{end}}`
   159  )
   160  
   161  // Issue 7032
   162  func TestAddParseTreeToUnparsedTemplate(t *testing.T) {
   163  	master := "{{define \"master\"}}{{end}}"
   164  	tmpl := New("master")
   165  	tree, err := parse.Parse("master", master, "", "", nil)
   166  	if err != nil {
   167  		t.Fatalf("unexpected parse err: %v", err)
   168  	}
   169  	masterTree := tree["master"]
   170  	tmpl.AddParseTree("master", masterTree) // used to panic
   171  }
   172  
   173  func TestRedefinition(t *testing.T) {
   174  	var tmpl *Template
   175  	var err error
   176  	if tmpl, err = New("tmpl1").Parse(`{{define "test"}}foo{{end}}`); err != nil {
   177  		t.Fatalf("parse 1: %v", err)
   178  	}
   179  	if _, err = tmpl.Parse(`{{define "test"}}bar{{end}}`); err != nil {
   180  		t.Fatalf("got error %v, expected nil", err)
   181  	}
   182  	if _, err = tmpl.New("tmpl2").Parse(`{{define "test"}}bar{{end}}`); err != nil {
   183  		t.Fatalf("got error %v, expected nil", err)
   184  	}
   185  }
   186  
   187  // Issue 10879
   188  func TestEmptyTemplateCloneCrash(t *testing.T) {
   189  	t1 := New("base")
   190  	t1.Clone() // used to panic
   191  }
   192  
   193  // Issue 10910, 10926
   194  func TestTemplateLookUp(t *testing.T) {
   195  	t.Skip("broken on html/template") // TODO
   196  	t1 := New("foo")
   197  	if t1.Lookup("foo") != nil {
   198  		t.Error("Lookup returned non-nil value for undefined template foo")
   199  	}
   200  	t1.New("bar")
   201  	if t1.Lookup("bar") != nil {
   202  		t.Error("Lookup returned non-nil value for undefined template bar")
   203  	}
   204  	t1.Parse(`{{define "foo"}}test{{end}}`)
   205  	if t1.Lookup("foo") == nil {
   206  		t.Error("Lookup returned nil value for defined template")
   207  	}
   208  }
   209  
   210  func TestParse(t *testing.T) {
   211  	// In multiple calls to Parse with the same receiver template, only one call
   212  	// can contain text other than space, comments, and template definitions
   213  	t1 := New("test")
   214  	if _, err := t1.Parse(`{{define "test"}}{{end}}`); err != nil {
   215  		t.Fatalf("parsing test: %s", err)
   216  	}
   217  	if _, err := t1.Parse(`{{define "test"}}{{/* this is a comment */}}{{end}}`); err != nil {
   218  		t.Fatalf("parsing test: %s", err)
   219  	}
   220  	if _, err := t1.Parse(`{{define "test"}}foo{{end}}`); err != nil {
   221  		t.Fatalf("parsing test: %s", err)
   222  	}
   223  }
   224  
   225  func TestEmptyTemplate(t *testing.T) {
   226  	cases := []struct {
   227  		defn []string
   228  		in   string
   229  		want string
   230  	}{
   231  		{[]string{"x", "y"}, "", "y"},
   232  		{[]string{""}, "once", ""},
   233  		{[]string{"", ""}, "twice", ""},
   234  		{[]string{"{{.}}", "{{.}}"}, "twice", "twice"},
   235  		{[]string{"{{/* a comment */}}", "{{/* a comment */}}"}, "comment", ""},
   236  		{[]string{"{{.}}", ""}, "twice", "twice"}, // TODO: should want "" not "twice"
   237  	}
   238  
   239  	for i, c := range cases {
   240  		root := New("root")
   241  
   242  		var (
   243  			m   *Template
   244  			err error
   245  		)
   246  		for _, d := range c.defn {
   247  			m, err = root.New(c.in).Parse(d)
   248  			if err != nil {
   249  				t.Fatal(err)
   250  			}
   251  		}
   252  		buf := &strings.Builder{}
   253  		if err := m.Execute(buf, c.in); err != nil {
   254  			t.Error(i, err)
   255  			continue
   256  		}
   257  		if buf.String() != c.want {
   258  			t.Errorf("expected string %q: got %q", c.want, buf.String())
   259  		}
   260  	}
   261  }
   262  
   263  // Issue 19249 was a regression in 1.8 caused by the handling of empty
   264  // templates added in that release, which got different answers depending
   265  // on the order templates appeared in the internal map.
   266  func TestIssue19294(t *testing.T) {
   267  	// The empty block in "xhtml" should be replaced during execution
   268  	// by the contents of "stylesheet", but if the internal map associating
   269  	// names with templates is built in the wrong order, the empty block
   270  	// looks non-empty and this doesn't happen.
   271  	var inlined = map[string]string{
   272  		"stylesheet": `{{define "stylesheet"}}stylesheet{{end}}`,
   273  		"xhtml":      `{{block "stylesheet" .}}{{end}}`,
   274  	}
   275  	all := []string{"stylesheet", "xhtml"}
   276  	for i := 0; i < 100; i++ {
   277  		res, err := New("title.xhtml").Parse(`{{template "xhtml" .}}`)
   278  		if err != nil {
   279  			t.Fatal(err)
   280  		}
   281  		for _, name := range all {
   282  			_, err := res.New(name).Parse(inlined[name])
   283  			if err != nil {
   284  				t.Fatal(err)
   285  			}
   286  		}
   287  		var buf strings.Builder
   288  		res.Execute(&buf, 0)
   289  		if buf.String() != "stylesheet" {
   290  			t.Fatalf("iteration %d: got %q; expected %q", i, buf.String(), "stylesheet")
   291  		}
   292  	}
   293  }