From fa69b581e17a96c8b28bc2f4b3d9a7b6fb5a0b6c Mon Sep 17 00:00:00 2001 From: John Date: Tue, 12 Mar 2019 23:50:30 +0800 Subject: [PATCH] update unit test cases --- g/frame/gins/gins_database_test.go | 8 +- g/os/gview/internal/fmtsort/export_test.go | 11 - g/os/gview/internal/fmtsort/sort_test.go | 212 --- .../internal/text/template/example_test.go | 110 -- .../text/template/examplefiles_test.go | 182 -- .../text/template/examplefunc_test.go | 54 - .../gview/internal/text/template/exec_test.go | 1512 ----------------- .../internal/text/template/multi_test.go | 423 ----- .../internal/text/template/parse/lex_test.go | 544 ------ .../text/template/parse/parse_test.go | 542 ------ .../text/template/testdata/file1.tmpl | 2 - .../text/template/testdata/file2.tmpl | 2 - .../text/template/testdata/tmpl1.tmpl | 3 - .../text/template/testdata/tmpl2.tmpl | 3 - 14 files changed, 4 insertions(+), 3604 deletions(-) delete mode 100644 g/os/gview/internal/fmtsort/export_test.go delete mode 100644 g/os/gview/internal/fmtsort/sort_test.go delete mode 100644 g/os/gview/internal/text/template/example_test.go delete mode 100644 g/os/gview/internal/text/template/examplefiles_test.go delete mode 100644 g/os/gview/internal/text/template/examplefunc_test.go delete mode 100644 g/os/gview/internal/text/template/exec_test.go delete mode 100644 g/os/gview/internal/text/template/multi_test.go delete mode 100644 g/os/gview/internal/text/template/parse/lex_test.go delete mode 100644 g/os/gview/internal/text/template/parse/parse_test.go delete mode 100644 g/os/gview/internal/text/template/testdata/file1.tmpl delete mode 100644 g/os/gview/internal/text/template/testdata/file2.tmpl delete mode 100644 g/os/gview/internal/text/template/testdata/tmpl1.tmpl delete mode 100644 g/os/gview/internal/text/template/testdata/tmpl2.tmpl diff --git a/g/frame/gins/gins_database_test.go b/g/frame/gins/gins_database_test.go index da9324512..85b949a33 100644 --- a/g/frame/gins/gins_database_test.go +++ b/g/frame/gins/gins_database_test.go @@ -24,8 +24,8 @@ test = "v=1" host = "127.0.0.1" port = "3306" user = "root" - pass = "12345678" - # pass = "" + pass = "" + # pass = "12345678" name = "test" type = "mysql" role = "master" @@ -35,8 +35,8 @@ test = "v=1" host = "127.0.0.1" port = "3306" user = "root" - pass = "12345678" - # pass = "" + pass = "" + # pass = "12345678" name = "test" type = "mysql" role = "master" diff --git a/g/os/gview/internal/fmtsort/export_test.go b/g/os/gview/internal/fmtsort/export_test.go deleted file mode 100644 index 25cbb5d4f..000000000 --- a/g/os/gview/internal/fmtsort/export_test.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package fmtsort - -import "reflect" - -func Compare(a, b reflect.Value) int { - return compare(a, b) -} diff --git a/g/os/gview/internal/fmtsort/sort_test.go b/g/os/gview/internal/fmtsort/sort_test.go deleted file mode 100644 index 49a50a859..000000000 --- a/g/os/gview/internal/fmtsort/sort_test.go +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package fmtsort_test - -import ( - "fmt" - "github.com/gogf/gf/g/os/gview/internal/fmtsort" - "math" - "reflect" - "strings" - "testing" -) - -var compareTests = [][]reflect.Value{ - ct(reflect.TypeOf(int(0)), -1, 0, 1), - ct(reflect.TypeOf(int8(0)), -1, 0, 1), - ct(reflect.TypeOf(int16(0)), -1, 0, 1), - ct(reflect.TypeOf(int32(0)), -1, 0, 1), - ct(reflect.TypeOf(int64(0)), -1, 0, 1), - ct(reflect.TypeOf(uint(0)), 0, 1, 5), - ct(reflect.TypeOf(uint8(0)), 0, 1, 5), - ct(reflect.TypeOf(uint16(0)), 0, 1, 5), - ct(reflect.TypeOf(uint32(0)), 0, 1, 5), - ct(reflect.TypeOf(uint64(0)), 0, 1, 5), - ct(reflect.TypeOf(uintptr(0)), 0, 1, 5), - ct(reflect.TypeOf(string("")), "", "a", "ab"), - ct(reflect.TypeOf(float32(0)), math.NaN(), math.Inf(-1), -1e10, 0, 1e10, math.Inf(1)), - ct(reflect.TypeOf(float64(0)), math.NaN(), math.Inf(-1), -1e10, 0, 1e10, math.Inf(1)), - ct(reflect.TypeOf(complex64(0+1i)), -1-1i, -1+0i, -1+1i, 0-1i, 0+0i, 0+1i, 1-1i, 1+0i, 1+1i), - ct(reflect.TypeOf(complex128(0+1i)), -1-1i, -1+0i, -1+1i, 0-1i, 0+0i, 0+1i, 1-1i, 1+0i, 1+1i), - ct(reflect.TypeOf(false), false, true), - ct(reflect.TypeOf(&ints[0]), &ints[0], &ints[1], &ints[2]), - ct(reflect.TypeOf(chans[0]), chans[0], chans[1], chans[2]), - ct(reflect.TypeOf(toy{}), toy{0, 1}, toy{0, 2}, toy{1, -1}, toy{1, 1}), - ct(reflect.TypeOf([2]int{}), [2]int{1, 1}, [2]int{1, 2}, [2]int{2, 0}), - ct(reflect.TypeOf(interface{}(interface{}(0))), iFace, 1, 2, 3), -} - -var iFace interface{} - -func ct(typ reflect.Type, args ...interface{}) []reflect.Value { - value := make([]reflect.Value, len(args)) - for i, v := range args { - x := reflect.ValueOf(v) - if !x.IsValid() { // Make it a typed nil. - x = reflect.Zero(typ) - } else { - x = x.Convert(typ) - } - value[i] = x - } - return value -} - -func TestCompare(t *testing.T) { - for _, test := range compareTests { - for i, v0 := range test { - for j, v1 := range test { - c := fmtsort.Compare(v0, v1) - var expect int - switch { - case i == j: - expect = 0 - // NaNs are tricky. - if typ := v0.Type(); (typ.Kind() == reflect.Float32 || typ.Kind() == reflect.Float64) && math.IsNaN(v0.Float()) { - expect = -1 - } - case i < j: - expect = -1 - case i > j: - expect = 1 - } - if c != expect { - t.Errorf("%s: compare(%v,%v)=%d; expect %d", v0.Type(), v0, v1, c, expect) - } - } - } - } -} - -type sortTest struct { - data interface{} // Always a map. - print string // Printed result using our custom printer. -} - -var sortTests = []sortTest{ - { - map[int]string{7: "bar", -3: "foo"}, - "-3:foo 7:bar", - }, - { - map[uint8]string{7: "bar", 3: "foo"}, - "3:foo 7:bar", - }, - { - map[string]string{"7": "bar", "3": "foo"}, - "3:foo 7:bar", - }, - { - map[float64]string{7: "bar", -3: "foo", math.NaN(): "nan", math.Inf(0): "inf"}, - "NaN:nan -3:foo 7:bar +Inf:inf", - }, - { - map[complex128]string{7 + 2i: "bar2", 7 + 1i: "bar", -3: "foo", complex(math.NaN(), 0i): "nan", complex(math.Inf(0), 0i): "inf"}, - "(NaN+0i):nan (-3+0i):foo (7+1i):bar (7+2i):bar2 (+Inf+0i):inf", - }, - { - map[bool]string{true: "true", false: "false"}, - "false:false true:true", - }, - { - chanMap(), - "CHAN0:0 CHAN1:1 CHAN2:2", - }, - { - pointerMap(), - "PTR0:0 PTR1:1 PTR2:2", - }, - { - map[toy]string{toy{7, 2}: "72", toy{7, 1}: "71", toy{3, 4}: "34"}, - "{3 4}:34 {7 1}:71 {7 2}:72", - }, - { - map[[2]int]string{{7, 2}: "72", {7, 1}: "71", {3, 4}: "34"}, - "[3 4]:34 [7 1]:71 [7 2]:72", - }, - { - map[interface{}]string{7: "7", 4: "4", 3: "3", nil: "nil"}, - ":nil 3:3 4:4 7:7", - }, -} - -func sprint(data interface{}) string { - om := fmtsort.Sort(reflect.ValueOf(data)) - if om == nil { - return "nil" - } - b := new(strings.Builder) - for i, key := range om.Key { - if i > 0 { - b.WriteRune(' ') - } - b.WriteString(sprintKey(key)) - b.WriteRune(':') - b.WriteString(fmt.Sprint(om.Value[i])) - } - return b.String() -} - -// sprintKey formats a reflect.Value but gives reproducible values for some -// problematic types such as pointers. Note that it only does special handling -// for the troublesome types used in the test cases; it is not a general -// printer. -func sprintKey(key reflect.Value) string { - switch str := key.Type().String(); str { - case "*int": - ptr := key.Interface().(*int) - for i := range ints { - if ptr == &ints[i] { - return fmt.Sprintf("PTR%d", i) - } - } - return "PTR???" - case "chan int": - c := key.Interface().(chan int) - for i := range chans { - if c == chans[i] { - return fmt.Sprintf("CHAN%d", i) - } - } - return "CHAN???" - default: - return fmt.Sprint(key) - } -} - -var ( - ints [3]int - chans = [3]chan int{make(chan int), make(chan int), make(chan int)} -) - -func pointerMap() map[*int]string { - m := make(map[*int]string) - for i := 2; i >= 0; i-- { - m[&ints[i]] = fmt.Sprint(i) - } - return m -} - -func chanMap() map[chan int]string { - m := make(map[chan int]string) - for i := 2; i >= 0; i-- { - m[chans[i]] = fmt.Sprint(i) - } - return m -} - -type toy struct { - A int // Exported. - b int // Unexported. -} - -func TestOrder(t *testing.T) { - for _, test := range sortTests { - got := sprint(test.data) - if got != test.print { - t.Errorf("%s: got %q, want %q", reflect.TypeOf(test.data), got, test.print) - } - } -} diff --git a/g/os/gview/internal/text/template/example_test.go b/g/os/gview/internal/text/template/example_test.go deleted file mode 100644 index 9cab2e832..000000000 --- a/g/os/gview/internal/text/template/example_test.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package template_test - -import ( - "log" - "os" - "strings" - "text/template" -) - -func ExampleTemplate() { - // Define a template. - const letter = ` -Dear {{.Name}}, -{{if .Attended}} -It was a pleasure to see you at the wedding. -{{- else}} -It is a shame you couldn't make it to the wedding. -{{- end}} -{{with .Gift -}} -Thank you for the lovely {{.}}. -{{end}} -Best wishes, -Josie -` - - // Prepare some data to insert into the template. - type Recipient struct { - Name, Gift string - Attended bool - } - var recipients = []Recipient{ - {"Aunt Mildred", "bone china tea set", true}, - {"Uncle John", "moleskin pants", false}, - {"Cousin Rodney", "", false}, - } - - // Create a new template and parse the letter into it. - t := template.Must(template.New("letter").Parse(letter)) - - // Execute the template for each recipient. - for _, r := range recipients { - err := t.Execute(os.Stdout, r) - if err != nil { - log.Println("executing template:", err) - } - } - - // Output: - // Dear Aunt Mildred, - // - // It was a pleasure to see you at the wedding. - // Thank you for the lovely bone china tea set. - // - // Best wishes, - // Josie - // - // Dear Uncle John, - // - // It is a shame you couldn't make it to the wedding. - // Thank you for the lovely moleskin pants. - // - // Best wishes, - // Josie - // - // Dear Cousin Rodney, - // - // It is a shame you couldn't make it to the wedding. - // - // Best wishes, - // Josie -} - -// The following example is duplicated in html/template; keep them in sync. - -func ExampleTemplate_block() { - const ( - master = `Names:{{block "list" .}}{{"\n"}}{{range .}}{{println "-" .}}{{end}}{{end}}` - overlay = `{{define "list"}} {{join . ", "}}{{end}} ` - ) - var ( - funcs = template.FuncMap{"join": strings.Join} - guardians = []string{"Gamora", "Groot", "Nebula", "Rocket", "Star-Lord"} - ) - masterTmpl, err := template.New("master").Funcs(funcs).Parse(master) - if err != nil { - log.Fatal(err) - } - overlayTmpl, err := template.Must(masterTmpl.Clone()).Parse(overlay) - if err != nil { - log.Fatal(err) - } - if err := masterTmpl.Execute(os.Stdout, guardians); err != nil { - log.Fatal(err) - } - if err := overlayTmpl.Execute(os.Stdout, guardians); err != nil { - log.Fatal(err) - } - // Output: - // Names: - // - Gamora - // - Groot - // - Nebula - // - Rocket - // - Star-Lord - // Names: Gamora, Groot, Nebula, Rocket, Star-Lord -} diff --git a/g/os/gview/internal/text/template/examplefiles_test.go b/g/os/gview/internal/text/template/examplefiles_test.go deleted file mode 100644 index a15c7a62a..000000000 --- a/g/os/gview/internal/text/template/examplefiles_test.go +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package template_test - -import ( - "io" - "io/ioutil" - "log" - "os" - "path/filepath" - "text/template" -) - -// templateFile defines the contents of a template to be stored in a file, for testing. -type templateFile struct { - name string - contents string -} - -func createTestDir(files []templateFile) string { - dir, err := ioutil.TempDir("", "template") - if err != nil { - log.Fatal(err) - } - for _, file := range files { - f, err := os.Create(filepath.Join(dir, file.name)) - if err != nil { - log.Fatal(err) - } - defer f.Close() - _, err = io.WriteString(f, file.contents) - if err != nil { - log.Fatal(err) - } - } - return dir -} - -// Here we demonstrate loading a set of templates from a directory. -func ExampleTemplate_glob() { - // Here we create a temporary directory and populate it with our sample - // template definition files; usually the template files would already - // exist in some location known to the program. - dir := createTestDir([]templateFile{ - // T0.tmpl is a plain template file that just invokes T1. - {"T0.tmpl", `T0 invokes T1: ({{template "T1"}})`}, - // T1.tmpl defines a template, T1 that invokes T2. - {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`}, - // T2.tmpl defines a template T2. - {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`}, - }) - // Clean up after the test; another quirk of running as an example. - defer os.RemoveAll(dir) - - // pattern is the glob pattern used to find all the template files. - pattern := filepath.Join(dir, "*.tmpl") - - // Here starts the example proper. - // T0.tmpl is the first name matched, so it becomes the starting template, - // the value returned by ParseGlob. - tmpl := template.Must(template.ParseGlob(pattern)) - - err := tmpl.Execute(os.Stdout, nil) - if err != nil { - log.Fatalf("template execution: %s", err) - } - // Output: - // T0 invokes T1: (T1 invokes T2: (This is T2)) -} - -// This example demonstrates one way to share some templates -// and use them in different contexts. In this variant we add multiple driver -// templates by hand to an existing bundle of templates. -func ExampleTemplate_helpers() { - // Here we create a temporary directory and populate it with our sample - // template definition files; usually the template files would already - // exist in some location known to the program. - dir := createTestDir([]templateFile{ - // T1.tmpl defines a template, T1 that invokes T2. - {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`}, - // T2.tmpl defines a template T2. - {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`}, - }) - // Clean up after the test; another quirk of running as an example. - defer os.RemoveAll(dir) - - // pattern is the glob pattern used to find all the template files. - pattern := filepath.Join(dir, "*.tmpl") - - // Here starts the example proper. - // Load the helpers. - templates := template.Must(template.ParseGlob(pattern)) - // Add one driver template to the bunch; we do this with an explicit template definition. - _, err := templates.Parse("{{define `driver1`}}Driver 1 calls T1: ({{template `T1`}})\n{{end}}") - if err != nil { - log.Fatal("parsing driver1: ", err) - } - // Add another driver template. - _, err = templates.Parse("{{define `driver2`}}Driver 2 calls T2: ({{template `T2`}})\n{{end}}") - if err != nil { - log.Fatal("parsing driver2: ", err) - } - // We load all the templates before execution. This package does not require - // that behavior but html/template's escaping does, so it's a good habit. - err = templates.ExecuteTemplate(os.Stdout, "driver1", nil) - if err != nil { - log.Fatalf("driver1 execution: %s", err) - } - err = templates.ExecuteTemplate(os.Stdout, "driver2", nil) - if err != nil { - log.Fatalf("driver2 execution: %s", err) - } - // Output: - // Driver 1 calls T1: (T1 invokes T2: (This is T2)) - // Driver 2 calls T2: (This is T2) -} - -// This example demonstrates how to use one group of driver -// templates with distinct sets of helper templates. -func ExampleTemplate_share() { - // Here we create a temporary directory and populate it with our sample - // template definition files; usually the template files would already - // exist in some location known to the program. - dir := createTestDir([]templateFile{ - // T0.tmpl is a plain template file that just invokes T1. - {"T0.tmpl", "T0 ({{.}} version) invokes T1: ({{template `T1`}})\n"}, - // T1.tmpl defines a template, T1 that invokes T2. Note T2 is not defined - {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`}, - }) - // Clean up after the test; another quirk of running as an example. - defer os.RemoveAll(dir) - - // pattern is the glob pattern used to find all the template files. - pattern := filepath.Join(dir, "*.tmpl") - - // Here starts the example proper. - // Load the drivers. - drivers := template.Must(template.ParseGlob(pattern)) - - // We must define an implementation of the T2 template. First we clone - // the drivers, then add a definition of T2 to the template name space. - - // 1. Clone the helper set to create a new name space from which to run them. - first, err := drivers.Clone() - if err != nil { - log.Fatal("cloning helpers: ", err) - } - // 2. Define T2, version A, and parse it. - _, err = first.Parse("{{define `T2`}}T2, version A{{end}}") - if err != nil { - log.Fatal("parsing T2: ", err) - } - - // Now repeat the whole thing, using a different version of T2. - // 1. Clone the drivers. - second, err := drivers.Clone() - if err != nil { - log.Fatal("cloning drivers: ", err) - } - // 2. Define T2, version B, and parse it. - _, err = second.Parse("{{define `T2`}}T2, version B{{end}}") - if err != nil { - log.Fatal("parsing T2: ", err) - } - - // Execute the templates in the reverse order to verify the - // first is unaffected by the second. - err = second.ExecuteTemplate(os.Stdout, "T0.tmpl", "second") - if err != nil { - log.Fatalf("second execution: %s", err) - } - err = first.ExecuteTemplate(os.Stdout, "T0.tmpl", "first") - if err != nil { - log.Fatalf("first: execution: %s", err) - } - - // Output: - // T0 (second version) invokes T1: (T1 invokes T2: (T2, version B)) - // T0 (first version) invokes T1: (T1 invokes T2: (T2, version A)) -} diff --git a/g/os/gview/internal/text/template/examplefunc_test.go b/g/os/gview/internal/text/template/examplefunc_test.go deleted file mode 100644 index 080b5e3a0..000000000 --- a/g/os/gview/internal/text/template/examplefunc_test.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package template_test - -import ( - "log" - "os" - "strings" - "text/template" -) - -// This example demonstrates a custom function to process template text. -// It installs the strings.Title function and uses it to -// Make Title Text Look Good In Our Template's Output. -func ExampleTemplate_func() { - // First we create a FuncMap with which to register the function. - funcMap := template.FuncMap{ - // The name "title" is what the function will be called in the template text. - "title": strings.Title, - } - - // A simple template definition to test our function. - // We print the input text several ways: - // - the original - // - title-cased - // - title-cased and then printed with %q - // - printed with %q and then title-cased. - const templateText = ` -Input: {{printf "%q" .}} -Output 0: {{title .}} -Output 1: {{title . | printf "%q"}} -Output 2: {{printf "%q" . | title}} -` - - // Create a template, add the function map, and parse the text. - tmpl, err := template.New("titleTest").Funcs(funcMap).Parse(templateText) - if err != nil { - log.Fatalf("parsing: %s", err) - } - - // Run the template to verify the output. - err = tmpl.Execute(os.Stdout, "the go programming language") - if err != nil { - log.Fatalf("execution: %s", err) - } - - // Output: - // Input: "the go programming language" - // Output 0: The Go Programming Language - // Output 1: "The Go Programming Language" - // Output 2: "The Go Programming Language" -} diff --git a/g/os/gview/internal/text/template/exec_test.go b/g/os/gview/internal/text/template/exec_test.go deleted file mode 100644 index bfd6d38bf..000000000 --- a/g/os/gview/internal/text/template/exec_test.go +++ /dev/null @@ -1,1512 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package template - -import ( - "bytes" - "errors" - "flag" - "fmt" - "io/ioutil" - "reflect" - "strings" - "testing" -) - -var debug = flag.Bool("debug", false, "show the errors produced by the tests") - -// T has lots of interesting pieces to use to test execution. -type T struct { - // Basics - True bool - I int - U16 uint16 - X string - FloatZero float64 - ComplexZero complex128 - // Nested structs. - U *U - // Struct with String method. - V0 V - V1, V2 *V - // Struct with Error method. - W0 W - W1, W2 *W - // Slices - SI []int - SIEmpty []int - SB []bool - // Maps - MSI map[string]int - MSIone map[string]int // one element, for deterministic output - MSIEmpty map[string]int - MXI map[interface{}]int - MII map[int]int - MI32S map[int32]string - MI64S map[int64]string - MUI32S map[uint32]string - MUI64S map[uint64]string - MI8S map[int8]string - MUI8S map[uint8]string - SMSI []map[string]int - // Empty interfaces; used to see if we can dig inside one. - Empty0 interface{} // nil - Empty1 interface{} - Empty2 interface{} - Empty3 interface{} - Empty4 interface{} - // Non-empty interfaces. - NonEmptyInterface I - NonEmptyInterfacePtS *I - // Stringer. - Str fmt.Stringer - Err error - // Pointers - PI *int - PS *string - PSI *[]int - NIL *int - // Function (not method) - BinaryFunc func(string, string) string - VariadicFunc func(...string) string - VariadicFuncInt func(int, ...string) string - NilOKFunc func(*int) bool - ErrFunc func() (string, error) - PanicFunc func() string - // Template to test evaluation of templates. - Tmpl *Template - // Unexported field; cannot be accessed by template. - unexported int -} - -type S []string - -func (S) Method0() string { - return "M0" -} - -type U struct { - V string -} - -type V struct { - j int -} - -func (v *V) String() string { - if v == nil { - return "nilV" - } - return fmt.Sprintf("<%d>", v.j) -} - -type W struct { - k int -} - -func (w *W) Error() string { - if w == nil { - return "nilW" - } - return fmt.Sprintf("[%d]", w.k) -} - -var siVal = I(S{"a", "b"}) - -var tVal = &T{ - True: true, - I: 17, - U16: 16, - X: "x", - U: &U{"v"}, - V0: V{6666}, - V1: &V{7777}, // leave V2 as nil - W0: W{888}, - W1: &W{999}, // leave W2 as nil - SI: []int{3, 4, 5}, - SB: []bool{true, false}, - MSI: map[string]int{"one": 1, "two": 2, "three": 3}, - MSIone: map[string]int{"one": 1}, - MXI: map[interface{}]int{"one": 1}, - MII: map[int]int{1: 1}, - MI32S: map[int32]string{1: "one", 2: "two"}, - MI64S: map[int64]string{2: "i642", 3: "i643"}, - MUI32S: map[uint32]string{2: "u322", 3: "u323"}, - MUI64S: map[uint64]string{2: "ui642", 3: "ui643"}, - MI8S: map[int8]string{2: "i82", 3: "i83"}, - MUI8S: map[uint8]string{2: "u82", 3: "u83"}, - SMSI: []map[string]int{ - {"one": 1, "two": 2}, - {"eleven": 11, "twelve": 12}, - }, - Empty1: 3, - Empty2: "empty2", - Empty3: []int{7, 8}, - Empty4: &U{"UinEmpty"}, - NonEmptyInterface: &T{X: "x"}, - NonEmptyInterfacePtS: &siVal, - Str: bytes.NewBuffer([]byte("foozle")), - Err: errors.New("erroozle"), - PI: newInt(23), - PS: newString("a string"), - PSI: newIntSlice(21, 22, 23), - BinaryFunc: func(a, b string) string { return fmt.Sprintf("[%s=%s]", a, b) }, - VariadicFunc: func(s ...string) string { return fmt.Sprint("<", strings.Join(s, "+"), ">") }, - VariadicFuncInt: func(a int, s ...string) string { return fmt.Sprint(a, "=<", strings.Join(s, "+"), ">") }, - NilOKFunc: func(s *int) bool { return s == nil }, - ErrFunc: func() (string, error) { return "bla", nil }, - PanicFunc: func() string { panic("test panic") }, - Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X -} - -var tSliceOfNil = []*T{nil} - -// A non-empty interface. -type I interface { - Method0() string -} - -var iVal I = tVal - -// Helpers for creation. -func newInt(n int) *int { - return &n -} - -func newString(s string) *string { - return &s -} - -func newIntSlice(n ...int) *[]int { - p := new([]int) - *p = make([]int, len(n)) - copy(*p, n) - return p -} - -// Simple methods with and without arguments. -func (t *T) Method0() string { - return "M0" -} - -func (t *T) Method1(a int) int { - return a -} - -func (t *T) Method2(a uint16, b string) string { - return fmt.Sprintf("Method2: %d %s", a, b) -} - -func (t *T) Method3(v interface{}) string { - return fmt.Sprintf("Method3: %v", v) -} - -func (t *T) Copy() *T { - n := new(T) - *n = *t - return n -} - -func (t *T) MAdd(a int, b []int) []int { - v := make([]int, len(b)) - for i, x := range b { - v[i] = x + a - } - return v -} - -var myError = errors.New("my error") - -// MyError returns a value and an error according to its argument. -func (t *T) MyError(error bool) (bool, error) { - if error { - return true, myError - } - return false, nil -} - -// A few methods to test chaining. -func (t *T) GetU() *U { - return t.U -} - -func (u *U) TrueFalse(b bool) string { - if b { - return "true" - } - return "" -} - -func typeOf(arg interface{}) string { - return fmt.Sprintf("%T", arg) -} - -type execTest struct { - name string - input string - output string - data interface{} - ok bool -} - -// bigInt and bigUint are hex string representing numbers either side -// of the max int boundary. -// We do it this way so the test doesn't depend on ints being 32 bits. -var ( - bigInt = fmt.Sprintf("0x%x", int(1<", tVal, true}, - {"map .one interface", "{{.MXI.one}}", "1", tVal, true}, - {"map .WRONG args", "{{.MSI.one 1}}", "", tVal, false}, - {"map .WRONG type", "{{.MII.one}}", "", tVal, false}, - - // Dots of all kinds to test basic evaluation. - {"dot int", "<{{.}}>", "<13>", 13, true}, - {"dot uint", "<{{.}}>", "<14>", uint(14), true}, - {"dot float", "<{{.}}>", "<15.1>", 15.1, true}, - {"dot bool", "<{{.}}>", "", true, true}, - {"dot complex", "<{{.}}>", "<(16.2-17i)>", 16.2 - 17i, true}, - {"dot string", "<{{.}}>", "", "hello", true}, - {"dot slice", "<{{.}}>", "<[-1 -2 -3]>", []int{-1, -2, -3}, true}, - {"dot map", "<{{.}}>", "", map[string]int{"two": 22}, true}, - {"dot struct", "<{{.}}>", "<{7 seven}>", struct { - a int - b string - }{7, "seven"}, true}, - - // Variables. - {"$ int", "{{$}}", "123", 123, true}, - {"$.I", "{{$.I}}", "17", tVal, true}, - {"$.U.V", "{{$.U.V}}", "v", tVal, true}, - {"declare in action", "{{$x := $.U.V}}{{$x}}", "v", tVal, true}, - {"simple assignment", "{{$x := 2}}{{$x = 3}}{{$x}}", "3", tVal, true}, - {"nested assignment", - "{{$x := 2}}{{if true}}{{$x = 3}}{{end}}{{$x}}", - "3", tVal, true}, - {"nested assignment changes the last declaration", - "{{$x := 1}}{{if true}}{{$x := 2}}{{if true}}{{$x = 3}}{{end}}{{end}}{{$x}}", - "1", tVal, true}, - - // Type with String method. - {"V{6666}.String()", "-{{.V0}}-", "-<6666>-", tVal, true}, - {"&V{7777}.String()", "-{{.V1}}-", "-<7777>-", tVal, true}, - {"(*V)(nil).String()", "-{{.V2}}-", "-nilV-", tVal, true}, - - // Type with Error method. - {"W{888}.Error()", "-{{.W0}}-", "-[888]-", tVal, true}, - {"&W{999}.Error()", "-{{.W1}}-", "-[999]-", tVal, true}, - {"(*W)(nil).Error()", "-{{.W2}}-", "-nilW-", tVal, true}, - - // Pointers. - {"*int", "{{.PI}}", "23", tVal, true}, - {"*string", "{{.PS}}", "a string", tVal, true}, - {"*[]int", "{{.PSI}}", "[21 22 23]", tVal, true}, - {"*[]int[1]", "{{index .PSI 1}}", "22", tVal, true}, - {"NIL", "{{.NIL}}", "", tVal, true}, - - // Empty interfaces holding values. - {"empty nil", "{{.Empty0}}", "", tVal, true}, - {"empty with int", "{{.Empty1}}", "3", tVal, true}, - {"empty with string", "{{.Empty2}}", "empty2", tVal, true}, - {"empty with slice", "{{.Empty3}}", "[7 8]", tVal, true}, - {"empty with struct", "{{.Empty4}}", "{UinEmpty}", tVal, true}, - {"empty with struct, field", "{{.Empty4.V}}", "UinEmpty", tVal, true}, - - // Edge cases with with an interface value - {"field on interface", "{{.foo}}", "", nil, true}, - {"field on parenthesized interface", "{{(.).foo}}", "", nil, true}, - - // Method calls. - {".Method0", "-{{.Method0}}-", "-M0-", tVal, true}, - {".Method1(1234)", "-{{.Method1 1234}}-", "-1234-", tVal, true}, - {".Method1(.I)", "-{{.Method1 .I}}-", "-17-", tVal, true}, - {".Method2(3, .X)", "-{{.Method2 3 .X}}-", "-Method2: 3 x-", tVal, true}, - {".Method2(.U16, `str`)", "-{{.Method2 .U16 `str`}}-", "-Method2: 16 str-", tVal, true}, - {".Method2(.U16, $x)", "{{if $x := .X}}-{{.Method2 .U16 $x}}{{end}}-", "-Method2: 16 x-", tVal, true}, - {".Method3(nil constant)", "-{{.Method3 nil}}-", "-Method3: -", tVal, true}, - {".Method3(nil value)", "-{{.Method3 .MXI.unset}}-", "-Method3: -", tVal, true}, - {"method on var", "{{if $x := .}}-{{$x.Method2 .U16 $x.X}}{{end}}-", "-Method2: 16 x-", tVal, true}, - {"method on chained var", - "{{range .MSIone}}{{if $.U.TrueFalse $.True}}{{$.U.TrueFalse $.True}}{{else}}WRONG{{end}}{{end}}", - "true", tVal, true}, - {"chained method", - "{{range .MSIone}}{{if $.GetU.TrueFalse $.True}}{{$.U.TrueFalse $.True}}{{else}}WRONG{{end}}{{end}}", - "true", tVal, true}, - {"chained method on variable", - "{{with $x := .}}{{with .SI}}{{$.GetU.TrueFalse $.True}}{{end}}{{end}}", - "true", tVal, true}, - {".NilOKFunc not nil", "{{call .NilOKFunc .PI}}", "false", tVal, true}, - {".NilOKFunc nil", "{{call .NilOKFunc nil}}", "true", tVal, true}, - {"method on nil value from slice", "-{{range .}}{{.Method1 1234}}{{end}}-", "-1234-", tSliceOfNil, true}, - - // Function call builtin. - {".BinaryFunc", "{{call .BinaryFunc `1` `2`}}", "[1=2]", tVal, true}, - {".VariadicFunc0", "{{call .VariadicFunc}}", "<>", tVal, true}, - {".VariadicFunc2", "{{call .VariadicFunc `he` `llo`}}", "", tVal, true}, - {".VariadicFuncInt", "{{call .VariadicFuncInt 33 `he` `llo`}}", "33=", tVal, true}, - {"if .BinaryFunc call", "{{ if .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{end}}", "[1=2]", tVal, true}, - {"if not .BinaryFunc call", "{{ if not .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{else}}No{{end}}", "No", tVal, true}, - {"Interface Call", `{{stringer .S}}`, "foozle", map[string]interface{}{"S": bytes.NewBufferString("foozle")}, true}, - {".ErrFunc", "{{call .ErrFunc}}", "bla", tVal, true}, - {"call nil", "{{call nil}}", "", tVal, false}, - - // Erroneous function calls (check args). - {".BinaryFuncTooFew", "{{call .BinaryFunc `1`}}", "", tVal, false}, - {".BinaryFuncTooMany", "{{call .BinaryFunc `1` `2` `3`}}", "", tVal, false}, - {".BinaryFuncBad0", "{{call .BinaryFunc 1 3}}", "", tVal, false}, - {".BinaryFuncBad1", "{{call .BinaryFunc `1` 3}}", "", tVal, false}, - {".VariadicFuncBad0", "{{call .VariadicFunc 3}}", "", tVal, false}, - {".VariadicFuncIntBad0", "{{call .VariadicFuncInt}}", "", tVal, false}, - {".VariadicFuncIntBad`", "{{call .VariadicFuncInt `x`}}", "", tVal, false}, - {".VariadicFuncNilBad", "{{call .VariadicFunc nil}}", "", tVal, false}, - - // Pipelines. - {"pipeline", "-{{.Method0 | .Method2 .U16}}-", "-Method2: 16 M0-", tVal, true}, - {"pipeline func", "-{{call .VariadicFunc `llo` | call .VariadicFunc `he` }}-", "->-", tVal, true}, - - // Nil values aren't missing arguments. - {"nil pipeline", "{{ .Empty0 | call .NilOKFunc }}", "true", tVal, true}, - {"nil call arg", "{{ call .NilOKFunc .Empty0 }}", "true", tVal, true}, - {"bad nil pipeline", "{{ .Empty0 | .VariadicFunc }}", "", tVal, false}, - - // Parenthesized expressions - {"parens in pipeline", "{{printf `%d %d %d` (1) (2 | add 3) (add 4 (add 5 6))}}", "1 5 15", tVal, true}, - - // Parenthesized expressions with field accesses - {"parens: $ in paren", "{{($).X}}", "x", tVal, true}, - {"parens: $.GetU in paren", "{{($.GetU).V}}", "v", tVal, true}, - {"parens: $ in paren in pipe", "{{($ | echo).X}}", "x", tVal, true}, - {"parens: spaces and args", `{{(makemap "up" "down" "left" "right").left}}`, "right", tVal, true}, - - // If. - {"if true", "{{if true}}TRUE{{end}}", "TRUE", tVal, true}, - {"if false", "{{if false}}TRUE{{else}}FALSE{{end}}", "FALSE", tVal, true}, - {"if nil", "{{if nil}}TRUE{{end}}", "", tVal, false}, - {"if 1", "{{if 1}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true}, - {"if 0", "{{if 0}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, - {"if 1.5", "{{if 1.5}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true}, - {"if 0.0", "{{if .FloatZero}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, - {"if 1.5i", "{{if 1.5i}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true}, - {"if 0.0i", "{{if .ComplexZero}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, - {"if emptystring", "{{if ``}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, - {"if string", "{{if `notempty`}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true}, - {"if emptyslice", "{{if .SIEmpty}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, - {"if slice", "{{if .SI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true}, - {"if emptymap", "{{if .MSIEmpty}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, - {"if map", "{{if .MSI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true}, - {"if map unset", "{{if .MXI.none}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, - {"if map not unset", "{{if not .MXI.none}}ZERO{{else}}NON-ZERO{{end}}", "ZERO", tVal, true}, - {"if $x with $y int", "{{if $x := true}}{{with $y := .I}}{{$x}},{{$y}}{{end}}{{end}}", "true,17", tVal, true}, - {"if $x with $x int", "{{if $x := true}}{{with $x := .I}}{{$x}},{{end}}{{$x}}{{end}}", "17,true", tVal, true}, - {"if else if", "{{if false}}FALSE{{else if true}}TRUE{{end}}", "TRUE", tVal, true}, - {"if else chain", "{{if eq 1 3}}1{{else if eq 2 3}}2{{else if eq 3 3}}3{{end}}", "3", tVal, true}, - - // Print etc. - {"print", `{{print "hello, print"}}`, "hello, print", tVal, true}, - {"print 123", `{{print 1 2 3}}`, "1 2 3", tVal, true}, - {"print nil", `{{print nil}}`, "", tVal, true}, - {"println", `{{println 1 2 3}}`, "1 2 3\n", tVal, true}, - {"printf int", `{{printf "%04x" 127}}`, "007f", tVal, true}, - {"printf float", `{{printf "%g" 3.5}}`, "3.5", tVal, true}, - {"printf complex", `{{printf "%g" 1+7i}}`, "(1+7i)", tVal, true}, - {"printf string", `{{printf "%s" "hello"}}`, "hello", tVal, true}, - {"printf function", `{{printf "%#q" zeroArgs}}`, "`zeroArgs`", tVal, true}, - {"printf field", `{{printf "%s" .U.V}}`, "v", tVal, true}, - {"printf method", `{{printf "%s" .Method0}}`, "M0", tVal, true}, - {"printf dot", `{{with .I}}{{printf "%d" .}}{{end}}`, "17", tVal, true}, - {"printf var", `{{with $x := .I}}{{printf "%d" $x}}{{end}}`, "17", tVal, true}, - {"printf lots", `{{printf "%d %s %g %s" 127 "hello" 7-3i .Method0}}`, "127 hello (7-3i) M0", tVal, true}, - - // HTML. - {"html", `{{html ""}}`, - "<script>alert("XSS");</script>", nil, true}, - {"html pipeline", `{{printf "" | html}}`, - "<script>alert("XSS");</script>", nil, true}, - {"html", `{{html .PS}}`, "a string", tVal, true}, - {"html typed nil", `{{html .NIL}}`, "<nil>", tVal, true}, - {"html untyped nil", `{{html .Empty0}}`, "<no value>", tVal, true}, - - // JavaScript. - {"js", `{{js .}}`, `It\'d be nice.`, `It'd be nice.`, true}, - - // URL query. - {"urlquery", `{{"http://www.example.org/"|urlquery}}`, "http%3A%2F%2Fwww.example.org%2F", nil, true}, - - // Booleans - {"not", "{{not true}} {{not false}}", "false true", nil, true}, - {"and", "{{and false 0}} {{and 1 0}} {{and 0 true}} {{and 1 1}}", "false 0 0 1", nil, true}, - {"or", "{{or 0 0}} {{or 1 0}} {{or 0 true}} {{or 1 1}}", "0 1 true 1", nil, true}, - {"boolean if", "{{if and true 1 `hi`}}TRUE{{else}}FALSE{{end}}", "TRUE", tVal, true}, - {"boolean if not", "{{if and true 1 `hi` | not}}TRUE{{else}}FALSE{{end}}", "FALSE", nil, true}, - - // Indexing. - {"slice[0]", "{{index .SI 0}}", "3", tVal, true}, - {"slice[1]", "{{index .SI 1}}", "4", tVal, true}, - {"slice[HUGE]", "{{index .SI 10}}", "", tVal, false}, - {"slice[WRONG]", "{{index .SI `hello`}}", "", tVal, false}, - {"slice[nil]", "{{index .SI nil}}", "", tVal, false}, - {"map[one]", "{{index .MSI `one`}}", "1", tVal, true}, - {"map[two]", "{{index .MSI `two`}}", "2", tVal, true}, - {"map[NO]", "{{index .MSI `XXX`}}", "0", tVal, true}, - {"map[nil]", "{{index .MSI nil}}", "", tVal, false}, - {"map[``]", "{{index .MSI ``}}", "0", tVal, true}, - {"map[WRONG]", "{{index .MSI 10}}", "", tVal, false}, - {"double index", "{{index .SMSI 1 `eleven`}}", "11", tVal, true}, - {"nil[1]", "{{index nil 1}}", "", tVal, false}, - {"map MI64S", "{{index .MI64S 2}}", "i642", tVal, true}, - {"map MI32S", "{{index .MI32S 2}}", "two", tVal, true}, - {"map MUI64S", "{{index .MUI64S 3}}", "ui643", tVal, true}, - {"map MI8S", "{{index .MI8S 3}}", "i83", tVal, true}, - {"map MUI8S", "{{index .MUI8S 2}}", "u82", tVal, true}, - - // Len. - {"slice", "{{len .SI}}", "3", tVal, true}, - {"map", "{{len .MSI }}", "3", tVal, true}, - {"len of int", "{{len 3}}", "", tVal, false}, - {"len of nothing", "{{len .Empty0}}", "", tVal, false}, - - // With. - {"with true", "{{with true}}{{.}}{{end}}", "true", tVal, true}, - {"with false", "{{with false}}{{.}}{{else}}FALSE{{end}}", "FALSE", tVal, true}, - {"with 1", "{{with 1}}{{.}}{{else}}ZERO{{end}}", "1", tVal, true}, - {"with 0", "{{with 0}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true}, - {"with 1.5", "{{with 1.5}}{{.}}{{else}}ZERO{{end}}", "1.5", tVal, true}, - {"with 0.0", "{{with .FloatZero}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true}, - {"with 1.5i", "{{with 1.5i}}{{.}}{{else}}ZERO{{end}}", "(0+1.5i)", tVal, true}, - {"with 0.0i", "{{with .ComplexZero}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true}, - {"with emptystring", "{{with ``}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, - {"with string", "{{with `notempty`}}{{.}}{{else}}EMPTY{{end}}", "notempty", tVal, true}, - {"with emptyslice", "{{with .SIEmpty}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, - {"with slice", "{{with .SI}}{{.}}{{else}}EMPTY{{end}}", "[3 4 5]", tVal, true}, - {"with emptymap", "{{with .MSIEmpty}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, - {"with map", "{{with .MSIone}}{{.}}{{else}}EMPTY{{end}}", "map[one:1]", tVal, true}, - {"with empty interface, struct field", "{{with .Empty4}}{{.V}}{{end}}", "UinEmpty", tVal, true}, - {"with $x int", "{{with $x := .I}}{{$x}}{{end}}", "17", tVal, true}, - {"with $x struct.U.V", "{{with $x := $}}{{$x.U.V}}{{end}}", "v", tVal, true}, - {"with variable and action", "{{with $x := $}}{{$y := $.U.V}}{{$y}}{{end}}", "v", tVal, true}, - - // Range. - {"range []int", "{{range .SI}}-{{.}}-{{end}}", "-3--4--5-", tVal, true}, - {"range empty no else", "{{range .SIEmpty}}-{{.}}-{{end}}", "", tVal, true}, - {"range []int else", "{{range .SI}}-{{.}}-{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true}, - {"range empty else", "{{range .SIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, - {"range []bool", "{{range .SB}}-{{.}}-{{end}}", "-true--false-", tVal, true}, - {"range []int method", "{{range .SI | .MAdd .I}}-{{.}}-{{end}}", "-20--21--22-", tVal, true}, - {"range map", "{{range .MSI}}-{{.}}-{{end}}", "-1--3--2-", tVal, true}, - {"range empty map no else", "{{range .MSIEmpty}}-{{.}}-{{end}}", "", tVal, true}, - {"range map else", "{{range .MSI}}-{{.}}-{{else}}EMPTY{{end}}", "-1--3--2-", tVal, true}, - {"range empty map else", "{{range .MSIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, - {"range empty interface", "{{range .Empty3}}-{{.}}-{{else}}EMPTY{{end}}", "-7--8-", tVal, true}, - {"range empty nil", "{{range .Empty0}}-{{.}}-{{end}}", "", tVal, true}, - {"range $x SI", "{{range $x := .SI}}<{{$x}}>{{end}}", "<3><4><5>", tVal, true}, - {"range $x $y SI", "{{range $x, $y := .SI}}<{{$x}}={{$y}}>{{end}}", "<0=3><1=4><2=5>", tVal, true}, - {"range $x MSIone", "{{range $x := .MSIone}}<{{$x}}>{{end}}", "<1>", tVal, true}, - {"range $x $y MSIone", "{{range $x, $y := .MSIone}}<{{$x}}={{$y}}>{{end}}", "", tVal, true}, - {"range $x PSI", "{{range $x := .PSI}}<{{$x}}>{{end}}", "<21><22><23>", tVal, true}, - {"declare in range", "{{range $x := .PSI}}<{{$foo:=$x}}{{$x}}>{{end}}", "<21><22><23>", tVal, true}, - {"range count", `{{range $i, $x := count 5}}[{{$i}}]{{$x}}{{end}}`, "[0]a[1]b[2]c[3]d[4]e", tVal, true}, - {"range nil count", `{{range $i, $x := count 0}}{{else}}empty{{end}}`, "empty", tVal, true}, - - // Cute examples. - {"or as if true", `{{or .SI "slice is empty"}}`, "[3 4 5]", tVal, true}, - {"or as if false", `{{or .SIEmpty "slice is empty"}}`, "slice is empty", tVal, true}, - - // Error handling. - {"error method, error", "{{.MyError true}}", "", tVal, false}, - {"error method, no error", "{{.MyError false}}", "false", tVal, true}, - - // Fixed bugs. - // Must separate dot and receiver; otherwise args are evaluated with dot set to variable. - {"bug0", "{{range .MSIone}}{{if $.Method1 .}}X{{end}}{{end}}", "X", tVal, true}, - // Do not loop endlessly in indirect for non-empty interfaces. - // The bug appears with *interface only; looped forever. - {"bug1", "{{.Method0}}", "M0", &iVal, true}, - // Was taking address of interface field, so method set was empty. - {"bug2", "{{$.NonEmptyInterface.Method0}}", "M0", tVal, true}, - // Struct values were not legal in with - mere oversight. - {"bug3", "{{with $}}{{.Method0}}{{end}}", "M0", tVal, true}, - // Nil interface values in if. - {"bug4", "{{if .Empty0}}non-nil{{else}}nil{{end}}", "nil", tVal, true}, - // Stringer. - {"bug5", "{{.Str}}", "foozle", tVal, true}, - {"bug5a", "{{.Err}}", "erroozle", tVal, true}, - // Args need to be indirected and dereferenced sometimes. - {"bug6a", "{{vfunc .V0 .V1}}", "vfunc", tVal, true}, - {"bug6b", "{{vfunc .V0 .V0}}", "vfunc", tVal, true}, - {"bug6c", "{{vfunc .V1 .V0}}", "vfunc", tVal, true}, - {"bug6d", "{{vfunc .V1 .V1}}", "vfunc", tVal, true}, - // Legal parse but illegal execution: non-function should have no arguments. - {"bug7a", "{{3 2}}", "", tVal, false}, - {"bug7b", "{{$x := 1}}{{$x 2}}", "", tVal, false}, - {"bug7c", "{{$x := 1}}{{3 | $x}}", "", tVal, false}, - // Pipelined arg was not being type-checked. - {"bug8a", "{{3|oneArg}}", "", tVal, false}, - {"bug8b", "{{4|dddArg 3}}", "", tVal, false}, - // A bug was introduced that broke map lookups for lower-case names. - {"bug9", "{{.cause}}", "neglect", map[string]string{"cause": "neglect"}, true}, - // Field chain starting with function did not work. - {"bug10", "{{mapOfThree.three}}-{{(mapOfThree).three}}", "3-3", 0, true}, - // Dereferencing nil pointer while evaluating function arguments should not panic. Issue 7333. - {"bug11", "{{valueString .PS}}", "", T{}, false}, - // 0xef gave constant type float64. Issue 8622. - {"bug12xe", "{{printf `%T` 0xef}}", "int", T{}, true}, - {"bug12xE", "{{printf `%T` 0xEE}}", "int", T{}, true}, - {"bug12Xe", "{{printf `%T` 0Xef}}", "int", T{}, true}, - {"bug12XE", "{{printf `%T` 0XEE}}", "int", T{}, true}, - // Chained nodes did not work as arguments. Issue 8473. - {"bug13", "{{print (.Copy).I}}", "17", tVal, true}, - // Didn't protect against nil or literal values in field chains. - {"bug14a", "{{(nil).True}}", "", tVal, false}, - {"bug14b", "{{$x := nil}}{{$x.anything}}", "", tVal, false}, - {"bug14c", `{{$x := (1.0)}}{{$y := ("hello")}}{{$x.anything}}{{$y.true}}`, "", tVal, false}, - // Didn't call validateType on function results. Issue 10800. - {"bug15", "{{valueString returnInt}}", "", tVal, false}, - // Variadic function corner cases. Issue 10946. - {"bug16a", "{{true|printf}}", "", tVal, false}, - {"bug16b", "{{1|printf}}", "", tVal, false}, - {"bug16c", "{{1.1|printf}}", "", tVal, false}, - {"bug16d", "{{'x'|printf}}", "", tVal, false}, - {"bug16e", "{{0i|printf}}", "", tVal, false}, - {"bug16f", "{{true|twoArgs \"xxx\"}}", "", tVal, false}, - {"bug16g", "{{\"aaa\" |twoArgs \"bbb\"}}", "twoArgs=bbbaaa", tVal, true}, - {"bug16h", "{{1|oneArg}}", "", tVal, false}, - {"bug16i", "{{\"aaa\"|oneArg}}", "oneArg=aaa", tVal, true}, - {"bug16j", "{{1+2i|printf \"%v\"}}", "(1+2i)", tVal, true}, - {"bug16k", "{{\"aaa\"|printf }}", "aaa", tVal, true}, - {"bug17a", "{{.NonEmptyInterface.X}}", "x", tVal, true}, - {"bug17b", "-{{.NonEmptyInterface.Method1 1234}}-", "-1234-", tVal, true}, - {"bug17c", "{{len .NonEmptyInterfacePtS}}", "2", tVal, true}, - {"bug17d", "{{index .NonEmptyInterfacePtS 0}}", "a", tVal, true}, - {"bug17e", "{{range .NonEmptyInterfacePtS}}-{{.}}-{{end}}", "-a--b-", tVal, true}, -} - -func zeroArgs() string { - return "zeroArgs" -} - -func oneArg(a string) string { - return "oneArg=" + a -} - -func twoArgs(a, b string) string { - return "twoArgs=" + a + b -} - -func dddArg(a int, b ...string) string { - return fmt.Sprintln(a, b) -} - -// count returns a channel that will deliver n sequential 1-letter strings starting at "a" -func count(n int) chan string { - if n == 0 { - return nil - } - c := make(chan string) - go func() { - for i := 0; i < n; i++ { - c <- "abcdefghijklmnop"[i : i+1] - } - close(c) - }() - return c -} - -// vfunc takes a *V and a V -func vfunc(V, *V) string { - return "vfunc" -} - -// valueString takes a string, not a pointer. -func valueString(v string) string { - return "value is ignored" -} - -// returnInt returns an int -func returnInt() int { - return 7 -} - -func add(args ...int) int { - sum := 0 - for _, x := range args { - sum += x - } - return sum -} - -func echo(arg interface{}) interface{} { - return arg -} - -func makemap(arg ...string) map[string]string { - if len(arg)%2 != 0 { - panic("bad makemap") - } - m := make(map[string]string) - for i := 0; i < len(arg); i += 2 { - m[arg[i]] = arg[i+1] - } - return m -} - -func stringer(s fmt.Stringer) string { - return s.String() -} - -func mapOfThree() interface{} { - return map[string]int{"three": 3} -} - -func testExecute(execTests []execTest, template *Template, t *testing.T) { - b := new(bytes.Buffer) - funcs := FuncMap{ - "add": add, - "count": count, - "dddArg": dddArg, - "echo": echo, - "makemap": makemap, - "mapOfThree": mapOfThree, - "oneArg": oneArg, - "returnInt": returnInt, - "stringer": stringer, - "twoArgs": twoArgs, - "typeOf": typeOf, - "valueString": valueString, - "vfunc": vfunc, - "zeroArgs": zeroArgs, - } - for _, test := range execTests { - var tmpl *Template - var err error - if template == nil { - tmpl, err = New(test.name).Funcs(funcs).Parse(test.input) - } else { - tmpl, err = template.New(test.name).Funcs(funcs).Parse(test.input) - } - if err != nil { - t.Errorf("%s: parse error: %s", test.name, err) - continue - } - b.Reset() - err = tmpl.Execute(b, test.data) - switch { - case !test.ok && err == nil: - t.Errorf("%s: expected error; got none", test.name) - continue - case test.ok && err != nil: - t.Errorf("%s: unexpected execute error: %s", test.name, err) - continue - case !test.ok && err != nil: - // expected error, got one - if *debug { - fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err) - } - } - result := b.String() - if result != test.output { - t.Errorf("%s: expected\n\t%q\ngot\n\t%q", test.name, test.output, result) - } - } -} - -func TestExecute(t *testing.T) { - testExecute(execTests, nil, t) -} - -var delimPairs = []string{ - "", "", // default - "{{", "}}", // same as default - "<<", ">>", // distinct - "|", "|", // same - "(日)", "(本)", // peculiar -} - -func TestDelims(t *testing.T) { - const hello = "Hello, world" - var value = struct{ Str string }{hello} - for i := 0; i < len(delimPairs); i += 2 { - text := ".Str" - left := delimPairs[i+0] - trueLeft := left - right := delimPairs[i+1] - trueRight := right - if left == "" { // default case - trueLeft = "{{" - } - if right == "" { // default case - trueRight = "}}" - } - text = trueLeft + text + trueRight - // Now add a comment - text += trueLeft + "/*comment*/" + trueRight - // Now add an action containing a string. - text += trueLeft + `"` + trueLeft + `"` + trueRight - // At this point text looks like `{{.Str}}{{/*comment*/}}{{"{{"}}`. - tmpl, err := New("delims").Delims(left, right).Parse(text) - if err != nil { - t.Fatalf("delim %q text %q parse err %s", left, text, err) - } - var b = new(bytes.Buffer) - err = tmpl.Execute(b, value) - if err != nil { - t.Fatalf("delim %q exec err %s", left, err) - } - if b.String() != hello+trueLeft { - t.Errorf("expected %q got %q", hello+trueLeft, b.String()) - } - } -} - -// Check that an error from a method flows back to the top. -func TestExecuteError(t *testing.T) { - b := new(bytes.Buffer) - tmpl := New("error") - _, err := tmpl.Parse("{{.MyError true}}") - if err != nil { - t.Fatalf("parse error: %s", err) - } - err = tmpl.Execute(b, tVal) - if err == nil { - t.Errorf("expected error; got none") - } else if !strings.Contains(err.Error(), myError.Error()) { - if *debug { - fmt.Printf("test execute error: %s\n", err) - } - t.Errorf("expected myError; got %s", err) - } -} - -const execErrorText = `line 1 -line 2 -line 3 -{{template "one" .}} -{{define "one"}}{{template "two" .}}{{end}} -{{define "two"}}{{template "three" .}}{{end}} -{{define "three"}}{{index "hi" $}}{{end}}` - -// Check that an error from a nested template contains all the relevant information. -func TestExecError(t *testing.T) { - tmpl, err := New("top").Parse(execErrorText) - if err != nil { - t.Fatal("parse error:", err) - } - var b bytes.Buffer - err = tmpl.Execute(&b, 5) // 5 is out of range indexing "hi" - if err == nil { - t.Fatal("expected error") - } - const want = `template: top:7:20: executing "three" at : error calling index: index out of range: 5` - got := err.Error() - if got != want { - t.Errorf("expected\n%q\ngot\n%q", want, got) - } -} - -func TestJSEscaping(t *testing.T) { - testCases := []struct { - in, exp string - }{ - {`a`, `a`}, - {`'foo`, `\'foo`}, - {`Go "jump" \`, `Go \"jump\" \\`}, - {`Yukihiro says "今日は世界"`, `Yukihiro says \"今日は世界\"`}, - {"unprintable \uFDFF", `unprintable \uFDFF`}, - {``, `\x3Chtml\x3E`}, - } - for _, tc := range testCases { - s := JSEscapeString(tc.in) - if s != tc.exp { - t.Errorf("JS escaping [%s] got [%s] want [%s]", tc.in, s, tc.exp) - } - } -} - -// A nice example: walk a binary tree. - -type Tree struct { - Val int - Left, Right *Tree -} - -// Use different delimiters to test Set.Delims. -// Also test the trimming of leading and trailing spaces. -const treeTemplate = ` - (- define "tree" -) - [ - (- .Val -) - (- with .Left -) - (template "tree" . -) - (- end -) - (- with .Right -) - (- template "tree" . -) - (- end -) - ] - (- end -) -` - -func TestTree(t *testing.T) { - var tree = &Tree{ - 1, - &Tree{ - 2, &Tree{ - 3, - &Tree{ - 4, nil, nil, - }, - nil, - }, - &Tree{ - 5, - &Tree{ - 6, nil, nil, - }, - nil, - }, - }, - &Tree{ - 7, - &Tree{ - 8, - &Tree{ - 9, nil, nil, - }, - nil, - }, - &Tree{ - 10, - &Tree{ - 11, nil, nil, - }, - nil, - }, - }, - } - tmpl, err := New("root").Delims("(", ")").Parse(treeTemplate) - if err != nil { - t.Fatal("parse error:", err) - } - var b bytes.Buffer - const expect = "[1[2[3[4]][5[6]]][7[8[9]][10[11]]]]" - // First by looking up the template. - err = tmpl.Lookup("tree").Execute(&b, tree) - if err != nil { - t.Fatal("exec error:", err) - } - result := b.String() - if result != expect { - t.Errorf("expected %q got %q", expect, result) - } - // Then direct to execution. - b.Reset() - err = tmpl.ExecuteTemplate(&b, "tree", tree) - if err != nil { - t.Fatal("exec error:", err) - } - result = b.String() - if result != expect { - t.Errorf("expected %q got %q", expect, result) - } -} - -func TestExecuteOnNewTemplate(t *testing.T) { - // This is issue 3872. - New("Name").Templates() - // This is issue 11379. - new(Template).Templates() - new(Template).Parse("") - new(Template).New("abc").Parse("") - new(Template).Execute(nil, nil) // returns an error (but does not crash) - new(Template).ExecuteTemplate(nil, "XXX", nil) // returns an error (but does not crash) -} - -const testTemplates = `{{define "one"}}one{{end}}{{define "two"}}two{{end}}` - -func TestMessageForExecuteEmpty(t *testing.T) { - // Test a truly empty template. - tmpl := New("empty") - var b bytes.Buffer - err := tmpl.Execute(&b, 0) - if err == nil { - t.Fatal("expected initial error") - } - got := err.Error() - want := `template: empty: "empty" is an incomplete or empty template` - if got != want { - t.Errorf("expected error %s got %s", want, got) - } - // Add a non-empty template to check that the error is helpful. - tests, err := New("").Parse(testTemplates) - if err != nil { - t.Fatal(err) - } - tmpl.AddParseTree("secondary", tests.Tree) - err = tmpl.Execute(&b, 0) - if err == nil { - t.Fatal("expected second error") - } - got = err.Error() - want = `template: empty: "empty" is an incomplete or empty template` - if got != want { - t.Errorf("expected error %s got %s", want, got) - } - // Make sure we can execute the secondary. - err = tmpl.ExecuteTemplate(&b, "secondary", 0) - if err != nil { - t.Fatal(err) - } -} - -func TestFinalForPrintf(t *testing.T) { - tmpl, err := New("").Parse(`{{"x" | printf}}`) - if err != nil { - t.Fatal(err) - } - var b bytes.Buffer - err = tmpl.Execute(&b, 0) - if err != nil { - t.Fatal(err) - } -} - -type cmpTest struct { - expr string - truth string - ok bool -} - -var cmpTests = []cmpTest{ - {"eq true true", "true", true}, - {"eq true false", "false", true}, - {"eq 1+2i 1+2i", "true", true}, - {"eq 1+2i 1+3i", "false", true}, - {"eq 1.5 1.5", "true", true}, - {"eq 1.5 2.5", "false", true}, - {"eq 1 1", "true", true}, - {"eq 1 2", "false", true}, - {"eq `xy` `xy`", "true", true}, - {"eq `xy` `xyz`", "false", true}, - {"eq .Uthree .Uthree", "true", true}, - {"eq .Uthree .Ufour", "false", true}, - {"eq 3 4 5 6 3", "true", true}, - {"eq 3 4 5 6 7", "false", true}, - {"ne true true", "false", true}, - {"ne true false", "true", true}, - {"ne 1+2i 1+2i", "false", true}, - {"ne 1+2i 1+3i", "true", true}, - {"ne 1.5 1.5", "false", true}, - {"ne 1.5 2.5", "true", true}, - {"ne 1 1", "false", true}, - {"ne 1 2", "true", true}, - {"ne `xy` `xy`", "false", true}, - {"ne `xy` `xyz`", "true", true}, - {"ne .Uthree .Uthree", "false", true}, - {"ne .Uthree .Ufour", "true", true}, - {"lt 1.5 1.5", "false", true}, - {"lt 1.5 2.5", "true", true}, - {"lt 1 1", "false", true}, - {"lt 1 2", "true", true}, - {"lt `xy` `xy`", "false", true}, - {"lt `xy` `xyz`", "true", true}, - {"lt .Uthree .Uthree", "false", true}, - {"lt .Uthree .Ufour", "true", true}, - {"le 1.5 1.5", "true", true}, - {"le 1.5 2.5", "true", true}, - {"le 2.5 1.5", "false", true}, - {"le 1 1", "true", true}, - {"le 1 2", "true", true}, - {"le 2 1", "false", true}, - {"le `xy` `xy`", "true", true}, - {"le `xy` `xyz`", "true", true}, - {"le `xyz` `xy`", "false", true}, - {"le .Uthree .Uthree", "true", true}, - {"le .Uthree .Ufour", "true", true}, - {"le .Ufour .Uthree", "false", true}, - {"gt 1.5 1.5", "false", true}, - {"gt 1.5 2.5", "false", true}, - {"gt 1 1", "false", true}, - {"gt 2 1", "true", true}, - {"gt 1 2", "false", true}, - {"gt `xy` `xy`", "false", true}, - {"gt `xy` `xyz`", "false", true}, - {"gt .Uthree .Uthree", "false", true}, - {"gt .Uthree .Ufour", "false", true}, - {"gt .Ufour .Uthree", "true", true}, - {"ge 1.5 1.5", "true", true}, - {"ge 1.5 2.5", "false", true}, - {"ge 2.5 1.5", "true", true}, - {"ge 1 1", "true", true}, - {"ge 1 2", "false", true}, - {"ge 2 1", "true", true}, - {"ge `xy` `xy`", "true", true}, - {"ge `xy` `xyz`", "false", true}, - {"ge `xyz` `xy`", "true", true}, - {"ge .Uthree .Uthree", "true", true}, - {"ge .Uthree .Ufour", "false", true}, - {"ge .Ufour .Uthree", "true", true}, - // Mixing signed and unsigned integers. - {"eq .Uthree .Three", "true", true}, - {"eq .Three .Uthree", "true", true}, - {"le .Uthree .Three", "true", true}, - {"le .Three .Uthree", "true", true}, - {"ge .Uthree .Three", "true", true}, - {"ge .Three .Uthree", "true", true}, - {"lt .Uthree .Three", "false", true}, - {"lt .Three .Uthree", "false", true}, - {"gt .Uthree .Three", "false", true}, - {"gt .Three .Uthree", "false", true}, - {"eq .Ufour .Three", "false", true}, - {"lt .Ufour .Three", "false", true}, - {"gt .Ufour .Three", "true", true}, - {"eq .NegOne .Uthree", "false", true}, - {"eq .Uthree .NegOne", "false", true}, - {"ne .NegOne .Uthree", "true", true}, - {"ne .Uthree .NegOne", "true", true}, - {"lt .NegOne .Uthree", "true", true}, - {"lt .Uthree .NegOne", "false", true}, - {"le .NegOne .Uthree", "true", true}, - {"le .Uthree .NegOne", "false", true}, - {"gt .NegOne .Uthree", "false", true}, - {"gt .Uthree .NegOne", "true", true}, - {"ge .NegOne .Uthree", "false", true}, - {"ge .Uthree .NegOne", "true", true}, - {"eq (index `x` 0) 'x'", "true", true}, // The example that triggered this rule. - {"eq (index `x` 0) 'y'", "false", true}, - // Errors - {"eq `xy` 1", "", false}, // Different types. - {"eq 2 2.0", "", false}, // Different types. - {"lt true true", "", false}, // Unordered types. - {"lt 1+0i 1+0i", "", false}, // Unordered types. -} - -func TestComparison(t *testing.T) { - b := new(bytes.Buffer) - var cmpStruct = struct { - Uthree, Ufour uint - NegOne, Three int - }{3, 4, -1, 3} - for _, test := range cmpTests { - text := fmt.Sprintf("{{if %s}}true{{else}}false{{end}}", test.expr) - tmpl, err := New("empty").Parse(text) - if err != nil { - t.Fatalf("%q: %s", test.expr, err) - } - b.Reset() - err = tmpl.Execute(b, &cmpStruct) - if test.ok && err != nil { - t.Errorf("%s errored incorrectly: %s", test.expr, err) - continue - } - if !test.ok && err == nil { - t.Errorf("%s did not error", test.expr) - continue - } - if b.String() != test.truth { - t.Errorf("%s: want %s; got %s", test.expr, test.truth, b.String()) - } - } -} - -func TestMissingMapKey(t *testing.T) { - data := map[string]int{ - "x": 99, - } - tmpl, err := New("t1").Parse("{{.x}} {{.y}}") - if err != nil { - t.Fatal(err) - } - var b bytes.Buffer - // By default, just get "" - err = tmpl.Execute(&b, data) - if err != nil { - t.Fatal(err) - } - want := "99 " - got := b.String() - if got != want { - t.Errorf("got %q; expected %q", got, want) - } - // Same if we set the option explicitly to the default. - tmpl.Option("missingkey=default") - b.Reset() - err = tmpl.Execute(&b, data) - if err != nil { - t.Fatal("default:", err) - } - want = "99 " - got = b.String() - if got != want { - t.Errorf("got %q; expected %q", got, want) - } - // Next we ask for a zero value - tmpl.Option("missingkey=zero") - b.Reset() - err = tmpl.Execute(&b, data) - if err != nil { - t.Fatal("zero:", err) - } - want = "99 0" - got = b.String() - if got != want { - t.Errorf("got %q; expected %q", got, want) - } - // Now we ask for an error. - tmpl.Option("missingkey=error") - err = tmpl.Execute(&b, data) - if err == nil { - t.Errorf("expected error; got none") - } - // same Option, but now a nil interface: ask for an error - err = tmpl.Execute(&b, nil) - t.Log(err) - if err == nil { - t.Errorf("expected error for nil-interface; got none") - } -} - -// Test that the error message for multiline unterminated string -// refers to the line number of the opening quote. -func TestUnterminatedStringError(t *testing.T) { - _, err := New("X").Parse("hello\n\n{{`unterminated\n\n\n\n}}\n some more\n\n") - if err == nil { - t.Fatal("expected error") - } - str := err.Error() - if !strings.Contains(str, "X:3: unexpected unterminated raw quoted string") { - t.Fatalf("unexpected error: %s", str) - } -} - -const alwaysErrorText = "always be failing" - -var alwaysError = errors.New(alwaysErrorText) - -type ErrorWriter int - -func (e ErrorWriter) Write(p []byte) (int, error) { - return 0, alwaysError -} - -func TestExecuteGivesExecError(t *testing.T) { - // First, a non-execution error shouldn't be an ExecError. - tmpl, err := New("X").Parse("hello") - if err != nil { - t.Fatal(err) - } - err = tmpl.Execute(ErrorWriter(0), 0) - if err == nil { - t.Fatal("expected error; got none") - } - if err.Error() != alwaysErrorText { - t.Errorf("expected %q error; got %q", alwaysErrorText, err) - } - // This one should be an ExecError. - tmpl, err = New("X").Parse("hello, {{.X.Y}}") - if err != nil { - t.Fatal(err) - } - err = tmpl.Execute(ioutil.Discard, 0) - if err == nil { - t.Fatal("expected error; got none") - } - eerr, ok := err.(ExecError) - if !ok { - t.Fatalf("did not expect ExecError %s", eerr) - } - expect := "field X in type int" - if !strings.Contains(err.Error(), expect) { - t.Errorf("expected %q; got %q", expect, err) - } -} - -func funcNameTestFunc() int { - return 0 -} - -func TestGoodFuncNames(t *testing.T) { - names := []string{ - "_", - "a", - "a1", - "a1", - "Ӵ", - } - for _, name := range names { - tmpl := New("X").Funcs( - FuncMap{ - name: funcNameTestFunc, - }, - ) - if tmpl == nil { - t.Fatalf("nil result for %q", name) - } - } -} - -func TestBadFuncNames(t *testing.T) { - names := []string{ - "", - "2", - "a-b", - } - for _, name := range names { - testBadFuncName(name, t) - } -} - -func testBadFuncName(name string, t *testing.T) { - t.Helper() - defer func() { - recover() - }() - New("X").Funcs( - FuncMap{ - name: funcNameTestFunc, - }, - ) - // If we get here, the name did not cause a panic, which is how Funcs - // reports an error. - t.Errorf("%q succeeded incorrectly as function name", name) -} - -func TestBlock(t *testing.T) { - const ( - input = `a({{block "inner" .}}bar({{.}})baz{{end}})b` - want = `a(bar(hello)baz)b` - overlay = `{{define "inner"}}foo({{.}})bar{{end}}` - want2 = `a(foo(goodbye)bar)b` - ) - tmpl, err := New("outer").Parse(input) - if err != nil { - t.Fatal(err) - } - tmpl2, err := Must(tmpl.Clone()).Parse(overlay) - if err != nil { - t.Fatal(err) - } - - var buf bytes.Buffer - if err := tmpl.Execute(&buf, "hello"); err != nil { - t.Fatal(err) - } - if got := buf.String(); got != want { - t.Errorf("got %q, want %q", got, want) - } - - buf.Reset() - if err := tmpl2.Execute(&buf, "goodbye"); err != nil { - t.Fatal(err) - } - if got := buf.String(); got != want2 { - t.Errorf("got %q, want %q", got, want2) - } -} - -// Check that calling an invalid field on nil pointer prints -// a field error instead of a distracting nil pointer error. -// https://golang.org/issue/15125 -func TestMissingFieldOnNil(t *testing.T) { - tmpl := Must(New("tmpl").Parse("{{.MissingField}}")) - var d *T - err := tmpl.Execute(ioutil.Discard, d) - got := "" - if err != nil { - got = err.Error() - } - want := "can't evaluate field MissingField in type *template.T" - if !strings.HasSuffix(got, want) { - t.Errorf("got error %q, want %q", got, want) - } -} - -func TestMaxExecDepth(t *testing.T) { - tmpl := Must(New("tmpl").Parse(`{{template "tmpl" .}}`)) - err := tmpl.Execute(ioutil.Discard, nil) - got := "" - if err != nil { - got = err.Error() - } - const want = "exceeded maximum template depth" - if !strings.Contains(got, want) { - t.Errorf("got error %q; want %q", got, want) - } -} - -func TestAddrOfIndex(t *testing.T) { - // golang.org/issue/14916. - // Before index worked on reflect.Values, the .String could not be - // found on the (incorrectly unaddressable) V value, - // in contrast to range, which worked fine. - // Also testing that passing a reflect.Value to tmpl.Execute works. - texts := []string{ - `{{range .}}{{.String}}{{end}}`, - `{{with index . 0}}{{.String}}{{end}}`, - } - for _, text := range texts { - tmpl := Must(New("tmpl").Parse(text)) - var buf bytes.Buffer - err := tmpl.Execute(&buf, reflect.ValueOf([]V{{1}})) - if err != nil { - t.Fatalf("%s: Execute: %v", text, err) - } - if buf.String() != "<1>" { - t.Fatalf("%s: template output = %q, want %q", text, &buf, "<1>") - } - } -} - -func TestInterfaceValues(t *testing.T) { - // golang.org/issue/17714. - // Before index worked on reflect.Values, interface values - // were always implicitly promoted to the underlying value, - // except that nil interfaces were promoted to the zero reflect.Value. - // Eliminating a round trip to interface{} and back to reflect.Value - // eliminated this promotion, breaking these cases. - tests := []struct { - text string - out string - }{ - {`{{index .Nil 1}}`, "ERROR: index of untyped nil"}, - {`{{index .Slice 2}}`, "2"}, - {`{{index .Slice .Two}}`, "2"}, - {`{{call .Nil 1}}`, "ERROR: call of nil"}, - {`{{call .PlusOne 1}}`, "2"}, - {`{{call .PlusOne .One}}`, "2"}, - {`{{and (index .Slice 0) true}}`, "0"}, - {`{{and .Zero true}}`, "0"}, - {`{{and (index .Slice 1) false}}`, "false"}, - {`{{and .One false}}`, "false"}, - {`{{or (index .Slice 0) false}}`, "false"}, - {`{{or .Zero false}}`, "false"}, - {`{{or (index .Slice 1) true}}`, "1"}, - {`{{or .One true}}`, "1"}, - {`{{not (index .Slice 0)}}`, "true"}, - {`{{not .Zero}}`, "true"}, - {`{{not (index .Slice 1)}}`, "false"}, - {`{{not .One}}`, "false"}, - {`{{eq (index .Slice 0) .Zero}}`, "true"}, - {`{{eq (index .Slice 1) .One}}`, "true"}, - {`{{ne (index .Slice 0) .Zero}}`, "false"}, - {`{{ne (index .Slice 1) .One}}`, "false"}, - {`{{ge (index .Slice 0) .One}}`, "false"}, - {`{{ge (index .Slice 1) .Zero}}`, "true"}, - {`{{gt (index .Slice 0) .One}}`, "false"}, - {`{{gt (index .Slice 1) .Zero}}`, "true"}, - {`{{le (index .Slice 0) .One}}`, "true"}, - {`{{le (index .Slice 1) .Zero}}`, "false"}, - {`{{lt (index .Slice 0) .One}}`, "true"}, - {`{{lt (index .Slice 1) .Zero}}`, "false"}, - } - - for _, tt := range tests { - tmpl := Must(New("tmpl").Parse(tt.text)) - var buf bytes.Buffer - err := tmpl.Execute(&buf, map[string]interface{}{ - "PlusOne": func(n int) int { - return n + 1 - }, - "Slice": []int{0, 1, 2, 3}, - "One": 1, - "Two": 2, - "Nil": nil, - "Zero": 0, - }) - if strings.HasPrefix(tt.out, "ERROR:") { - e := strings.TrimSpace(strings.TrimPrefix(tt.out, "ERROR:")) - if err == nil || !strings.Contains(err.Error(), e) { - t.Errorf("%s: Execute: %v, want error %q", tt.text, err, e) - } - continue - } - if err != nil { - t.Errorf("%s: Execute: %v", tt.text, err) - continue - } - if buf.String() != tt.out { - t.Errorf("%s: template output = %q, want %q", tt.text, &buf, tt.out) - } - } -} - -// Check that panics during calls are recovered and returned as errors. -func TestExecutePanicDuringCall(t *testing.T) { - funcs := map[string]interface{}{ - "doPanic": func() string { - panic("custom panic string") - }, - } - tests := []struct { - name string - input string - data interface{} - wantErr string - }{ - { - "direct func call panics", - "{{doPanic}}", (*T)(nil), - `template: t:1:2: executing "t" at : error calling doPanic: custom panic string`, - }, - { - "indirect func call panics", - "{{call doPanic}}", (*T)(nil), - `template: t:1:7: executing "t" at : error calling doPanic: custom panic string`, - }, - { - "direct method call panics", - "{{.GetU}}", (*T)(nil), - `template: t:1:2: executing "t" at <.GetU>: error calling GetU: runtime error: invalid memory address or nil pointer dereference`, - }, - { - "indirect method call panics", - "{{call .GetU}}", (*T)(nil), - `template: t:1:7: executing "t" at <.GetU>: error calling GetU: runtime error: invalid memory address or nil pointer dereference`, - }, - { - "func field call panics", - "{{call .PanicFunc}}", tVal, - `template: t:1:2: executing "t" at : error calling call: test panic`, - }, - } - for _, tc := range tests { - b := new(bytes.Buffer) - tmpl, err := New("t").Funcs(funcs).Parse(tc.input) - if err != nil { - t.Fatalf("parse error: %s", err) - } - err = tmpl.Execute(b, tc.data) - if err == nil { - t.Errorf("%s: expected error; got none", tc.name) - } else if !strings.Contains(err.Error(), tc.wantErr) { - if *debug { - fmt.Printf("%s: test execute error: %s\n", tc.name, err) - } - t.Errorf("%s: expected error:\n%s\ngot:\n%s", tc.name, tc.wantErr, err) - } - } -} diff --git a/g/os/gview/internal/text/template/multi_test.go b/g/os/gview/internal/text/template/multi_test.go deleted file mode 100644 index 5769470ff..000000000 --- a/g/os/gview/internal/text/template/multi_test.go +++ /dev/null @@ -1,423 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package template - -// Tests for multiple-template parsing and execution. - -import ( - "bytes" - "fmt" - "testing" - "text/template/parse" -) - -const ( - noError = true - hasError = false -) - -type multiParseTest struct { - name string - input string - ok bool - names []string - results []string -} - -var multiParseTests = []multiParseTest{ - {"empty", "", noError, - nil, - nil}, - {"one", `{{define "foo"}} FOO {{end}}`, noError, - []string{"foo"}, - []string{" FOO "}}, - {"two", `{{define "foo"}} FOO {{end}}{{define "bar"}} BAR {{end}}`, noError, - []string{"foo", "bar"}, - []string{" FOO ", " BAR "}}, - // errors - {"missing end", `{{define "foo"}} FOO `, hasError, - nil, - nil}, - {"malformed name", `{{define "foo}} FOO `, hasError, - nil, - nil}, -} - -func TestMultiParse(t *testing.T) { - for _, test := range multiParseTests { - template, err := New("root").Parse(test.input) - switch { - case err == nil && !test.ok: - t.Errorf("%q: expected error; got none", test.name) - continue - case err != nil && test.ok: - t.Errorf("%q: unexpected error: %v", test.name, err) - continue - case err != nil && !test.ok: - // expected error, got one - if *debug { - fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err) - } - continue - } - if template == nil { - continue - } - if len(template.tmpl) != len(test.names)+1 { // +1 for root - t.Errorf("%s: wrong number of templates; wanted %d got %d", test.name, len(test.names), len(template.tmpl)) - continue - } - for i, name := range test.names { - tmpl, ok := template.tmpl[name] - if !ok { - t.Errorf("%s: can't find template %q", test.name, name) - continue - } - result := tmpl.Root.String() - if result != test.results[i] { - t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.results[i]) - } - } - } -} - -var multiExecTests = []execTest{ - {"empty", "", "", nil, true}, - {"text", "some text", "some text", nil, true}, - {"invoke x", `{{template "x" .SI}}`, "TEXT", tVal, true}, - {"invoke x no args", `{{template "x"}}`, "TEXT", tVal, true}, - {"invoke dot int", `{{template "dot" .I}}`, "17", tVal, true}, - {"invoke dot []int", `{{template "dot" .SI}}`, "[3 4 5]", tVal, true}, - {"invoke dotV", `{{template "dotV" .U}}`, "v", tVal, true}, - {"invoke nested int", `{{template "nested" .I}}`, "17", tVal, true}, - {"variable declared by template", `{{template "nested" $x:=.SI}},{{index $x 1}}`, "[3 4 5],4", tVal, true}, - - // User-defined function: test argument evaluator. - {"testFunc literal", `{{oneArg "joe"}}`, "oneArg=joe", tVal, true}, - {"testFunc .", `{{oneArg .}}`, "oneArg=joe", "joe", true}, -} - -// These strings are also in testdata/*. -const multiText1 = ` - {{define "x"}}TEXT{{end}} - {{define "dotV"}}{{.V}}{{end}} -` - -const multiText2 = ` - {{define "dot"}}{{.}}{{end}} - {{define "nested"}}{{template "dot" .}}{{end}} -` - -func TestMultiExecute(t *testing.T) { - // Declare a couple of templates first. - template, err := New("root").Parse(multiText1) - if err != nil { - t.Fatalf("parse error for 1: %s", err) - } - _, err = template.Parse(multiText2) - if err != nil { - t.Fatalf("parse error for 2: %s", err) - } - testExecute(multiExecTests, template, t) -} - -func TestParseFiles(t *testing.T) { - _, err := ParseFiles("DOES NOT EXIST") - if err == nil { - t.Error("expected error for non-existent file; got none") - } - template := New("root") - _, err = template.ParseFiles("testdata/file1.tmpl", "testdata/file2.tmpl") - if err != nil { - t.Fatalf("error parsing files: %v", err) - } - testExecute(multiExecTests, template, t) -} - -func TestParseGlob(t *testing.T) { - _, err := ParseGlob("DOES NOT EXIST") - if err == nil { - t.Error("expected error for non-existent file; got none") - } - _, err = New("error").ParseGlob("[x") - if err == nil { - t.Error("expected error for bad pattern; got none") - } - template := New("root") - _, err = template.ParseGlob("testdata/file*.tmpl") - if err != nil { - t.Fatalf("error parsing files: %v", err) - } - testExecute(multiExecTests, template, t) -} - -// In these tests, actual content (not just template definitions) comes from the parsed files. - -var templateFileExecTests = []execTest{ - {"test", `{{template "tmpl1.tmpl"}}{{template "tmpl2.tmpl"}}`, "template1\n\ny\ntemplate2\n\nx\n", 0, true}, -} - -func TestParseFilesWithData(t *testing.T) { - template, err := New("root").ParseFiles("testdata/tmpl1.tmpl", "testdata/tmpl2.tmpl") - if err != nil { - t.Fatalf("error parsing files: %v", err) - } - testExecute(templateFileExecTests, template, t) -} - -func TestParseGlobWithData(t *testing.T) { - template, err := New("root").ParseGlob("testdata/tmpl*.tmpl") - if err != nil { - t.Fatalf("error parsing files: %v", err) - } - testExecute(templateFileExecTests, template, t) -} - -const ( - cloneText1 = `{{define "a"}}{{template "b"}}{{template "c"}}{{end}}` - cloneText2 = `{{define "b"}}b{{end}}` - cloneText3 = `{{define "c"}}root{{end}}` - cloneText4 = `{{define "c"}}clone{{end}}` -) - -func TestClone(t *testing.T) { - // Create some templates and clone the root. - root, err := New("root").Parse(cloneText1) - if err != nil { - t.Fatal(err) - } - _, err = root.Parse(cloneText2) - if err != nil { - t.Fatal(err) - } - clone := Must(root.Clone()) - // Add variants to both. - _, err = root.Parse(cloneText3) - if err != nil { - t.Fatal(err) - } - _, err = clone.Parse(cloneText4) - if err != nil { - t.Fatal(err) - } - // Verify that the clone is self-consistent. - for k, v := range clone.tmpl { - if k == clone.name && v.tmpl[k] != clone { - t.Error("clone does not contain root") - } - if v != v.tmpl[v.name] { - t.Errorf("clone does not contain self for %q", k) - } - } - // Execute root. - var b bytes.Buffer - err = root.ExecuteTemplate(&b, "a", 0) - if err != nil { - t.Fatal(err) - } - if b.String() != "broot" { - t.Errorf("expected %q got %q", "broot", b.String()) - } - // Execute copy. - b.Reset() - err = clone.ExecuteTemplate(&b, "a", 0) - if err != nil { - t.Fatal(err) - } - if b.String() != "bclone" { - t.Errorf("expected %q got %q", "bclone", b.String()) - } -} - -func TestAddParseTree(t *testing.T) { - // Create some templates. - root, err := New("root").Parse(cloneText1) - if err != nil { - t.Fatal(err) - } - _, err = root.Parse(cloneText2) - if err != nil { - t.Fatal(err) - } - // Add a new parse tree. - tree, err := parse.Parse("cloneText3", cloneText3, "", "", nil, builtins) - if err != nil { - t.Fatal(err) - } - added, err := root.AddParseTree("c", tree["c"]) - if err != nil { - t.Fatal(err) - } - // Execute. - var b bytes.Buffer - err = added.ExecuteTemplate(&b, "a", 0) - if err != nil { - t.Fatal(err) - } - if b.String() != "broot" { - t.Errorf("expected %q got %q", "broot", b.String()) - } -} - -// Issue 7032 -func TestAddParseTreeToUnparsedTemplate(t *testing.T) { - master := "{{define \"master\"}}{{end}}" - tmpl := New("master") - tree, err := parse.Parse("master", master, "", "", nil) - if err != nil { - t.Fatalf("unexpected parse err: %v", err) - } - masterTree := tree["master"] - tmpl.AddParseTree("master", masterTree) // used to panic -} - -func TestRedefinition(t *testing.T) { - var tmpl *Template - var err error - if tmpl, err = New("tmpl1").Parse(`{{define "test"}}foo{{end}}`); err != nil { - t.Fatalf("parse 1: %v", err) - } - if _, err = tmpl.Parse(`{{define "test"}}bar{{end}}`); err != nil { - t.Fatalf("got error %v, expected nil", err) - } - if _, err = tmpl.New("tmpl2").Parse(`{{define "test"}}bar{{end}}`); err != nil { - t.Fatalf("got error %v, expected nil", err) - } -} - -// Issue 10879 -func TestEmptyTemplateCloneCrash(t *testing.T) { - t1 := New("base") - t1.Clone() // used to panic -} - -// Issue 10910, 10926 -func TestTemplateLookUp(t *testing.T) { - t1 := New("foo") - if t1.Lookup("foo") != nil { - t.Error("Lookup returned non-nil value for undefined template foo") - } - t1.New("bar") - if t1.Lookup("bar") != nil { - t.Error("Lookup returned non-nil value for undefined template bar") - } - t1.Parse(`{{define "foo"}}test{{end}}`) - if t1.Lookup("foo") == nil { - t.Error("Lookup returned nil value for defined template") - } -} - -func TestNew(t *testing.T) { - // template with same name already exists - t1, _ := New("test").Parse(`{{define "test"}}foo{{end}}`) - t2 := t1.New("test") - - if t1.common != t2.common { - t.Errorf("t1 & t2 didn't share common struct; got %v != %v", t1.common, t2.common) - } - if t1.Tree == nil { - t.Error("defined template got nil Tree") - } - if t2.Tree != nil { - t.Error("undefined template got non-nil Tree") - } - - containsT1 := false - for _, tmpl := range t1.Templates() { - if tmpl == t2 { - t.Error("Templates included undefined template") - } - if tmpl == t1 { - containsT1 = true - } - } - if !containsT1 { - t.Error("Templates didn't include defined template") - } -} - -func TestParse(t *testing.T) { - // In multiple calls to Parse with the same receiver template, only one call - // can contain text other than space, comments, and template definitions - t1 := New("test") - if _, err := t1.Parse(`{{define "test"}}{{end}}`); err != nil { - t.Fatalf("parsing test: %s", err) - } - if _, err := t1.Parse(`{{define "test"}}{{/* this is a comment */}}{{end}}`); err != nil { - t.Fatalf("parsing test: %s", err) - } - if _, err := t1.Parse(`{{define "test"}}foo{{end}}`); err != nil { - t.Fatalf("parsing test: %s", err) - } -} - -func TestEmptyTemplate(t *testing.T) { - cases := []struct { - defn []string - in string - want string - }{ - {[]string{""}, "once", ""}, - {[]string{"", ""}, "twice", ""}, - {[]string{"{{.}}", "{{.}}"}, "twice", "twice"}, - {[]string{"{{/* a comment */}}", "{{/* a comment */}}"}, "comment", ""}, - {[]string{"{{.}}", ""}, "twice", ""}, - } - - for i, c := range cases { - root := New("root") - - var ( - m *Template - err error - ) - for _, d := range c.defn { - m, err = root.New(c.in).Parse(d) - if err != nil { - t.Fatal(err) - } - } - buf := &bytes.Buffer{} - if err := m.Execute(buf, c.in); err != nil { - t.Error(i, err) - continue - } - if buf.String() != c.want { - t.Errorf("expected string %q: got %q", c.want, buf.String()) - } - } -} - -// Issue 19249 was a regression in 1.8 caused by the handling of empty -// templates added in that release, which got different answers depending -// on the order templates appeared in the internal map. -func TestIssue19294(t *testing.T) { - // The empty block in "xhtml" should be replaced during execution - // by the contents of "stylesheet", but if the internal map associating - // names with templates is built in the wrong order, the empty block - // looks non-empty and this doesn't happen. - var inlined = map[string]string{ - "stylesheet": `{{define "stylesheet"}}stylesheet{{end}}`, - "xhtml": `{{block "stylesheet" .}}{{end}}`, - } - all := []string{"stylesheet", "xhtml"} - for i := 0; i < 100; i++ { - res, err := New("title.xhtml").Parse(`{{template "xhtml" .}}`) - if err != nil { - t.Fatal(err) - } - for _, name := range all { - _, err := res.New(name).Parse(inlined[name]) - if err != nil { - t.Fatal(err) - } - } - var buf bytes.Buffer - res.Execute(&buf, 0) - if buf.String() != "stylesheet" { - t.Fatalf("iteration %d: got %q; expected %q", i, buf.String(), "stylesheet") - } - } -} diff --git a/g/os/gview/internal/text/template/parse/lex_test.go b/g/os/gview/internal/text/template/parse/lex_test.go deleted file mode 100644 index 6e7ece9db..000000000 --- a/g/os/gview/internal/text/template/parse/lex_test.go +++ /dev/null @@ -1,544 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package parse - -import ( - "fmt" - "testing" -) - -// Make the types prettyprint. -var itemName = map[itemType]string{ - itemError: "error", - itemBool: "bool", - itemChar: "char", - itemCharConstant: "charconst", - itemComplex: "complex", - itemDeclare: ":=", - itemEOF: "EOF", - itemField: "field", - itemIdentifier: "identifier", - itemLeftDelim: "left delim", - itemLeftParen: "(", - itemNumber: "number", - itemPipe: "pipe", - itemRawString: "raw string", - itemRightDelim: "right delim", - itemRightParen: ")", - itemSpace: "space", - itemString: "string", - itemVariable: "variable", - - // keywords - itemDot: ".", - itemBlock: "block", - itemDefine: "define", - itemElse: "else", - itemIf: "if", - itemEnd: "end", - itemNil: "nil", - itemRange: "range", - itemTemplate: "template", - itemWith: "with", -} - -func (i itemType) String() string { - s := itemName[i] - if s == "" { - return fmt.Sprintf("item%d", int(i)) - } - return s -} - -type lexTest struct { - name string - input string - items []item -} - -func mkItem(typ itemType, text string) item { - return item{ - typ: typ, - val: text, - } -} - -var ( - tDot = mkItem(itemDot, ".") - tBlock = mkItem(itemBlock, "block") - tEOF = mkItem(itemEOF, "") - tFor = mkItem(itemIdentifier, "for") - tLeft = mkItem(itemLeftDelim, "{{") - tLpar = mkItem(itemLeftParen, "(") - tPipe = mkItem(itemPipe, "|") - tQuote = mkItem(itemString, `"abc \n\t\" "`) - tRange = mkItem(itemRange, "range") - tRight = mkItem(itemRightDelim, "}}") - tRpar = mkItem(itemRightParen, ")") - tSpace = mkItem(itemSpace, " ") - raw = "`" + `abc\n\t\" ` + "`" - rawNL = "`now is{{\n}}the time`" // Contains newline inside raw quote. - tRawQuote = mkItem(itemRawString, raw) - tRawQuoteNL = mkItem(itemRawString, rawNL) -) - -var lexTests = []lexTest{ - {"empty", "", []item{tEOF}}, - {"spaces", " \t\n", []item{mkItem(itemText, " \t\n"), tEOF}}, - {"text", `now is the time`, []item{mkItem(itemText, "now is the time"), tEOF}}, - {"text with comment", "hello-{{/* this is a comment */}}-world", []item{ - mkItem(itemText, "hello-"), - mkItem(itemText, "-world"), - tEOF, - }}, - {"punctuation", "{{,@% }}", []item{ - tLeft, - mkItem(itemChar, ","), - mkItem(itemChar, "@"), - mkItem(itemChar, "%"), - tSpace, - tRight, - tEOF, - }}, - {"parens", "{{((3))}}", []item{ - tLeft, - tLpar, - tLpar, - mkItem(itemNumber, "3"), - tRpar, - tRpar, - tRight, - tEOF, - }}, - {"empty action", `{{}}`, []item{tLeft, tRight, tEOF}}, - {"for", `{{for}}`, []item{tLeft, tFor, tRight, tEOF}}, - {"block", `{{block "foo" .}}`, []item{ - tLeft, tBlock, tSpace, mkItem(itemString, `"foo"`), tSpace, tDot, tRight, tEOF, - }}, - {"quote", `{{"abc \n\t\" "}}`, []item{tLeft, tQuote, tRight, tEOF}}, - {"raw quote", "{{" + raw + "}}", []item{tLeft, tRawQuote, tRight, tEOF}}, - {"raw quote with newline", "{{" + rawNL + "}}", []item{tLeft, tRawQuoteNL, tRight, tEOF}}, - {"numbers", "{{1 02 0x14 -7.2i 1e3 +1.2e-4 4.2i 1+2i}}", []item{ - tLeft, - mkItem(itemNumber, "1"), - tSpace, - mkItem(itemNumber, "02"), - tSpace, - mkItem(itemNumber, "0x14"), - tSpace, - mkItem(itemNumber, "-7.2i"), - tSpace, - mkItem(itemNumber, "1e3"), - tSpace, - mkItem(itemNumber, "+1.2e-4"), - tSpace, - mkItem(itemNumber, "4.2i"), - tSpace, - mkItem(itemComplex, "1+2i"), - tRight, - tEOF, - }}, - {"characters", `{{'a' '\n' '\'' '\\' '\u00FF' '\xFF' '本'}}`, []item{ - tLeft, - mkItem(itemCharConstant, `'a'`), - tSpace, - mkItem(itemCharConstant, `'\n'`), - tSpace, - mkItem(itemCharConstant, `'\''`), - tSpace, - mkItem(itemCharConstant, `'\\'`), - tSpace, - mkItem(itemCharConstant, `'\u00FF'`), - tSpace, - mkItem(itemCharConstant, `'\xFF'`), - tSpace, - mkItem(itemCharConstant, `'本'`), - tRight, - tEOF, - }}, - {"bools", "{{true false}}", []item{ - tLeft, - mkItem(itemBool, "true"), - tSpace, - mkItem(itemBool, "false"), - tRight, - tEOF, - }}, - {"dot", "{{.}}", []item{ - tLeft, - tDot, - tRight, - tEOF, - }}, - {"nil", "{{nil}}", []item{ - tLeft, - mkItem(itemNil, "nil"), - tRight, - tEOF, - }}, - {"dots", "{{.x . .2 .x.y.z}}", []item{ - tLeft, - mkItem(itemField, ".x"), - tSpace, - tDot, - tSpace, - mkItem(itemNumber, ".2"), - tSpace, - mkItem(itemField, ".x"), - mkItem(itemField, ".y"), - mkItem(itemField, ".z"), - tRight, - tEOF, - }}, - {"keywords", "{{range if else end with}}", []item{ - tLeft, - mkItem(itemRange, "range"), - tSpace, - mkItem(itemIf, "if"), - tSpace, - mkItem(itemElse, "else"), - tSpace, - mkItem(itemEnd, "end"), - tSpace, - mkItem(itemWith, "with"), - tRight, - tEOF, - }}, - {"variables", "{{$c := printf $ $hello $23 $ $var.Field .Method}}", []item{ - tLeft, - mkItem(itemVariable, "$c"), - tSpace, - mkItem(itemDeclare, ":="), - tSpace, - mkItem(itemIdentifier, "printf"), - tSpace, - mkItem(itemVariable, "$"), - tSpace, - mkItem(itemVariable, "$hello"), - tSpace, - mkItem(itemVariable, "$23"), - tSpace, - mkItem(itemVariable, "$"), - tSpace, - mkItem(itemVariable, "$var"), - mkItem(itemField, ".Field"), - tSpace, - mkItem(itemField, ".Method"), - tRight, - tEOF, - }}, - {"variable invocation", "{{$x 23}}", []item{ - tLeft, - mkItem(itemVariable, "$x"), - tSpace, - mkItem(itemNumber, "23"), - tRight, - tEOF, - }}, - {"pipeline", `intro {{echo hi 1.2 |noargs|args 1 "hi"}} outro`, []item{ - mkItem(itemText, "intro "), - tLeft, - mkItem(itemIdentifier, "echo"), - tSpace, - mkItem(itemIdentifier, "hi"), - tSpace, - mkItem(itemNumber, "1.2"), - tSpace, - tPipe, - mkItem(itemIdentifier, "noargs"), - tPipe, - mkItem(itemIdentifier, "args"), - tSpace, - mkItem(itemNumber, "1"), - tSpace, - mkItem(itemString, `"hi"`), - tRight, - mkItem(itemText, " outro"), - tEOF, - }}, - {"declaration", "{{$v := 3}}", []item{ - tLeft, - mkItem(itemVariable, "$v"), - tSpace, - mkItem(itemDeclare, ":="), - tSpace, - mkItem(itemNumber, "3"), - tRight, - tEOF, - }}, - {"2 declarations", "{{$v , $w := 3}}", []item{ - tLeft, - mkItem(itemVariable, "$v"), - tSpace, - mkItem(itemChar, ","), - tSpace, - mkItem(itemVariable, "$w"), - tSpace, - mkItem(itemDeclare, ":="), - tSpace, - mkItem(itemNumber, "3"), - tRight, - tEOF, - }}, - {"field of parenthesized expression", "{{(.X).Y}}", []item{ - tLeft, - tLpar, - mkItem(itemField, ".X"), - tRpar, - mkItem(itemField, ".Y"), - tRight, - tEOF, - }}, - {"trimming spaces before and after", "hello- {{- 3 -}} -world", []item{ - mkItem(itemText, "hello-"), - tLeft, - mkItem(itemNumber, "3"), - tRight, - mkItem(itemText, "-world"), - tEOF, - }}, - {"trimming spaces before and after comment", "hello- {{- /* hello */ -}} -world", []item{ - mkItem(itemText, "hello-"), - mkItem(itemText, "-world"), - tEOF, - }}, - // errors - {"badchar", "#{{\x01}}", []item{ - mkItem(itemText, "#"), - tLeft, - mkItem(itemError, "unrecognized character in action: U+0001"), - }}, - {"unclosed action", "{{\n}}", []item{ - tLeft, - mkItem(itemError, "unclosed action"), - }}, - {"EOF in action", "{{range", []item{ - tLeft, - tRange, - mkItem(itemError, "unclosed action"), - }}, - {"unclosed quote", "{{\"\n\"}}", []item{ - tLeft, - mkItem(itemError, "unterminated quoted string"), - }}, - {"unclosed raw quote", "{{`xx}}", []item{ - tLeft, - mkItem(itemError, "unterminated raw quoted string"), - }}, - {"unclosed char constant", "{{'\n}}", []item{ - tLeft, - mkItem(itemError, "unterminated character constant"), - }}, - {"bad number", "{{3k}}", []item{ - tLeft, - mkItem(itemError, `bad number syntax: "3k"`), - }}, - {"unclosed paren", "{{(3}}", []item{ - tLeft, - tLpar, - mkItem(itemNumber, "3"), - mkItem(itemError, `unclosed left paren`), - }}, - {"extra right paren", "{{3)}}", []item{ - tLeft, - mkItem(itemNumber, "3"), - tRpar, - mkItem(itemError, `unexpected right paren U+0029 ')'`), - }}, - - // Fixed bugs - // Many elements in an action blew the lookahead until - // we made lexInsideAction not loop. - {"long pipeline deadlock", "{{|||||}}", []item{ - tLeft, - tPipe, - tPipe, - tPipe, - tPipe, - tPipe, - tRight, - tEOF, - }}, - {"text with bad comment", "hello-{{/*/}}-world", []item{ - mkItem(itemText, "hello-"), - mkItem(itemError, `unclosed comment`), - }}, - {"text with comment close separated from delim", "hello-{{/* */ }}-world", []item{ - mkItem(itemText, "hello-"), - mkItem(itemError, `comment ends before closing delimiter`), - }}, - // This one is an error that we can't catch because it breaks templates with - // minimized JavaScript. Should have fixed it before Go 1.1. - {"unmatched right delimiter", "hello-{.}}-world", []item{ - mkItem(itemText, "hello-{.}}-world"), - tEOF, - }}, -} - -// collect gathers the emitted items into a slice. -func collect(t *lexTest, left, right string) (items []item) { - l := lex(t.name, t.input, left, right) - for { - item := l.nextItem() - items = append(items, item) - if item.typ == itemEOF || item.typ == itemError { - break - } - } - return -} - -func equal(i1, i2 []item, checkPos bool) bool { - if len(i1) != len(i2) { - return false - } - for k := range i1 { - if i1[k].typ != i2[k].typ { - return false - } - if i1[k].val != i2[k].val { - return false - } - if checkPos && i1[k].pos != i2[k].pos { - return false - } - if checkPos && i1[k].line != i2[k].line { - return false - } - } - return true -} - -func TestLex(t *testing.T) { - for _, test := range lexTests { - items := collect(&test, "", "") - if !equal(items, test.items, false) { - t.Errorf("%s: got\n\t%+v\nexpected\n\t%v", test.name, items, test.items) - } - } -} - -// Some easy cases from above, but with delimiters $$ and @@ -var lexDelimTests = []lexTest{ - {"punctuation", "$$,@%{{}}@@", []item{ - tLeftDelim, - mkItem(itemChar, ","), - mkItem(itemChar, "@"), - mkItem(itemChar, "%"), - mkItem(itemChar, "{"), - mkItem(itemChar, "{"), - mkItem(itemChar, "}"), - mkItem(itemChar, "}"), - tRightDelim, - tEOF, - }}, - {"empty action", `$$@@`, []item{tLeftDelim, tRightDelim, tEOF}}, - {"for", `$$for@@`, []item{tLeftDelim, tFor, tRightDelim, tEOF}}, - {"quote", `$$"abc \n\t\" "@@`, []item{tLeftDelim, tQuote, tRightDelim, tEOF}}, - {"raw quote", "$$" + raw + "@@", []item{tLeftDelim, tRawQuote, tRightDelim, tEOF}}, -} - -var ( - tLeftDelim = mkItem(itemLeftDelim, "$$") - tRightDelim = mkItem(itemRightDelim, "@@") -) - -func TestDelims(t *testing.T) { - for _, test := range lexDelimTests { - items := collect(&test, "$$", "@@") - if !equal(items, test.items, false) { - t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items) - } - } -} - -var lexPosTests = []lexTest{ - {"empty", "", []item{{itemEOF, 0, "", 1}}}, - {"punctuation", "{{,@%#}}", []item{ - {itemLeftDelim, 0, "{{", 1}, - {itemChar, 2, ",", 1}, - {itemChar, 3, "@", 1}, - {itemChar, 4, "%", 1}, - {itemChar, 5, "#", 1}, - {itemRightDelim, 6, "}}", 1}, - {itemEOF, 8, "", 1}, - }}, - {"sample", "0123{{hello}}xyz", []item{ - {itemText, 0, "0123", 1}, - {itemLeftDelim, 4, "{{", 1}, - {itemIdentifier, 6, "hello", 1}, - {itemRightDelim, 11, "}}", 1}, - {itemText, 13, "xyz", 1}, - {itemEOF, 16, "", 1}, - }}, - {"trimafter", "{{x -}}\n{{y}}", []item{ - {itemLeftDelim, 0, "{{", 1}, - {itemIdentifier, 2, "x", 1}, - {itemRightDelim, 5, "}}", 1}, - {itemLeftDelim, 8, "{{", 2}, - {itemIdentifier, 10, "y", 2}, - {itemRightDelim, 11, "}}", 2}, - {itemEOF, 13, "", 2}, - }}, - {"trimbefore", "{{x}}\n{{- y}}", []item{ - {itemLeftDelim, 0, "{{", 1}, - {itemIdentifier, 2, "x", 1}, - {itemRightDelim, 3, "}}", 1}, - {itemLeftDelim, 6, "{{", 2}, - {itemIdentifier, 10, "y", 2}, - {itemRightDelim, 11, "}}", 2}, - {itemEOF, 13, "", 2}, - }}, -} - -// The other tests don't check position, to make the test cases easier to construct. -// This one does. -func TestPos(t *testing.T) { - for _, test := range lexPosTests { - items := collect(&test, "", "") - if !equal(items, test.items, true) { - t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items) - if len(items) == len(test.items) { - // Detailed print; avoid item.String() to expose the position value. - for i := range items { - if !equal(items[i:i+1], test.items[i:i+1], true) { - i1 := items[i] - i2 := test.items[i] - t.Errorf("\t#%d: got {%v %d %q %d} expected {%v %d %q %d}", - i, i1.typ, i1.pos, i1.val, i1.line, i2.typ, i2.pos, i2.val, i2.line) - } - } - } - } - } -} - -// Test that an error shuts down the lexing goroutine. -func TestShutdown(t *testing.T) { - // We need to duplicate template.Parse here to hold on to the lexer. - const text = "erroneous{{define}}{{else}}1234" - lexer := lex("foo", text, "{{", "}}") - _, err := New("root").parseLexer(lexer) - if err == nil { - t.Fatalf("expected error") - } - // The error should have drained the input. Therefore, the lexer should be shut down. - token, ok := <-lexer.items - if ok { - t.Fatalf("input was not drained; got %v", token) - } -} - -// parseLexer is a local version of parse that lets us pass in the lexer instead of building it. -// We expect an error, so the tree set and funcs list are explicitly nil. -func (t *Tree) parseLexer(lex *lexer) (tree *Tree, err error) { - defer t.recover(&err) - t.ParseName = t.Name - t.startParse(nil, lex, map[string]*Tree{}) - t.parse() - t.add() - t.stopParse() - return t, nil -} diff --git a/g/os/gview/internal/text/template/parse/parse_test.go b/g/os/gview/internal/text/template/parse/parse_test.go deleted file mode 100644 index 15cc65670..000000000 --- a/g/os/gview/internal/text/template/parse/parse_test.go +++ /dev/null @@ -1,542 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package parse - -import ( - "flag" - "fmt" - "strings" - "testing" -) - -var debug = flag.Bool("debug", false, "show the errors produced by the main tests") - -type numberTest struct { - text string - isInt bool - isUint bool - isFloat bool - isComplex bool - int64 - uint64 - float64 - complex128 -} - -var numberTests = []numberTest{ - // basics - {"0", true, true, true, false, 0, 0, 0, 0}, - {"-0", true, true, true, false, 0, 0, 0, 0}, // check that -0 is a uint. - {"73", true, true, true, false, 73, 73, 73, 0}, - {"073", true, true, true, false, 073, 073, 073, 0}, - {"0x73", true, true, true, false, 0x73, 0x73, 0x73, 0}, - {"-73", true, false, true, false, -73, 0, -73, 0}, - {"+73", true, false, true, false, 73, 0, 73, 0}, - {"100", true, true, true, false, 100, 100, 100, 0}, - {"1e9", true, true, true, false, 1e9, 1e9, 1e9, 0}, - {"-1e9", true, false, true, false, -1e9, 0, -1e9, 0}, - {"-1.2", false, false, true, false, 0, 0, -1.2, 0}, - {"1e19", false, true, true, false, 0, 1e19, 1e19, 0}, - {"-1e19", false, false, true, false, 0, 0, -1e19, 0}, - {"4i", false, false, false, true, 0, 0, 0, 4i}, - {"-1.2+4.2i", false, false, false, true, 0, 0, 0, -1.2 + 4.2i}, - {"073i", false, false, false, true, 0, 0, 0, 73i}, // not octal! - // complex with 0 imaginary are float (and maybe integer) - {"0i", true, true, true, true, 0, 0, 0, 0}, - {"-1.2+0i", false, false, true, true, 0, 0, -1.2, -1.2}, - {"-12+0i", true, false, true, true, -12, 0, -12, -12}, - {"13+0i", true, true, true, true, 13, 13, 13, 13}, - // funny bases - {"0123", true, true, true, false, 0123, 0123, 0123, 0}, - {"-0x0", true, true, true, false, 0, 0, 0, 0}, - {"0xdeadbeef", true, true, true, false, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0}, - // character constants - {`'a'`, true, true, true, false, 'a', 'a', 'a', 0}, - {`'\n'`, true, true, true, false, '\n', '\n', '\n', 0}, - {`'\\'`, true, true, true, false, '\\', '\\', '\\', 0}, - {`'\''`, true, true, true, false, '\'', '\'', '\'', 0}, - {`'\xFF'`, true, true, true, false, 0xFF, 0xFF, 0xFF, 0}, - {`'パ'`, true, true, true, false, 0x30d1, 0x30d1, 0x30d1, 0}, - {`'\u30d1'`, true, true, true, false, 0x30d1, 0x30d1, 0x30d1, 0}, - {`'\U000030d1'`, true, true, true, false, 0x30d1, 0x30d1, 0x30d1, 0}, - // some broken syntax - {text: "+-2"}, - {text: "0x123."}, - {text: "1e."}, - {text: "0xi."}, - {text: "1+2."}, - {text: "'x"}, - {text: "'xx'"}, - {text: "'433937734937734969526500969526500'"}, // Integer too large - issue 10634. - // Issue 8622 - 0xe parsed as floating point. Very embarrassing. - {"0xef", true, true, true, false, 0xef, 0xef, 0xef, 0}, -} - -func TestNumberParse(t *testing.T) { - for _, test := range numberTests { - // If fmt.Sscan thinks it's complex, it's complex. We can't trust the output - // because imaginary comes out as a number. - var c complex128 - typ := itemNumber - var tree *Tree - if test.text[0] == '\'' { - typ = itemCharConstant - } else { - _, err := fmt.Sscan(test.text, &c) - if err == nil { - typ = itemComplex - } - } - n, err := tree.newNumber(0, test.text, typ) - ok := test.isInt || test.isUint || test.isFloat || test.isComplex - if ok && err != nil { - t.Errorf("unexpected error for %q: %s", test.text, err) - continue - } - if !ok && err == nil { - t.Errorf("expected error for %q", test.text) - continue - } - if !ok { - if *debug { - fmt.Printf("%s\n\t%s\n", test.text, err) - } - continue - } - if n.IsComplex != test.isComplex { - t.Errorf("complex incorrect for %q; should be %t", test.text, test.isComplex) - } - if test.isInt { - if !n.IsInt { - t.Errorf("expected integer for %q", test.text) - } - if n.Int64 != test.int64 { - t.Errorf("int64 for %q should be %d Is %d", test.text, test.int64, n.Int64) - } - } else if n.IsInt { - t.Errorf("did not expect integer for %q", test.text) - } - if test.isUint { - if !n.IsUint { - t.Errorf("expected unsigned integer for %q", test.text) - } - if n.Uint64 != test.uint64 { - t.Errorf("uint64 for %q should be %d Is %d", test.text, test.uint64, n.Uint64) - } - } else if n.IsUint { - t.Errorf("did not expect unsigned integer for %q", test.text) - } - if test.isFloat { - if !n.IsFloat { - t.Errorf("expected float for %q", test.text) - } - if n.Float64 != test.float64 { - t.Errorf("float64 for %q should be %g Is %g", test.text, test.float64, n.Float64) - } - } else if n.IsFloat { - t.Errorf("did not expect float for %q", test.text) - } - if test.isComplex { - if !n.IsComplex { - t.Errorf("expected complex for %q", test.text) - } - if n.Complex128 != test.complex128 { - t.Errorf("complex128 for %q should be %g Is %g", test.text, test.complex128, n.Complex128) - } - } else if n.IsComplex { - t.Errorf("did not expect complex for %q", test.text) - } - } -} - -type parseTest struct { - name string - input string - ok bool - result string // what the user would see in an error message. -} - -const ( - noError = true - hasError = false -) - -var parseTests = []parseTest{ - {"empty", "", noError, - ``}, - {"comment", "{{/*\n\n\n*/}}", noError, - ``}, - {"spaces", " \t\n", noError, - `" \t\n"`}, - {"text", "some text", noError, - `"some text"`}, - {"emptyAction", "{{}}", hasError, - `{{}}`}, - {"field", "{{.X}}", noError, - `{{.X}}`}, - {"simple command", "{{printf}}", noError, - `{{printf}}`}, - {"$ invocation", "{{$}}", noError, - "{{$}}"}, - {"variable invocation", "{{with $x := 3}}{{$x 23}}{{end}}", noError, - "{{with $x := 3}}{{$x 23}}{{end}}"}, - {"variable with fields", "{{$.I}}", noError, - "{{$.I}}"}, - {"multi-word command", "{{printf `%d` 23}}", noError, - "{{printf `%d` 23}}"}, - {"pipeline", "{{.X|.Y}}", noError, - `{{.X | .Y}}`}, - {"pipeline with decl", "{{$x := .X|.Y}}", noError, - `{{$x := .X | .Y}}`}, - {"nested pipeline", "{{.X (.Y .Z) (.A | .B .C) (.E)}}", noError, - `{{.X (.Y .Z) (.A | .B .C) (.E)}}`}, - {"field applied to parentheses", "{{(.Y .Z).Field}}", noError, - `{{(.Y .Z).Field}}`}, - {"simple if", "{{if .X}}hello{{end}}", noError, - `{{if .X}}"hello"{{end}}`}, - {"if with else", "{{if .X}}true{{else}}false{{end}}", noError, - `{{if .X}}"true"{{else}}"false"{{end}}`}, - {"if with else if", "{{if .X}}true{{else if .Y}}false{{end}}", noError, - `{{if .X}}"true"{{else}}{{if .Y}}"false"{{end}}{{end}}`}, - {"if else chain", "+{{if .X}}X{{else if .Y}}Y{{else if .Z}}Z{{end}}+", noError, - `"+"{{if .X}}"X"{{else}}{{if .Y}}"Y"{{else}}{{if .Z}}"Z"{{end}}{{end}}{{end}}"+"`}, - {"simple range", "{{range .X}}hello{{end}}", noError, - `{{range .X}}"hello"{{end}}`}, - {"chained field range", "{{range .X.Y.Z}}hello{{end}}", noError, - `{{range .X.Y.Z}}"hello"{{end}}`}, - {"nested range", "{{range .X}}hello{{range .Y}}goodbye{{end}}{{end}}", noError, - `{{range .X}}"hello"{{range .Y}}"goodbye"{{end}}{{end}}`}, - {"range with else", "{{range .X}}true{{else}}false{{end}}", noError, - `{{range .X}}"true"{{else}}"false"{{end}}`}, - {"range over pipeline", "{{range .X|.M}}true{{else}}false{{end}}", noError, - `{{range .X | .M}}"true"{{else}}"false"{{end}}`}, - {"range []int", "{{range .SI}}{{.}}{{end}}", noError, - `{{range .SI}}{{.}}{{end}}`}, - {"range 1 var", "{{range $x := .SI}}{{.}}{{end}}", noError, - `{{range $x := .SI}}{{.}}{{end}}`}, - {"range 2 vars", "{{range $x, $y := .SI}}{{.}}{{end}}", noError, - `{{range $x, $y := .SI}}{{.}}{{end}}`}, - {"constants", "{{range .SI 1 -3.2i true false 'a' nil}}{{end}}", noError, - `{{range .SI 1 -3.2i true false 'a' nil}}{{end}}`}, - {"template", "{{template `x`}}", noError, - `{{template "x"}}`}, - {"template with arg", "{{template `x` .Y}}", noError, - `{{template "x" .Y}}`}, - {"with", "{{with .X}}hello{{end}}", noError, - `{{with .X}}"hello"{{end}}`}, - {"with with else", "{{with .X}}hello{{else}}goodbye{{end}}", noError, - `{{with .X}}"hello"{{else}}"goodbye"{{end}}`}, - // Trimming spaces. - {"trim left", "x \r\n\t{{- 3}}", noError, `"x"{{3}}`}, - {"trim right", "{{3 -}}\n\n\ty", noError, `{{3}}"y"`}, - {"trim left and right", "x \r\n\t{{- 3 -}}\n\n\ty", noError, `"x"{{3}}"y"`}, - {"comment trim left", "x \r\n\t{{- /* hi */}}", noError, `"x"`}, - {"comment trim right", "{{/* hi */ -}}\n\n\ty", noError, `"y"`}, - {"comment trim left and right", "x \r\n\t{{- /* */ -}}\n\n\ty", noError, `"x""y"`}, - {"block definition", `{{block "foo" .}}hello{{end}}`, noError, - `{{template "foo" .}}`}, - // Errors. - {"unclosed action", "hello{{range", hasError, ""}, - {"unmatched end", "{{end}}", hasError, ""}, - {"unmatched else", "{{else}}", hasError, ""}, - {"unmatched else after if", "{{if .X}}hello{{end}}{{else}}", hasError, ""}, - {"multiple else", "{{if .X}}1{{else}}2{{else}}3{{end}}", hasError, ""}, - {"missing end", "hello{{range .x}}", hasError, ""}, - {"missing end after else", "hello{{range .x}}{{else}}", hasError, ""}, - {"undefined function", "hello{{undefined}}", hasError, ""}, - {"undefined variable", "{{$x}}", hasError, ""}, - {"variable undefined after end", "{{with $x := 4}}{{end}}{{$x}}", hasError, ""}, - {"variable undefined in template", "{{template $v}}", hasError, ""}, - {"declare with field", "{{with $x.Y := 4}}{{end}}", hasError, ""}, - {"template with field ref", "{{template .X}}", hasError, ""}, - {"template with var", "{{template $v}}", hasError, ""}, - {"invalid punctuation", "{{printf 3, 4}}", hasError, ""}, - {"multidecl outside range", "{{with $v, $u := 3}}{{end}}", hasError, ""}, - {"too many decls in range", "{{range $u, $v, $w := 3}}{{end}}", hasError, ""}, - {"dot applied to parentheses", "{{printf (printf .).}}", hasError, ""}, - {"adjacent args", "{{printf 3`x`}}", hasError, ""}, - {"adjacent args with .", "{{printf `x`.}}", hasError, ""}, - {"extra end after if", "{{if .X}}a{{else if .Y}}b{{end}}{{end}}", hasError, ""}, - // Other kinds of assignments and operators aren't available yet. - {"bug0a", "{{$x := 0}}{{$x}}", noError, "{{$x := 0}}{{$x}}"}, - {"bug0b", "{{$x += 1}}{{$x}}", hasError, ""}, - {"bug0c", "{{$x ! 2}}{{$x}}", hasError, ""}, - {"bug0d", "{{$x % 3}}{{$x}}", hasError, ""}, - // Check the parse fails for := rather than comma. - {"bug0e", "{{range $x := $y := 3}}{{end}}", hasError, ""}, - // Another bug: variable read must ignore following punctuation. - {"bug1a", "{{$x:=.}}{{$x!2}}", hasError, ""}, // ! is just illegal here. - {"bug1b", "{{$x:=.}}{{$x+2}}", hasError, ""}, // $x+2 should not parse as ($x) (+2). - {"bug1c", "{{$x:=.}}{{$x +2}}", noError, "{{$x := .}}{{$x +2}}"}, // It's OK with a space. - // dot following a literal value - {"dot after integer", "{{1.E}}", hasError, ""}, - {"dot after float", "{{0.1.E}}", hasError, ""}, - {"dot after boolean", "{{true.E}}", hasError, ""}, - {"dot after char", "{{'a'.any}}", hasError, ""}, - {"dot after string", `{{"hello".guys}}`, hasError, ""}, - {"dot after dot", "{{..E}}", hasError, ""}, - {"dot after nil", "{{nil.E}}", hasError, ""}, - // Wrong pipeline - {"wrong pipeline dot", "{{12|.}}", hasError, ""}, - {"wrong pipeline number", "{{.|12|printf}}", hasError, ""}, - {"wrong pipeline string", "{{.|printf|\"error\"}}", hasError, ""}, - {"wrong pipeline char", "{{12|printf|'e'}}", hasError, ""}, - {"wrong pipeline boolean", "{{.|true}}", hasError, ""}, - {"wrong pipeline nil", "{{'c'|nil}}", hasError, ""}, - {"empty pipeline", `{{printf "%d" ( ) }}`, hasError, ""}, - // Missing pipeline in block - {"block definition", `{{block "foo"}}hello{{end}}`, hasError, ""}, -} - -var builtins = map[string]interface{}{ - "printf": fmt.Sprintf, -} - -func testParse(doCopy bool, t *testing.T) { - textFormat = "%q" - defer func() { textFormat = "%s" }() - for _, test := range parseTests { - tmpl, err := New(test.name).Parse(test.input, "", "", make(map[string]*Tree), builtins) - switch { - case err == nil && !test.ok: - t.Errorf("%q: expected error; got none", test.name) - continue - case err != nil && test.ok: - t.Errorf("%q: unexpected error: %v", test.name, err) - continue - case err != nil && !test.ok: - // expected error, got one - if *debug { - fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err) - } - continue - } - var result string - if doCopy { - result = tmpl.Root.Copy().String() - } else { - result = tmpl.Root.String() - } - if result != test.result { - t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.result) - } - } -} - -func TestParse(t *testing.T) { - testParse(false, t) -} - -// Same as TestParse, but we copy the node first -func TestParseCopy(t *testing.T) { - testParse(true, t) -} - -type isEmptyTest struct { - name string - input string - empty bool -} - -var isEmptyTests = []isEmptyTest{ - {"empty", ``, true}, - {"nonempty", `hello`, false}, - {"spaces only", " \t\n \t\n", true}, - {"definition", `{{define "x"}}something{{end}}`, true}, - {"definitions and space", "{{define `x`}}something{{end}}\n\n{{define `y`}}something{{end}}\n\n", true}, - {"definitions and text", "{{define `x`}}something{{end}}\nx\n{{define `y`}}something{{end}}\ny\n", false}, - {"definition and action", "{{define `x`}}something{{end}}{{if 3}}foo{{end}}", false}, -} - -func TestIsEmpty(t *testing.T) { - if !IsEmptyTree(nil) { - t.Errorf("nil tree is not empty") - } - for _, test := range isEmptyTests { - tree, err := New("root").Parse(test.input, "", "", make(map[string]*Tree), nil) - if err != nil { - t.Errorf("%q: unexpected error: %v", test.name, err) - continue - } - if empty := IsEmptyTree(tree.Root); empty != test.empty { - t.Errorf("%q: expected %t got %t", test.name, test.empty, empty) - } - } -} - -func TestErrorContextWithTreeCopy(t *testing.T) { - tree, err := New("root").Parse("{{if true}}{{end}}", "", "", make(map[string]*Tree), nil) - if err != nil { - t.Fatalf("unexpected tree parse failure: %v", err) - } - treeCopy := tree.Copy() - wantLocation, wantContext := tree.ErrorContext(tree.Root.Nodes[0]) - gotLocation, gotContext := treeCopy.ErrorContext(treeCopy.Root.Nodes[0]) - if wantLocation != gotLocation { - t.Errorf("wrong error location want %q got %q", wantLocation, gotLocation) - } - if wantContext != gotContext { - t.Errorf("wrong error location want %q got %q", wantContext, gotContext) - } -} - -// All failures, and the result is a string that must appear in the error message. -var errorTests = []parseTest{ - // Check line numbers are accurate. - {"unclosed1", - "line1\n{{", - hasError, `unclosed1:2: unexpected unclosed action in command`}, - {"unclosed2", - "line1\n{{define `x`}}line2\n{{", - hasError, `unclosed2:3: unexpected unclosed action in command`}, - // Specific errors. - {"function", - "{{foo}}", - hasError, `function "foo" not defined`}, - {"comment", - "{{/*}}", - hasError, `unclosed comment`}, - {"lparen", - "{{.X (1 2 3}}", - hasError, `unclosed left paren`}, - {"rparen", - "{{.X 1 2 3)}}", - hasError, `unexpected ")"`}, - {"space", - "{{`x`3}}", - hasError, `in operand`}, - {"idchar", - "{{a#}}", - hasError, `'#'`}, - {"charconst", - "{{'a}}", - hasError, `unterminated character constant`}, - {"stringconst", - `{{"a}}`, - hasError, `unterminated quoted string`}, - {"rawstringconst", - "{{`a}}", - hasError, `unterminated raw quoted string`}, - {"number", - "{{0xi}}", - hasError, `number syntax`}, - {"multidefine", - "{{define `a`}}a{{end}}{{define `a`}}b{{end}}", - hasError, `multiple definition of template`}, - {"eof", - "{{range .X}}", - hasError, `unexpected EOF`}, - {"variable", - // Declare $x so it's defined, to avoid that error, and then check we don't parse a declaration. - "{{$x := 23}}{{with $x.y := 3}}{{$x 23}}{{end}}", - hasError, `unexpected ":="`}, - {"multidecl", - "{{$a,$b,$c := 23}}", - hasError, `too many declarations`}, - {"undefvar", - "{{$a}}", - hasError, `undefined variable`}, - {"wrongdot", - "{{true.any}}", - hasError, `unexpected . after term`}, - {"wrongpipeline", - "{{12|false}}", - hasError, `non executable command in pipeline`}, - {"emptypipeline", - `{{ ( ) }}`, - hasError, `missing value for parenthesized pipeline`}, - {"multilinerawstring", - "{{ $v := `\n` }} {{", - hasError, `multilinerawstring:2: unexpected unclosed action`}, - {"rangeundefvar", - "{{range $k}}{{end}}", - hasError, `undefined variable`}, - {"rangeundefvars", - "{{range $k, $v}}{{end}}", - hasError, `undefined variable`}, - {"rangemissingvalue1", - "{{range $k,}}{{end}}", - hasError, `missing value for range`}, - {"rangemissingvalue2", - "{{range $k, $v := }}{{end}}", - hasError, `missing value for range`}, - {"rangenotvariable1", - "{{range $k, .}}{{end}}", - hasError, `range can only initialize variables`}, - {"rangenotvariable2", - "{{range $k, 123 := .}}{{end}}", - hasError, `range can only initialize variables`}, -} - -func TestErrors(t *testing.T) { - for _, test := range errorTests { - t.Run(test.name, func(t *testing.T) { - _, err := New(test.name).Parse(test.input, "", "", make(map[string]*Tree)) - if err == nil { - t.Fatalf("expected error %q, got nil", test.result) - } - if !strings.Contains(err.Error(), test.result) { - t.Fatalf("error %q does not contain %q", err, test.result) - } - }) - } -} - -func TestBlock(t *testing.T) { - const ( - input = `a{{block "inner" .}}bar{{.}}baz{{end}}b` - outer = `a{{template "inner" .}}b` - inner = `bar{{.}}baz` - ) - treeSet := make(map[string]*Tree) - tmpl, err := New("outer").Parse(input, "", "", treeSet, nil) - if err != nil { - t.Fatal(err) - } - if g, w := tmpl.Root.String(), outer; g != w { - t.Errorf("outer template = %q, want %q", g, w) - } - inTmpl := treeSet["inner"] - if inTmpl == nil { - t.Fatal("block did not define template") - } - if g, w := inTmpl.Root.String(), inner; g != w { - t.Errorf("inner template = %q, want %q", g, w) - } -} - -func TestLineNum(t *testing.T) { - const count = 100 - text := strings.Repeat("{{printf 1234}}\n", count) - tree, err := New("bench").Parse(text, "", "", make(map[string]*Tree), builtins) - if err != nil { - t.Fatal(err) - } - // Check the line numbers. Each line is an action containing a template, followed by text. - // That's two nodes per line. - nodes := tree.Root.Nodes - for i := 0; i < len(nodes); i += 2 { - line := 1 + i/2 - // Action first. - action := nodes[i].(*ActionNode) - if action.Line != line { - t.Fatalf("line %d: action is line %d", line, action.Line) - } - pipe := action.Pipe - if pipe.Line != line { - t.Fatalf("line %d: pipe is line %d", line, pipe.Line) - } - } -} - -func BenchmarkParseLarge(b *testing.B) { - text := strings.Repeat("{{1234}}\n", 10000) - for i := 0; i < b.N; i++ { - _, err := New("bench").Parse(text, "", "", make(map[string]*Tree), builtins) - if err != nil { - b.Fatal(err) - } - } -} diff --git a/g/os/gview/internal/text/template/testdata/file1.tmpl b/g/os/gview/internal/text/template/testdata/file1.tmpl deleted file mode 100644 index febf9d9f8..000000000 --- a/g/os/gview/internal/text/template/testdata/file1.tmpl +++ /dev/null @@ -1,2 +0,0 @@ -{{define "x"}}TEXT{{end}} -{{define "dotV"}}{{.V}}{{end}} diff --git a/g/os/gview/internal/text/template/testdata/file2.tmpl b/g/os/gview/internal/text/template/testdata/file2.tmpl deleted file mode 100644 index 39bf6fb9e..000000000 --- a/g/os/gview/internal/text/template/testdata/file2.tmpl +++ /dev/null @@ -1,2 +0,0 @@ -{{define "dot"}}{{.}}{{end}} -{{define "nested"}}{{template "dot" .}}{{end}} diff --git a/g/os/gview/internal/text/template/testdata/tmpl1.tmpl b/g/os/gview/internal/text/template/testdata/tmpl1.tmpl deleted file mode 100644 index b72b3a340..000000000 --- a/g/os/gview/internal/text/template/testdata/tmpl1.tmpl +++ /dev/null @@ -1,3 +0,0 @@ -template1 -{{define "x"}}x{{end}} -{{template "y"}} diff --git a/g/os/gview/internal/text/template/testdata/tmpl2.tmpl b/g/os/gview/internal/text/template/testdata/tmpl2.tmpl deleted file mode 100644 index 16beba6e7..000000000 --- a/g/os/gview/internal/text/template/testdata/tmpl2.tmpl +++ /dev/null @@ -1,3 +0,0 @@ -template2 -{{define "y"}}y{{end}} -{{template "x"}}