Home | History | Annotate | Download | only in template
      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 	"strconv"
      9 	"strings"
     10 	"testing"
     11 )
     12 
     13 func TestEndsWithCSSKeyword(t *testing.T) {
     14 	tests := []struct {
     15 		css, kw string
     16 		want    bool
     17 	}{
     18 		{"", "url", false},
     19 		{"url", "url", true},
     20 		{"URL", "url", true},
     21 		{"Url", "url", true},
     22 		{"url", "important", false},
     23 		{"important", "important", true},
     24 		{"image-url", "url", false},
     25 		{"imageurl", "url", false},
     26 		{"image url", "url", true},
     27 	}
     28 	for _, test := range tests {
     29 		got := endsWithCSSKeyword([]byte(test.css), test.kw)
     30 		if got != test.want {
     31 			t.Errorf("want %t but got %t for css=%v, kw=%v", test.want, got, test.css, test.kw)
     32 		}
     33 	}
     34 }
     35 
     36 func TestIsCSSNmchar(t *testing.T) {
     37 	tests := []struct {
     38 		rune rune
     39 		want bool
     40 	}{
     41 		{0, false},
     42 		{'0', true},
     43 		{'9', true},
     44 		{'A', true},
     45 		{'Z', true},
     46 		{'a', true},
     47 		{'z', true},
     48 		{'_', true},
     49 		{'-', true},
     50 		{':', false},
     51 		{';', false},
     52 		{' ', false},
     53 		{0x7f, false},
     54 		{0x80, true},
     55 		{0x1234, true},
     56 		{0xd800, false},
     57 		{0xdc00, false},
     58 		{0xfffe, false},
     59 		{0x10000, true},
     60 		{0x110000, false},
     61 	}
     62 	for _, test := range tests {
     63 		got := isCSSNmchar(test.rune)
     64 		if got != test.want {
     65 			t.Errorf("%q: want %t but got %t", string(test.rune), test.want, got)
     66 		}
     67 	}
     68 }
     69 
     70 func TestDecodeCSS(t *testing.T) {
     71 	tests := []struct {
     72 		css, want string
     73 	}{
     74 		{``, ``},
     75 		{`foo`, `foo`},
     76 		{`foo\`, `foo`},
     77 		{`foo\\`, `foo\`},
     78 		{`\`, ``},
     79 		{`\A`, "\n"},
     80 		{`\a`, "\n"},
     81 		{`\0a`, "\n"},
     82 		{`\00000a`, "\n"},
     83 		{`\000000a`, "\u0000a"},
     84 		{`\1234 5`, "\u1234" + "5"},
     85 		{`\1234\20 5`, "\u1234" + " 5"},
     86 		{`\1234\A 5`, "\u1234" + "\n5"},
     87 		{"\\1234\t5", "\u1234" + "5"},
     88 		{"\\1234\n5", "\u1234" + "5"},
     89 		{"\\1234\r\n5", "\u1234" + "5"},
     90 		{`\12345`, "\U00012345"},
     91 		{`\\`, `\`},
     92 		{`\\ `, `\ `},
     93 		{`\"`, `"`},
     94 		{`\'`, `'`},
     95 		{`\.`, `.`},
     96 		{`\. .`, `. .`},
     97 		{
     98 			`The \3c i\3equick\3c/i\3e,\d\A\3cspan style=\27 color:brown\27\3e brown\3c/span\3e  fox jumps\2028over the \3c canine class=\22lazy\22 \3e dog\3c/canine\3e`,
     99 			"The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>",
    100 		},
    101 	}
    102 	for _, test := range tests {
    103 		got1 := string(decodeCSS([]byte(test.css)))
    104 		if got1 != test.want {
    105 			t.Errorf("%q: want\n\t%q\nbut got\n\t%q", test.css, test.want, got1)
    106 		}
    107 		recoded := cssEscaper(got1)
    108 		if got2 := string(decodeCSS([]byte(recoded))); got2 != test.want {
    109 			t.Errorf("%q: escape & decode not dual for %q", test.css, recoded)
    110 		}
    111 	}
    112 }
    113 
    114 func TestHexDecode(t *testing.T) {
    115 	for i := 0; i < 0x200000; i += 101 /* coprime with 16 */ {
    116 		s := strconv.FormatInt(int64(i), 16)
    117 		if got := int(hexDecode([]byte(s))); got != i {
    118 			t.Errorf("%s: want %d but got %d", s, i, got)
    119 		}
    120 		s = strings.ToUpper(s)
    121 		if got := int(hexDecode([]byte(s))); got != i {
    122 			t.Errorf("%s: want %d but got %d", s, i, got)
    123 		}
    124 	}
    125 }
    126 
    127 func TestSkipCSSSpace(t *testing.T) {
    128 	tests := []struct {
    129 		css, want string
    130 	}{
    131 		{"", ""},
    132 		{"foo", "foo"},
    133 		{"\n", ""},
    134 		{"\r\n", ""},
    135 		{"\r", ""},
    136 		{"\t", ""},
    137 		{" ", ""},
    138 		{"\f", ""},
    139 		{" foo", "foo"},
    140 		{"  foo", " foo"},
    141 		{`\20`, `\20`},
    142 	}
    143 	for _, test := range tests {
    144 		got := string(skipCSSSpace([]byte(test.css)))
    145 		if got != test.want {
    146 			t.Errorf("%q: want %q but got %q", test.css, test.want, got)
    147 		}
    148 	}
    149 }
    150 
    151 func TestCSSEscaper(t *testing.T) {
    152 	input := ("\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f" +
    153 		"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
    154 		` !"#$%&'()*+,-./` +
    155 		`0123456789:;<=>?` +
    156 		`@ABCDEFGHIJKLMNO` +
    157 		`PQRSTUVWXYZ[\]^_` +
    158 		"`abcdefghijklmno" +
    159 		"pqrstuvwxyz{|}~\x7f" +
    160 		"\u00A0\u0100\u2028\u2029\ufeff\U0001D11E")
    161 
    162 	want := ("\\0\x01\x02\x03\x04\x05\x06\x07" +
    163 		"\x08\\9 \\a\x0b\\c \\d\x0E\x0F" +
    164 		"\x10\x11\x12\x13\x14\x15\x16\x17" +
    165 		"\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
    166 		` !\22#$%\26\27\28\29*\2b,-.\2f ` +
    167 		`0123456789\3a\3b\3c=\3e?` +
    168 		`@ABCDEFGHIJKLMNO` +
    169 		`PQRSTUVWXYZ[\\]^_` +
    170 		"`abcdefghijklmno" +
    171 		`pqrstuvwxyz\7b|\7d~` + "\u007f" +
    172 		"\u00A0\u0100\u2028\u2029\ufeff\U0001D11E")
    173 
    174 	got := cssEscaper(input)
    175 	if got != want {
    176 		t.Errorf("encode: want\n\t%q\nbut got\n\t%q", want, got)
    177 	}
    178 
    179 	got = string(decodeCSS([]byte(got)))
    180 	if input != got {
    181 		t.Errorf("decode: want\n\t%q\nbut got\n\t%q", input, got)
    182 	}
    183 }
    184 
    185 func TestCSSValueFilter(t *testing.T) {
    186 	tests := []struct {
    187 		css, want string
    188 	}{
    189 		{"", ""},
    190 		{"foo", "foo"},
    191 		{"0", "0"},
    192 		{"0px", "0px"},
    193 		{"-5px", "-5px"},
    194 		{"1.25in", "1.25in"},
    195 		{"+.33em", "+.33em"},
    196 		{"100%", "100%"},
    197 		{"12.5%", "12.5%"},
    198 		{".foo", ".foo"},
    199 		{"#bar", "#bar"},
    200 		{"corner-radius", "corner-radius"},
    201 		{"-moz-corner-radius", "-moz-corner-radius"},
    202 		{"#000", "#000"},
    203 		{"#48f", "#48f"},
    204 		{"#123456", "#123456"},
    205 		{"U+00-FF, U+980-9FF", "U+00-FF, U+980-9FF"},
    206 		{"color: red", "color: red"},
    207 		{"<!--", "ZgotmplZ"},
    208 		{"-->", "ZgotmplZ"},
    209 		{"<![CDATA[", "ZgotmplZ"},
    210 		{"]]>", "ZgotmplZ"},
    211 		{"</style", "ZgotmplZ"},
    212 		{`"`, "ZgotmplZ"},
    213 		{`'`, "ZgotmplZ"},
    214 		{"`", "ZgotmplZ"},
    215 		{"\x00", "ZgotmplZ"},
    216 		{"/* foo */", "ZgotmplZ"},
    217 		{"//", "ZgotmplZ"},
    218 		{"[href=~", "ZgotmplZ"},
    219 		{"expression(alert(1337))", "ZgotmplZ"},
    220 		{"-expression(alert(1337))", "ZgotmplZ"},
    221 		{"expression", "ZgotmplZ"},
    222 		{"Expression", "ZgotmplZ"},
    223 		{"EXPRESSION", "ZgotmplZ"},
    224 		{"-moz-binding", "ZgotmplZ"},
    225 		{"-expr\x00ession(alert(1337))", "ZgotmplZ"},
    226 		{`-expr\0ession(alert(1337))`, "ZgotmplZ"},
    227 		{`-express\69on(alert(1337))`, "ZgotmplZ"},
    228 		{`-express\69 on(alert(1337))`, "ZgotmplZ"},
    229 		{`-exp\72 ession(alert(1337))`, "ZgotmplZ"},
    230 		{`-exp\52 ession(alert(1337))`, "ZgotmplZ"},
    231 		{`-exp\000052 ession(alert(1337))`, "ZgotmplZ"},
    232 		{`-expre\0000073sion`, "-expre\x073sion"},
    233 		{`@import url evil.css`, "ZgotmplZ"},
    234 	}
    235 	for _, test := range tests {
    236 		got := cssValueFilter(test.css)
    237 		if got != test.want {
    238 			t.Errorf("%q: want %q but got %q", test.css, test.want, got)
    239 		}
    240 	}
    241 }
    242 
    243 func BenchmarkCSSEscaper(b *testing.B) {
    244 	for i := 0; i < b.N; i++ {
    245 		cssEscaper("The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>")
    246 	}
    247 }
    248 
    249 func BenchmarkCSSEscaperNoSpecials(b *testing.B) {
    250 	for i := 0; i < b.N; i++ {
    251 		cssEscaper("The quick, brown fox jumps over the lazy dog.")
    252 	}
    253 }
    254 
    255 func BenchmarkDecodeCSS(b *testing.B) {
    256 	s := []byte(`The \3c i\3equick\3c/i\3e,\d\A\3cspan style=\27 color:brown\27\3e brown\3c/span\3e fox jumps\2028over the \3c canine class=\22lazy\22 \3edog\3c/canine\3e`)
    257 	b.ResetTimer()
    258 	for i := 0; i < b.N; i++ {
    259 		decodeCSS(s)
    260 	}
    261 }
    262 
    263 func BenchmarkDecodeCSSNoSpecials(b *testing.B) {
    264 	s := []byte("The quick, brown fox jumps over the lazy dog.")
    265 	b.ResetTimer()
    266 	for i := 0; i < b.N; i++ {
    267 		decodeCSS(s)
    268 	}
    269 }
    270 
    271 func BenchmarkCSSValueFilter(b *testing.B) {
    272 	for i := 0; i < b.N; i++ {
    273 		cssValueFilter(`  e\78preS\0Sio/**/n(alert(1337))`)
    274 	}
    275 }
    276 
    277 func BenchmarkCSSValueFilterOk(b *testing.B) {
    278 	for i := 0; i < b.N; i++ {
    279 		cssValueFilter(`Times New Roman`)
    280 	}
    281 }
    282