blob: 7524ac8b251583fcbbdedb5f321f4c539cf56bce [file] [log] [blame]
Rob Pikecd7826e2011-06-23 09:27:28 +10001// Copyright 2011 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
5package template
6
7import (
Rob Pike64228e32011-07-06 17:46:36 +10008 "flag"
Rob Pikecd7826e2011-06-23 09:27:28 +10009 "fmt"
10 "testing"
11)
12
Rob Pike64228e32011-07-06 17:46:36 +100013var debug = flag.Bool("debug", false, "show the errors produced by the tests")
Rob Pikecd7826e2011-06-23 09:27:28 +100014
15type numberTest struct {
16 text string
17 isInt bool
18 isUint bool
19 isFloat bool
Rob Pike13f88972011-07-04 15:15:47 +100020 isComplex bool
Rob Pikecd7826e2011-06-23 09:27:28 +100021 int64
22 uint64
23 float64
Rob Pike13f88972011-07-04 15:15:47 +100024 complex128
Rob Pikecd7826e2011-06-23 09:27:28 +100025}
26
27var numberTests = []numberTest{
28 // basics
Rob Pike13f88972011-07-04 15:15:47 +100029 {"0", true, true, true, false, 0, 0, 0, 0},
30 {"-0", true, true, true, false, 0, 0, 0, 0}, // check that -0 is a uint.
31 {"73", true, true, true, false, 73, 73, 73, 0},
32 {"-73", true, false, true, false, -73, 0, -73, 0},
33 {"+73", true, false, true, false, 73, 0, 73, 0},
34 {"100", true, true, true, false, 100, 100, 100, 0},
35 {"1e9", true, true, true, false, 1e9, 1e9, 1e9, 0},
36 {"-1e9", true, false, true, false, -1e9, 0, -1e9, 0},
37 {"-1.2", false, false, true, false, 0, 0, -1.2, 0},
38 {"1e19", false, true, true, false, 0, 1e19, 1e19, 0},
39 {"-1e19", false, false, true, false, 0, 0, -1e19, 0},
40 {"4i", false, false, false, true, 0, 0, 0, 4i},
41 {"-1.2+4.2i", false, false, false, true, 0, 0, 0, -1.2 + 4.2i},
42 // complex with 0 imaginary are float (and maybe integer)
43 {"0i", true, true, true, true, 0, 0, 0, 0},
44 {"-1.2+0i", false, false, true, true, 0, 0, -1.2, -1.2},
45 {"-12+0i", true, false, true, true, -12, 0, -12, -12},
46 {"13+0i", true, true, true, true, 13, 13, 13, 13},
Rob Pikecd7826e2011-06-23 09:27:28 +100047 // funny bases
Rob Pike13f88972011-07-04 15:15:47 +100048 {"0123", true, true, true, false, 0123, 0123, 0123, 0},
49 {"-0x0", true, true, true, false, 0, 0, 0, 0},
50 {"0xdeadbeef", true, true, true, false, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0},
Rob Pikecd7826e2011-06-23 09:27:28 +100051 // some broken syntax
52 {text: "+-2"},
53 {text: "0x123."},
54 {text: "1e."},
55 {text: "0xi."},
Rob Pike13f88972011-07-04 15:15:47 +100056 {text: "1+2."},
Rob Pikecd7826e2011-06-23 09:27:28 +100057}
58
59func TestNumberParse(t *testing.T) {
60 for _, test := range numberTests {
Rob Pike13f88972011-07-04 15:15:47 +100061 // If fmt.Sscan thinks it's complex, it's complex. We can't trust the output
62 // because imaginary comes out as a number.
63 var c complex128
64 _, err := fmt.Sscan(test.text, &c)
65 n, err := newNumber(test.text, err == nil)
66 ok := test.isInt || test.isUint || test.isFloat || test.isComplex
Rob Pikecd7826e2011-06-23 09:27:28 +100067 if ok && err != nil {
68 t.Errorf("unexpected error for %q", test.text)
69 continue
70 }
71 if !ok && err == nil {
72 t.Errorf("expected error for %q", test.text)
73 continue
74 }
75 if !ok {
76 continue
77 }
Rob Pike13f88972011-07-04 15:15:47 +100078 if n.isComplex != test.isComplex {
79 t.Errorf("complex incorrect for %q; should be %t", test.text, test.isComplex)
Rob Pikecd7826e2011-06-23 09:27:28 +100080 }
81 if test.isInt {
82 if !n.isInt {
83 t.Errorf("expected integer for %q", test.text)
84 }
85 if n.int64 != test.int64 {
86 t.Errorf("int64 for %q should be %d is %d", test.text, test.int64, n.int64)
87 }
88 } else if n.isInt {
89 t.Errorf("did not expect integer for %q", test.text)
90 }
91 if test.isUint {
92 if !n.isUint {
93 t.Errorf("expected unsigned integer for %q", test.text)
94 }
95 if n.uint64 != test.uint64 {
96 t.Errorf("uint64 for %q should be %d is %d", test.text, test.uint64, n.uint64)
97 }
98 } else if n.isUint {
99 t.Errorf("did not expect unsigned integer for %q", test.text)
100 }
101 if test.isFloat {
102 if !n.isFloat {
103 t.Errorf("expected float for %q", test.text)
104 }
105 if n.float64 != test.float64 {
106 t.Errorf("float64 for %q should be %g is %g", test.text, test.float64, n.float64)
107 }
108 } else if n.isFloat {
109 t.Errorf("did not expect float for %q", test.text)
110 }
Rob Pike13f88972011-07-04 15:15:47 +1000111 if test.isComplex {
112 if !n.isComplex {
113 t.Errorf("expected complex for %q", test.text)
114 }
115 if n.complex128 != test.complex128 {
116 t.Errorf("complex128 for %q should be %g is %g", test.text, test.complex128, n.complex128)
117 }
118 } else if n.isComplex {
119 t.Errorf("did not expect complex for %q", test.text)
120 }
Rob Pikecd7826e2011-06-23 09:27:28 +1000121 }
122}
123
Rob Pikecd7826e2011-06-23 09:27:28 +1000124type parseTest struct {
125 name string
126 input string
127 ok bool
128 result string
129}
130
131const (
132 noError = true
133 hasError = false
134)
135
136var parseTests = []parseTest{
137 {"empty", "", noError,
138 `[]`},
139 {"spaces", " \t\n", noError,
140 `[(text: " \t\n")]`},
141 {"text", "some text", noError,
142 `[(text: "some text")]`},
Rob Pike8d538c62011-07-07 10:56:33 +1000143 {"emptyAction", "{{}}", hasError,
Rob Pikecd7826e2011-06-23 09:27:28 +1000144 `[(action: [])]`},
Rob Pike81592c22011-06-28 23:04:08 +1000145 {"field", "{{.X}}", noError,
146 `[(action: [(command: [F=[X]])])]`},
Rob Pike5b165822011-07-05 16:02:34 +1000147 {"simple command", "{{printf}}", noError,
148 `[(action: [(command: [I=printf])])]`},
Rob Pikee7030e72011-07-11 10:01:15 +1000149 {"$ invocation", "{{$}}", noError,
150 "[(action: [(command: [V=$])])]"},
151 {"variable invocation", "{{with $x := 3}}{{$x 23}}{{end}}", noError,
152 "[({{with $x := [(command: [N=3])]}} [(action: [(command: [V=$x N=23])])])]"},
Rob Pike5b165822011-07-05 16:02:34 +1000153 {"multi-word command", "{{printf `%d` 23}}", noError,
154 "[(action: [(command: [I=printf S=`%d` N=23])])]"},
155 {"pipeline", "{{.X|.Y}}", noError,
156 `[(action: [(command: [F=[X]]) (command: [F=[Y]])])]`},
Rob Pikeb8c66422011-07-08 17:54:16 +1000157 {"pipeline with decl", "{{$x := .X|.Y}}", noError,
158 `[(action: $x := [(command: [F=[X]]) (command: [F=[Y]])])]`},
159 {"declaration", "{{.X|.Y}}", noError,
160 `[(action: [(command: [F=[X]]) (command: [F=[Y]])])]`},
Rob Pike13f88972011-07-04 15:15:47 +1000161 {"simple if", "{{if .X}}hello{{end}}", noError,
162 `[({{if [(command: [F=[X]])]}} [(text: "hello")])]`},
163 {"if with else", "{{if .X}}true{{else}}false{{end}}", noError,
164 `[({{if [(command: [F=[X]])]}} [(text: "true")] {{else}} [(text: "false")])]`},
Rob Pike81592c22011-06-28 23:04:08 +1000165 {"simple range", "{{range .X}}hello{{end}}", noError,
166 `[({{range [(command: [F=[X]])]}} [(text: "hello")])]`},
167 {"chained field range", "{{range .X.Y.Z}}hello{{end}}", noError,
168 `[({{range [(command: [F=[X Y Z]])]}} [(text: "hello")])]`},
169 {"nested range", "{{range .X}}hello{{range .Y}}goodbye{{end}}{{end}}", noError,
170 `[({{range [(command: [F=[X]])]}} [(text: "hello")({{range [(command: [F=[Y]])]}} [(text: "goodbye")])])]`},
171 {"range with else", "{{range .X}}true{{else}}false{{end}}", noError,
172 `[({{range [(command: [F=[X]])]}} [(text: "true")] {{else}} [(text: "false")])]`},
173 {"range over pipeline", "{{range .X|.M}}true{{else}}false{{end}}", noError,
174 `[({{range [(command: [F=[X]]) (command: [F=[M]])]}} [(text: "true")] {{else}} [(text: "false")])]`},
175 {"range []int", "{{range .SI}}{{.}}{{end}}", noError,
176 `[({{range [(command: [F=[SI]])]}} [(action: [(command: [{{<.>}}])])])]`},
Rob Pikec756a192011-06-29 15:02:04 +1000177 {"constants", "{{range .SI 1 -3.2i true false }}{{end}}", noError,
178 `[({{range [(command: [F=[SI] N=1 N=-3.2i B=true B=false])]}} [])]`},
Rob Pike3987b912011-07-10 07:32:01 +1000179 {"template", "{{template `x`}}", noError,
180 "[{{template S=`x`}}]"},
Rob Pike5b165822011-07-05 16:02:34 +1000181 {"template", "{{template `x` .Y}}", noError,
182 "[{{template S=`x` [(command: [F=[Y]])]}}]"},
Rob Pike13f88972011-07-04 15:15:47 +1000183 {"with", "{{with .X}}hello{{end}}", noError,
184 `[({{with [(command: [F=[X]])]}} [(text: "hello")])]`},
185 {"with with else", "{{with .X}}hello{{else}}goodbye{{end}}", noError,
186 `[({{with [(command: [F=[X]])]}} [(text: "hello")] {{else}} [(text: "goodbye")])]`},
Rob Pikecd7826e2011-06-23 09:27:28 +1000187 // Errors.
188 {"unclosed action", "hello{{range", hasError, ""},
Rob Pikee7030e72011-07-11 10:01:15 +1000189 {"unmatched end", "{{end}}", hasError, ""},
Rob Pikecd7826e2011-06-23 09:27:28 +1000190 {"missing end", "hello{{range .x}}", hasError, ""},
191 {"missing end after else", "hello{{range .x}}{{else}}", hasError, ""},
Rob Pike5b165822011-07-05 16:02:34 +1000192 {"undefined function", "hello{{undefined}}", hasError, ""},
Rob Pikee7030e72011-07-11 10:01:15 +1000193 {"undefined variable", "{{$x}}", hasError, ""},
194 {"variable undefined after end", "{{with $x := 4}}{{end}}{{$x}}", hasError, ""},
Rob Pikecd7826e2011-06-23 09:27:28 +1000195}
196
197func TestParse(t *testing.T) {
198 for _, test := range parseTests {
199 tmpl := New(test.name)
200 err := tmpl.Parse(test.input)
201 switch {
202 case err == nil && !test.ok:
203 t.Errorf("%q: expected error; got none", test.name)
204 continue
205 case err != nil && test.ok:
206 t.Errorf("%q: unexpected error: %v", test.name, err)
207 continue
208 case err != nil && !test.ok:
209 // expected error, got one
Rob Pike64228e32011-07-06 17:46:36 +1000210 if *debug {
Rob Pikecd7826e2011-06-23 09:27:28 +1000211 fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err)
212 }
213 continue
214 }
215 result := tmpl.root.String()
216 if result != test.result {
217 t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.result)
218 }
219 }
220}