github.com/hbdrawn/golang@v0.0.0-20141214014649-6b835209aba2/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 fail.
    82  	t4 := Must(t0.Clone())
    83  	if _, err := t4.Parse(`{{define "lhs"}} FAIL {{end}}`); err == nil {
    84  		t.Error(`redefine "lhs": got nil err want non-nil`)
    85  	}
    86  
    87  	// Execute t0.
    88  	b.Reset()
    89  	if err := t0.ExecuteTemplate(b, "a", "<i>*/"); err != nil {
    90  		t.Fatal(err)
    91  	}
    92  	if got, want := b.String(), ` ( &lt;i&gt;*/ ) `; got != want {
    93  		t.Errorf("t0: got %q want %q", got, want)
    94  	}
    95  
    96  	// Clone t0. This should fail, as t0 has already executed.
    97  	if _, err := t0.Clone(); err == nil {
    98  		t.Error(`t0.Clone(): got nil err want non-nil`)
    99  	}
   100  
   101  	// Similarly, cloning sub-templates should fail.
   102  	if _, err := t0.Lookup("a").Clone(); err == nil {
   103  		t.Error(`t0.Lookup("a").Clone(): got nil err want non-nil`)
   104  	}
   105  	if _, err := t0.Lookup("lhs").Clone(); err == nil {
   106  		t.Error(`t0.Lookup("lhs").Clone(): got nil err want non-nil`)
   107  	}
   108  
   109  	// Execute t3.
   110  	b.Reset()
   111  	if err := t3.ExecuteTemplate(b, "a", "<i>*/"); err != nil {
   112  		t.Fatal(err)
   113  	}
   114  	if got, want := b.String(), ` <style> ZgotmplZ </style> `; got != want {
   115  		t.Errorf("t3: got %q want %q", got, want)
   116  	}
   117  }
   118  
   119  func TestTemplates(t *testing.T) {
   120  	names := []string{"t0", "a", "lhs", "rhs"}
   121  	// Some template definitions borrowed from TestClone.
   122  	const tmpl = `
   123  		{{define "a"}}{{template "lhs"}}{{.}}{{template "rhs"}}{{end}}
   124  		{{define "lhs"}} <a href=" {{end}}
   125  		{{define "rhs"}} "></a> {{end}}`
   126  	t0 := Must(New("t0").Parse(tmpl))
   127  	templates := t0.Templates()
   128  	if len(templates) != len(names) {
   129  		t.Errorf("expected %d templates; got %d", len(names), len(templates))
   130  	}
   131  	for _, name := range names {
   132  		found := false
   133  		for _, tmpl := range templates {
   134  			if name == tmpl.text.Name() {
   135  				found = true
   136  				break
   137  			}
   138  		}
   139  		if !found {
   140  			t.Error("could not find template", name)
   141  		}
   142  	}
   143  }
   144  
   145  // This used to crash; http://golang.org/issue/3281
   146  func TestCloneCrash(t *testing.T) {
   147  	t1 := New("all")
   148  	Must(t1.New("t1").Parse(`{{define "foo"}}foo{{end}}`))
   149  	t1.Clone()
   150  }
   151  
   152  // Ensure that this guarantee from the docs is upheld:
   153  // "Further calls to Parse in the copy will add templates
   154  // to the copy but not to the original."
   155  func TestCloneThenParse(t *testing.T) {
   156  	t0 := Must(New("t0").Parse(`{{define "a"}}{{template "embedded"}}{{end}}`))
   157  	t1 := Must(t0.Clone())
   158  	Must(t1.Parse(`{{define "embedded"}}t1{{end}}`))
   159  	if len(t0.Templates())+1 != len(t1.Templates()) {
   160  		t.Error("adding a template to a clone added it to the original")
   161  	}
   162  	// double check that the embedded template isn't available in the original
   163  	err := t0.ExecuteTemplate(ioutil.Discard, "a", nil)
   164  	if err == nil {
   165  		t.Error("expected 'no such template' error")
   166  	}
   167  }
   168  
   169  // https://code.google.com/p/go/issues/detail?id=5980
   170  func TestFuncMapWorksAfterClone(t *testing.T) {
   171  	funcs := FuncMap{"customFunc": func() (string, error) {
   172  		return "", errors.New("issue5980")
   173  	}}
   174  
   175  	// get the expected error output (no clone)
   176  	uncloned := Must(New("").Funcs(funcs).Parse("{{customFunc}}"))
   177  	wantErr := uncloned.Execute(ioutil.Discard, nil)
   178  
   179  	// toClone must be the same as uncloned. It has to be recreated from scratch,
   180  	// since cloning cannot occur after execution.
   181  	toClone := Must(New("").Funcs(funcs).Parse("{{customFunc}}"))
   182  	cloned := Must(toClone.Clone())
   183  	gotErr := cloned.Execute(ioutil.Discard, nil)
   184  
   185  	if wantErr.Error() != gotErr.Error() {
   186  		t.Errorf("clone error message mismatch want %q got %q", wantErr, gotErr)
   187  	}
   188  }