2009-04-08 23:08:55 -06:00
|
|
|
// 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 (
|
|
|
|
"fmt";
|
|
|
|
"io";
|
|
|
|
"os";
|
2009-04-09 00:33:31 -06:00
|
|
|
"reflect";
|
2009-04-08 23:08:55 -06:00
|
|
|
"template";
|
|
|
|
"testing";
|
|
|
|
)
|
|
|
|
|
|
|
|
type Test struct {
|
|
|
|
in, out string
|
|
|
|
}
|
|
|
|
|
|
|
|
type T struct {
|
|
|
|
item string;
|
|
|
|
value string;
|
|
|
|
}
|
|
|
|
|
|
|
|
type S struct {
|
|
|
|
header string;
|
|
|
|
integer int;
|
2009-04-15 01:05:47 -06:00
|
|
|
raw string;
|
2009-04-08 23:08:55 -06:00
|
|
|
data []T;
|
|
|
|
pdata []*T;
|
|
|
|
empty []*T;
|
2009-04-15 01:26:49 -06:00
|
|
|
emptystring string;
|
2009-04-08 23:08:55 -06:00
|
|
|
null []*T;
|
|
|
|
}
|
|
|
|
|
|
|
|
var t1 = T{ "ItemNumber1", "ValueNumber1" }
|
|
|
|
var t2 = T{ "ItemNumber2", "ValueNumber2" }
|
|
|
|
|
2009-04-13 20:29:38 -06:00
|
|
|
func uppercase(v interface{}) string {
|
|
|
|
s := v.(string);
|
2009-04-09 00:33:31 -06:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-04-13 20:29:38 -06:00
|
|
|
func plus1(v interface{}) string {
|
|
|
|
i := v.(int);
|
2009-04-09 00:33:31 -06:00
|
|
|
return fmt.Sprint(i + 1);
|
|
|
|
}
|
|
|
|
|
2009-04-13 20:29:38 -06:00
|
|
|
func writer(f func(interface{}) string) (func(io.Write, interface{}, string)) {
|
|
|
|
return func(w io.Write, v interface{}, format string) {
|
|
|
|
io.WriteString(w, f(v));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-04-09 00:33:31 -06:00
|
|
|
var formatters = FormatterMap {
|
2009-04-13 20:29:38 -06:00
|
|
|
"uppercase" : writer(uppercase),
|
|
|
|
"+1" : writer(plus1),
|
2009-04-09 00:33:31 -06:00
|
|
|
}
|
|
|
|
|
2009-04-08 23:08:55 -06:00
|
|
|
var tests = []*Test {
|
|
|
|
// Simple
|
|
|
|
&Test{ "", "" },
|
|
|
|
&Test{ "abc\ndef\n", "abc\ndef\n" },
|
|
|
|
&Test{ " {.meta-left} \n", "{" },
|
|
|
|
&Test{ " {.meta-right} \n", "}" },
|
|
|
|
&Test{ " {.space} \n", " " },
|
|
|
|
&Test{ " {#comment} \n", "" },
|
|
|
|
|
2009-04-20 19:51:13 -06:00
|
|
|
// Variables at top level
|
|
|
|
&Test{
|
|
|
|
"{header}={integer}\n",
|
|
|
|
|
|
|
|
"Header=77\n"
|
|
|
|
},
|
|
|
|
|
2009-04-08 23:08:55 -06:00
|
|
|
// Section
|
|
|
|
&Test{
|
|
|
|
"{.section data }\n"
|
|
|
|
"some text for the section\n"
|
|
|
|
"{.end}\n",
|
|
|
|
|
|
|
|
"some text for the section\n"
|
|
|
|
},
|
|
|
|
&Test{
|
|
|
|
"{.section data }\n"
|
|
|
|
"{header}={integer}\n"
|
|
|
|
"{.end}\n",
|
|
|
|
|
|
|
|
"Header=77\n"
|
|
|
|
},
|
|
|
|
&Test{
|
|
|
|
"{.section pdata }\n"
|
|
|
|
"{header}={integer}\n"
|
|
|
|
"{.end}\n",
|
|
|
|
|
|
|
|
"Header=77\n"
|
|
|
|
},
|
|
|
|
&Test{
|
|
|
|
"{.section pdata }\n"
|
|
|
|
"data present\n"
|
|
|
|
"{.or}\n"
|
|
|
|
"data not present\n"
|
|
|
|
"{.end}\n",
|
|
|
|
|
|
|
|
"data present\n"
|
|
|
|
},
|
|
|
|
&Test{
|
|
|
|
"{.section empty }\n"
|
|
|
|
"data present\n"
|
|
|
|
"{.or}\n"
|
|
|
|
"data not present\n"
|
|
|
|
"{.end}\n",
|
|
|
|
|
|
|
|
"data not present\n"
|
|
|
|
},
|
|
|
|
&Test{
|
|
|
|
"{.section null }\n"
|
|
|
|
"data present\n"
|
|
|
|
"{.or}\n"
|
|
|
|
"data not present\n"
|
|
|
|
"{.end}\n",
|
|
|
|
|
|
|
|
"data not present\n"
|
|
|
|
},
|
|
|
|
&Test{
|
|
|
|
"{.section pdata }\n"
|
|
|
|
"{header}={integer}\n"
|
|
|
|
"{.section @ }\n"
|
|
|
|
"{header}={integer}\n"
|
|
|
|
"{.end}\n"
|
|
|
|
"{.end}\n",
|
|
|
|
|
|
|
|
"Header=77\n"
|
|
|
|
"Header=77\n"
|
|
|
|
},
|
2009-04-14 01:06:49 -06:00
|
|
|
&Test{
|
|
|
|
"{.section data}{.end} {header}\n",
|
|
|
|
|
|
|
|
" Header\n"
|
|
|
|
},
|
2009-04-08 23:08:55 -06:00
|
|
|
|
|
|
|
// Repeated
|
|
|
|
&Test{
|
|
|
|
"{.section pdata }\n"
|
|
|
|
"{.repeated section @ }\n"
|
|
|
|
"{item}={value}\n"
|
|
|
|
"{.end}\n"
|
|
|
|
"{.end}\n",
|
|
|
|
|
|
|
|
"ItemNumber1=ValueNumber1\n"
|
|
|
|
"ItemNumber2=ValueNumber2\n"
|
|
|
|
},
|
2009-04-20 19:51:13 -06:00
|
|
|
&Test{
|
|
|
|
"{.section pdata }\n"
|
|
|
|
"{.repeated section @ }\n"
|
|
|
|
"{item}={value}\n"
|
|
|
|
"{.or}\n"
|
|
|
|
"this should not appear\n"
|
|
|
|
"{.end}\n"
|
|
|
|
"{.end}\n",
|
|
|
|
|
|
|
|
"ItemNumber1=ValueNumber1\n"
|
|
|
|
"ItemNumber2=ValueNumber2\n"
|
|
|
|
},
|
|
|
|
&Test{
|
|
|
|
"{.section @ }\n"
|
|
|
|
"{.repeated section empty }\n"
|
|
|
|
"{item}={value}\n"
|
|
|
|
"{.or}\n"
|
|
|
|
"this should appear: empty field\n"
|
|
|
|
"{.end}\n"
|
|
|
|
"{.end}\n",
|
|
|
|
|
|
|
|
"this should appear: empty field\n"
|
|
|
|
},
|
2009-04-09 00:33:31 -06:00
|
|
|
|
|
|
|
// Formatters
|
|
|
|
&Test{
|
|
|
|
"{.section pdata }\n"
|
|
|
|
"{header|uppercase}={integer|+1}\n"
|
2009-04-09 01:10:46 -06:00
|
|
|
"{header|html}={integer|str}\n"
|
2009-04-09 00:33:31 -06:00
|
|
|
"{.end}\n",
|
|
|
|
|
|
|
|
"HEADER=78\n"
|
2009-04-09 01:10:46 -06:00
|
|
|
"Header=77\n"
|
2009-04-09 00:33:31 -06:00
|
|
|
},
|
2009-04-15 01:26:49 -06:00
|
|
|
|
2009-04-15 01:05:47 -06:00
|
|
|
&Test{
|
|
|
|
"{raw}\n"
|
|
|
|
"{raw|html}\n",
|
2009-04-15 01:26:49 -06:00
|
|
|
|
2009-04-15 01:05:47 -06:00
|
|
|
"&<>!@ #$%^\n"
|
|
|
|
"&<>!@ #$%^\n"
|
|
|
|
},
|
2009-04-15 01:26:49 -06:00
|
|
|
|
|
|
|
&Test{
|
|
|
|
"{.section emptystring}emptystring{.end}\n"
|
|
|
|
"{.section header}header{.end}\n",
|
|
|
|
|
|
|
|
"\nheader\n"
|
|
|
|
},
|
2009-04-08 23:08:55 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestAll(t *testing.T) {
|
|
|
|
s := new(S);
|
|
|
|
// initialized by hand for clarity.
|
|
|
|
s.header = "Header";
|
|
|
|
s.integer = 77;
|
2009-04-15 01:05:47 -06:00
|
|
|
s.raw = "&<>!@ #$%^";
|
2009-04-08 23:08:55 -06:00
|
|
|
s.data = []T{ t1, t2 };
|
|
|
|
s.pdata = []*T{ &t1, &t2 };
|
|
|
|
s.empty = []*T{ };
|
|
|
|
s.null = nil;
|
|
|
|
|
|
|
|
var buf io.ByteBuffer;
|
|
|
|
for i, test := range tests {
|
|
|
|
buf.Reset();
|
2009-04-17 01:08:24 -06:00
|
|
|
tmpl, err := Parse(test.in, formatters);
|
2009-04-14 01:06:49 -06:00
|
|
|
if err != nil {
|
2009-04-17 01:08:24 -06:00
|
|
|
t.Error("unexpected parse error:", err);
|
2009-04-14 01:06:49 -06:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
err = tmpl.Execute(s, &buf);
|
2009-04-08 23:08:55 -06:00
|
|
|
if err != nil {
|
2009-04-14 01:06:49 -06:00
|
|
|
t.Error("unexpected execute error:", err)
|
2009-04-08 23:08:55 -06:00
|
|
|
}
|
|
|
|
if string(buf.Data()) != test.out {
|
|
|
|
t.Errorf("for %q: expected %q got %q", test.in, test.out, string(buf.Data()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-14 02:12:20 -06:00
|
|
|
func TestStringDriverType(t *testing.T) {
|
2009-04-17 01:08:24 -06:00
|
|
|
tmpl, err := Parse("template: {@}", nil);
|
2009-04-14 01:06:49 -06:00
|
|
|
if err != nil {
|
|
|
|
t.Error("unexpected parse error:", err)
|
|
|
|
}
|
2009-04-14 02:12:20 -06:00
|
|
|
var b io.ByteBuffer;
|
|
|
|
err = tmpl.Execute("hello", &b);
|
2009-04-14 22:25:33 -06:00
|
|
|
if err != nil {
|
2009-04-14 23:35:18 -06:00
|
|
|
t.Error("unexpected execute error:", err)
|
2009-04-14 22:25:33 -06:00
|
|
|
}
|
2009-04-14 02:12:20 -06:00
|
|
|
s := string(b.Data());
|
|
|
|
if s != "template: hello" {
|
|
|
|
t.Errorf("failed passing string as data: expected %q got %q", "template: hello", s)
|
2009-04-08 23:08:55 -06:00
|
|
|
}
|
|
|
|
}
|
2009-04-14 22:25:33 -06:00
|
|
|
|
|
|
|
func TestTwice(t *testing.T) {
|
2009-04-17 01:08:24 -06:00
|
|
|
tmpl, err := Parse("template: {@}", nil);
|
2009-04-14 22:25:33 -06:00
|
|
|
if err != nil {
|
|
|
|
t.Error("unexpected parse error:", err)
|
|
|
|
}
|
|
|
|
var b io.ByteBuffer;
|
|
|
|
err = tmpl.Execute("hello", &b);
|
|
|
|
if err != nil {
|
|
|
|
t.Error("unexpected parse error:", err)
|
|
|
|
}
|
|
|
|
s := string(b.Data());
|
|
|
|
text := "template: hello";
|
|
|
|
if s != text {
|
|
|
|
t.Errorf("failed passing string as data: expected %q got %q", text, s);
|
|
|
|
}
|
|
|
|
err = tmpl.Execute("hello", &b);
|
|
|
|
if err != nil {
|
|
|
|
t.Error("unexpected parse error:", err)
|
|
|
|
}
|
|
|
|
s = string(b.Data());
|
|
|
|
text += text;
|
|
|
|
if s != text {
|
|
|
|
t.Errorf("failed passing string as data: expected %q got %q", text, s);
|
|
|
|
}
|
|
|
|
}
|
2009-04-14 23:35:18 -06:00
|
|
|
|
|
|
|
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;
|
2009-04-17 01:08:24 -06:00
|
|
|
err := tmpl.Parse(text);
|
2009-04-14 23:35:18 -06:00
|
|
|
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 io.ByteBuffer;
|
|
|
|
err = tmpl.Execute("hello", &b);
|
|
|
|
s := string(b.Data());
|
|
|
|
if s != "template: hello" + ldelim + rdelim {
|
|
|
|
t.Errorf("failed delim check(%q %q) %q got %q", ldelim, rdelim, text, s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|