1// Copyright 2009 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 strconv_test 6 7import ( 8 . "strconv" 9 "strings" 10 "testing" 11 "unicode" 12) 13 14// Verify that our IsPrint agrees with unicode.IsPrint. 15func TestIsPrint(t *testing.T) { 16 n := 0 17 for r := rune(0); r <= unicode.MaxRune; r++ { 18 if IsPrint(r) != unicode.IsPrint(r) { 19 t.Errorf("IsPrint(%U)=%t incorrect", r, IsPrint(r)) 20 n++ 21 if n > 10 { 22 return 23 } 24 } 25 } 26} 27 28// Verify that our IsGraphic agrees with unicode.IsGraphic. 29func TestIsGraphic(t *testing.T) { 30 n := 0 31 for r := rune(0); r <= unicode.MaxRune; r++ { 32 if IsGraphic(r) != unicode.IsGraphic(r) { 33 t.Errorf("IsGraphic(%U)=%t incorrect", r, IsGraphic(r)) 34 n++ 35 if n > 10 { 36 return 37 } 38 } 39 } 40} 41 42type quoteTest struct { 43 in string 44 out string 45 ascii string 46 graphic string 47} 48 49var quotetests = []quoteTest{ 50 {"\a\b\f\r\n\t\v", `"\a\b\f\r\n\t\v"`, `"\a\b\f\r\n\t\v"`, `"\a\b\f\r\n\t\v"`}, 51 {"\\", `"\\"`, `"\\"`, `"\\"`}, 52 {"abc\xffdef", `"abc\xffdef"`, `"abc\xffdef"`, `"abc\xffdef"`}, 53 {"\u263a", `"☺"`, `"\u263a"`, `"☺"`}, 54 {"\U0010ffff", `"\U0010ffff"`, `"\U0010ffff"`, `"\U0010ffff"`}, 55 {"\x04", `"\x04"`, `"\x04"`, `"\x04"`}, 56 // Some non-printable but graphic runes. Final column is double-quoted. 57 {"!\u00a0!\u2000!\u3000!", `"!\u00a0!\u2000!\u3000!"`, `"!\u00a0!\u2000!\u3000!"`, "\"!\u00a0!\u2000!\u3000!\""}, 58 {"\x7f", `"\x7f"`, `"\x7f"`, `"\x7f"`}, 59} 60 61func TestQuote(t *testing.T) { 62 for _, tt := range quotetests { 63 if out := Quote(tt.in); out != tt.out { 64 t.Errorf("Quote(%s) = %s, want %s", tt.in, out, tt.out) 65 } 66 if out := AppendQuote([]byte("abc"), tt.in); string(out) != "abc"+tt.out { 67 t.Errorf("AppendQuote(%q, %s) = %s, want %s", "abc", tt.in, out, "abc"+tt.out) 68 } 69 } 70} 71 72func TestQuoteToASCII(t *testing.T) { 73 for _, tt := range quotetests { 74 if out := QuoteToASCII(tt.in); out != tt.ascii { 75 t.Errorf("QuoteToASCII(%s) = %s, want %s", tt.in, out, tt.ascii) 76 } 77 if out := AppendQuoteToASCII([]byte("abc"), tt.in); string(out) != "abc"+tt.ascii { 78 t.Errorf("AppendQuoteToASCII(%q, %s) = %s, want %s", "abc", tt.in, out, "abc"+tt.ascii) 79 } 80 } 81} 82 83func TestQuoteToGraphic(t *testing.T) { 84 for _, tt := range quotetests { 85 if out := QuoteToGraphic(tt.in); out != tt.graphic { 86 t.Errorf("QuoteToGraphic(%s) = %s, want %s", tt.in, out, tt.graphic) 87 } 88 if out := AppendQuoteToGraphic([]byte("abc"), tt.in); string(out) != "abc"+tt.graphic { 89 t.Errorf("AppendQuoteToGraphic(%q, %s) = %s, want %s", "abc", tt.in, out, "abc"+tt.graphic) 90 } 91 } 92} 93 94func BenchmarkQuote(b *testing.B) { 95 for i := 0; i < b.N; i++ { 96 Quote("\a\b\f\r\n\t\v\a\b\f\r\n\t\v\a\b\f\r\n\t\v") 97 } 98} 99 100func BenchmarkQuoteRune(b *testing.B) { 101 for i := 0; i < b.N; i++ { 102 QuoteRune('\a') 103 } 104} 105 106var benchQuoteBuf []byte 107 108func BenchmarkAppendQuote(b *testing.B) { 109 for i := 0; i < b.N; i++ { 110 benchQuoteBuf = AppendQuote(benchQuoteBuf[:0], "\a\b\f\r\n\t\v\a\b\f\r\n\t\v\a\b\f\r\n\t\v") 111 } 112} 113 114var benchQuoteRuneBuf []byte 115 116func BenchmarkAppendQuoteRune(b *testing.B) { 117 for i := 0; i < b.N; i++ { 118 benchQuoteRuneBuf = AppendQuoteRune(benchQuoteRuneBuf[:0], '\a') 119 } 120} 121 122type quoteRuneTest struct { 123 in rune 124 out string 125 ascii string 126 graphic string 127} 128 129var quoterunetests = []quoteRuneTest{ 130 {'a', `'a'`, `'a'`, `'a'`}, 131 {'\a', `'\a'`, `'\a'`, `'\a'`}, 132 {'\\', `'\\'`, `'\\'`, `'\\'`}, 133 {0xFF, `'ÿ'`, `'\u00ff'`, `'ÿ'`}, 134 {0x263a, `'☺'`, `'\u263a'`, `'☺'`}, 135 {0xdead, `'�'`, `'\ufffd'`, `'�'`}, 136 {0xfffd, `'�'`, `'\ufffd'`, `'�'`}, 137 {0x0010ffff, `'\U0010ffff'`, `'\U0010ffff'`, `'\U0010ffff'`}, 138 {0x0010ffff + 1, `'�'`, `'\ufffd'`, `'�'`}, 139 {0x04, `'\x04'`, `'\x04'`, `'\x04'`}, 140 // Some differences between graphic and printable. Note the last column is double-quoted. 141 {'\u00a0', `'\u00a0'`, `'\u00a0'`, "'\u00a0'"}, 142 {'\u2000', `'\u2000'`, `'\u2000'`, "'\u2000'"}, 143 {'\u3000', `'\u3000'`, `'\u3000'`, "'\u3000'"}, 144} 145 146func TestQuoteRune(t *testing.T) { 147 for _, tt := range quoterunetests { 148 if out := QuoteRune(tt.in); out != tt.out { 149 t.Errorf("QuoteRune(%U) = %s, want %s", tt.in, out, tt.out) 150 } 151 if out := AppendQuoteRune([]byte("abc"), tt.in); string(out) != "abc"+tt.out { 152 t.Errorf("AppendQuoteRune(%q, %U) = %s, want %s", "abc", tt.in, out, "abc"+tt.out) 153 } 154 } 155} 156 157func TestQuoteRuneToASCII(t *testing.T) { 158 for _, tt := range quoterunetests { 159 if out := QuoteRuneToASCII(tt.in); out != tt.ascii { 160 t.Errorf("QuoteRuneToASCII(%U) = %s, want %s", tt.in, out, tt.ascii) 161 } 162 if out := AppendQuoteRuneToASCII([]byte("abc"), tt.in); string(out) != "abc"+tt.ascii { 163 t.Errorf("AppendQuoteRuneToASCII(%q, %U) = %s, want %s", "abc", tt.in, out, "abc"+tt.ascii) 164 } 165 } 166} 167 168func TestQuoteRuneToGraphic(t *testing.T) { 169 for _, tt := range quoterunetests { 170 if out := QuoteRuneToGraphic(tt.in); out != tt.graphic { 171 t.Errorf("QuoteRuneToGraphic(%U) = %s, want %s", tt.in, out, tt.graphic) 172 } 173 if out := AppendQuoteRuneToGraphic([]byte("abc"), tt.in); string(out) != "abc"+tt.graphic { 174 t.Errorf("AppendQuoteRuneToGraphic(%q, %U) = %s, want %s", "abc", tt.in, out, "abc"+tt.graphic) 175 } 176 } 177} 178 179type canBackquoteTest struct { 180 in string 181 out bool 182} 183 184var canbackquotetests = []canBackquoteTest{ 185 {"`", false}, 186 {string(rune(0)), false}, 187 {string(rune(1)), false}, 188 {string(rune(2)), false}, 189 {string(rune(3)), false}, 190 {string(rune(4)), false}, 191 {string(rune(5)), false}, 192 {string(rune(6)), false}, 193 {string(rune(7)), false}, 194 {string(rune(8)), false}, 195 {string(rune(9)), true}, // \t 196 {string(rune(10)), false}, 197 {string(rune(11)), false}, 198 {string(rune(12)), false}, 199 {string(rune(13)), false}, 200 {string(rune(14)), false}, 201 {string(rune(15)), false}, 202 {string(rune(16)), false}, 203 {string(rune(17)), false}, 204 {string(rune(18)), false}, 205 {string(rune(19)), false}, 206 {string(rune(20)), false}, 207 {string(rune(21)), false}, 208 {string(rune(22)), false}, 209 {string(rune(23)), false}, 210 {string(rune(24)), false}, 211 {string(rune(25)), false}, 212 {string(rune(26)), false}, 213 {string(rune(27)), false}, 214 {string(rune(28)), false}, 215 {string(rune(29)), false}, 216 {string(rune(30)), false}, 217 {string(rune(31)), false}, 218 {string(rune(0x7F)), false}, 219 {`' !"#$%&'()*+,-./:;<=>?@[\]^_{|}~`, true}, 220 {`0123456789`, true}, 221 {`ABCDEFGHIJKLMNOPQRSTUVWXYZ`, true}, 222 {`abcdefghijklmnopqrstuvwxyz`, true}, 223 {`☺`, true}, 224 {"\x80", false}, 225 {"a\xe0\xa0z", false}, 226 {"\ufeffabc", false}, 227 {"a\ufeffz", false}, 228} 229 230func TestCanBackquote(t *testing.T) { 231 for _, tt := range canbackquotetests { 232 if out := CanBackquote(tt.in); out != tt.out { 233 t.Errorf("CanBackquote(%q) = %v, want %v", tt.in, out, tt.out) 234 } 235 } 236} 237 238type unQuoteTest struct { 239 in string 240 out string 241} 242 243var unquotetests = []unQuoteTest{ 244 {`""`, ""}, 245 {`"a"`, "a"}, 246 {`"abc"`, "abc"}, 247 {`"☺"`, "☺"}, 248 {`"hello world"`, "hello world"}, 249 {`"\xFF"`, "\xFF"}, 250 {`"\377"`, "\377"}, 251 {`"\u1234"`, "\u1234"}, 252 {`"\U00010111"`, "\U00010111"}, 253 {`"\U0001011111"`, "\U0001011111"}, 254 {`"\a\b\f\n\r\t\v\\\""`, "\a\b\f\n\r\t\v\\\""}, 255 {`"'"`, "'"}, 256 257 {`'a'`, "a"}, 258 {`'☹'`, "☹"}, 259 {`'\a'`, "\a"}, 260 {`'\x10'`, "\x10"}, 261 {`'\377'`, "\377"}, 262 {`'\u1234'`, "\u1234"}, 263 {`'\U00010111'`, "\U00010111"}, 264 {`'\t'`, "\t"}, 265 {`' '`, " "}, 266 {`'\''`, "'"}, 267 {`'"'`, "\""}, 268 269 {"``", ``}, 270 {"`a`", `a`}, 271 {"`abc`", `abc`}, 272 {"`☺`", `☺`}, 273 {"`hello world`", `hello world`}, 274 {"`\\xFF`", `\xFF`}, 275 {"`\\377`", `\377`}, 276 {"`\\`", `\`}, 277 {"`\n`", "\n"}, 278 {"` `", ` `}, 279 {"` `", ` `}, 280 {"`a\rb`", "ab"}, 281} 282 283var misquoted = []string{ 284 ``, 285 `"`, 286 `"a`, 287 `"'`, 288 `b"`, 289 `"\"`, 290 `"\9"`, 291 `"\19"`, 292 `"\129"`, 293 `'\'`, 294 `'\9'`, 295 `'\19'`, 296 `'\129'`, 297 `'ab'`, 298 `"\x1!"`, 299 `"\U12345678"`, 300 `"\z"`, 301 "`", 302 "`xxx", 303 "``x\r", 304 "`\"", 305 `"\'"`, 306 `'\"'`, 307 "\"\n\"", 308 "\"\\n\n\"", 309 "'\n'", 310 `"\udead"`, 311 `"\ud83d\ude4f"`, 312} 313 314func TestUnquote(t *testing.T) { 315 for _, tt := range unquotetests { 316 testUnquote(t, tt.in, tt.out, nil) 317 } 318 for _, tt := range quotetests { 319 testUnquote(t, tt.out, tt.in, nil) 320 } 321 for _, s := range misquoted { 322 testUnquote(t, s, "", ErrSyntax) 323 } 324} 325 326// Issue 23685: invalid UTF-8 should not go through the fast path. 327func TestUnquoteInvalidUTF8(t *testing.T) { 328 tests := []struct { 329 in string 330 331 // one of: 332 want string 333 wantErr error 334 }{ 335 {in: `"foo"`, want: "foo"}, 336 {in: `"foo`, wantErr: ErrSyntax}, 337 {in: `"` + "\xc0" + `"`, want: "\xef\xbf\xbd"}, 338 {in: `"a` + "\xc0" + `"`, want: "a\xef\xbf\xbd"}, 339 {in: `"\t` + "\xc0" + `"`, want: "\t\xef\xbf\xbd"}, 340 } 341 for _, tt := range tests { 342 testUnquote(t, tt.in, tt.want, tt.wantErr) 343 } 344} 345 346func testUnquote(t *testing.T, in, want string, wantErr error) { 347 // Test Unquote. 348 got, gotErr := Unquote(in) 349 if got != want || gotErr != wantErr { 350 t.Errorf("Unquote(%q) = (%q, %v), want (%q, %v)", in, got, gotErr, want, wantErr) 351 } 352 353 // Test QuotedPrefix. 354 // Adding an arbitrary suffix should not change the result of QuotedPrefix 355 // assume that the suffix doesn't accidentally terminate a truncated input. 356 if gotErr == nil { 357 want = in 358 } 359 suffix := "\n\r\\\"`'" // special characters for quoted strings 360 if len(in) > 0 { 361 suffix = strings.ReplaceAll(suffix, in[:1], "") 362 } 363 in += suffix 364 got, gotErr = QuotedPrefix(in) 365 if gotErr == nil && wantErr != nil { 366 _, wantErr = Unquote(got) // original input had trailing junk, reparse with only valid prefix 367 want = got 368 } 369 if got != want || gotErr != wantErr { 370 t.Errorf("QuotedPrefix(%q) = (%q, %v), want (%q, %v)", in, got, gotErr, want, wantErr) 371 } 372} 373 374func BenchmarkUnquoteEasy(b *testing.B) { 375 for i := 0; i < b.N; i++ { 376 Unquote(`"Give me a rock, paper and scissors and I will move the world."`) 377 } 378} 379 380func BenchmarkUnquoteHard(b *testing.B) { 381 for i := 0; i < b.N; i++ { 382 Unquote(`"\x47ive me a \x72ock, \x70aper and \x73cissors and \x49 will move the world."`) 383 } 384} 385