github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/gnovm/stdlibs/unicode/utf8/utf8_test.gno (about) 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 5 package utf8_test 6 7 import ( 8 "bytes" 9 "testing" 10 "unicode" 11 "unicode/utf8" 12 ) 13 14 // Validate the constants redefined from unicode. 15 func init() { 16 if utf8.MaxRune != unicode.MaxRune { 17 panic("utf8.MaxRune is wrong") 18 } 19 if utf8.RuneError != unicode.ReplacementChar { 20 panic("utf8.RuneError is wrong") 21 } 22 } 23 24 // Validate the constants redefined from unicode. 25 func TestConstants(t *testing.T) { 26 if utf8.MaxRune != unicode.MaxRune { 27 t.Errorf("utf8.MaxRune is wrong: %x should be %x", utf8.MaxRune, unicode.MaxRune) 28 } 29 if utf8.RuneError != unicode.ReplacementChar { 30 t.Errorf("utf8.RuneError is wrong: %x should be %x", utf8.RuneError, unicode.ReplacementChar) 31 } 32 } 33 34 type Utf8Map struct { 35 r rune 36 str string 37 } 38 39 var utf8map = []Utf8Map{ 40 {0x0000, "\x00"}, 41 {0x0001, "\x01"}, 42 {0x007e, "\x7e"}, 43 {0x007f, "\x7f"}, 44 {0x0080, "\xc2\x80"}, 45 {0x0081, "\xc2\x81"}, 46 {0x00bf, "\xc2\xbf"}, 47 {0x00c0, "\xc3\x80"}, 48 {0x00c1, "\xc3\x81"}, 49 {0x00c8, "\xc3\x88"}, 50 {0x00d0, "\xc3\x90"}, 51 {0x00e0, "\xc3\xa0"}, 52 {0x00f0, "\xc3\xb0"}, 53 {0x00f8, "\xc3\xb8"}, 54 {0x00ff, "\xc3\xbf"}, 55 {0x0100, "\xc4\x80"}, 56 {0x07ff, "\xdf\xbf"}, 57 {0x0400, "\xd0\x80"}, 58 {0x0800, "\xe0\xa0\x80"}, 59 {0x0801, "\xe0\xa0\x81"}, 60 {0x1000, "\xe1\x80\x80"}, 61 {0xd000, "\xed\x80\x80"}, 62 {0xd7ff, "\xed\x9f\xbf"}, // last code point before surrogate half. 63 {0xe000, "\xee\x80\x80"}, // first code point after surrogate half. 64 {0xfffe, "\xef\xbf\xbe"}, 65 {0xffff, "\xef\xbf\xbf"}, 66 {0x10000, "\xf0\x90\x80\x80"}, 67 {0x10001, "\xf0\x90\x80\x81"}, 68 {0x40000, "\xf1\x80\x80\x80"}, 69 {0x10fffe, "\xf4\x8f\xbf\xbe"}, 70 {0x10ffff, "\xf4\x8f\xbf\xbf"}, 71 {0xFFFD, "\xef\xbf\xbd"}, 72 } 73 74 var surrogateMap = []Utf8Map{ 75 {0xd800, "\xed\xa0\x80"}, // surrogate min decodes to (RuneError, 1) 76 {0xdfff, "\xed\xbf\xbf"}, // surrogate max decodes to (RuneError, 1) 77 } 78 79 var testStrings = []string{ 80 "", 81 "abcd", 82 "☺☻☹", 83 "日a本b語ç日ð本Ê語þ日¥本¼語i日©", 84 "日a本b語ç日ð本Ê語þ日¥本¼語i日©日a本b語ç日ð本Ê語þ日¥本¼語i日©日a本b語ç日ð本Ê語þ日¥本¼語i日©", 85 "\x80\x80\x80\x80", 86 } 87 88 func TestFullRune(t *testing.T) { 89 for _, m := range utf8map { 90 b := []byte(m.str) 91 if !utf8.FullRune(b) { 92 t.Errorf("FullRune(%q) (%U) = false, want true", b, m.r) 93 } 94 s := m.str 95 if !utf8.FullRuneInString(s) { 96 t.Errorf("FullRuneInString(%q) (%U) = false, want true", s, m.r) 97 } 98 b1 := b[0 : len(b)-1] 99 if utf8.FullRune(b1) { 100 t.Errorf("FullRune(%q) = true, want false", b1) 101 } 102 s1 := string(b1) 103 if utf8.FullRuneInString(s1) { 104 t.Errorf("FullRune(%q) = true, want false", s1) 105 } 106 } 107 for _, s := range []string{"\xc0", "\xc1"} { 108 b := []byte(s) 109 if !utf8.FullRune(b) { 110 t.Errorf("FullRune(%q) = false, want true", s) 111 } 112 if !utf8.FullRuneInString(s) { 113 t.Errorf("FullRuneInString(%q) = false, want true", s) 114 } 115 } 116 } 117 118 func TestEncodeRune(t *testing.T) { 119 for _, m := range utf8map { 120 b := []byte(m.str) 121 var buf [10]byte 122 n := utf8.EncodeRune(buf[0:], m.r) 123 b1 := buf[0:n] 124 if !bytes.Equal(b, b1) { 125 t.Errorf("EncodeRune(%#04x) = %q want %q", m.r, b1, b) 126 } 127 } 128 } 129 130 func TestDecodeRune(t *testing.T) { 131 for _, m := range utf8map { 132 b := []byte(m.str) 133 r, size := utf8.DecodeRune(b) 134 if r != m.r || size != len(b) { 135 t.Errorf("DecodeRune(%q) = %#04x, %d want %#04x, %d", b, r, size, m.r, len(b)) 136 } 137 s := m.str 138 r, size = utf8.DecodeRuneInString(s) 139 if r != m.r || size != len(b) { 140 t.Errorf("DecodeRuneInString(%q) = %#04x, %d want %#04x, %d", s, r, size, m.r, len(b)) 141 } 142 143 // there's an extra byte that bytes left behind - make sure trailing byte works 144 r, size = utf8.DecodeRune(b[0:cap(b)]) 145 if r != m.r || size != len(b) { 146 t.Errorf("DecodeRune(%q) = %#04x, %d want %#04x, %d", b, r, size, m.r, len(b)) 147 } 148 s = m.str + "\x00" 149 r, size = utf8.DecodeRuneInString(s) 150 if r != m.r || size != len(b) { 151 t.Errorf("DecodeRuneInString(%q) = %#04x, %d want %#04x, %d", s, r, size, m.r, len(b)) 152 } 153 154 // make sure missing bytes fail 155 wantsize := 1 156 if wantsize >= len(b) { 157 wantsize = 0 158 } 159 r, size = utf8.DecodeRune(b[0 : len(b)-1]) 160 if r != utf8.RuneError || size != wantsize { 161 t.Errorf("DecodeRune(%q) = %#04x, %d want %#04x, %d", b[0:len(b)-1], r, size, utf8.RuneError, wantsize) 162 } 163 s = m.str[0 : len(m.str)-1] 164 r, size = utf8.DecodeRuneInString(s) 165 if r != utf8.RuneError || size != wantsize { 166 t.Errorf("DecodeRuneInString(%q) = %#04x, %d want %#04x, %d", s, r, size, utf8.RuneError, wantsize) 167 } 168 169 // make sure bad sequences fail 170 if len(b) == 1 { 171 b[0] = 0x80 172 } else { 173 b[len(b)-1] = 0x7F 174 } 175 r, size = utf8.DecodeRune(b) 176 if r != utf8.RuneError || size != 1 { 177 t.Errorf("DecodeRune(%q) = %#04x, %d want %#04x, %d", b, r, size, utf8.RuneError, 1) 178 } 179 s = string(b) 180 r, size = utf8.DecodeRuneInString(s) 181 if r != utf8.RuneError || size != 1 { 182 t.Errorf("DecodeRuneInString(%q) = %#04x, %d want %#04x, %d", s, r, size, utf8.RuneError, 1) 183 } 184 } 185 } 186 187 func TestDecodeSurrogateRune(t *testing.T) { 188 for _, m := range surrogateMap { 189 b := []byte(m.str) 190 r, size := utf8.DecodeRune(b) 191 if r != utf8.RuneError || size != 1 { 192 t.Errorf("DecodeRune(%q) = %x, %d want %x, %d", b, r, size, utf8.RuneError, 1) 193 } 194 s := m.str 195 r, size = utf8.DecodeRuneInString(s) 196 if r != utf8.RuneError || size != 1 { 197 t.Errorf("DecodeRuneInString(%q) = %x, %d want %x, %d", b, r, size, utf8.RuneError, 1) 198 } 199 } 200 } 201 202 // Check that DecodeRune and DecodeLastRune correspond to 203 // the equivalent range loop. 204 func TestSequencing(t *testing.T) { 205 for _, ts := range testStrings { 206 for _, m := range utf8map { 207 for _, s := range []string{ts + m.str, m.str + ts, ts + m.str + ts} { 208 testSequence(t, s) 209 } 210 } 211 } 212 } 213 214 func runtimeRuneCount(s string) int { 215 return len([]rune(s)) // Replaced by gc with call to runtime.countrunes(s). 216 } 217 218 // Check that a range loop, len([]rune(string)) optimization and 219 // []rune conversions visit the same runes. 220 // Not really a test of this package, but the assumption is used here and 221 // it's good to verify. 222 func TestRuntimeConversion(t *testing.T) { 223 for _, ts := range testStrings { 224 count := utf8.RuneCountInString(ts) 225 if n := runtimeRuneCount(ts); n != count { 226 t.Errorf("%q: len([]rune()) counted %d runes; got %d from RuneCountInString", ts, n, count) 227 break 228 } 229 230 runes := []rune(ts) 231 if n := len(runes); n != count { 232 t.Errorf("%q: []rune() has length %d; got %d from RuneCountInString", ts, n, count) 233 break 234 } 235 i := 0 236 for _, r := range ts { 237 if r != runes[i] { 238 t.Errorf("%q[%d]: expected %c (%U); got %c (%U)", ts, i, runes[i], runes[i], r, r) 239 } 240 i++ 241 } 242 } 243 } 244 245 var invalidSequenceTests = []string{ 246 "\xed\xa0\x80\x80", // surrogate min 247 "\xed\xbf\xbf\x80", // surrogate max 248 249 // xx 250 "\x91\x80\x80\x80", 251 252 // s1 253 "\xC2\x7F\x80\x80", 254 "\xC2\xC0\x80\x80", 255 "\xDF\x7F\x80\x80", 256 "\xDF\xC0\x80\x80", 257 258 // s2 259 "\xE0\x9F\xBF\x80", 260 "\xE0\xA0\x7F\x80", 261 "\xE0\xBF\xC0\x80", 262 "\xE0\xC0\x80\x80", 263 264 // s3 265 "\xE1\x7F\xBF\x80", 266 "\xE1\x80\x7F\x80", 267 "\xE1\xBF\xC0\x80", 268 "\xE1\xC0\x80\x80", 269 270 // s4 271 "\xED\x7F\xBF\x80", 272 "\xED\x80\x7F\x80", 273 "\xED\x9F\xC0\x80", 274 "\xED\xA0\x80\x80", 275 276 // s5 277 "\xF0\x8F\xBF\xBF", 278 "\xF0\x90\x7F\xBF", 279 "\xF0\x90\x80\x7F", 280 "\xF0\xBF\xBF\xC0", 281 "\xF0\xBF\xC0\x80", 282 "\xF0\xC0\x80\x80", 283 284 // s6 285 "\xF1\x7F\xBF\xBF", 286 "\xF1\x80\x7F\xBF", 287 "\xF1\x80\x80\x7F", 288 "\xF1\xBF\xBF\xC0", 289 "\xF1\xBF\xC0\x80", 290 "\xF1\xC0\x80\x80", 291 292 // s7 293 "\xF4\x7F\xBF\xBF", 294 "\xF4\x80\x7F\xBF", 295 "\xF4\x80\x80\x7F", 296 "\xF4\x8F\xBF\xC0", 297 "\xF4\x8F\xC0\x80", 298 "\xF4\x90\x80\x80", 299 } 300 301 func runtimeDecodeRune(s string) rune { 302 for _, r := range s { 303 return r 304 } 305 return -1 306 } 307 308 func TestDecodeInvalidSequence(t *testing.T) { 309 for _, s := range invalidSequenceTests { 310 r1, _ := utf8.DecodeRune([]byte(s)) 311 if want := utf8.RuneError; r1 != want { 312 t.Errorf("DecodeRune(%#x) = %#04x, want %#04x", s, r1, want) 313 return 314 } 315 r2, _ := utf8.DecodeRuneInString(s) 316 if want := utf8.RuneError; r2 != want { 317 t.Errorf("DecodeRuneInString(%q) = %#04x, want %#04x", s, r2, want) 318 return 319 } 320 if r1 != r2 { 321 t.Errorf("DecodeRune(%#x) = %#04x mismatch with DecodeRuneInString(%q) = %#04x", s, r1, s, r2) 322 return 323 } 324 r3 := runtimeDecodeRune(s) 325 if r2 != r3 { 326 t.Errorf("DecodeRuneInString(%q) = %#04x mismatch with runtime.decoderune(%q) = %#04x", s, r2, s, r3) 327 return 328 } 329 } 330 } 331 332 func testSequence(t *testing.T, s string) { 333 type info struct { 334 index int 335 r rune 336 } 337 index := make([]info, len(s)) 338 b := []byte(s) 339 si := 0 340 j := 0 341 for i, r := range s { 342 if si != i { 343 t.Errorf("Sequence(%q) mismatched index %d, want %d", s, si, i) 344 return 345 } 346 index[j] = info{i, r} 347 j++ 348 r1, size1 := utf8.DecodeRune(b[i:]) 349 if r != r1 { 350 t.Errorf("DecodeRune(%q) = %#04x, want %#04x", s[i:], r1, r) 351 return 352 } 353 r2, size2 := utf8.DecodeRuneInString(s[i:]) 354 if r != r2 { 355 t.Errorf("DecodeRuneInString(%q) = %#04x, want %#04x", s[i:], r2, r) 356 return 357 } 358 if size1 != size2 { 359 t.Errorf("DecodeRune/DecodeRuneInString(%q) size mismatch %d/%d", s[i:], size1, size2) 360 return 361 } 362 si += size1 363 } 364 j-- 365 for si = len(s); si > 0; { 366 r1, size1 := utf8.DecodeLastRune(b[0:si]) 367 r2, size2 := utf8.DecodeLastRuneInString(s[0:si]) 368 if size1 != size2 { 369 t.Errorf("DecodeLastRune/DecodeLastRuneInString(%q, %d) size mismatch %d/%d", s, si, size1, size2) 370 return 371 } 372 if r1 != index[j].r { 373 t.Errorf("DecodeLastRune(%q, %d) = %#04x, want %#04x", s, si, r1, index[j].r) 374 return 375 } 376 if r2 != index[j].r { 377 t.Errorf("DecodeLastRuneInString(%q, %d) = %#04x, want %#04x", s, si, r2, index[j].r) 378 return 379 } 380 si -= size1 381 if si != index[j].index { 382 t.Errorf("DecodeLastRune(%q) index mismatch at %d, want %d", s, si, index[j].index) 383 return 384 } 385 j-- 386 } 387 if si != 0 { 388 t.Errorf("DecodeLastRune(%q) finished at %d, not 0", s, si) 389 } 390 } 391 392 // Check that negative runes encode as U+FFFD. 393 func TestNegativeRune(t *testing.T) { 394 errorbuf := make([]byte, utf8.UTFMax) 395 errorbuf = errorbuf[0:utf8.EncodeRune(errorbuf, utf8.RuneError)] 396 buf := make([]byte, utf8.UTFMax) 397 buf = buf[0:utf8.EncodeRune(buf, -1)] 398 if !bytes.Equal(buf, errorbuf) { 399 t.Errorf("incorrect encoding [% x] for -1; expected [% x]", buf, errorbuf) 400 } 401 } 402 403 type RuneCountTest struct { 404 in string 405 out int 406 } 407 408 var runecounttests = []RuneCountTest{ 409 {"abcd", 4}, 410 {"☺☻☹", 3}, 411 {"1,2,3,4", 7}, 412 {"\xe2\x00", 2}, 413 {"\xe2\x80", 2}, 414 {"a\xe2\x80", 3}, 415 } 416 417 func TestRuneCount(t *testing.T) { 418 for _, tt := range runecounttests { 419 if out := utf8.RuneCountInString(tt.in); out != tt.out { 420 t.Errorf("RuneCountInString(%q) = %d, want %d", tt.in, out, tt.out) 421 } 422 if out := utf8.RuneCount([]byte(tt.in)); out != tt.out { 423 t.Errorf("RuneCount(%q) = %d, want %d", tt.in, out, tt.out) 424 } 425 } 426 } 427 428 type RuneLenTest struct { 429 r rune 430 size int 431 } 432 433 var runelentests = []RuneLenTest{ 434 {0, 1}, 435 {'e', 1}, 436 {'é', 2}, 437 {'☺', 3}, 438 {utf8.RuneError, 3}, 439 {utf8.MaxRune, 4}, 440 {0xD800, -1}, 441 {0xDFFF, -1}, 442 {utf8.MaxRune + 1, -1}, 443 {-1, -1}, 444 } 445 446 func TestRuneLen(t *testing.T) { 447 for _, tt := range runelentests { 448 if size := utf8.RuneLen(tt.r); size != tt.size { 449 t.Errorf("RuneLen(%#U) = %d, want %d", tt.r, size, tt.size) 450 } 451 } 452 } 453 454 type ValidTest struct { 455 in string 456 out bool 457 } 458 459 var validTests = []ValidTest{ 460 {"", true}, 461 {"a", true}, 462 {"abc", true}, 463 {"Ж", true}, 464 {"ЖЖ", true}, 465 {"брэд-ЛГТМ", true}, 466 {"☺☻☹", true}, 467 {"aa\xe2", false}, 468 {string([]byte{66, 250}), false}, 469 {string([]byte{66, 250, 67}), false}, 470 {"a\uFFFDb", true}, 471 {string("\xF4\x8F\xBF\xBF"), true}, // U+10FFFF 472 {string("\xF4\x90\x80\x80"), false}, // U+10FFFF+1; out of range 473 {string("\xF7\xBF\xBF\xBF"), false}, // 0x1FFFFF; out of range 474 {string("\xFB\xBF\xBF\xBF\xBF"), false}, // 0x3FFFFFF; out of range 475 {string("\xc0\x80"), false}, // U+0000 encoded in two bytes: incorrect 476 {string("\xed\xa0\x80"), false}, // U+D800 high surrogate (sic) 477 {string("\xed\xbf\xbf"), false}, // U+DFFF low surrogate (sic) 478 } 479 480 func TestValid(t *testing.T) { 481 for _, tt := range validTests { 482 if utf8.Valid([]byte(tt.in)) != tt.out { 483 t.Errorf("Valid(%q) = %v; want %v", tt.in, !tt.out, tt.out) 484 } 485 if utf8.ValidString(tt.in) != tt.out { 486 t.Errorf("ValidString(%q) = %v; want %v", tt.in, !tt.out, tt.out) 487 } 488 } 489 } 490 491 type ValidRuneTest struct { 492 r rune 493 ok bool 494 } 495 496 var validrunetests = []ValidRuneTest{ 497 {0, true}, 498 {'e', true}, 499 {'é', true}, 500 {'☺', true}, 501 {utf8.RuneError, true}, 502 {utf8.MaxRune, true}, 503 {0xD7FF, true}, 504 {0xD800, false}, 505 {0xDFFF, false}, 506 {0xE000, true}, 507 {utf8.MaxRune + 1, false}, 508 {-1, false}, 509 } 510 511 func TestValidRune(t *testing.T) { 512 for _, tt := range validrunetests { 513 if ok := utf8.ValidRune(tt.r); ok != tt.ok { 514 t.Errorf("ValidRune(%#U) = %t, want %t", tt.r, ok, tt.ok) 515 } 516 } 517 } 518 519 func BenchmarkRuneCountTenASCIIChars(b *testing.B) { 520 s := []byte("0123456789") 521 for i := 0; i < b.N; i++ { 522 utf8.RuneCount(s) 523 } 524 } 525 526 func BenchmarkRuneCountTenJapaneseChars(b *testing.B) { 527 s := []byte("日本語日本語日本語日") 528 for i := 0; i < b.N; i++ { 529 utf8.RuneCount(s) 530 } 531 } 532 533 func BenchmarkRuneCountInStringTenASCIIChars(b *testing.B) { 534 for i := 0; i < b.N; i++ { 535 utf8.RuneCountInString("0123456789") 536 } 537 } 538 539 func BenchmarkRuneCountInStringTenJapaneseChars(b *testing.B) { 540 for i := 0; i < b.N; i++ { 541 utf8.RuneCountInString("日本語日本語日本語日") 542 } 543 } 544 545 func BenchmarkValidTenASCIIChars(b *testing.B) { 546 s := []byte("0123456789") 547 for i := 0; i < b.N; i++ { 548 utf8.Valid(s) 549 } 550 } 551 552 func BenchmarkValidTenJapaneseChars(b *testing.B) { 553 s := []byte("日本語日本語日本語日") 554 for i := 0; i < b.N; i++ { 555 utf8.Valid(s) 556 } 557 } 558 559 func BenchmarkValidStringTenASCIIChars(b *testing.B) { 560 for i := 0; i < b.N; i++ { 561 utf8.ValidString("0123456789") 562 } 563 } 564 565 func BenchmarkValidStringTenJapaneseChars(b *testing.B) { 566 for i := 0; i < b.N; i++ { 567 utf8.ValidString("日本語日本語日本語日") 568 } 569 } 570 571 func BenchmarkEncodeASCIIRune(b *testing.B) { 572 buf := make([]byte, utf8.UTFMax) 573 for i := 0; i < b.N; i++ { 574 utf8.EncodeRune(buf, 'a') 575 } 576 } 577 578 func BenchmarkEncodeJapaneseRune(b *testing.B) { 579 buf := make([]byte, utf8.UTFMax) 580 for i := 0; i < b.N; i++ { 581 utf8.EncodeRune(buf, '本') 582 } 583 } 584 585 func BenchmarkDecodeASCIIRune(b *testing.B) { 586 a := []byte{'a'} 587 for i := 0; i < b.N; i++ { 588 utf8.DecodeRune(a) 589 } 590 } 591 592 func BenchmarkDecodeJapaneseRune(b *testing.B) { 593 nihon := []byte("本") 594 for i := 0; i < b.N; i++ { 595 utf8.DecodeRune(nihon) 596 } 597 } 598 599 // boolSink is used to reference the return value of benchmarked 600 // functions to avoid dead code elimination. 601 var boolSink bool 602 603 func BenchmarkFullRune(b *testing.B) { 604 benchmarks := []struct { 605 name string 606 data []byte 607 }{ 608 {"ASCII", []byte("a")}, 609 {"Incomplete", []byte("\xf0\x90\x80")}, 610 {"Japanese", []byte("本")}, 611 } 612 for _, bm := range benchmarks { 613 b.Run(bm.name, func(b *testing.B) { 614 for i := 0; i < b.N; i++ { 615 boolSink = utf8.FullRune(bm.data) 616 } 617 }) 618 } 619 }