github.com/sean-/go@v0.0.0-20151219100004-97f854cd7bb6/src/html/template/clone_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  package template
     6  
     7  import (
     8  	"bytes"
     9  	"errors"
    10  	"io/ioutil"
    11  	"testing"
    12  	"text/template/parse"
    13  )
    14  
    15  func TestAddParseTree(t *testing.T) {
    16  	root := Must(New("root").Parse(`{{define "a"}} {{.}} {{template "b"}} {{.}} "></a>{{end}}`))
    17  	tree, err := parse.Parse("t", `{{define "b"}}<a href="{{end}}`, "", "", nil, nil)
    18  	if err != nil {
    19  		t.Fatal(err)
    20  	}
    21  	added := Must(root.AddParseTree("b", tree["b"]))
    22  	b := new(bytes.Buffer)
    23  	err = added.ExecuteTemplate(b, "a", "1>0")
    24  	if err != nil {
    25  		t.Fatal(err)
    26  	}
    27  	if got, want := b.String(), ` 1&gt;0 <a href=" 1%3e0 "></a>`; got != want {
    28  		t.Errorf("got %q want %q", got, want)
    29  	}
    30  }
    31  
    32  func TestClone(t *testing.T) {
    33  	// The {{.}} will be executed with data "<i>*/" in different contexts.
    34  	// In the t0 template, it will be in a text context.
    35  	// In the t1 template, it will be in a URL context.
    36  	// In the t2 template, it will be in a JavaScript context.
    37  	// In the t3 template, it will be in a CSS context.
    38  	const tmpl = `{{define "a"}}{{template "lhs"}}{{.}}{{template "rhs"}}{{end}}`
    39  	b := new(bytes.Buffer)
    40  
    41  	// Create an incomplete template t0.
    42  	t0 := Must(New("t0").Parse(tmpl))
    43  
    44  	// Clone t0 as t1.
    45  	t1 := Must(t0.Clone())
    46  	Must(t1.Parse(`{{define "lhs"}} <a href=" {{end}}`))
    47  	Must(t1.Parse(`{{define "rhs"}} "></a> {{end}}`))
    48  
    49  	// Execute t1.
    50  	b.Reset()
    51  	if err := t1.ExecuteTemplate(b, "a", "<i>*/"); err != nil {
    52  		t.Fatal(err)
    53  	}
    54  	if got, want := b.String(), ` <a href=" %3ci%3e*/ "></a> `; got != want {
    55  		t.Errorf("t1: got %q want %q", got, want)
    56  	}
    57  
    58  	// Clone t0 as t2.
    59  	t2 := Must(t0.Clone())
    60  	Must(t2.Parse(`{{define "lhs"}} <p onclick="javascript: {{end}}`))
    61  	Must(t2.Parse(`{{define "rhs"}} "></p> {{end}}`))
    62  
    63  	// Execute t2.
    64  	b.Reset()
    65  	if err := t2.ExecuteTemplate(b, "a", "<i>*/"); err != nil {
    66  		t.Fatal(err)
    67  	}
    68  	if got, want := b.String(), ` <p onclick="javascript: &#34;\u003ci\u003e*/&#34; "></p> `; got != want {
    69  		t.Errorf("t2: got %q want %q", got, want)
    70  	}
    71  
    72  	// Clone t0 as t3, but do not execute t3 yet.
    73  	t3 := Must(t0.Clone())
    74  	Must(t3.Parse(`{{define "lhs"}} <style> {{end}}`))
    75  	Must(t3.Parse(`{{define "rhs"}} </style> {{end}}`))
    76  
    77  	// Complete t0.
    78  	Must(t0.Parse(`{{define "lhs"}} ( {{end}}`))
    79  	Must(t0.Parse(`{{define "rhs"}} ) {{end}}`))
    80  
    81  	// Clone t0 as t4. Redefining the "lhs" template should not fail.
    82  	t4 := Must(t0.Clone())
    83  	if _, err := t4.Parse(`{{define "lhs"}} OK {{end}}`); err != nil {
    84  		t.Errorf(`redefine "lhs": got err %v want nil`, err)
    85  	}
    86  	// Cloning t1 should fail as it has been executed.
    87  	if _, err := t1.Clone(); err == nil {
    88  		t.Error("cloning t1: got nil err want non-nil")
    89  	}
    90  	// Redefining the "lhs" template in t1 should fail as it has been executed.
    91  	if _, err := t1.Parse(`{{define "lhs"}} OK {{end}}`); err == nil {
    92  		t.Error(`redefine "lhs": got nil err want non-nil`)
    93  	}
    94  
    95  	// Execute t0.
    96  	b.Reset()
    97  	if err := t0.ExecuteTemplate(b, "a", "<i>*/"); err != nil {
    98  		t.Fatal(err)
    99  	}
   100  	if got, want := b.String(), ` ( &lt;i&gt;*/ ) `; got != want {
   101  		t.Errorf("t0: got %q want %q", got, want)
   102  	}
   103  
   104  	// Clone t0. This should fail, as t0 has already executed.
   105  	if _, err := t0.Clone(); err == nil {
   106  		t.Error(`t0.Clone(): got nil err want non-nil`)
   107  	}
   108  
   109  	// Similarly, cloning sub-templates should fail.
   110  	if _, err := t0.Lookup("a").Clone(); err == nil {
   111  		t.Error(`t0.Lookup("a").Clone(): got nil err want non-nil`)
   112  	}
   113  	if _, err := t0.Lookup("lhs").Clone(); err == nil {
   114  		t.Error(`t0.Lookup("lhs").Clone(): got nil err want non-nil`)
   115  	}
   116  
   117  	// Execute t3.
   118  	b.Reset()
   119  	if err := t3.ExecuteTemplate(b, "a", "<i>*/"); err != nil {
   120  		t.Fatal(err)
   121  	}
   122  	if got, want := b.String(), ` <style> ZgotmplZ </style> `; got != want {
   123  		t.Errorf("t3: got %q want %q", got, want)
   124  	}
   125  }
   126  
   127  func TestTemplates(t *testing.T) {
   128  	names := []string{"t0", "a", "lhs", "rhs"}
   129  	// Some template definitions borrowed from TestClone.
   130  	const tmpl = `
   131  		{{define "a"}}{{template "lhs"}}{{.}}{{template "rhs"}}{{end}}
   132  		{{define "lhs"}} <a href=" {{end}}
   133  		{{define "rhs"}} "></a> {{end}}`
   134  	t0 := Must(New("t0").Parse(tmpl))
   135  	templates := t0.Templates()
   136  	if len(templates) != len(names) {
   137  		t.Errorf("expected %d templates; got %d", len(names), len(templates))
   138  	}
   139  	for _, name := range names {
   140  		found := false
   141  		for _, tmpl := range templates {
   142  			if name == tmpl.text.Name() {
   143  				found = true
   144  				break
   145  			}
   146  		}
   147  		if !found {
   148  			t.Error("could not find template", name)
   149  		}
   150  	}
   151  }
   152  
   153  // This used to crash; https://golang.org/issue/3281
   154  func TestCloneCrash(t *testing.T) {
   155  	t1 := New("all")
   156  	Must(t1.New("t1").Parse(`{{define "foo"}}foo{{end}}`))
   157  	t1.Clone()
   158  }
   159  
   160  // Ensure that this guarantee from the docs is upheld:
   161  // "Further calls to Parse in the copy will add templates
   162  // to the copy but not to the original."
   163  func TestCloneThenParse(t *testing.T) {
   164  	t0 := Must(New("t0").Parse(`{{define "a"}}{{template "embedded"}}{{end}}`))
   165  	t1 := Must(t0.Clone())
   166  	Must(t1.Parse(`{{define "embedded"}}t1{{end}}`))
   167  	if len(t0.Templates())+1 != len(t1.Templates()) {
   168  		t.Error("adding a template to a clone added it to the original")
   169  	}
   170  	// double check that the embedded template isn't available in the original
   171  	err := t0.ExecuteTemplate(ioutil.Discard, "a", nil)
   172  	if err == nil {
   173  		t.Error("expected 'no such template' error")
   174  	}
   175  }
   176  
   177  // https://golang.org/issue/5980
   178  func TestFuncMapWorksAfterClone(t *testing.T) {
   179  	funcs := FuncMap{"customFunc": func() (string, error) {
   180  		return "", errors.New("issue5980")
   181  	}}
   182  
   183  	// get the expected error output (no clone)
   184  	uncloned := Must(New("").Funcs(funcs).Parse("{{customFunc}}"))
   185  	wantErr := uncloned.Execute(ioutil.Discard, nil)
   186  
   187  	// toClone must be the same as uncloned. It has to be recreated from scratch,
   188  	// since cloning cannot occur after execution.
   189  	toClone := Must(New("").Funcs(funcs).Parse("{{customFunc}}"))
   190  	cloned := Must(toClone.Clone())
   191  	gotErr := cloned.Execute(ioutil.Discard, nil)
   192  
   193  	if wantErr.Error() != gotErr.Error() {
   194  		t.Errorf("clone error message mismatch want %q got %q", wantErr, gotErr)
   195  	}
   196  }