hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

clone_test.go (8277B)

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