Rob Pike | cd7826e | 2011-06-23 09:27:28 +1000 | [diff] [blame] | 1 | // 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 | |
| 5 | package template |
| 6 | |
| 7 | import ( |
Rob Pike | 64228e3 | 2011-07-06 17:46:36 +1000 | [diff] [blame] | 8 | "flag" |
Rob Pike | cd7826e | 2011-06-23 09:27:28 +1000 | [diff] [blame] | 9 | "fmt" |
| 10 | "testing" |
| 11 | ) |
| 12 | |
Rob Pike | 64228e3 | 2011-07-06 17:46:36 +1000 | [diff] [blame] | 13 | var debug = flag.Bool("debug", false, "show the errors produced by the tests") |
Rob Pike | cd7826e | 2011-06-23 09:27:28 +1000 | [diff] [blame] | 14 | |
| 15 | type numberTest struct { |
| 16 | text string |
| 17 | isInt bool |
| 18 | isUint bool |
| 19 | isFloat bool |
Rob Pike | 13f8897 | 2011-07-04 15:15:47 +1000 | [diff] [blame] | 20 | isComplex bool |
Rob Pike | cd7826e | 2011-06-23 09:27:28 +1000 | [diff] [blame] | 21 | int64 |
| 22 | uint64 |
| 23 | float64 |
Rob Pike | 13f8897 | 2011-07-04 15:15:47 +1000 | [diff] [blame] | 24 | complex128 |
Rob Pike | cd7826e | 2011-06-23 09:27:28 +1000 | [diff] [blame] | 25 | } |
| 26 | |
| 27 | var numberTests = []numberTest{ |
| 28 | // basics |
Rob Pike | 13f8897 | 2011-07-04 15:15:47 +1000 | [diff] [blame] | 29 | {"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 Pike | cd7826e | 2011-06-23 09:27:28 +1000 | [diff] [blame] | 47 | // funny bases |
Rob Pike | 13f8897 | 2011-07-04 15:15:47 +1000 | [diff] [blame] | 48 | {"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 Pike | cd7826e | 2011-06-23 09:27:28 +1000 | [diff] [blame] | 51 | // some broken syntax |
| 52 | {text: "+-2"}, |
| 53 | {text: "0x123."}, |
| 54 | {text: "1e."}, |
| 55 | {text: "0xi."}, |
Rob Pike | 13f8897 | 2011-07-04 15:15:47 +1000 | [diff] [blame] | 56 | {text: "1+2."}, |
Rob Pike | cd7826e | 2011-06-23 09:27:28 +1000 | [diff] [blame] | 57 | } |
| 58 | |
| 59 | func TestNumberParse(t *testing.T) { |
| 60 | for _, test := range numberTests { |
Rob Pike | 13f8897 | 2011-07-04 15:15:47 +1000 | [diff] [blame] | 61 | // 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 Pike | cd7826e | 2011-06-23 09:27:28 +1000 | [diff] [blame] | 67 | 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 Pike | 13f8897 | 2011-07-04 15:15:47 +1000 | [diff] [blame] | 78 | if n.isComplex != test.isComplex { |
| 79 | t.Errorf("complex incorrect for %q; should be %t", test.text, test.isComplex) |
Rob Pike | cd7826e | 2011-06-23 09:27:28 +1000 | [diff] [blame] | 80 | } |
| 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 Pike | 13f8897 | 2011-07-04 15:15:47 +1000 | [diff] [blame] | 111 | 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 Pike | cd7826e | 2011-06-23 09:27:28 +1000 | [diff] [blame] | 121 | } |
| 122 | } |
| 123 | |
Rob Pike | cd7826e | 2011-06-23 09:27:28 +1000 | [diff] [blame] | 124 | type parseTest struct { |
| 125 | name string |
| 126 | input string |
| 127 | ok bool |
| 128 | result string |
| 129 | } |
| 130 | |
| 131 | const ( |
| 132 | noError = true |
| 133 | hasError = false |
| 134 | ) |
| 135 | |
| 136 | var 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 Pike | 8d538c6 | 2011-07-07 10:56:33 +1000 | [diff] [blame] | 143 | {"emptyAction", "{{}}", hasError, |
Rob Pike | cd7826e | 2011-06-23 09:27:28 +1000 | [diff] [blame] | 144 | `[(action: [])]`}, |
Rob Pike | 81592c2 | 2011-06-28 23:04:08 +1000 | [diff] [blame] | 145 | {"field", "{{.X}}", noError, |
| 146 | `[(action: [(command: [F=[X]])])]`}, |
Rob Pike | 5b16582 | 2011-07-05 16:02:34 +1000 | [diff] [blame] | 147 | {"simple command", "{{printf}}", noError, |
| 148 | `[(action: [(command: [I=printf])])]`}, |
Rob Pike | e7030e7 | 2011-07-11 10:01:15 +1000 | [diff] [blame^] | 149 | {"$ 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 Pike | 5b16582 | 2011-07-05 16:02:34 +1000 | [diff] [blame] | 153 | {"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 Pike | b8c6642 | 2011-07-08 17:54:16 +1000 | [diff] [blame] | 157 | {"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 Pike | 13f8897 | 2011-07-04 15:15:47 +1000 | [diff] [blame] | 161 | {"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 Pike | 81592c2 | 2011-06-28 23:04:08 +1000 | [diff] [blame] | 165 | {"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 Pike | c756a19 | 2011-06-29 15:02:04 +1000 | [diff] [blame] | 177 | {"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 Pike | 3987b91 | 2011-07-10 07:32:01 +1000 | [diff] [blame] | 179 | {"template", "{{template `x`}}", noError, |
| 180 | "[{{template S=`x`}}]"}, |
Rob Pike | 5b16582 | 2011-07-05 16:02:34 +1000 | [diff] [blame] | 181 | {"template", "{{template `x` .Y}}", noError, |
| 182 | "[{{template S=`x` [(command: [F=[Y]])]}}]"}, |
Rob Pike | 13f8897 | 2011-07-04 15:15:47 +1000 | [diff] [blame] | 183 | {"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 Pike | cd7826e | 2011-06-23 09:27:28 +1000 | [diff] [blame] | 187 | // Errors. |
| 188 | {"unclosed action", "hello{{range", hasError, ""}, |
Rob Pike | e7030e7 | 2011-07-11 10:01:15 +1000 | [diff] [blame^] | 189 | {"unmatched end", "{{end}}", hasError, ""}, |
Rob Pike | cd7826e | 2011-06-23 09:27:28 +1000 | [diff] [blame] | 190 | {"missing end", "hello{{range .x}}", hasError, ""}, |
| 191 | {"missing end after else", "hello{{range .x}}{{else}}", hasError, ""}, |
Rob Pike | 5b16582 | 2011-07-05 16:02:34 +1000 | [diff] [blame] | 192 | {"undefined function", "hello{{undefined}}", hasError, ""}, |
Rob Pike | e7030e7 | 2011-07-11 10:01:15 +1000 | [diff] [blame^] | 193 | {"undefined variable", "{{$x}}", hasError, ""}, |
| 194 | {"variable undefined after end", "{{with $x := 4}}{{end}}{{$x}}", hasError, ""}, |
Rob Pike | cd7826e | 2011-06-23 09:27:28 +1000 | [diff] [blame] | 195 | } |
| 196 | |
| 197 | func 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 Pike | 64228e3 | 2011-07-06 17:46:36 +1000 | [diff] [blame] | 210 | if *debug { |
Rob Pike | cd7826e | 2011-06-23 09:27:28 +1000 | [diff] [blame] | 211 | 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 | } |