mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			811 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			811 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
// Copyright 2009 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"
 | 
						|
	"encoding/json"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"io/ioutil"
 | 
						|
	"os"
 | 
						|
	"strings"
 | 
						|
	"testing"
 | 
						|
)
 | 
						|
 | 
						|
type Test struct {
 | 
						|
	in, out, err string
 | 
						|
}
 | 
						|
 | 
						|
type T struct {
 | 
						|
	Item  string
 | 
						|
	Value string
 | 
						|
}
 | 
						|
 | 
						|
type U struct {
 | 
						|
	Mp map[string]int
 | 
						|
}
 | 
						|
 | 
						|
type S struct {
 | 
						|
	Header        string
 | 
						|
	HeaderPtr     *string
 | 
						|
	Integer       int
 | 
						|
	IntegerPtr    *int
 | 
						|
	NilPtr        *int
 | 
						|
	InnerT        T
 | 
						|
	InnerPointerT *T
 | 
						|
	Data          []T
 | 
						|
	Pdata         []*T
 | 
						|
	Empty         []*T
 | 
						|
	Emptystring   string
 | 
						|
	Null          []*T
 | 
						|
	Vec           []interface{}
 | 
						|
	True          bool
 | 
						|
	False         bool
 | 
						|
	Mp            map[string]string
 | 
						|
	JSON          interface{}
 | 
						|
	Innermap      U
 | 
						|
	Stringmap     map[string]string
 | 
						|
	Ptrmap        map[string]*string
 | 
						|
	Iface         interface{}
 | 
						|
	Ifaceptr      interface{}
 | 
						|
}
 | 
						|
 | 
						|
func (s *S) PointerMethod() string { return "ptrmethod!" }
 | 
						|
 | 
						|
func (s S) ValueMethod() string { return "valmethod!" }
 | 
						|
 | 
						|
var t1 = T{"ItemNumber1", "ValueNumber1"}
 | 
						|
var t2 = T{"ItemNumber2", "ValueNumber2"}
 | 
						|
 | 
						|
func uppercase(v interface{}) string {
 | 
						|
	s := v.(string)
 | 
						|
	t := ""
 | 
						|
	for i := 0; i < len(s); i++ {
 | 
						|
		c := s[i]
 | 
						|
		if 'a' <= c && c <= 'z' {
 | 
						|
			c = c + 'A' - 'a'
 | 
						|
		}
 | 
						|
		t += string(c)
 | 
						|
	}
 | 
						|
	return t
 | 
						|
}
 | 
						|
 | 
						|
func plus1(v interface{}) string {
 | 
						|
	i := v.(int)
 | 
						|
	return fmt.Sprint(i + 1)
 | 
						|
}
 | 
						|
 | 
						|
