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.
17 func TestAddParseTree(t
*testing
.T
) {
18 root
:= Must(New("root").Parse(`{{define "a"}} {{.}} {{template "b"}} {{.}} "></a>{{end}}`))
19 tree
, err
:= parse
.Parse("t", `{{define "b"}}<a href="{{end}}`, "", "", nil, nil)
23 added
:= Must(root
.AddParseTree("b", tree
["b"]))
24 b
:= new(bytes
.Buffer
)
25 err
= added
.ExecuteTemplate(b
, "a", "1>0")
29 if got
, want
:= b
.String(), ` 1>0 <a href=" 1%3e0 "></a>`; got
!= want
{
30 t
.Errorf("got %q want %q", got
, want
)
34 func TestClone(t
*testing
.T
) {
35 // The {{.}} will be executed with data "<i>*/" in different contexts.
36 // In the t0 template, it will be in a text context.
37 // In the t1 template, it will be in a URL context.
38 // In the t2 template, it will be in a JavaScript context.
39 // In the t3 template, it will be in a CSS context.
40 const tmpl
= `{{define "a"}}{{template "lhs"}}{{.}}{{template "rhs"}}{{end}}`
41 b
:= new(bytes
.Buffer
)
43 // Create an incomplete template t0.
44 t0
:= Must(New("t0").Parse(tmpl
))
47 t1
:= Must(t0
.Clone())
48 Must(t1
.Parse(`{{define "lhs"}} <a href=" {{end}}`))
49 Must(t1
.Parse(`{{define "rhs"}} "></a> {{end}}`))
53 if err
:= t1
.ExecuteTemplate(b
, "a", "<i>*/"); err
!= nil {
56 if got
, want
:= b
.String(), ` <a href=" %3ci%3e*/ "></a> `; got
!= want
{
57 t
.Errorf("t1: got %q want %q", got
, want
)
61 t2
:= Must(t0
.Clone())
62 Must(t2
.Parse(`{{define "lhs"}} <p onclick="javascript: {{end}}`))
63 Must(t2
.Parse(`{{define "rhs"}} "></p> {{end}}`))
67 if err
:= t2
.ExecuteTemplate(b
, "a", "<i>*/"); err
!= nil {
70 if got
, want
:= b
.String(), ` <p onclick="javascript: "\u003ci\u003e*/" "></p> `; got
!= want
{
71 t
.Errorf("t2: got %q want %q", got
, want
)
74 // Clone t0 as t3, but do not execute t3 yet.
75 t3
:= Must(t0
.Clone())
76 Must(t3
.Parse(`{{define "lhs"}} <style> {{end}}`))
77 Must(t3
.Parse(`{{define "rhs"}} </style> {{end}}`))
80 Must(t0
.Parse(`{{define "lhs"}} ( {{end}}`))
81 Must(t0
.Parse(`{{define "rhs"}} ) {{end}}`))
83 // Clone t0 as t4. Redefining the "lhs" template should not fail.
84 t4
:= Must(t0
.Clone())
85 if _
, err
:= t4
.Parse(`{{define "lhs"}} OK {{end}}`); err
!= nil {
86 t
.Errorf(`redefine "lhs": got err %v want nil`, err
)
88 // Cloning t1 should fail as it has been executed.
89 if _
, err
:= t1
.Clone(); err
== nil {
90 t
.Error("cloning t1: got nil err want non-nil")
92 // Redefining the "lhs" template in t1 should fail as it has been executed.
93 if _
, err
:= t1
.Parse(`{{define "lhs"}} OK {{end}}`); err
== nil {
94 t
.Error(`redefine "lhs": got nil err want non-nil`)
99 if err
:= t0
.ExecuteTemplate(b
, "a", "<i>*/"); err
!= nil {
102 if got
, want
:= b
.String(), ` ( <i>*/ ) `; got
!= want
{
103 t
.Errorf("t0: got %q want %q", got
, want
)
106 // Clone t0. This should fail, as t0 has already executed.
107 if _
, err
:= t0
.Clone(); err
== nil {
108 t
.Error(`t0.Clone(): got nil err want non-nil`)
111 // Similarly, cloning sub-templates should fail.
112 if _
, err
:= t0
.Lookup("a").Clone(); err
== nil {
113 t
.Error(`t0.Lookup("a").Clone(): got nil err want non-nil`)
115 if _
, err
:= t0
.Lookup("lhs").Clone(); err
== nil {
116 t
.Error(`t0.Lookup("lhs").Clone(): got nil err want non-nil`)
121 if err
:= t3
.ExecuteTemplate(b
, "a", "<i>*/"); err
!= nil {
124 if got
, want
:= b
.String(), ` <style> ZgotmplZ </style> `; got
!= want
{
125 t
.Errorf("t3: got %q want %q", got
, want
)
129 func TestTemplates(t
*testing
.T
) {
130 names
:= []string{"t0", "a", "lhs", "rhs"}
131 // Some template definitions borrowed from TestClone.
133 {{define "a"}}{{template "lhs"}}{{.}}{{template "rhs"}}{{end}}
134 {{define "lhs"}} <a href=" {{end}}
135 {{define "rhs"}} "></a> {{end}}`
136 t0
:= Must(New("t0").Parse(tmpl
))
137 templates
:= t0
.Templates()
138 if len(templates
) != len(names
) {
139 t
.Errorf("expected %d templates; got %d", len(names
), len(templates
))
141 for _
, name
:= range names
{
143 for _
, tmpl
:= range templates
{
144 if name
== tmpl
.text
.Name() {
150 t
.Error("could not find template", name
)
155 // This used to crash; https://golang.org/issue/3281
156 func TestCloneCrash(t
*testing
.T
) {
158 Must(t1
.New("t1").Parse(`{{define "foo"}}foo{{end}}`))
162 // Ensure that this guarantee from the docs is upheld:
163 // "Further calls to Parse in the copy will add templates
164 // to the copy but not to the original."
165 func TestCloneThenParse(t
*testing
.T
) {
166 t0
:= Must(New("t0").Parse(`{{define "a"}}{{template "embedded"}}{{end}}`))
167 t1
:= Must(t0
.Clone())
168 Must(t1
.Parse(`{{define "embedded"}}t1{{end}}`))
169 if len(t0
.Templates())+1 != len(t1
.Templates()) {
170 t
.Error("adding a template to a clone added it to the original")
172 // double check that the embedded template isn't available in the original
173 err
:= t0
.ExecuteTemplate(ioutil
.Discard
, "a", nil)
175 t
.Error("expected 'no such template' error")
179 // https://golang.org/issue/5980
180 func TestFuncMapWorksAfterClone(t
*testing
.T
) {
181 funcs
:= FuncMap
{"customFunc": func() (string, error
) {
182 return "", errors
.New("issue5980")
185 // get the expected error output (no clone)
186 uncloned
:= Must(New("").Funcs(funcs
).Parse("{{customFunc}}"))
187 wantErr
:= uncloned
.Execute(ioutil
.Discard
, nil)
189 // toClone must be the same as uncloned. It has to be recreated from scratch,
190 // since cloning cannot occur after execution.
191 toClone
:= Must(New("").Funcs(funcs
).Parse("{{customFunc}}"))
192 cloned
:= Must(toClone
.Clone())
193 gotErr
:= cloned
.Execute(ioutil
.Discard
, nil)
195 if wantErr
.Error() != gotErr
.Error() {
196 t
.Errorf("clone error message mismatch want %q got %q", wantErr
, gotErr
)
200 // https://golang.org/issue/16101
201 func TestTemplateCloneExecuteRace(t
*testing
.T
) {
203 input
= `<title>{{block "a" .}}a{{end}}</title><body>{{block "b" .}}b{{end}}<body>`
204 overlay
= `{{define "b"}}A{{end}}`
206 outer
:= Must(New("outer").Parse(input
))
207 tmpl
:= Must(Must(outer
.Clone()).Parse(overlay
))
209 var wg sync
.WaitGroup
210 for i
:= 0; i
< 10; i
++ {
214 for i
:= 0; i
< 100; i
++ {
215 if err
:= tmpl
.Execute(ioutil
.Discard
, "data"); err
!= nil {
224 func TestTemplateCloneLookup(t
*testing
.T
) {
225 // Template.escape makes an assumption that the template associated
226 // with t.Name() is t. Check that this holds.
227 tmpl
:= Must(New("x").Parse("a"))
228 tmpl
= Must(tmpl
.Clone())
229 if tmpl
.Lookup(tmpl
.Name()) != tmpl
{
230 t
.Error("after Clone, tmpl.Lookup(tmpl.Name()) != tmpl")
234 func TestCloneGrowth(t
*testing
.T
) {
235 tmpl
:= Must(New("root").Parse(`<title>{{block "B". }}Arg{{end}}</title>`))
236 tmpl
= Must(tmpl
.Clone())
237 Must(tmpl
.Parse(`{{define "B"}}Text{{end}}`))
238 for i
:= 0; i
< 10; i
++ {
239 tmpl
.Execute(ioutil
.Discard
, nil)
241 if len(tmpl
.DefinedTemplates()) > 200 {
242 t
.Fatalf("too many templates: %v", len(tmpl
.DefinedTemplates()))
246 // https://golang.org/issue/17735
247 func TestCloneRedefinedName(t
*testing
.T
) {
249 {{ define "a" -}}<title>{{ template "b" . -}}</title>{{ end -}}
250 {{ define "b" }}{{ end -}}
252 const page
= `{{ template "a" . }}`
254 t1
:= Must(New("a").Parse(base
))
256 for i
:= 0; i
< 2; i
++ {
257 t2
:= Must(t1
.Clone())
258 t2
= Must(t2
.New(fmt
.Sprintf("%d", i
)).Parse(page
))
259 err
:= t2
.Execute(ioutil
.Discard
, nil)