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