github.com/llvm-mirror/llgo@v0.0.0-20190322182713-bf6f0a60fce1/third_party/gofrontend/libgo/go/old/template/template_test.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package template
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/json"
    10  	"fmt"
    11  	"io"
    12  	"io/ioutil"
    13  	"os"
    14  	"strings"
    15  	"testing"
    16  )
    17  
    18  type Test struct {
    19  	in, out, err string
    20  }
    21  
    22  type T struct {
    23  	Item  string
    24  	Value string
    25  }
    26  
    27  type U struct {
    28  	Mp map[string]int
    29  }
    30  
    31  type S struct {
    32  	Header        string
    33  	HeaderPtr     *string
    34  	Integer       int
    35  	IntegerPtr    *int
    36  	NilPtr        *int
    37  	InnerT        T
    38  	InnerPointerT *T
    39  	Data          []T
    40  	Pdata         []*T
    41  	Empty         []*T
    42  	Emptystring   string
    43  	Null          []*T
    44  	Vec           []interface{}
    45  	True          bool
    46  	False         bool
    47  	Mp            map[string]string
    48  	JSON          interface{}
    49  	Innermap      U
    50  	Stringmap     map[string]string
    51  	Ptrmap        map[string]*string
    52  	Iface         interface{}
    53  	Ifaceptr      interface{}
    54  }
    55  
    56  func (s *S) PointerMethod() string { return "ptrmethod!" }
    57  
    58  func (s S) ValueMethod() string { return "valmethod!" }
    59  
    60  var t1 = T{"ItemNumber1", "ValueNumber1"}
    61  var t2 = T{"ItemNumber2", "ValueNumber2"}
    62  
    63  func uppercase(v interface{}) string {
    64  	s := v.(string)
    65  	t := ""
    66  	for i := 0; i < len(s); i++ {
    67  		c := s[i]
    68  		if 'a' <= c && c <= 'z' {
    69  			c = c + 'A' - 'a'
    70  		}
    71  		t += string(c)
    72  	}
    73  	return t
    74  }
    75  
    76  func plus1(v interface{}) string {
    77  	i := v.(int)
    78  	return fmt.Sprint(i + 1)
    79  }
    80  
    81  func writer(f func(interface{}) string) func(io.Writer, string, ...interface{}) {
    82  	return func(w io.Writer, format string, v ...interface{}) {
    83  		if len(v) != 1 {
    84  			panic("test writer expected one arg")
    85  		}
    86  		io.WriteString(w, f(v[0]))
    87  	}
    88  }
    89  
    90  func multiword(w io.Writer, format string, value ...interface{}) {
    91  	for _, v := range value {
    92  		fmt.Fprintf(w, "<%v>", v)
    93  	}
    94  }
    95  
    96  func printf(w io.Writer, format string, v ...interface{}) {
    97  	io.WriteString(w, fmt.Sprintf(v[0].(string), v[1:]...))
    98  }
    99  
   100  var formatters = FormatterMap{
   101  	"uppercase": writer(uppercase),
   102  	"+1":        writer(plus1),
   103  	"multiword": multiword,
   104  	"printf":    printf,
   105  }
   106  
   107  var tests = []*Test{
   108  	// Simple
   109  	{"", "", ""},
   110  	{"abc", "abc", ""},
   111  	{"abc\ndef\n", "abc\ndef\n", ""},
   112  	{" {.meta-left}   \n", "{", ""},
   113  	{" {.meta-right}   \n", "}", ""},
   114  	{" {.space}   \n", " ", ""},
   115  	{" {.tab}   \n", "\t", ""},
   116  	{"     {#comment}   \n", "", ""},
   117  	{"\tSome Text\t\n", "\tSome Text\t\n", ""},
   118  	{" {.meta-right} {.meta-right} {.meta-right} \n", " } } } \n", ""},
   119  
   120  	// Variables at top level
   121  	{
   122  		in: "{Header}={Integer}\n",
   123  
   124  		out: "Header=77\n",
   125  	},
   126  
   127  	{
   128  		in: "Pointers: {*HeaderPtr}={*IntegerPtr}\n",
   129  
   130  		out: "Pointers: Header=77\n",
   131  	},
   132  
   133  	{
   134  		in: "Stars but not pointers: {*Header}={*Integer}\n",
   135  
   136  		out: "Stars but not pointers: Header=77\n",
   137  	},
   138  
   139  	{
   140  		in: "nil pointer: {*NilPtr}={*Integer}\n",
   141  
   142  		out: "nil pointer: <nil>=77\n",
   143  	},
   144  
   145  	{
   146  		in: `{"Strings" ":"} {""} {"|"} {"\t\u0123 \x23\\"} {"\"}{\\"}`,
   147  
   148  		out: "Strings:  | \t\u0123 \x23\\ \"}{\\",
   149  	},
   150  
   151  	{
   152  		in: "{`Raw strings` `:`} {``} {`|`} {`\\t\\u0123 \\x23\\`} {`}{\\`}",
   153  
   154  		out: "Raw strings:  | \\t\\u0123 \\x23\\ }{\\",
   155  	},
   156  
   157  	{
   158  		in: "Characters: {'a'} {'\\u0123'} {' '} {'{'} {'|'} {'}'}",
   159  
   160  		out: "Characters: 97 291 32 123 124 125",
   161  	},
   162  
   163  	{
   164  		in: "Integers: {1} {-2} {+42} {0777} {0x0a}",
   165  
   166  		out: "Integers: 1 -2 42 511 10",
   167  	},
   168  
   169  	{
   170  		in: "Floats: {.5} {-.5} {1.1} {-2.2} {+42.1} {1e10} {1.2e-3} {1.2e3} {-1.2e3}",
   171  
   172  		out: "Floats: 0.5 -0.5 1.1 -2.2 42.1 1e+10 0.0012 1200 -1200",
   173  	},
   174  
   175  	// Method at top level
   176  	{
   177  		in: "ptrmethod={PointerMethod}\n",
   178  
   179  		out: "ptrmethod=ptrmethod!\n",
   180  	},
   181  
   182  	{
   183  		in: "valmethod={ValueMethod}\n",
   184  
   185  		out: "valmethod=valmethod!\n",
   186  	},
   187  
   188  	// Section
   189  	{
   190  		in: "{.section Data }\n" +
   191  			"some text for the section\n" +
   192  			"{.end}\n",
   193  
   194  		out: "some text for the section\n",
   195  	},
   196  	{
   197  		in: "{.section Data }\n" +
   198  			"{Header}={Integer}\n" +
   199  			"{.end}\n",
   200  
   201  		out: "Header=77\n",
   202  	},
   203  	{
   204  		in: "{.section Pdata }\n" +
   205  			"{Header}={Integer}\n" +
   206  			"{.end}\n",
   207  
   208  		out: "Header=77\n",
   209  	},
   210  	{
   211  		in: "{.section Pdata }\n" +
   212  			"data present\n" +
   213  			"{.or}\n" +
   214  			"data not present\n" +
   215  			"{.end}\n",
   216  
   217  		out: "data present\n",
   218  	},
   219  	{
   220  		in: "{.section Empty }\n" +
   221  			"data present\n" +
   222  			"{.or}\n" +
   223  			"data not present\n" +
   224  			"{.end}\n",
   225  
   226  		out: "data not present\n",
   227  	},
   228  	{
   229  		in: "{.section Null }\n" +
   230  			"data present\n" +
   231  			"{.or}\n" +
   232  			"data not present\n" +
   233  			"{.end}\n",
   234  
   235  		out: "data not present\n",
   236  	},
   237  	{
   238  		in: "{.section Pdata }\n" +
   239  			"{Header}={Integer}\n" +
   240  			"{.section @ }\n" +
   241  			"{Header}={Integer}\n" +
   242  			"{.end}\n" +
   243  			"{.end}\n",
   244  
   245  		out: "Header=77\n" +
   246  			"Header=77\n",
   247  	},
   248  
   249  	{
   250  		in: "{.section Data}{.end} {Header}\n",
   251  
   252  		out: " Header\n",
   253  	},
   254  
   255  	{
   256  		in: "{.section Integer}{@}{.end}",
   257  
   258  		out: "77",
   259  	},
   260  
   261  	// Repeated
   262  	{
   263  		in: "{.section Pdata }\n" +
   264  			"{.repeated section @ }\n" +
   265  			"{Item}={Value}\n" +
   266  			"{.end}\n" +
   267  			"{.end}\n",
   268  
   269  		out: "ItemNumber1=ValueNumber1\n" +
   270  			"ItemNumber2=ValueNumber2\n",
   271  	},
   272  	{
   273  		in: "{.section Pdata }\n" +
   274  			"{.repeated section @ }\n" +
   275  			"{Item}={Value}\n" +
   276  			"{.or}\n" +
   277  			"this should not appear\n" +
   278  			"{.end}\n" +
   279  			"{.end}\n",
   280  
   281  		out: "ItemNumber1=ValueNumber1\n" +
   282  			"ItemNumber2=ValueNumber2\n",
   283  	},
   284  	{
   285  		in: "{.section @ }\n" +
   286  			"{.repeated section Empty }\n" +
   287  			"{Item}={Value}\n" +
   288  			"{.or}\n" +
   289  			"this should appear: empty field\n" +
   290  			"{.end}\n" +
   291  			"{.end}\n",
   292  
   293  		out: "this should appear: empty field\n",
   294  	},
   295  	{
   296  		in: "{.repeated section Pdata }\n" +
   297  			"{Item}\n" +
   298  			"{.alternates with}\n" +
   299  			"is\nover\nmultiple\nlines\n" +
   300  			"{.end}\n",
   301  
   302  		out: "ItemNumber1\n" +
   303  			"is\nover\nmultiple\nlines\n" +
   304  			"ItemNumber2\n",
   305  	},
   306  	{
   307  		in: "{.repeated section Pdata }\n" +
   308  			"{Item}\n" +
   309  			"{.alternates with}\n" +
   310  			"is\nover\nmultiple\nlines\n" +
   311  			" {.end}\n",
   312  
   313  		out: "ItemNumber1\n" +
   314  			"is\nover\nmultiple\nlines\n" +
   315  			"ItemNumber2\n",
   316  	},
   317  	{
   318  		in: "{.section Pdata }\n" +
   319  			"{.repeated section @ }\n" +
   320  			"{Item}={Value}\n" +
   321  			"{.alternates with}DIVIDER\n" +
   322  			"{.or}\n" +
   323  			"this should not appear\n" +
   324  			"{.end}\n" +
   325  			"{.end}\n",
   326  
   327  		out: "ItemNumber1=ValueNumber1\n" +
   328  			"DIVIDER\n" +
   329  			"ItemNumber2=ValueNumber2\n",
   330  	},
   331  	{
   332  		in: "{.repeated section Vec }\n" +
   333  			"{@}\n" +
   334  			"{.end}\n",
   335  
   336  		out: "elt1\n" +
   337  			"elt2\n",
   338  	},
   339  	// Same but with a space before {.end}: was a bug.
   340  	{
   341  		in: "{.repeated section Vec }\n" +
   342  			"{@} {.end}\n",
   343  
   344  		out: "elt1 elt2 \n",
   345  	},
   346  	{
   347  		in: "{.repeated section Integer}{.end}",
   348  
   349  		err: "line 1: .repeated: cannot repeat Integer (type int)",
   350  	},
   351  
   352  	// Nested names
   353  	{
   354  		in: "{.section @ }\n" +
   355  			"{InnerT.Item}={InnerT.Value}\n" +
   356  			"{.end}",
   357  
   358  		out: "ItemNumber1=ValueNumber1\n",
   359  	},
   360  	{
   361  		in: "{.section @ }\n" +
   362  			"{InnerT.Item}={.section InnerT}{.section Value}{@}{.end}{.end}\n" +
   363  			"{.end}",
   364  
   365  		out: "ItemNumber1=ValueNumber1\n",
   366  	},
   367  
   368  	{
   369  		in: "{.section Emptystring}emptystring{.end}\n" +
   370  			"{.section Header}header{.end}\n",
   371  
   372  		out: "\nheader\n",
   373  	},
   374  
   375  	{
   376  		in: "{.section True}1{.or}2{.end}\n" +
   377  			"{.section False}3{.or}4{.end}\n",
   378  
   379  		out: "1\n4\n",
   380  	},
   381  
   382  	// Maps
   383  
   384  	{
   385  		in: "{Mp.mapkey}\n",
   386  
   387  		out: "Ahoy!\n",
   388  	},
   389  	{
   390  		in: "{Innermap.Mp.innerkey}\n",
   391  
   392  		out: "55\n",
   393  	},
   394  	{
   395  		in: "{.section Innermap}{.section Mp}{innerkey}{.end}{.end}\n",
   396  
   397  		out: "55\n",
   398  	},
   399  	{
   400  		in: "{.section JSON}{.repeated section maps}{a}{b}{.end}{.end}\n",
   401  
   402  		out: "1234\n",
   403  	},
   404  	{
   405  		in: "{Stringmap.stringkey1}\n",
   406  
   407  		out: "stringresult\n",
   408  	},
   409  	{
   410  		in: "{.repeated section Stringmap}\n" +
   411  			"{@}\n" +
   412  			"{.end}",
   413  
   414  		out: "stringresult\n" +
   415  			"stringresult\n",
   416  	},
   417  	{
   418  		in: "{.repeated section Stringmap}\n" +
   419  			"\t{@}\n" +
   420  			"{.end}",
   421  
   422  		out: "\tstringresult\n" +
   423  			"\tstringresult\n",
   424  	},
   425  	{
   426  		in: "{*Ptrmap.stringkey1}\n",
   427  
   428  		out: "pointedToString\n",
   429  	},
   430  	{
   431  		in: "{.repeated section Ptrmap}\n" +
   432  			"{*@}\n" +
   433  			"{.end}",
   434  
   435  		out: "pointedToString\n" +
   436  			"pointedToString\n",
   437  	},
   438  
   439  	// Interface values
   440  
   441  	{
   442  		in: "{Iface}",
   443  
   444  		out: "[1 2 3]",
   445  	},
   446  	{
   447  		in: "{.repeated section Iface}{@}{.alternates with} {.end}",
   448  
   449  		out: "1 2 3",
   450  	},
   451  	{
   452  		in: "{.section Iface}{@}{.end}",
   453  
   454  		out: "[1 2 3]",
   455  	},
   456  	{
   457  		in: "{.section Ifaceptr}{Item} {Value}{.end}",
   458  
   459  		out: "Item Value",
   460  	},
   461  }
   462  
   463  func TestAll(t *testing.T) {
   464  	// Parse
   465  	testAll(t, func(test *Test) (*Template, error) { return Parse(test.in, formatters) })
   466  	// ParseFile
   467  	f, err := ioutil.TempFile("", "template-test")
   468  	if err != nil {
   469  		t.Fatal(err)
   470  	}
   471  	defer func() {
   472  		name := f.Name()
   473  		f.Close()
   474  		os.Remove(name)
   475  	}()
   476  	testAll(t, func(test *Test) (*Template, error) {
   477  		err := ioutil.WriteFile(f.Name(), []byte(test.in), 0600)
   478  		if err != nil {
   479  			t.Error("unexpected write error:", err)
   480  			return nil, err
   481  		}
   482  		return ParseFile(f.Name(), formatters)
   483  	})
   484  	// tmpl.ParseFile
   485  	testAll(t, func(test *Test) (*Template, error) {
   486  		err := ioutil.WriteFile(f.Name(), []byte(test.in), 0600)
   487  		if err != nil {
   488  			t.Error("unexpected write error:", err)
   489  			return nil, err
   490  		}
   491  		tmpl := New(formatters)
   492  		return tmpl, tmpl.ParseFile(f.Name())
   493  	})
   494  }
   495  
   496  func testAll(t *testing.T, parseFunc func(*Test) (*Template, error)) {
   497  	s := new(S)
   498  	// initialized by hand for clarity.
   499  	s.Header = "Header"
   500  	s.HeaderPtr = &s.Header
   501  	s.Integer = 77
   502  	s.IntegerPtr = &s.Integer
   503  	s.InnerT = t1
   504  	s.Data = []T{t1, t2}
   505  	s.Pdata = []*T{&t1, &t2}
   506  	s.Empty = []*T{}
   507  	s.Null = nil
   508  	s.Vec = []interface{}{"elt1", "elt2"}
   509  	s.True = true
   510  	s.False = false
   511  	s.Mp = make(map[string]string)
   512  	s.Mp["mapkey"] = "Ahoy!"
   513  	json.Unmarshal([]byte(`{"maps":[{"a":1,"b":2},{"a":3,"b":4}]}`), &s.JSON)
   514  	s.Innermap.Mp = make(map[string]int)
   515  	s.Innermap.Mp["innerkey"] = 55
   516  	s.Stringmap = make(map[string]string)
   517  	s.Stringmap["stringkey1"] = "stringresult" // the same value so repeated section is order-independent
   518  	s.Stringmap["stringkey2"] = "stringresult"
   519  	s.Ptrmap = make(map[string]*string)
   520  	x := "pointedToString"
   521  	s.Ptrmap["stringkey1"] = &x // the same value so repeated section is order-independent
   522  	s.Ptrmap["stringkey2"] = &x
   523  	s.Iface = []int{1, 2, 3}
   524  	s.Ifaceptr = &T{"Item", "Value"}
   525  
   526  	var buf bytes.Buffer
   527  	for _, test := range tests {
   528  		buf.Reset()
   529  		tmpl, err := parseFunc(test)
   530  		if err != nil {
   531  			t.Error("unexpected parse error: ", err)
   532  			continue
   533  		}
   534  		err = tmpl.Execute(&buf, s)
   535  		if test.err == "" {
   536  			if err != nil {
   537  				t.Error("unexpected execute error:", err)
   538  			}
   539  		} else {
   540  			if err == nil {
   541  				t.Errorf("expected execute error %q, got nil", test.err)
   542  			} else if err.Error() != test.err {
   543  				t.Errorf("expected execute error %q, got %q", test.err, err.Error())
   544  			}
   545  		}
   546  		if buf.String() != test.out {
   547  			t.Errorf("for %q: expected %q got %q", test.in, test.out, buf.String())
   548  		}
   549  	}
   550  }
   551  
   552  func TestMapDriverType(t *testing.T) {
   553  	mp := map[string]string{"footer": "Ahoy!"}
   554  	tmpl, err := Parse("template: {footer}", nil)
   555  	if err != nil {
   556  		t.Error("unexpected parse error:", err)
   557  	}
   558  	var b bytes.Buffer
   559  	err = tmpl.Execute(&b, mp)
   560  	if err != nil {
   561  		t.Error("unexpected execute error:", err)
   562  	}
   563  	s := b.String()
   564  	expect := "template: Ahoy!"
   565  	if s != expect {
   566  		t.Errorf("failed passing string as data: expected %q got %q", expect, s)
   567  	}
   568  }
   569  
   570  func TestMapNoEntry(t *testing.T) {
   571  	mp := make(map[string]int)
   572  	tmpl, err := Parse("template: {notthere}!", nil)
   573  	if err != nil {
   574  		t.Error("unexpected parse error:", err)
   575  	}
   576  	var b bytes.Buffer
   577  	err = tmpl.Execute(&b, mp)
   578  	if err != nil {
   579  		t.Error("unexpected execute error:", err)
   580  	}
   581  	s := b.String()
   582  	expect := "template: 0!"
   583  	if s != expect {
   584  		t.Errorf("failed passing string as data: expected %q got %q", expect, s)
   585  	}
   586  }
   587  
   588  func TestStringDriverType(t *testing.T) {
   589  	tmpl, err := Parse("template: {@}", nil)
   590  	if err != nil {
   591  		t.Error("unexpected parse error:", err)
   592  	}
   593  	var b bytes.Buffer
   594  	err = tmpl.Execute(&b, "hello")
   595  	if err != nil {
   596  		t.Error("unexpected execute error:", err)
   597  	}
   598  	s := b.String()
   599  	expect := "template: hello"
   600  	if s != expect {
   601  		t.Errorf("failed passing string as data: expected %q got %q", expect, s)
   602  	}
   603  }
   604  
   605  func TestTwice(t *testing.T) {
   606  	tmpl, err := Parse("template: {@}", nil)
   607  	if err != nil {
   608  		t.Error("unexpected parse error:", err)
   609  	}
   610  	var b bytes.Buffer
   611  	err = tmpl.Execute(&b, "hello")
   612  	if err != nil {
   613  		t.Error("unexpected parse error:", err)
   614  	}
   615  	s := b.String()
   616  	expect := "template: hello"
   617  	if s != expect {
   618  		t.Errorf("failed passing string as data: expected %q got %q", expect, s)
   619  	}
   620  	err = tmpl.Execute(&b, "hello")
   621  	if err != nil {
   622  		t.Error("unexpected parse error:", err)
   623  	}
   624  	s = b.String()
   625  	expect += expect
   626  	if s != expect {
   627  		t.Errorf("failed passing string as data: expected %q got %q", expect, s)
   628  	}
   629  }
   630  
   631  func TestCustomDelims(t *testing.T) {
   632  	// try various lengths.  zero should catch error.
   633  	for i := 0; i < 7; i++ {
   634  		for j := 0; j < 7; j++ {
   635  			tmpl := New(nil)
   636  			// first two chars deliberately the same to test equal left and right delims
   637  			ldelim := "$!#$%^&"[0:i]
   638  			rdelim := "$*&^%$!"[0:j]
   639  			tmpl.SetDelims(ldelim, rdelim)
   640  			// if braces, this would be template: {@}{.meta-left}{.meta-right}
   641  			text := "template: " +
   642  				ldelim + "@" + rdelim +
   643  				ldelim + ".meta-left" + rdelim +
   644  				ldelim + ".meta-right" + rdelim
   645  			err := tmpl.Parse(text)
   646  			if err != nil {
   647  				if i == 0 || j == 0 { // expected
   648  					continue
   649  				}
   650  				t.Error("unexpected parse error:", err)
   651  			} else if i == 0 || j == 0 {
   652  				t.Errorf("expected parse error for empty delimiter: %d %d %q %q", i, j, ldelim, rdelim)
   653  				continue
   654  			}
   655  			var b bytes.Buffer
   656  			err = tmpl.Execute(&b, "hello")
   657  			s := b.String()
   658  			if s != "template: hello"+ldelim+rdelim {
   659  				t.Errorf("failed delim check(%q %q) %q got %q", ldelim, rdelim, text, s)
   660  			}
   661  		}
   662  	}
   663  }
   664  
   665  // Test that a variable evaluates to the field itself and does not further indirection
   666  func TestVarIndirection(t *testing.T) {
   667  	s := new(S)
   668  	// initialized by hand for clarity.
   669  	s.InnerPointerT = &t1
   670  
   671  	var buf bytes.Buffer
   672  	input := "{.section @}{InnerPointerT}{.end}"
   673  	tmpl, err := Parse(input, nil)
   674  	if err != nil {
   675  		t.Fatal("unexpected parse error:", err)
   676  	}
   677  	err = tmpl.Execute(&buf, s)
   678  	if err != nil {
   679  		t.Fatal("unexpected execute error:", err)
   680  	}
   681  	expect := fmt.Sprintf("%v", &t1) // output should be hex address of t1
   682  	if buf.String() != expect {
   683  		t.Errorf("for %q: expected %q got %q", input, expect, buf.String())
   684  	}
   685  }
   686  
   687  func TestHTMLFormatterWithByte(t *testing.T) {
   688  	s := "Test string."
   689  	b := []byte(s)
   690  	var buf bytes.Buffer
   691  	HTMLFormatter(&buf, "", b)
   692  	bs := buf.String()
   693  	if bs != s {
   694  		t.Errorf("munged []byte, expected: %s got: %s", s, bs)
   695  	}
   696  }
   697  
   698  type UF struct {
   699  	I int
   700  	s string
   701  }
   702  
   703  func TestReferenceToUnexported(t *testing.T) {
   704  	u := &UF{3, "hello"}
   705  	var buf bytes.Buffer
   706  	input := "{.section @}{I}{s}{.end}"
   707  	tmpl, err := Parse(input, nil)
   708  	if err != nil {
   709  		t.Fatal("unexpected parse error:", err)
   710  	}
   711  	err = tmpl.Execute(&buf, u)
   712  	if err == nil {
   713  		t.Fatal("expected execute error, got none")
   714  	}
   715  	if strings.Index(err.Error(), "not exported") < 0 {
   716  		t.Fatal("expected unexported error; got", err)
   717  	}
   718  }
   719  
   720  var formatterTests = []Test{
   721  	{
   722  		in: "{Header|uppercase}={Integer|+1}\n" +
   723  			"{Header|html}={Integer|str}\n",
   724  
   725  		out: "HEADER=78\n" +
   726  			"Header=77\n",
   727  	},
   728  
   729  	{
   730  		in: "{Header|uppercase}={Integer Header|multiword}\n" +
   731  			"{Header|html}={Header Integer|multiword}\n" +
   732  			"{Header|html}={Header Integer}\n",
   733  
   734  		out: "HEADER=<77><Header>\n" +
   735  			"Header=<Header><77>\n" +
   736  			"Header=Header77\n",
   737  	},
   738  	{
   739  		in: "{Raw}\n" +
   740  			"{Raw|html}\n",
   741  
   742  		out: "a <&> b\n" +
   743  			"a &lt;&amp;&gt; b\n",
   744  	},
   745  	{
   746  		in:  "{Bytes}",
   747  		out: "hello",
   748  	},
   749  	{
   750  		in:  "{Raw|uppercase|html|html}",
   751  		out: "A &amp;lt;&amp;amp;&amp;gt; B",
   752  	},
   753  	{
   754  		in:  "{Header Integer|multiword|html}",
   755  		out: "&lt;Header&gt;&lt;77&gt;",
   756  	},
   757  	{
   758  		in:  "{Integer|no_formatter|html}",
   759  		err: `unknown formatter: "no_formatter"`,
   760  	},
   761  	{
   762  		in:  "{Integer|||||}", // empty string is a valid formatter
   763  		out: "77",
   764  	},
   765  	{
   766  		in:  `{"%.02f 0x%02X" 1.1 10|printf}`,
   767  		out: "1.10 0x0A",
   768  	},
   769  	{
   770  		in:  `{""|}{""||}{""|printf}`, // Issue #1896.
   771  		out: "",
   772  	},
   773  }
   774  
   775  func TestFormatters(t *testing.T) {
   776  	data := map[string]interface{}{
   777  		"Header":  "Header",
   778  		"Integer": 77,
   779  		"Raw":     "a <&> b",
   780  		"Bytes":   []byte("hello"),
   781  	}
   782  	for _, c := range formatterTests {
   783  		tmpl, err := Parse(c.in, formatters)
   784  		if err != nil {
   785  			if c.err == "" {
   786  				t.Error("unexpected parse error:", err)
   787  				continue
   788  			}
   789  			if strings.Index(err.Error(), c.err) < 0 {
   790  				t.Errorf("unexpected error: expected %q, got %q", c.err, err.Error())
   791  				continue
   792  			}
   793  		} else {
   794  			if c.err != "" {
   795  				t.Errorf("For %q, expected error, got none.", c.in)
   796  				continue
   797  			}
   798  			var buf bytes.Buffer
   799  			err = tmpl.Execute(&buf, data)
   800  			if err != nil {
   801  				t.Error("unexpected Execute error: ", err)
   802  				continue
   803  			}
   804  			actual := buf.String()
   805  			if actual != c.out {
   806  				t.Errorf("for %q: expected %q but got %q.", c.in, c.out, actual)
   807  			}
   808  		}
   809  	}
   810  }