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.
51 Stringmap map[string]string
52 Ptrmap map[string]*string
57 func (s *S) PointerMethod() string { return "ptrmethod!" }
59 func (s S) ValueMethod() string { return "valmethod!" }
61 var t1 = T{"ItemNumber1", "ValueNumber1"}
62 var t2 = T{"ItemNumber2", "ValueNumber2"}
64 func uppercase(v interface{}) string {
67 for i := 0; i < len(s); i++ {
69 if 'a' <= c && c <= 'z' {
77 func plus1(v interface{}) string {
79 return fmt.Sprint(i + 1)
82 func writer(f func(interface{}) string) func(io.Writer, string, ...interface{}) {
83 return func(w io.Writer, format string, v ...interface{}) {
85 panic("test writer expected one arg")
87 io.WriteString(w, f(v[0]))
91 func multiword(w io.Writer, format string, value ...interface{}) {
92 for _, v := range value {
93 fmt.Fprintf(w, "<%v>", v)
97 func printf(w io.Writer, format string, v ...interface{}) {
98 io.WriteString(w, fmt.Sprintf(v[0].(string), v[1:]...))
101 var formatters = FormatterMap{
102 "uppercase": writer(uppercase),
104 "multiword": multiword,
111 &Test{"abc", "abc", ""},
112 &Test{"abc\ndef\n", "abc\ndef\n", ""},
113 &Test{" {.meta-left} \n", "{", ""},
114 &Test{" {.meta-right} \n", "}", ""},
115 &Test{" {.space} \n", " ", ""},
116 &Test{" {.tab} \n", "\t", ""},
117 &Test{" {#comment} \n", "", ""},
118 &Test{"\tSome Text\t\n", "\tSome Text\t\n", ""},
119 &Test{" {.meta-right} {.meta-right} {.meta-right} \n", " } } } \n", ""},
121 // Variables at top level
123 in: "{Header}={Integer}\n",
129 in: "Pointers: {*HeaderPtr}={*IntegerPtr}\n",
131 out: "Pointers: Header=77\n",
135 in: "Stars but not pointers: {*Header}={*Integer}\n",
137 out: "Stars but not pointers: Header=77\n",
141 in: "nil pointer: {*NilPtr}={*Integer}\n",
143 out: "nil pointer: <nil>=77\n",
147 in: `{"Strings" ":"} {""} {"|"} {"\t\u0123 \x23\\"} {"\"}{\\"}`,
149 out: "Strings: | \t\u0123 \x23\\ \"}{\\",
153 in: "{`Raw strings` `:`} {``} {`|`} {`\\t\\u0123 \\x23\\`} {`}{\\`}",
155 out: "Raw strings: | \\t\\u0123 \\x23\\ }{\\",
159 in: "Characters: {'a'} {'\\u0123'} {' '} {'{'} {'|'} {'}'}",
161 out: "Characters: 97 291 32 123 124 125",
165 in: "Integers: {1} {-2} {+42} {0777} {0x0a}",
167 out: "Integers: 1 -2 42 511 10",
171 in: "Floats: {.5} {-.5} {1.1} {-2.2} {+42.1} {1e10} {1.2e-3} {1.2e3} {-1.2e3}",
173 out: "Floats: 0.5 -0.5 1.1 -2.2 42.1 1e+10 0.0012 1200 -1200",
176 // Method at top level
178 in: "ptrmethod={PointerMethod}\n",
180 out: "ptrmethod=ptrmethod!\n",
184 in: "valmethod={ValueMethod}\n",
186 out: "valmethod=valmethod!\n",
191 in: "{.section Data }\n" +
192 "some text for the section\n" +
195 out: "some text for the section\n",
198 in: "{.section Data }\n" +
199 "{Header}={Integer}\n" +
205 in: "{.section Pdata }\n" +
206 "{Header}={Integer}\n" +
212 in: "{.section Pdata }\n" +
215 "data not present\n" +
218 out: "data present\n",
221 in: "{.section Empty }\n" +
224 "data not present\n" +
227 out: "data not present\n",
230 in: "{.section Null }\n" +
233 "data not present\n" +
236 out: "data not present\n",
239 in: "{.section Pdata }\n" +
240 "{Header}={Integer}\n" +
242 "{Header}={Integer}\n" +
251 in: "{.section Data}{.end} {Header}\n",
257 in: "{.section Integer}{@}{.end}",
264 in: "{.section Pdata }\n" +
265 "{.repeated section @ }\n" +
270 out: "ItemNumber1=ValueNumber1\n" +
271 "ItemNumber2=ValueNumber2\n",
274 in: "{.section Pdata }\n" +
275 "{.repeated section @ }\n" +
278 "this should not appear\n" +
282 out: "ItemNumber1=ValueNumber1\n" +
283 "ItemNumber2=ValueNumber2\n",
286 in: "{.section @ }\n" +
287 "{.repeated section Empty }\n" +
290 "this should appear: empty field\n" +
294 out: "this should appear: empty field\n",
297 in: "{.repeated section Pdata }\n" +
299 "{.alternates with}\n" +
300 "is\nover\nmultiple\nlines\n" +
303 out: "ItemNumber1\n" +
304 "is\nover\nmultiple\nlines\n" +
308 in: "{.repeated section Pdata }\n" +
310 "{.alternates with}\n" +
311 "is\nover\nmultiple\nlines\n" +
314 out: "ItemNumber1\n" +
315 "is\nover\nmultiple\nlines\n" +
319 in: "{.section Pdata }\n" +
320 "{.repeated section @ }\n" +
322 "{.alternates with}DIVIDER\n" +
324 "this should not appear\n" +
328 out: "ItemNumber1=ValueNumber1\n" +
330 "ItemNumber2=ValueNumber2\n",
333 in: "{.repeated section Vec }\n" +
340 // Same but with a space before {.end}: was a bug.
342 in: "{.repeated section Vec }\n" +
348 in: "{.repeated section Integer}{.end}",
350 err: "line 1: .repeated: cannot repeat Integer (type int)",
355 in: "{.section @ }\n" +
356 "{InnerT.Item}={InnerT.Value}\n" +
359 out: "ItemNumber1=ValueNumber1\n",
362 in: "{.section @ }\n" +
363 "{InnerT.Item}={.section InnerT}{.section Value}{@}{.end}{.end}\n" +
366 out: "ItemNumber1=ValueNumber1\n",
370 in: "{.section Emptystring}emptystring{.end}\n" +
371 "{.section Header}header{.end}\n",
377 in: "{.section True}1{.or}2{.end}\n" +
378 "{.section False}3{.or}4{.end}\n",
391 in: "{Innermap.Mp.innerkey}\n",
396 in: "{.section Innermap}{.section Mp}{innerkey}{.end}{.end}\n",
401 in: "{.section JSON}{.repeated section maps}{a}{b}{.end}{.end}\n",
406 in: "{Stringmap.stringkey1}\n",
408 out: "stringresult\n",
411 in: "{.repeated section Stringmap}\n" +
415 out: "stringresult\n" +
419 in: "{.repeated section Stringmap}\n" +
423 out: "\tstringresult\n" +
427 in: "{*Ptrmap.stringkey1}\n",
429 out: "pointedToString\n",
432 in: "{.repeated section Ptrmap}\n" +
436 out: "pointedToString\n" +
448 in: "{.repeated section Iface}{@}{.alternates with} {.end}",
453 in: "{.section Iface}{@}{.end}",
458 in: "{.section Ifaceptr}{Item} {Value}{.end}",
464 func TestAll(t *testing.T) {
466 testAll(t, func(test *Test) (*Template, os.Error) { return Parse(test.in, formatters) })
468 testAll(t, func(test *Test) (*Template, os.Error) {
469 err := ioutil.WriteFile("_test/test.tmpl", []byte(test.in), 0600)
471 t.Error("unexpected write error:", err)
474 return ParseFile("_test/test.tmpl", formatters)
477 testAll(t, func(test *Test) (*Template, os.Error) {
478 err := ioutil.WriteFile("_test/test.tmpl", []byte(test.in), 0600)
480 t.Error("unexpected write error:", err)
483 tmpl := New(formatters)
484 return tmpl, tmpl.ParseFile("_test/test.tmpl")
488 func testAll(t *testing.T, parseFunc func(*Test) (*Template, os.Error)) {
490 // initialized by hand for clarity.
492 s.HeaderPtr = &s.Header
494 s.IntegerPtr = &s.Integer
497 s.Pdata = []*T{&t1, &t2}
500 s.Vec = new(vector.Vector)
505 s.Mp = make(map[string]string)
506 s.Mp["mapkey"] = "Ahoy!"
507 json.Unmarshal([]byte(`{"maps":[{"a":1,"b":2},{"a":3,"b":4}]}`), &s.JSON)
508 s.Innermap.Mp = make(map[string]int)
509 s.Innermap.Mp["innerkey"] = 55
510 s.Stringmap = make(map[string]string)
511 s.Stringmap["stringkey1"] = "stringresult" // the same value so repeated section is order-independent
512 s.Stringmap["stringkey2"] = "stringresult"
513 s.Ptrmap = make(map[string]*string)
514 x := "pointedToString"
515 s.Ptrmap["stringkey1"] = &x // the same value so repeated section is order-independent
516 s.Ptrmap["stringkey2"] = &x
517 s.Iface = []int{1, 2, 3}
518 s.Ifaceptr = &T{"Item", "Value"}
521 for _, test := range tests {
523 tmpl, err := parseFunc(test)
525 t.Error("unexpected parse error: ", err)
528 err = tmpl.Execute(&buf, s)
531 t.Error("unexpected execute error:", err)
535 t.Errorf("expected execute error %q, got nil", test.err)
536 } else if err.String() != test.err {
537 t.Errorf("expected execute error %q, got %q", test.err, err.String())
540 if buf.String() != test.out {
541 t.Errorf("for %q: expected %q got %q", test.in, test.out, buf.String())
546 func TestMapDriverType(t *testing.T) {
547 mp := map[string]string{"footer": "Ahoy!"}
548 tmpl, err := Parse("template: {footer}", nil)
550 t.Error("unexpected parse error:", err)
553 err = tmpl.Execute(&b, mp)
555 t.Error("unexpected execute error:", err)
558 expect := "template: Ahoy!"
560 t.Errorf("failed passing string as data: expected %q got %q", expect, s)
564 func TestMapNoEntry(t *testing.T) {
565 mp := make(map[string]int)
566 tmpl, err := Parse("template: {notthere}!", nil)
568 t.Error("unexpected parse error:", err)
571 err = tmpl.Execute(&b, mp)
573 t.Error("unexpected execute error:", err)
576 expect := "template: 0!"
578 t.Errorf("failed passing string as data: expected %q got %q", expect, s)
582 func TestStringDriverType(t *testing.T) {
583 tmpl, err := Parse("template: {@}", nil)
585 t.Error("unexpected parse error:", err)
588 err = tmpl.Execute(&b, "hello")
590 t.Error("unexpected execute error:", err)
593 expect := "template: hello"
595 t.Errorf("failed passing string as data: expected %q got %q", expect, s)
599 func TestTwice(t *testing.T) {
600 tmpl, err := Parse("template: {@}", nil)
602 t.Error("unexpected parse error:", err)
605 err = tmpl.Execute(&b, "hello")
607 t.Error("unexpected parse error:", err)
610 expect := "template: hello"
612 t.Errorf("failed passing string as data: expected %q got %q", expect, s)
614 err = tmpl.Execute(&b, "hello")
616 t.Error("unexpected parse error:", err)
621 t.Errorf("failed passing string as data: expected %q got %q", expect, s)
625 func TestCustomDelims(t *testing.T) {
626 // try various lengths. zero should catch error.
627 for i := 0; i < 7; i++ {
628 for j := 0; j < 7; j++ {
630 // first two chars deliberately the same to test equal left and right delims
631 ldelim := "$!#$%^&"[0:i]
632 rdelim := "$*&^%$!"[0:j]
633 tmpl.SetDelims(ldelim, rdelim)
634 // if braces, this would be template: {@}{.meta-left}{.meta-right}
635 text := "template: " +
636 ldelim + "@" + rdelim +
637 ldelim + ".meta-left" + rdelim +
638 ldelim + ".meta-right" + rdelim
639 err := tmpl.Parse(text)
641 if i == 0 || j == 0 { // expected
644 t.Error("unexpected parse error:", err)
645 } else if i == 0 || j == 0 {
646 t.Errorf("expected parse error for empty delimiter: %d %d %q %q", i, j, ldelim, rdelim)
650 err = tmpl.Execute(&b, "hello")
652 if s != "template: hello"+ldelim+rdelim {
653 t.Errorf("failed delim check(%q %q) %q got %q", ldelim, rdelim, text, s)
659 // Test that a variable evaluates to the field itself and does not further indirection
660 func TestVarIndirection(t *testing.T) {
662 // initialized by hand for clarity.
663 s.InnerPointerT = &t1
666 input := "{.section @}{InnerPointerT}{.end}"
667 tmpl, err := Parse(input, nil)
669 t.Fatal("unexpected parse error:", err)
671 err = tmpl.Execute(&buf, s)
673 t.Fatal("unexpected execute error:", err)
675 expect := fmt.Sprintf("%v", &t1) // output should be hex address of t1
676 if buf.String() != expect {
677 t.Errorf("for %q: expected %q got %q", input, expect, buf.String())
681 func TestHTMLFormatterWithByte(t *testing.T) {
685 HTMLFormatter(&buf, "", b)
688 t.Errorf("munged []byte, expected: %s got: %s", s, bs)
697 func TestReferenceToUnexported(t *testing.T) {
700 input := "{.section @}{I}{s}{.end}"
701 tmpl, err := Parse(input, nil)
703 t.Fatal("unexpected parse error:", err)
705 err = tmpl.Execute(&buf, u)
707 t.Fatal("expected execute error, got none")
709 if strings.Index(err.String(), "not exported") < 0 {
710 t.Fatal("expected unexported error; got", err)
714 var formatterTests = []Test{
716 in: "{Header|uppercase}={Integer|+1}\n" +
717 "{Header|html}={Integer|str}\n",
724 in: "{Header|uppercase}={Integer Header|multiword}\n" +
725 "{Header|html}={Header Integer|multiword}\n" +
726 "{Header|html}={Header Integer}\n",
728 out: "HEADER=<77><Header>\n" +
729 "Header=<Header><77>\n" +
737 "a <&> b\n",
744 in: "{Raw|uppercase|html|html}",
745 out: "A &lt;&amp;&gt; B",
748 in: "{Header Integer|multiword|html}",
749 out: "<Header><77>",
752 in: "{Integer|no_formatter|html}",
753 err: `unknown formatter: "no_formatter"`,
756 in: "{Integer|||||}", // empty string is a valid formatter
760 in: `{"%.02f 0x%02X" 1.1 10|printf}`,
764 in: `{""|}{""||}{""|printf}`, // Issue #1896.
769 func TestFormatters(t *testing.T) {
770 data := map[string]interface{}{
774 "Bytes": []byte("hello"),
776 for _, c := range formatterTests {
777 tmpl, err := Parse(c.in, formatters)
780 t.Error("unexpected parse error:", err)
783 if strings.Index(err.String(), c.err) < 0 {
784 t.Errorf("unexpected error: expected %q, got %q", c.err, err.String())
789 t.Errorf("For %q, expected error, got none.", c.in)
792 buf := bytes.NewBuffer(nil)
793 err = tmpl.Execute(buf, data)
795 t.Error("unexpected Execute error: ", err)
798 actual := buf.String()
800 t.Errorf("for %q: expected %q but got %q.", c.in, c.out, actual)