github.com/geraldss/go/src@v0.0.0-20210511222824-ac7d0ebfc235/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 "fmt" 11 "io" 12 "strings" 13 "sync" 14 "testing" 15 "text/template/parse" 16 ) 17 18 func TestAddParseTreeHTML(t *testing.T) { 19 root := Must(New("root").Parse(`{{define "a"}} {{.}} {{template "b"}} {{.}} "></a>{{end}}`)) 20 tree, err := parse.Parse("t", `{{define "b"}}<a href="{{end}}`, "", "", nil, nil) 21 if err != nil { 22 t.Fatal(err) 23 } 24 added := Must(root.AddParseTree("b", tree["b"])) 25 b := new(bytes.Buffer) 26 err = added.ExecuteTemplate(b, "a", "1>0") 27 if err != nil { 28 t.Fatal(err) 29 } 30 if got, want := b.String(), ` 1>0 <a href=" 1%3e0 "></a>`; got != want { 31 t.Errorf("got %q want %q", got, want) 32 } 33 } 34 35 func TestClone(t *testing.T) { 36 // The {{.}} will be executed with data "<i>*/" in different contexts. 37 // In the t0 template, it will be in a text context. 38 // In the t1 template, it will be in a URL context. 39 // In the t2 template, it will be in a JavaScript context. 40 // In the t3 template, it will be in a CSS context. 41 const tmpl = `{{define "a"}}{{template "lhs"}}{{.}}{{template "rhs"}}{{end}}` 42 b := new(bytes.Buffer) 43 44 // Create an incomplete template t0. 45 t0 := Must(New("t0").Parse(tmpl)) 46 47 // Clone t0 as t1. 48 t1 := Must(t0.Clone()) 49 Must(t1.Parse(`{{define "lhs"}} <a href=" {{end}}`)) 50 Must(t1.Parse(`{{define "rhs"}} "></a> {{end}}`)) 51 52 // Execute t1. 53 b.Reset() 54 if err := t1.ExecuteTemplate(b, "a", "<i>*/"); err != nil { 55 t.Fatal(err) 56 } 57 if got, want := b.String(), ` <a href=" %3ci%3e*/ "></a> `; got != want { 58 t.Errorf("t1: got %q want %q", got, want) 59 } 60 61 // Clone t0 as t2. 62 t2 := Must(t0.Clone()) 63 Must(t2.Parse(`{{define "lhs"}} <p onclick="javascript: {{end}}`)) 64 Must(t2.Parse(`{{define "rhs"}} "></p> {{end}}`)) 65 66 // Execute t2. 67 b.Reset() 68 if err := t2.ExecuteTemplate(b, "a", "<i>*/"); err != nil { 69 t.Fatal(err) 70 } 71 if got, want := b.String(), ` <p onclick="javascript: "\u003ci\u003e*/" "></p> `; got != want { 72 t.Errorf("t2: got %q want %q", got, want) 73 } 74 75 // Clone t0 as t3, but do not execute t3 yet. 76 t3 := Must(t0.Clone()) 77 Must(t3.Parse(`{{define "lhs"}} <style> {{end}}`)) 78 Must(t3.Parse(`{{define "rhs"}} </style> {{end}}`)) 79 80 // Complete t0. 81 Must(t0.Parse(`{{define "lhs"}} ( {{end}}`)) 82 Must(t0.Parse(`{{define "rhs"}} ) {{end}}`)) 83 84 // Clone t0 as t4. Redefining the "lhs" template should not fail. 85 t4 := Must(t0.Clone()) 86 if _, err := t4.Parse(`{{define "lhs"}} OK {{end}}`); err != nil { 87 t.Errorf(`redefine "lhs": got err %v want nil`, err) 88 } 89 // Cloning t1 should fail as it has been executed. 90 if _, err := t1.Clone(); err == nil { 91 t.Error("cloning t1: got nil err want non-nil") 92 } 93 // Redefining the "lhs" template in t1 should fail as it has been executed. 94 if _, err := t1.Parse(`{{define "lhs"}} OK {{end}}`); err == nil { 95 t.Error(`redefine "lhs": got nil err want non-nil`) 96 } 97 98 // Execute t0. 99 b.Reset() 100 if err := t0.ExecuteTemplate(b, "a", "<i>*/"); err != nil { 101 t.Fatal(err) 102 } 103 if got, want := b.String(), ` ( <i>*/ ) `; got != want { 104 t.Errorf("t0: got %q want %q", got, want) 105 } 106 107 // Clone t0. This should fail, as t0 has already executed. 108 if _, err := t0.Clone(); err == nil { 109 t.Error(`t0.Clone(): got nil err want non-nil`) 110 } 111 112 // Similarly, cloning sub-templates should fail. 113 if _, err := t0.Lookup("a").Clone(); err == nil { 114 t.Error(`t0.Lookup("a").Clone(): got nil err want non-nil`) 115 } 116 if _, err := t0.Lookup("lhs").Clone(); err == nil { 117 t.Error(`t0.Lookup("lhs").Clone(): got nil err want non-nil`) 118 } 119 120 // Execute t3. 121 b.Reset() 122 if err := t3.ExecuteTemplate(b, "a", "<i>*/"); err != nil { 123 t.Fatal(err) 124 } 125 if got, want := b.String(), ` <style> ZgotmplZ </style> `; got != want { 126 t.Errorf("t3: got %q want %q", got, want) 127 } 128 } 129 130 func TestTemplates(t *testing.T) { 131 names := []string{"t0", "a", "lhs", "rhs"} 132 // Some template definitions borrowed from TestClone. 133 const tmpl = ` 134 {{define "a"}}{{template "lhs"}}{{.}}{{template "rhs"}}{{end}} 135 {{define "lhs"}} <a href=" {{end}} 136 {{define "rhs"}} "></a> {{end}}` 137 t0 := Must(New("t0").Parse(tmpl)) 138 templates := t0.Templates() 139 if len(templates) != len(names) { 140 t.Errorf("expected %d templates; got %d", len(names), len(templates)) 141 } 142 for _, name := range names { 143 found := false 144 for _, tmpl := range templates { 145 if name == tmpl.text.Name() { 146 found = true 147 break 148 } 149 } 150 if !found { 151 t.Error("could not find template", name) 152 } 153 } 154 } 155 156 // This used to crash; https://golang.org/issue/3281 157 func TestCloneCrash(t *testing.T) { 158 t1 := New("all") 159 Must(t1.New("t1").Parse(`{{define "foo"}}foo{{end}}`)) 160 t1.Clone() 161 } 162 163 // Ensure that this guarantee from the docs is upheld: 164 // "Further calls to Parse in the copy will add templates 165 // to the copy but not to the original." 166 func TestCloneThenParse(t *testing.T) { 167 t0 := Must(New("t0").Parse(`{{define "a"}}{{template "embedded"}}{{end}}`)) 168 t1 := Must(t0.Clone()) 169 Must(t1.Parse(`{{define "embedded"}}t1{{end}}`)) 170 if len(t0.Templates())+1 != len(t1.Templates()) { 171 t.Error("adding a template to a clone added it to the original") 172 } 173 // double check that the embedded template isn't available in the original 174 err := t0.ExecuteTemplate(io.Discard, "a", nil) 175 if err == nil { 176 t.Error("expected 'no such template' error") 177 } 178 } 179 180 // https://golang.org/issue/5980 181 func TestFuncMapWorksAfterClone(t *testing.T) { 182 funcs := FuncMap{"customFunc": func() (string, error) { 183 return "", errors.New("issue5980") 184 }} 185 186 // get the expected error output (no clone) 187 uncloned := Must(New("").Funcs(funcs).Parse("{{customFunc}}")) 188 wantErr := uncloned.Execute(io.Discard, nil) 189 190 // toClone must be the same as uncloned. It has to be recreated from scratch, 191 // since cloning cannot occur after execution. 192 toClone := Must(New("").Funcs(funcs).Parse("{{customFunc}}")) 193 cloned := Must(toClone.Clone()) 194 gotErr := cloned.Execute(io.Discard, nil) 195 196 if wantErr.Error() != gotErr.Error() { 197 t.Errorf("clone error message mismatch want %q got %q", wantErr, gotErr) 198 } 199 } 200 201 // https://golang.org/issue/16101 202 func TestTemplateCloneExecuteRace(t *testing.T) { 203 const ( 204 input = `<title>{{block "a" .}}a{{end}}</title><body>{{block "b" .}}b{{end}}<body>` 205 overlay = `{{define "b"}}A{{end}}` 206 ) 207 outer := Must(New("outer").Parse(input)) 208 tmpl := Must(Must(outer.Clone()).Parse(overlay)) 209 210 var wg sync.WaitGroup 211 for i := 0; i < 10; i++ { 212 wg.Add(1) 213 go func() { 214 defer wg.Done() 215 for i := 0; i < 100; i++ { 216 if err := tmpl.Execute(io.Discard, "data"); err != nil { 217 panic(err) 218 } 219 } 220 }() 221 } 222 wg.Wait() 223 } 224 225 func TestTemplateCloneLookup(t *testing.T) { 226 // Template.escape makes an assumption that the template associated 227 // with t.Name() is t. Check that this holds. 228 tmpl := Must(New("x").Parse("a")) 229 tmpl = Must(tmpl.Clone()) 230 if tmpl.Lookup(tmpl.Name()) != tmpl { 231 t.Error("after Clone, tmpl.Lookup(tmpl.Name()) != tmpl") 232 } 233 } 234 235 func TestCloneGrowth(t *testing.T) { 236 tmpl := Must(New("root").Parse(`<title>{{block "B". }}Arg{{end}}</title>`)) 237 tmpl = Must(tmpl.Clone()) 238 Must(tmpl.Parse(`{{define "B"}}Text{{end}}`)) 239 for i := 0; i < 10; i++ { 240 tmpl.Execute(io.Discard, nil) 241 } 242 if len(tmpl.DefinedTemplates()) > 200 { 243 t.Fatalf("too many templates: %v", len(tmpl.DefinedTemplates())) 244 } 245 } 246 247 // https://golang.org/issue/17735 248 func TestCloneRedefinedName(t *testing.T) { 249 const base = ` 250 {{ define "a" -}}<title>{{ template "b" . -}}</title>{{ end -}} 251 {{ define "b" }}{{ end -}} 252 ` 253 const page = `{{ template "a" . }}` 254 255 t1 := Must(New("a").Parse(base)) 256 257 for i := 0; i < 2; i++ { 258 t2 := Must(t1.Clone()) 259 t2 = Must(t2.New(fmt.Sprintf("%d", i)).Parse(page)) 260 err := t2.Execute(io.Discard, nil) 261 if err != nil { 262 t.Fatal(err) 263 } 264 } 265 } 266 267 // Issue 24791. 268 func TestClonePipe(t *testing.T) { 269 a := Must(New("a").Parse(`{{define "a"}}{{range $v := .A}}{{$v}}{{end}}{{end}}`)) 270 data := struct{ A []string }{A: []string{"hi"}} 271 b := Must(a.Clone()) 272 var buf strings.Builder 273 if err := b.Execute(&buf, &data); err != nil { 274 t.Fatal(err) 275 } 276 if got, want := buf.String(), "hi"; got != want { 277 t.Errorf("got %q want %q", got, want) 278 } 279 }