func writer(f func(interface{}) string) func(io.Writer, string, ...interface{}) {
 | 
						|
	return func(w io.Writer, format string, v ...interface{}) {
 | 
						|
		if len(v) != 1 {
 | 
						|
			panic("test writer expected one arg")
 | 
						|
		}
 | 
						|
		io.WriteString(w, f(v[0]))
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func multiword(w io.Writer, format string, value ...interface{}) {
 | 
						|
	for _, v := range value {
 | 
						|
		fmt.Fprintf(w, "<%v>", v)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func printf(w io.Writer, format string, v ...interface{}) {
 | 
						|
	io.WriteString(w, fmt.Sprintf(v[0].(string), v[1:]...))
 | 
						|
}
 | 
						|
 | 
						|
var formatters = FormatterMap{
 | 
						|
	"uppercase": writer(uppercase),
 | 
						|
	"+1":        writer(plus1),
 | 
						|
	"multiword": multiword,
 | 
						|
	"printf":    printf,
 | 
						|
}
 | 
						|
 | 
						|
var tests = []*Test{
 | 
						|
	// Simple
 | 
						|
	{"", "", ""},
 | 
						|
	{"abc", "abc", ""},
 | 
						|
	{"abc\ndef\n", "abc\ndef\n", ""},
 | 
						|
	{" {.meta-left}   \n", "{", ""},
 | 
						|
	{" {.meta-right}   \n", "}", ""},
 | 
						|
	{" {.space}   \n", " ", ""},
 | 
						|
	{" {.tab}   \n", "\t", ""},
 | 
						|
	{"     {#comment}   \n", "", ""},
 | 
						|
	{"\tSome Text\t\n", "\tSome Text\t\n", ""},
 | 
						|
	{" {.meta-right} {.meta-right} {.meta-right} \n", " } } } \n", ""},
 | 
						|
 | 
						|
	// Variables at top level
 | 
						|
	{
 | 
						|
		in: "{Header}={Integer}\n",
 | 
						|
 | 
						|
		out: "Header=77\n",
 | 
						|
	},
 | 
						|
 | 
						|
	{
 | 
						|
		in: "Pointers: {*HeaderPtr}={*IntegerPtr}\n",
 | 
						|
 | 
						|
		out: "Pointers: Header=77\n",
 | 
						|
	},
 | 
						|
 | 
						|
	{
 | 
						|
		in: "Stars but not pointers: {*Header}={*Integer}\n",
 | 
						|
 | 
						|
		out: "Stars but not pointers: Header=77\n",
 | 
						|
	},
 | 
						|
 | 
						|
	{
 | 
						|
		in: "nil pointer: {*NilPtr}={*Integer}\n",
 | 
						|
 | 
						|
		out: "nil pointer: <nil>=77\n",
 | 
						|
	},
 | 
						|
 | 
						|
	{
 | 
						|
		in: `{"Strings" ":"} {""} {"|"} {"\t\u0123 \x23\\"} {"\"}{\\"}`,
 | 
						|
 | 
						|
		out: "Strings:  | \t\u0123 \x23\\ \"}{\\",
 | 
						|
	},
 | 
						|
 | 
						|
	{
 | 
						|
		in: "{`Raw strings` `:`} {``} {`|`} {`\\t\\u0123 \\x23\\`} {`}{\\`}",
 | 
						|
 | 
						|
		out: "Raw strings:  | \\t\\u0123 \\x23\\ }{\\",
 | 
						|
	},
 | 
						|
 | 
						|
	{
 | 
						|
		in: "Characters: {'a'} {'\\u0123'} {' '} {'{'} {'|'} {'}'}",
 | 
						|
 | 
						|
		out: "Characters: 97 291 32 123 124 125",
 | 
						|
	},
 | 
						|
 | 
						|
	{
 | 
						|
		in: "Integers: {1} {-2} {+42} {0777} {0x0a}",
 | 
						|
 | 
						|
		out: "Integers: 1 -2 42 511 10",
 | 
						|
	},
 | 
						|
 | 
						|
	{
 | 
						|
		in: "Floats: {.5} {-.5} {1.1} {-2.2} {+42.1} {1e10} {1.2e-3} {1.2e3} {-1.2e3}",
 | 
						|
 | 
						|
		out: "Floats: 0.5 -0.5 1.1 -2.2 42.1 1e+10 0.0012 1200 -1200",
 | 
						|
	},
 | 
						|
 | 
						|
	// Method at top level
 | 
						|
	{
 | 
						|
		in: "ptrmethod={PointerMethod}\n",
 | 
						|
 | 
						|
		out: "ptrmethod=ptrmethod!\n",
 | 
						|
	},
 | 
						|
 | 
						|
	{
 | 
						|
		in: "valmethod={ValueMethod}\n",
 | 
						|
 | 
						|
		out: "valmethod=valmethod!\n",
 | 
						|
	},
 | 
						|
 | 
						|
	// Section
 | 
						|
	{
 | 
						|
		in: "{.section Data }\n" +
 | 
						|
			"some text for the section\n" +
 | 
						|
			"{.end}\n",
 | 
						|
 | 
						|
		out: "some text for the section\n",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "{.section Data }\n" +
 | 
						|
			"{Header}={Integer}\n" +
 | 
						|
			"{.end}\n",
 | 
						|
 | 
						|
		out: "Header=77\n",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "{.section Pdata }\n" +
 | 
						|
			"{Header}={Integer}\n" +
 | 
						|
			"{.end}\n",
 | 
						|
 | 
						|
		out: "Header=77\n",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "{.section Pdata }\n" +
 | 
						|
			"data present\n" +
 | 
						|
			"{.or}\n" +
 | 
						|
			"data not present\n" +
 | 
						|
			"{.end}\n",
 | 
						|
 | 
						|
		out: "data present\n",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "{.section Empty }\n" +
 | 
						|
			"data present\n" +
 | 
						|
			"{.or}\n" +
 | 
						|
			"data not present\n" +
 | 
						|
			"{.end}\n",
 | 
						|
 | 
						|
		out: "data not present\n",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "{.section Null }\n" +
 | 
						|
			"data present\n" +
 | 
						|
			"{.or}\n" +
 | 
						|
			"data not present\n" +
 | 
						|
			"{.end}\n",
 | 
						|
 | 
						|
		out: "data not present\n",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "{.section Pdata }\n" +
 | 
						|
			"{Header}={Integer}\n" +
 | 
						|
			"{.section @ }\n" +
 | 
						|
			"{Header}={Integer}\n" +
 | 
						|
			"{.end}\n" +
 | 
						|
			"{.end}\n",
 | 
						|
 | 
						|
		out: "Header=77\n" +
 | 
						|
			"Header=77\n",
 | 
						|
	},
 | 
						|
 | 
						|
	{
 | 
						|
		in: "{.section Data}{.end} {Header}\n",
 | 
						|
 | 
						|
		out: " Header\n",
 | 
						|
	},
 | 
						|
 | 
						|
	{
 | 
						|
		in: "{.section Integer}{@}{.end}",
 | 
						|
 | 
						|
		out: "77",
 | 
						|
	},
 | 
						|
 | 
						|
	// Repeated
 | 
						|
	{
 | 
						|
		in: "{.section Pdata }\n" +
 | 
						|
			"{.repeated section @ }\n" +
 | 
						|
			"{Item}={Value}\n" +
 | 
						|
			"{.end}\n" +
 | 
						|
			"{.end}\n",
 | 
						|
 | 
						|
		out: "ItemNumber1=ValueNumber1\n" +
 | 
						|
			"ItemNumber2=ValueNumber2\n",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "{.section Pdata }\n" +
 | 
						|
			"{.repeated section @ }\n" +
 | 
						|
			"{Item}={Value}\n" +
 | 
						|
			"{.or}\n" +
 | 
						|
			"this should not appear\n" +
 | 
						|
			"{.end}\n" +
 | 
						|
			"{.end}\n",
 | 
						|
 | 
						|
		out: "ItemNumber1=ValueNumber1\n" +
 | 
						|
			"ItemNumber2=ValueNumber2\n",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "{.section @ }\n" +
 | 
						|
			"{.repeated section Empty }\n" +
 | 
						|
			"{Item}={Value}\n" +
 | 
						|
			"{.or}\n" +
 | 
						|
			"this should appear: empty field\n" +
 | 
						|
			"{.end}\n" +
 | 
						|
			"{.end}\n",
 | 
						|
 | 
						|
		out: "this should appear: empty field\n",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "{.repeated section Pdata }\n" +
 | 
						|
			"{Item}\n" +
 | 
						|
			"{.alternates with}\n" +
 | 
						|
			"is\nover\nmultiple\nlines\n" +
 | 
						|
			"{.end}\n",
 | 
						|
 | 
						|
		out: "ItemNumber1\n" +
 | 
						|
			"is\nover\nmultiple\nlines\n" +
 | 
						|
			"ItemNumber2\n",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "{.repeated section Pdata }\n" +
 | 
						|
			"{Item}\n" +
 | 
						|
			"{.alternates with}\n" +
 | 
						|
			"is\nover\nmultiple\nlines\n" +
 | 
						|
			" {.end}\n",
 | 
						|
 | 
						|
		out: "ItemNumber1\n" +
 | 
						|
			"is\nover\nmultiple\nlines\n" +
 | 
						|
			"ItemNumber2\n",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "{.section Pdata }\n" +
 | 
						|
			"{.repeated section @ }\n" +
 | 
						|
			"{Item}={Value}\n" +
 | 
						|
			"{.alternates with}DIVIDER\n" +
 | 
						|
			"{.or}\n" +
 | 
						|
			"this should not appear\n" +
 | 
						|
			"{.end}\n" +
 | 
						|
			"{.end}\n",
 | 
						|
 | 
						|
		out: "ItemNumber1=ValueNumber1\n" +
 | 
						|
			"DIVIDER\n" +
 | 
						|
			"ItemNumber2=ValueNumber2\n",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "{.repeated section Vec }\n" +
 | 
						|
			"{@}\n" +
 | 
						|
			"{.end}\n",
 | 
						|
 | 
						|
		out: "elt1\n" +
 | 
						|
			"elt2\n",
 | 
						|
	},
 | 
						|
	// Same but with a space before {.end}: was a bug.
 | 
						|
	{
 | 
						|
		in: "{.repeated section Vec }\n" +
 | 
						|
			"{@} {.end}\n",
 | 
						|
 | 
						|
		out: "elt1 elt2 \n",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "{.repeated section Integer}{.end}",
 | 
						|
 | 
						|
		err: "line 1: .repeated: cannot repeat Integer (type int)",
 | 
						|
	},
 | 
						|
 | 
						|
	// Nested names
 | 
						|
	{
 | 
						|
		in: "{.section @ }\n" +
 | 
						|
			"{InnerT.Item}={InnerT.Value}\n" +
 | 
						|
			"{.end}",
 | 
						|
 | 
						|
		out: "ItemNumber1=ValueNumber1\n",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "{.section @ }\n" +
 | 
						|
			"{InnerT.Item}={.section InnerT}{.section Value}{@}{.end}{.end}\n" +
 | 
						|
			"{.end}",
 | 
						|
 | 
						|
		out: "ItemNumber1=ValueNumber1\n",
 | 
						|
	},
 | 
						|
 | 
						|
	{
 | 
						|
		in: "{.section Emptystring}emptystring{.end}\n" +
 | 
						|
			"{.section Header}header{.end}\n",
 | 
						|
 | 
						|
		out: "\nheader\n",
 | 
						|
	},
 | 
						|
 | 
						|
	{
 | 
						|
		in: "{.section True}1{.or}2{.end}\n" +
 | 
						|
			"{.section False}3{.or}4{.end}\n",
 | 
						|
 | 
						|
		out: "1\n4\n",
 | 
						|
	},
 | 
						|
 | 
						|
	// Maps
 | 
						|
 | 
						|
	{
 | 
						|
		in: "{Mp.mapkey}\n",
 | 
						|
 | 
						|
		out: "Ahoy!\n",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "{Innermap.Mp.innerkey}\n",
 | 
						|
 | 
						|
		out: "55\n",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "{.section Innermap}{.section Mp}{innerkey}{.end}{.end}\n",
 | 
						|
 | 
						|
		out: "55\n",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "{.section JSON}{.repeated section maps}{a}{b}{.end}{.end}\n",
 | 
						|
 | 
						|
		out: "1234\n",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "{Stringmap.stringkey1}\n",
 | 
						|
 | 
						|
		out: "stringresult\n",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "{.repeated section Stringmap}\n" +
 | 
						|
			"{@}\n" +
 | 
						|
			"{.end}",
 | 
						|
 | 
						|
		out: "stringresult\n" +
 | 
						|
			"stringresult\n",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "{.repeated section Stringmap}\n" +
 | 
						|
			"\t{@}\n" +
 | 
						|
			"{.end}",
 | 
						|
 | 
						|
		out: "\tstringresult\n" +
 | 
						|
			"\tstringresult\n",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "{*Ptrmap.stringkey1}\n",
 | 
						|
 | 
						|
		out: "pointedToString\n",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "{.repeated section Ptrmap}\n" +
 | 
						|
			"{*@}\n" +
 | 
						|
			"{.end}",
 | 
						|
 | 
						|
		out: "pointedToString\n" +
 | 
						|
			"pointedToString\n",
 | 
						|
	},
 | 
						|
 | 
						|
	// Interface values
 | 
						|
 | 
						|
	{
 | 
						|
		in: "{Iface}",
 | 
						|
 | 
						|
		out: "[1 2 3]",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "{.repeated section Iface}{@}{.alternates with} {.end}",
 | 
						|
 | 
						|
		out: "1 2 3",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "{.section Iface}{@}{.end}",
 | 
						|
 | 
						|
		out: "[1 2 3]",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "{.section Ifaceptr}{Item} {Value}{.end}",
 | 
						|
 | 
						|
		out: "Item Value",
 | 
						|
	},
 | 
						|
}
 | 
						|
 | 
						|
func TestAll(t *testing.T) {
 | 
						|
	// Parse
 | 
						|
	testAll(t, func(test *Test) (*Template, error) { return Parse(test.in, formatters) })
 | 
						|
	// ParseFile
 | 
						|
	f, err := ioutil.TempFile("", "template-test")
 | 
						|
	if err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
	defer func() {
 | 
						|
		name := f.Name()
 | 
						|
		f.Close()
 | 
						|
		os.Remove(name)
 | 
						|
	}()
 | 
						|
	testAll(t, func(test *Test) (*Template, error) {
 | 
						|
		err := ioutil.WriteFile(f.Name(), []byte(test.in), 0600)
 | 
						|
		if err != nil {
 | 
						|
			t.Error("unexpected write error:", err)
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		return ParseFile(f.Name(), formatters)
 | 
						|
	})
 | 
						|
	// tmpl.ParseFile
 | 
						|
	testAll(t, func(test *Test) (*Template, error) {
 | 
						|
		err := ioutil.WriteFile(f.Name(), []byte(test.in), 0600)
 | 
						|
		if err != nil {
 | 
						|
			t.Error("unexpected write error:", err)
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		tmpl := New(formatters)
 | 
						|
		return tmpl, tmpl.ParseFile(f.Name())
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func testAll(t *testing.T, parseFunc func(*Test) (*Template, error)) {
 | 
						|
	s := new(S)
 | 
						|
	// initialized by hand for clarity.
 | 
						|
	s.Header = "Header"
 | 
						|
	s.HeaderPtr = &s.Header
 | 
						|
	s.Integer = 77
 | 
						|
	s.IntegerPtr = &s.Integer
 | 
						|
	s.InnerT = t1
 | 
						|
	s.Data = []T{t1, t2}
 | 
						|
	s.Pdata = []*T{&t1, &t2}
 | 
						|
	s.Empty = []*T{}
 | 
						|
	s.Null = nil
 | 
						|
	s.Vec = []interface{}{"elt1", "elt2"}
 | 
						|
	s.True = true
 | 
						|
	s.False = false
 | 
						|
	s.Mp = make(map[string]string)
 | 
						|
	s.Mp["mapkey"] = "Ahoy!"
 | 
						|
	json.Unmarshal([]byte(`{"maps":[{"a":1,"b":2},{"a":3,"b":4}]}`), &s.JSON)
 | 
						|
	s.Innermap.Mp = make(map[string]int)
 | 
						|
	s.Innermap.Mp["innerkey"] = 55
 | 
						|
	s.Stringmap = make(map[string]string)
 | 
						|
	s.Stringmap["stringkey1"] = "stringresult" // the same value so repeated section is order-independent
 | 
						|
	s.Stringmap["stringkey2"] = "stringresult"
 | 
						|
	s.Ptrmap = make(map[string]*string)
 | 
						|
	x := "pointedToString"
 | 
						|
	s.Ptrmap["stringkey1"] = &x // the same value so repeated section is order-independent
 | 
						|
	s.Ptrmap["stringkey2"] = &x
 | 
						|
	s.Iface = []int{1, 2, 3}
 | 
						|
	s.Ifaceptr = &T{"Item", "Value"}
 | 
						|
 | 
						|
	var buf bytes.Buffer
 | 
						|
	for _, test := range tests {
 | 
						|
		buf.Reset()
 | 
						|
		tmpl, err := parseFunc(test)
 | 
						|
		if err != nil {
 | 
						|
			t.Error("unexpected parse error: ", err)
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		err = tmpl.Execute(&buf, s)
 | 
						|
		if test.err == "" {
 | 
						|
			if err != nil {
 | 
						|
				t.Error("unexpected execute error:", err)
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			if err == nil {
 | 
						|
				t.Errorf("expected execute error %q, got nil", test.err)
 | 
						|
			} else if err.Error() != test.err {
 | 
						|
				t.Errorf("expected execute error %q, got %q", test.err, err.Error())
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if buf.String() != test.out {
 | 
						|
			t.Errorf("for %q: expected %q got %q", test.in, test.out, buf.String())
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestMapDriverType(t *testing.T) {
 | 
						|
	mp := map[string]string{"footer": "Ahoy!"}
 | 
						|
	tmpl, err := Parse("template: {footer}", nil)
 | 
						|
	if err != nil {
 | 
						|
		t.Error("unexpected parse error:", err)
 | 
						|
	}
 | 
						|
	var b bytes.Buffer
 | 
						|
	err = tmpl.Execute(&b, mp)
 | 
						|
	if err != nil {
 | 
						|
		t.Error("unexpected execute error:", err)
 | 
						|
	}
 | 
						|
	s := b.String()
 | 
						|
	expect := "template: Ahoy!"
 | 
						|
	if s != expect {
 | 
						|
		t.Errorf("failed passing string as data: expected %q got %q", expect, s)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestMapNoEntry(t *testing.T) {
 | 
						|
	mp := make(map[string]int)
 | 
						|
	tmpl, err := Parse("template: {notthere}!", nil)
 | 
						|
	if err != nil {
 | 
						|
		t.Error("unexpected parse error:", err)
 | 
						|
	}
 | 
						|
	var b bytes.Buffer
 | 
						|
	err = tmpl.Execute(&b, mp)
 | 
						|
	if err != nil {
 | 
						|
		t.Error("unexpected execute error:", err)
 | 
						|
	}
 | 
						|
	s := b.String()
 | 
						|
	expect := "template: 0!"
 | 
						|
	if s != expect {
 | 
						|
		t.Errorf("failed passing string as data: expected %q got %q", expect, s)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestStringDriverType(t *testing.T) {
 | 
						|
	tmpl, err := Parse("template: {@}", nil)
 | 
						|
	if err != nil {
 | 
						|
		t.Error("unexpected parse error:", err)
 | 
						|
	}
 | 
						|
	var b bytes.Buffer
 | 
						|
	err = tmpl.Execute(&b, "hello")
 | 
						|
	if err != nil {
 | 
						|
		t.Error("unexpected execute error:", err)
 | 
						|
	}
 | 
						|
	s := b.String()
 | 
						|
	expect := "template: hello"
 | 
						|
	if s != expect {
 | 
						|
		t.Errorf("failed passing string as data: expected %q got %q", expect, s)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestTwice(t *testing.T) {
 | 
						|
	tmpl, err := Parse("template: {@}", nil)
 | 
						|
	if err != nil {
 | 
						|
		t.Error("unexpected parse error:", err)
 | 
						|
	}
 | 
						|
	var b bytes.Buffer
 | 
						|
	err = tmpl.Execute(&b, "hello")
 | 
						|
	if err != nil {
 | 
						|
		t.Error("unexpected parse error:", err)
 | 
						|
	}
 | 
						|
	s := b.String()
 | 
						|
	expect := "template: hello"
 | 
						|
	if s != expect {
 | 
						|
		t.Errorf("failed passing string as data: expected %q got %q", expect, s)
 | 
						|
	}
 | 
						|
	err = tmpl.Execute(&b, "hello")
 | 
						|
	if err != nil {
 | 
						|
		t.Error("unexpected parse error:", err)
 | 
						|
	}
 | 
						|
	s = b.String()
 | 
						|
	expect += expect
 | 
						|
	if s != expect {
 | 
						|
		t.Errorf("failed passing string as data: expected %q got %q", expect, s)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestCustomDelims(t *testing.T) {
 | 
						|
	// try various lengths.  zero should catch error.
 | 
						|
	for i := 0; i < 7; i++ {
 | 
						|
		for j := 0; j < 7; j++ {
 | 
						|
			tmpl := New(nil)
 | 
						|
			// first two chars deliberately the same to test equal left and right delims
 | 
						|
			ldelim := "$!#$%^&"[0:i]
 | 
						|
			rdelim := "$*&^%$!"[0:j]
 | 
						|
			tmpl.SetDelims(ldelim, rdelim)
 | 
						|
			// if braces, this would be template: {@}{.meta-left}{.meta-right}
 | 
						|
			text := "template: " +
 | 
						|
				ldelim + "@" + rdelim +
 | 
						|
				ldelim + ".meta-left" + rdelim +
 | 
						|
				ldelim + ".meta-right" + rdelim
 | 
						|
			err := tmpl.Parse(text)
 | 
						|
			if err != nil {
 | 
						|
				if i == 0 || j == 0 { // expected
 | 
						|
					continue
 | 
						|
				}
 | 
						|
				t.Error("unexpected parse error:", err)
 | 
						|
			} else if i == 0 || j == 0 {
 | 
						|
				t.Errorf("expected parse error for empty delimiter: %d %d %q %q", i, j, ldelim, rdelim)
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			var b bytes.Buffer
 | 
						|
			err = tmpl.Execute(&b, "hello")
 | 
						|
			s := b.String()
 | 
						|
			if s != "template: hello"+ldelim+rdelim {
 | 
						|
				t.Errorf("failed delim check(%q %q) %q got %q", ldelim, rdelim, text, s)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Test that a variable evaluates to the field itself and does not further indirection
 | 
						|
func TestVarIndirection(t *testing.T) {
 | 
						|
	s := new(S)
 | 
						|
	// initialized by hand for clarity.
 | 
						|
	s.InnerPointerT = &t1
 | 
						|
 | 
						|
	var buf bytes.Buffer
 | 
						|
	input := "{.section @}{InnerPointerT}{.end}"
 | 
						|
	tmpl, err := Parse(input, nil)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatal("unexpected parse error:", err)
 | 
						|
	}
 | 
						|
	err = tmpl.Execute(&buf, s)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatal("unexpected execute error:", err)
 | 
						|
	}
 | 
						|
	expect := fmt.Sprintf("%v", &t1) // output should be hex address of t1
 | 
						|
	if buf.String() != expect {
 | 
						|
		t.Errorf("for %q: expected %q got %q", input, expect, buf.String())
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestHTMLFormatterWithByte(t *testing.T) {
 | 
						|
	s := "Test string."
 | 
						|
	b := []byte(s)
 | 
						|
	var buf bytes.Buffer
 | 
						|
	HTMLFormatter(&buf, "", b)
 | 
						|
	bs := buf.String()
 | 
						|
	if bs != s {
 | 
						|
		t.Errorf("munged []byte, expected: %s got: %s", s, bs)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type UF struct {
 | 
						|
	I int
 | 
						|
	s string
 | 
						|
}
 | 
						|
 | 
						|
func TestReferenceToUnexported(t *testing.T) {
 | 
						|
	u := &UF{3, "hello"}
 | 
						|
	var buf bytes.Buffer
 | 
						|
	input := "{.section @}{I}{s}{.end}"
 | 
						|
	tmpl, err := Parse(input, nil)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatal("unexpected parse error:", err)
 | 
						|
	}
 | 
						|
	err = tmpl.Execute(&buf, u)
 | 
						|
	if err == nil {
 | 
						|
		t.Fatal("expected execute error, got none")
 | 
						|
	}
 | 
						|
	if strings.Index(err.Error(), "not exported") < 0 {
 | 
						|
		t.Fatal("expected unexported error; got", err)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
var formatterTests = []Test{
 | 
						|
	{
 | 
						|
		in: "{Header|uppercase}={Integer|+1}\n" +
 | 
						|
			"{Header|html}={Integer|str}\n",
 | 
						|
 | 
						|
		out: "HEADER=78\n" +
 | 
						|
			"Header=77\n",
 | 
						|
	},
 | 
						|
 | 
						|
	{
 | 
						|
		in: "{Header|uppercase}={Integer Header|multiword}\n" +
 | 
						|
			"{Header|html}={Header Integer|multiword}\n" +
 | 
						|
			"{Header|html}={Header Integer}\n",
 | 
						|
 | 
						|
		out: "HEADER=<77><Header>\n" +
 | 
						|
			"Header=<Header><77>\n" +
 | 
						|
			"Header=Header77\n",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "{Raw}\n" +
 | 
						|
			"{Raw|html}\n",
 | 
						|
 | 
						|
		out: "a <&> b\n" +
 | 
						|
			"a <&> b\n",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in:  "{Bytes}",
 | 
						|
		out: "hello",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in:  "{Raw|uppercase|html|html}",
 | 
						|
		out: "A &lt;&amp;&gt; B",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in:  "{Header Integer|multiword|html}",
 | 
						|
		out: "<Header><77>",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in:  "{Integer|no_formatter|html}",
 | 
						|
		err: `unknown formatter: "no_formatter"`,
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in:  "{Integer|||||}", // empty string is a valid formatter
 | 
						|
		out: "77",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in:  `{"%.02f 0x%02X" 1.1 10|printf}`,
 | 
						|
		out: "1.10 0x0A",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in:  `{""|}{""||}{""|printf}`, // Issue #1896.
 | 
						|
		out: "",
 | 
						|
	},
 | 
						|
}
 | 
						|
 | 
						|
func TestFormatters(t *testing.T) {
 | 
						|
	data := map[string]interface{}{
 | 
						|
		"Header":  "Header",
 | 
						|
		"Integer": 77,
 | 
						|
		"Raw":     "a <&> b",
 | 
						|
		"Bytes":   []byte("hello"),
 | 
						|
	}
 | 
						|
	for _, c := range formatterTests {
 | 
						|
		tmpl, err := Parse(c.in, formatters)
 | 
						|
		if err != nil {
 | 
						|
			if c.err == "" {
 | 
						|
				t.Error("unexpected parse error:", err)
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			if strings.Index(err.Error(), c.err) < 0 {
 | 
						|
				t.Errorf("unexpected error: expected %q, got %q", c.err, err.Error())
 | 
						|
				continue
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			if c.err != "" {
 | 
						|
				t.Errorf("For %q, expected error, got none.", c.in)
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			var buf bytes.Buffer
 | 
						|
			err = tmpl.Execute(&buf, data)
 | 
						|
			if err != nil {
 | 
						|
				t.Error("unexpected Execute error: ", err)
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			actual := buf.String()
 | 
						|
			if actual != c.out {
 | 
						|
				t.Errorf("for %q: expected %q but got %q.", c.in, c.out, actual)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 |