Imported Upstream version 4.8.1
[platform/upstream/gcc48.git] / libgo / go / html / template / js_test.go
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 (
8         "bytes"
9         "math"
10         "strings"
11         "testing"
12 )
13
14 func TestNextJsCtx(t *testing.T) {
15         tests := []struct {
16                 jsCtx jsCtx
17                 s     string
18         }{
19                 // Statement terminators precede regexps.
20                 {jsCtxRegexp, ";"},
21                 // This is not airtight.
22                 //     ({ valueOf: function () { return 1 } } / 2)
23                 // is valid JavaScript but in practice, devs do not do this.
24                 // A block followed by a statement starting with a RegExp is
25                 // much more common:
26                 //     while (x) {...} /foo/.test(x) || panic()
27                 {jsCtxRegexp, "}"},
28                 // But member, call, grouping, and array expression terminators
29                 // precede div ops.
30                 {jsCtxDivOp, ")"},
31                 {jsCtxDivOp, "]"},
32                 // At the start of a primary expression, array, or expression
33                 // statement, expect a regexp.
34                 {jsCtxRegexp, "("},
35                 {jsCtxRegexp, "["},
36                 {jsCtxRegexp, "{"},
37                 // Assignment operators precede regexps as do all exclusively
38                 // prefix and binary operators.
39                 {jsCtxRegexp, "="},
40                 {jsCtxRegexp, "+="},
41                 {jsCtxRegexp, "*="},
42                 {jsCtxRegexp, "*"},
43                 {jsCtxRegexp, "!"},
44                 // Whether the + or - is infix or prefix, it cannot precede a
45                 // div op.
46                 {jsCtxRegexp, "+"},
47                 {jsCtxRegexp, "-"},
48                 // An incr/decr op precedes a div operator.
49                 // This is not airtight. In (g = ++/h/i) a regexp follows a
50                 // pre-increment operator, but in practice devs do not try to
51                 // increment or decrement regular expressions.
52                 // (g++/h/i) where ++ is a postfix operator on g is much more
53                 // common.
54                 {jsCtxDivOp, "--"},
55                 {jsCtxDivOp, "++"},
56                 {jsCtxDivOp, "x--"},
57                 // When we have many dashes or pluses, then they are grouped
58                 // left to right.
59                 {jsCtxRegexp, "x---"}, // A postfix -- then a -.
60                 // return followed by a slash returns the regexp literal or the
61                 // slash starts a regexp literal in an expression statement that
62                 // is dead code.
63                 {jsCtxRegexp, "return"},
64                 {jsCtxRegexp, "return "},
65                 {jsCtxRegexp, "return\t"},
66                 {jsCtxRegexp, "return\n"},
67                 {jsCtxRegexp, "return\u2028"},
68                 // Identifiers can be divided and cannot validly be preceded by
69                 // a regular expressions. Semicolon insertion cannot happen
70                 // between an identifier and a regular expression on a new line
71                 // because the one token lookahead for semicolon insertion has
72                 // to conclude that it could be a div binary op and treat it as
73                 // such.
74                 {jsCtxDivOp, "x"},
75                 {jsCtxDivOp, "x "},
76                 {jsCtxDivOp, "x\t"},
77                 {jsCtxDivOp, "x\n"},
78                 {jsCtxDivOp, "x\u2028"},
79                 {jsCtxDivOp, "preturn"},
80                 // Numbers precede div ops.
81                 {jsCtxDivOp, "0"},
82                 // Dots that are part of a number are div preceders.
83                 {jsCtxDivOp, "0."},
84         }
85
86         for _, test := range tests {
87                 if nextJSCtx([]byte(test.s), jsCtxRegexp) != test.jsCtx {
88                         t.Errorf("want %s got %q", test.jsCtx, test.s)
89                 }
90                 if nextJSCtx([]byte(test.s), jsCtxDivOp) != test.jsCtx {
91                         t.Errorf("want %s got %q", test.jsCtx, test.s)
92                 }
93         }
94
95         if nextJSCtx([]byte("   "), jsCtxRegexp) != jsCtxRegexp {
96                 t.Error("Blank tokens")
97         }
98
99         if nextJSCtx([]byte("   "), jsCtxDivOp) != jsCtxDivOp {
100                 t.Error("Blank tokens")
101         }
102 }
103
104 func TestJSValEscaper(t *testing.T) {
105         tests := []struct {
106                 x  interface{}
107                 js string
108         }{
109                 {int(42), " 42 "},
110                 {uint(42), " 42 "},
111                 {int16(42), " 42 "},
112                 {uint16(42), " 42 "},
113                 {int32(-42), " -42 "},
114                 {uint32(42), " 42 "},
115                 {int16(-42), " -42 "},
116                 {uint16(42), " 42 "},
117                 {int64(-42), " -42 "},
118                 {uint64(42), " 42 "},
119                 {uint64(1) << 53, " 9007199254740992 "},
120                 // ulp(1 << 53) > 1 so this loses precision in JS
121                 // but it is still a representable integer literal.
122                 {uint64(1)<<53 + 1, " 9007199254740993 "},
123                 {float32(1.0), " 1 "},
124                 {float32(-1.0), " -1 "},
125                 {float32(0.5), " 0.5 "},
126                 {float32(-0.5), " -0.5 "},
127                 {float32(1.0) / float32(256), " 0.00390625 "},
128                 {float32(0), " 0 "},
129                 {math.Copysign(0, -1), " -0 "},
130                 {float64(1.0), " 1 "},
131                 {float64(-1.0), " -1 "},
132                 {float64(0.5), " 0.5 "},
133                 {float64(-0.5), " -0.5 "},
134                 {float64(0), " 0 "},
135                 {math.Copysign(0, -1), " -0 "},
136                 {"", `""`},
137                 {"foo", `"foo"`},
138                 // Newlines.
139                 {"\r\n\u2028\u2029", `"\r\n\u2028\u2029"`},
140                 // "\v" == "v" on IE 6 so use "\x0b" instead.
141                 {"\t\x0b", `"\u0009\u000b"`},
142                 {struct{ X, Y int }{1, 2}, `{"X":1,"Y":2}`},
143                 {[]interface{}{}, "[]"},
144                 {[]interface{}{42, "foo", nil}, `[42,"foo",null]`},
145                 {[]string{"<!--", "</script>", "-->"}, `["\u003c!--","\u003c/script\u003e","--\u003e"]`},
146                 {"<!--", `"\u003c!--"`},
147                 {"-->", `"--\u003e"`},
148                 {"<![CDATA[", `"\u003c![CDATA["`},
149                 {"]]>", `"]]\u003e"`},
150                 {"</script", `"\u003c/script"`},
151                 {"\U0001D11E", "\"\U0001D11E\""}, // or "\uD834\uDD1E"
152         }
153
154         for _, test := range tests {
155                 if js := jsValEscaper(test.x); js != test.js {
156                         t.Errorf("%+v: want\n\t%q\ngot\n\t%q", test.x, test.js, js)
157                 }
158                 // Make sure that escaping corner cases are not broken
159                 // by nesting.
160                 a := []interface{}{test.x}
161                 want := "[" + strings.TrimSpace(test.js) + "]"
162                 if js := jsValEscaper(a); js != want {
163                         t.Errorf("%+v: want\n\t%q\ngot\n\t%q", a, want, js)
164                 }
165         }
166 }
167
168 func TestJSStrEscaper(t *testing.T) {
169         tests := []struct {
170                 x   interface{}
171                 esc string
172         }{
173                 {"", ``},
174                 {"foo", `foo`},
175                 {"\u0000", `\0`},
176                 {"\t", `\t`},
177                 {"\n", `\n`},
178                 {"\r", `\r`},
179                 {"\u2028", `\u2028`},
180                 {"\u2029", `\u2029`},
181                 {"\\", `\\`},
182                 {"\\n", `\\n`},
183                 {"foo\r\nbar", `foo\r\nbar`},
184                 // Preserve attribute boundaries.
185                 {`"`, `\x22`},
186                 {`'`, `\x27`},
187                 // Allow embedding in HTML without further escaping.
188                 {`&amp;`, `\x26amp;`},
189                 // Prevent breaking out of text node and element boundaries.
190                 {"</script>", `\x3c\/script\x3e`},
191                 {"<![CDATA[", `\x3c![CDATA[`},
192                 {"]]>", `]]\x3e`},
193                 // http://dev.w3.org/html5/markup/aria/syntax.html#escaping-text-span
194                 //   "The text in style, script, title, and textarea elements
195                 //   must not have an escaping text span start that is not
196                 //   followed by an escaping text span end."
197                 // Furthermore, spoofing an escaping text span end could lead
198                 // to different interpretation of a </script> sequence otherwise
199                 // masked by the escaping text span, and spoofing a start could
200                 // allow regular text content to be interpreted as script
201                 // allowing script execution via a combination of a JS string
202                 // injection followed by an HTML text injection.
203                 {"<!--", `\x3c!--`},
204                 {"-->", `--\x3e`},
205                 // From http://code.google.com/p/doctype/wiki/ArticleUtf7
206                 {"+ADw-script+AD4-alert(1)+ADw-/script+AD4-",
207                         `\x2bADw-script\x2bAD4-alert(1)\x2bADw-\/script\x2bAD4-`,
208                 },
209                 // Invalid UTF-8 sequence
210                 {"foo\xA0bar", "foo\xA0bar"},
211                 // Invalid unicode scalar value.
212                 {"foo\xed\xa0\x80bar", "foo\xed\xa0\x80bar"},
213         }
214
215         for _, test := range tests {
216                 esc := jsStrEscaper(test.x)
217                 if esc != test.esc {
218                         t.Errorf("%q: want %q got %q", test.x, test.esc, esc)
219                 }
220         }
221 }
222
223 func TestJSRegexpEscaper(t *testing.T) {
224         tests := []struct {
225                 x   interface{}
226                 esc string
227         }{
228                 {"", `(?:)`},
229                 {"foo", `foo`},
230                 {"\u0000", `\0`},
231                 {"\t", `\t`},
232                 {"\n", `\n`},
233                 {"\r", `\r`},
234                 {"\u2028", `\u2028`},
235                 {"\u2029", `\u2029`},
236                 {"\\", `\\`},
237                 {"\\n", `\\n`},
238                 {"foo\r\nbar", `foo\r\nbar`},
239                 // Preserve attribute boundaries.
240                 {`"`, `\x22`},
241                 {`'`, `\x27`},
242                 // Allow embedding in HTML without further escaping.
243                 {`&amp;`, `\x26amp;`},
244                 // Prevent breaking out of text node and element boundaries.
245                 {"</script>", `\x3c\/script\x3e`},
246                 {"<![CDATA[", `\x3c!\[CDATA\[`},
247                 {"]]>", `\]\]\x3e`},
248                 // Escaping text spans.
249                 {"<!--", `\x3c!\-\-`},
250                 {"-->", `\-\-\x3e`},
251                 {"*", `\*`},
252                 {"+", `\x2b`},
253                 {"?", `\?`},
254                 {"[](){}", `\[\]\(\)\{\}`},
255                 {"$foo|x.y", `\$foo\|x\.y`},
256                 {"x^y", `x\^y`},
257         }
258
259         for _, test := range tests {
260                 esc := jsRegexpEscaper(test.x)
261                 if esc != test.esc {
262                         t.Errorf("%q: want %q got %q", test.x, test.esc, esc)
263                 }
264         }
265 }
266
267 func TestEscapersOnLower7AndSelectHighCodepoints(t *testing.T) {
268         input := ("\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f" +
269                 "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
270                 ` !"#$%&'()*+,-./` +
271                 `0123456789:;<=>?` +
272                 `@ABCDEFGHIJKLMNO` +
273                 `PQRSTUVWXYZ[\]^_` +
274                 "`abcdefghijklmno" +
275                 "pqrstuvwxyz{|}~\x7f" +
276                 "\u00A0\u0100\u2028\u2029\ufeff\U0001D11E")
277
278         tests := []struct {
279                 name    string
280                 escaper func(...interface{}) string
281                 escaped string
282         }{
283                 {
284                         "jsStrEscaper",
285                         jsStrEscaper,
286                         "\\0\x01\x02\x03\x04\x05\x06\x07" +
287                                 "\x08\\t\\n\\x0b\\f\\r\x0E\x0F" +
288                                 "\x10\x11\x12\x13\x14\x15\x16\x17" +
289                                 "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
290                                 ` !\x22#$%\x26\x27()*\x2b,-.\/` +
291                                 `0123456789:;\x3c=\x3e?` +
292                                 `@ABCDEFGHIJKLMNO` +
293                                 `PQRSTUVWXYZ[\\]^_` +
294                                 "`abcdefghijklmno" +
295                                 "pqrstuvwxyz{|}~\x7f" +
296                                 "\u00A0\u0100\\u2028\\u2029\ufeff\U0001D11E",
297                 },
298                 {
299                         "jsRegexpEscaper",
300                         jsRegexpEscaper,
301                         "\\0\x01\x02\x03\x04\x05\x06\x07" +
302                                 "\x08\\t\\n\\x0b\\f\\r\x0E\x0F" +
303                                 "\x10\x11\x12\x13\x14\x15\x16\x17" +
304                                 "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
305                                 ` !\x22#\$%\x26\x27\(\)\*\x2b,\-\.\/` +
306                                 `0123456789:;\x3c=\x3e\?` +
307                                 `@ABCDEFGHIJKLMNO` +
308                                 `PQRSTUVWXYZ\[\\\]\^_` +
309                                 "`abcdefghijklmno" +
310                                 `pqrstuvwxyz\{\|\}~` + "\u007f" +
311                                 "\u00A0\u0100\\u2028\\u2029\ufeff\U0001D11E",
312                 },
313         }
314
315         for _, test := range tests {
316                 if s := test.escaper(input); s != test.escaped {
317                         t.Errorf("%s once: want\n\t%q\ngot\n\t%q", test.name, test.escaped, s)
318                         continue
319                 }
320
321                 // Escape it rune by rune to make sure that any
322                 // fast-path checking does not break escaping.
323                 var buf bytes.Buffer
324                 for _, c := range input {
325                         buf.WriteString(test.escaper(string(c)))
326                 }
327
328                 if s := buf.String(); s != test.escaped {
329                         t.Errorf("%s rune-wise: want\n\t%q\ngot\n\t%q", test.name, test.escaped, s)
330                         continue
331                 }
332         }
333 }
334
335 func BenchmarkJSValEscaperWithNum(b *testing.B) {
336         for i := 0; i < b.N; i++ {
337                 jsValEscaper(3.141592654)
338         }
339 }
340
341 func BenchmarkJSValEscaperWithStr(b *testing.B) {
342         for i := 0; i < b.N; i++ {
343                 jsValEscaper("The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>")
344         }
345 }
346
347 func BenchmarkJSValEscaperWithStrNoSpecials(b *testing.B) {
348         for i := 0; i < b.N; i++ {
349                 jsValEscaper("The quick, brown fox jumps over the lazy dog")
350         }
351 }
352
353 func BenchmarkJSValEscaperWithObj(b *testing.B) {
354         o := struct {
355                 S string
356                 N int
357         }{
358                 "The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>\u2028",
359                 42,
360         }
361         for i := 0; i < b.N; i++ {
362                 jsValEscaper(o)
363         }
364 }
365
366 func BenchmarkJSValEscaperWithObjNoSpecials(b *testing.B) {
367         o := struct {
368                 S string
369                 N int
370         }{
371                 "The quick, brown fox jumps over the lazy dog",
372                 42,
373         }
374         for i := 0; i < b.N; i++ {
375                 jsValEscaper(o)
376         }
377 }
378
379 func BenchmarkJSStrEscaperNoSpecials(b *testing.B) {
380         for i := 0; i < b.N; i++ {
381                 jsStrEscaper("The quick, brown fox jumps over the lazy dog.")
382         }
383 }
384
385 func BenchmarkJSStrEscaper(b *testing.B) {
386         for i := 0; i < b.N; i++ {
387                 jsStrEscaper("The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>")
388         }
389 }
390
391 func BenchmarkJSRegexpEscaperNoSpecials(b *testing.B) {
392         for i := 0; i < b.N; i++ {
393                 jsRegexpEscaper("The quick, brown fox jumps over the lazy dog")
394         }
395 }
396
397 func BenchmarkJSRegexpEscaper(b *testing.B) {
398         for i := 0; i < b.N; i++ {
399                 jsRegexpEscaper("The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>")
400         }
401 }