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 bytes_test 6 7import ( 8 . "bytes" 9 "fmt" 10 "internal/testenv" 11 "math/rand" 12 "reflect" 13 "strings" 14 "testing" 15 "unicode" 16 "unicode/utf8" 17) 18 19func eq(a, b []string) bool { 20 if len(a) != len(b) { 21 return false 22 } 23 for i := 0; i < len(a); i++ { 24 if a[i] != b[i] { 25 return false 26 } 27 } 28 return true 29} 30 31func sliceOfString(s [][]byte) []string { 32 result := make([]string, len(s)) 33 for i, v := range s { 34 result[i] = string(v) 35 } 36 return result 37} 38 39// For ease of reading, the test cases use strings that are converted to byte 40// slices before invoking the functions. 41 42var abcd = "abcd" 43var faces = "☺☻☹" 44var commas = "1,2,3,4" 45var dots = "1....2....3....4" 46 47type BinOpTest struct { 48 a string 49 b string 50 i int 51} 52 53func TestEqual(t *testing.T) { 54 for _, tt := range compareTests { 55 eql := Equal(tt.a, tt.b) 56 if eql != (tt.i == 0) { 57 t.Errorf(`Equal(%q, %q) = %v`, tt.a, tt.b, eql) 58 } 59 eql = EqualPortable(tt.a, tt.b) 60 if eql != (tt.i == 0) { 61 t.Errorf(`EqualPortable(%q, %q) = %v`, tt.a, tt.b, eql) 62 } 63 } 64} 65 66func TestEqualExhaustive(t *testing.T) { 67 var size = 128 68 if testing.Short() { 69 size = 32 70 } 71 a := make([]byte, size) 72 b := make([]byte, size) 73 b_init := make([]byte, size) 74 // randomish but deterministic data 75 for i := 0; i < size; i++ { 76 a[i] = byte(17 * i) 77 b_init[i] = byte(23*i + 100) 78 } 79 80 for len := 0; len <= size; len++ { 81 for x := 0; x <= size-len; x++ { 82 for y := 0; y <= size-len; y++ { 83 copy(b, b_init) 84 copy(b[y:y+len], a[x:x+len]) 85 if !Equal(a[x:x+len], b[y:y+len]) || !Equal(b[y:y+len], a[x:x+len]) { 86 t.Errorf("Equal(%d, %d, %d) = false", len, x, y) 87 } 88 } 89 } 90 } 91} 92 93// make sure Equal returns false for minimally different strings. The data 94// is all zeros except for a single one in one location. 95func TestNotEqual(t *testing.T) { 96 var size = 128 97 if testing.Short() { 98 size = 32 99 } 100 a := make([]byte, size) 101 b := make([]byte, size) 102 103 for len := 0; len <= size; len++ { 104 for x := 0; x <= size-len; x++ { 105 for y := 0; y <= size-len; y++ { 106 for diffpos := x; diffpos < x+len; diffpos++ { 107 a[diffpos] = 1 108 if Equal(a[x:x+len], b[y:y+len]) || Equal(b[y:y+len], a[x:x+len]) { 109 t.Errorf("NotEqual(%d, %d, %d, %d) = true", len, x, y, diffpos) 110 } 111 a[diffpos] = 0 112 } 113 } 114 } 115 } 116} 117 118var indexTests = []BinOpTest{ 119 {"", "", 0}, 120 {"", "a", -1}, 121 {"", "foo", -1}, 122 {"fo", "foo", -1}, 123 {"foo", "baz", -1}, 124 {"foo", "foo", 0}, 125 {"oofofoofooo", "f", 2}, 126 {"oofofoofooo", "foo", 4}, 127 {"barfoobarfoo", "foo", 3}, 128 {"foo", "", 0}, 129 {"foo", "o", 1}, 130 {"abcABCabc", "A", 3}, 131 // cases with one byte strings - test IndexByte and special case in Index() 132 {"", "a", -1}, 133 {"x", "a", -1}, 134 {"x", "x", 0}, 135 {"abc", "a", 0}, 136 {"abc", "b", 1}, 137 {"abc", "c", 2}, 138 {"abc", "x", -1}, 139 {"barfoobarfooyyyzzzyyyzzzyyyzzzyyyxxxzzzyyy", "x", 33}, 140 {"foofyfoobarfoobar", "y", 4}, 141 {"oooooooooooooooooooooo", "r", -1}, 142 // test fallback to Rabin-Karp. 143 {"oxoxoxoxoxoxoxoxoxoxoxoy", "oy", 22}, 144 {"oxoxoxoxoxoxoxoxoxoxoxox", "oy", -1}, 145} 146 147var lastIndexTests = []BinOpTest{ 148 {"", "", 0}, 149 {"", "a", -1}, 150 {"", "foo", -1}, 151 {"fo", "foo", -1}, 152 {"foo", "foo", 0}, 153 {"foo", "f", 0}, 154 {"oofofoofooo", "f", 7}, 155 {"oofofoofooo", "foo", 7}, 156 {"barfoobarfoo", "foo", 9}, 157 {"foo", "", 3}, 158 {"foo", "o", 2}, 159 {"abcABCabc", "A", 3}, 160 {"abcABCabc", "a", 6}, 161} 162 163var indexAnyTests = []BinOpTest{ 164 {"", "", -1}, 165 {"", "a", -1}, 166 {"", "abc", -1}, 167 {"a", "", -1}, 168 {"a", "a", 0}, 169 {"aaa", "a", 0}, 170 {"abc", "xyz", -1}, 171 {"abc", "xcz", 2}, 172 {"ab☺c", "x☺yz", 2}, 173 {"a☺b☻c☹d", "cx", len("a☺b☻")}, 174 {"a☺b☻c☹d", "uvw☻xyz", len("a☺b")}, 175 {"aRegExp*", ".(|)*+?^$[]", 7}, 176 {dots + dots + dots, " ", -1}, 177 {"012abcba210", "\xffb", 4}, 178 {"012\x80bcb\x80210", "\xffb", 3}, 179} 180 181var lastIndexAnyTests = []BinOpTest{ 182 {"", "", -1}, 183 {"", "a", -1}, 184 {"", "abc", -1}, 185 {"a", "", -1}, 186 {"a", "a", 0}, 187 {"aaa", "a", 2}, 188 {"abc", "xyz", -1}, 189 {"abc", "ab", 1}, 190 {"ab☺c", "x☺yz", 2}, 191 {"a☺b☻c☹d", "cx", len("a☺b☻")}, 192 {"a☺b☻c☹d", "uvw☻xyz", len("a☺b")}, 193 {"a.RegExp*", ".(|)*+?^$[]", 8}, 194 {dots + dots + dots, " ", -1}, 195 {"012abcba210", "\xffb", 6}, 196 {"012\x80bcb\x80210", "\xffb", 7}, 197} 198 199// Execute f on each test case. funcName should be the name of f; it's used 200// in failure reports. 201func runIndexTests(t *testing.T, f func(s, sep []byte) int, funcName string, testCases []BinOpTest) { 202 for _, test := range testCases { 203 a := []byte(test.a) 204 b := []byte(test.b) 205 actual := f(a, b) 206 if actual != test.i { 207 t.Errorf("%s(%q,%q) = %v; want %v", funcName, a, b, actual, test.i) 208 } 209 } 210} 211 212func runIndexAnyTests(t *testing.T, f func(s []byte, chars string) int, funcName string, testCases []BinOpTest) { 213 for _, test := range testCases { 214 a := []byte(test.a) 215 actual := f(a, test.b) 216 if actual != test.i { 217 t.Errorf("%s(%q,%q) = %v; want %v", funcName, a, test.b, actual, test.i) 218 } 219 } 220} 221 222func TestIndex(t *testing.T) { runIndexTests(t, Index, "Index", indexTests) } 223func TestLastIndex(t *testing.T) { runIndexTests(t, LastIndex, "LastIndex", lastIndexTests) } 224func TestIndexAny(t *testing.T) { runIndexAnyTests(t, IndexAny, "IndexAny", indexAnyTests) } 225func TestLastIndexAny(t *testing.T) { 226 runIndexAnyTests(t, LastIndexAny, "LastIndexAny", lastIndexAnyTests) 227} 228 229func TestIndexByte(t *testing.T) { 230 for _, tt := range indexTests { 231 if len(tt.b) != 1 { 232 continue 233 } 234 a := []byte(tt.a) 235 b := tt.b[0] 236 pos := IndexByte(a, b) 237 if pos != tt.i { 238 t.Errorf(`IndexByte(%q, '%c') = %v`, tt.a, b, pos) 239 } 240 posp := IndexBytePortable(a, b) 241 if posp != tt.i { 242 t.Errorf(`indexBytePortable(%q, '%c') = %v`, tt.a, b, posp) 243 } 244 } 245} 246 247func TestLastIndexByte(t *testing.T) { 248 testCases := []BinOpTest{ 249 {"", "q", -1}, 250 {"abcdef", "q", -1}, 251 {"abcdefabcdef", "a", len("abcdef")}, // something in the middle 252 {"abcdefabcdef", "f", len("abcdefabcde")}, // last byte 253 {"zabcdefabcdef", "z", 0}, // first byte 254 {"a☺b☻c☹d", "b", len("a☺")}, // non-ascii 255 } 256 for _, test := range testCases { 257 actual := LastIndexByte([]byte(test.a), test.b[0]) 258 if actual != test.i { 259 t.Errorf("LastIndexByte(%q,%c) = %v; want %v", test.a, test.b[0], actual, test.i) 260 } 261 } 262} 263 264// test a larger buffer with different sizes and alignments 265func TestIndexByteBig(t *testing.T) { 266 var n = 1024 267 if testing.Short() { 268 n = 128 269 } 270 b := make([]byte, n) 271 for i := 0; i < n; i++ { 272 // different start alignments 273 b1 := b[i:] 274 for j := 0; j < len(b1); j++ { 275 b1[j] = 'x' 276 pos := IndexByte(b1, 'x') 277 if pos != j { 278 t.Errorf("IndexByte(%q, 'x') = %v", b1, pos) 279 } 280 b1[j] = 0 281 pos = IndexByte(b1, 'x') 282 if pos != -1 { 283 t.Errorf("IndexByte(%q, 'x') = %v", b1, pos) 284 } 285 } 286 // different end alignments 287 b1 = b[:i] 288 for j := 0; j < len(b1); j++ { 289 b1[j] = 'x' 290 pos := IndexByte(b1, 'x') 291 if pos != j { 292 t.Errorf("IndexByte(%q, 'x') = %v", b1, pos) 293 } 294 b1[j] = 0 295 pos = IndexByte(b1, 'x') 296 if pos != -1 { 297 t.Errorf("IndexByte(%q, 'x') = %v", b1, pos) 298 } 299 } 300 // different start and end alignments 301 b1 = b[i/2 : n-(i+1)/2] 302 for j := 0; j < len(b1); j++ { 303 b1[j] = 'x' 304 pos := IndexByte(b1, 'x') 305 if pos != j { 306 t.Errorf("IndexByte(%q, 'x') = %v", b1, pos) 307 } 308 b1[j] = 0 309 pos = IndexByte(b1, 'x') 310 if pos != -1 { 311 t.Errorf("IndexByte(%q, 'x') = %v", b1, pos) 312 } 313 } 314 } 315} 316 317// test a small index across all page offsets 318func TestIndexByteSmall(t *testing.T) { 319 b := make([]byte, 5015) // bigger than a page 320 // Make sure we find the correct byte even when straddling a page. 321 for i := 0; i <= len(b)-15; i++ { 322 for j := 0; j < 15; j++ { 323 b[i+j] = byte(100 + j) 324 } 325 for j := 0; j < 15; j++ { 326 p := IndexByte(b[i:i+15], byte(100+j)) 327 if p != j { 328 t.Errorf("IndexByte(%q, %d) = %d", b[i:i+15], 100+j, p) 329 } 330 } 331 for j := 0; j < 15; j++ { 332 b[i+j] = 0 333 } 334 } 335 // Make sure matches outside the slice never trigger. 336 for i := 0; i <= len(b)-15; i++ { 337 for j := 0; j < 15; j++ { 338 b[i+j] = 1 339 } 340 for j := 0; j < 15; j++ { 341 p := IndexByte(b[i:i+15], byte(0)) 342 if p != -1 { 343 t.Errorf("IndexByte(%q, %d) = %d", b[i:i+15], 0, p) 344 } 345 } 346 for j := 0; j < 15; j++ { 347 b[i+j] = 0 348 } 349 } 350} 351 352func TestIndexRune(t *testing.T) { 353 tests := []struct { 354 in string 355 rune rune 356 want int 357 }{ 358 {"", 'a', -1}, 359 {"", '☺', -1}, 360 {"foo", '☹', -1}, 361 {"foo", 'o', 1}, 362 {"foo☺bar", '☺', 3}, 363 {"foo☺☻☹bar", '☹', 9}, 364 {"a A x", 'A', 2}, 365 {"some_text=some_value", '=', 9}, 366 {"☺a", 'a', 3}, 367 {"a☻☺b", '☺', 4}, 368 369 // RuneError should match any invalid UTF-8 byte sequence. 370 {"�", '�', 0}, 371 {"\xff", '�', 0}, 372 {"☻x�", '�', len("☻x")}, 373 {"☻x\xe2\x98", '�', len("☻x")}, 374 {"☻x\xe2\x98�", '�', len("☻x")}, 375 {"☻x\xe2\x98x", '�', len("☻x")}, 376 377 // Invalid rune values should never match. 378 {"a☺b☻c☹d\xe2\x98�\xff�\xed\xa0\x80", -1, -1}, 379 {"a☺b☻c☹d\xe2\x98�\xff�\xed\xa0\x80", 0xD800, -1}, // Surrogate pair 380 {"a☺b☻c☹d\xe2\x98�\xff�\xed\xa0\x80", utf8.MaxRune + 1, -1}, 381 } 382 for _, tt := range tests { 383 if got := IndexRune([]byte(tt.in), tt.rune); got != tt.want { 384 t.Errorf("IndexRune(%q, %d) = %v; want %v", tt.in, tt.rune, got, tt.want) 385 } 386 } 387 388 haystack := []byte("test世界") 389 allocs := testing.AllocsPerRun(1000, func() { 390 if i := IndexRune(haystack, 's'); i != 2 { 391 t.Fatalf("'s' at %d; want 2", i) 392 } 393 if i := IndexRune(haystack, '世'); i != 4 { 394 t.Fatalf("'世' at %d; want 4", i) 395 } 396 }) 397 if allocs != 0 { 398 t.Errorf("expected no allocations, got %f", allocs) 399 } 400} 401 402// test count of a single byte across page offsets 403func TestCountByte(t *testing.T) { 404 b := make([]byte, 5015) // bigger than a page 405 windows := []int{1, 2, 3, 4, 15, 16, 17, 31, 32, 33, 63, 64, 65, 128} 406 testCountWindow := func(i, window int) { 407 for j := 0; j < window; j++ { 408 b[i+j] = byte(100) 409 p := Count(b[i:i+window], []byte{100}) 410 if p != j+1 { 411 t.Errorf("TestCountByte.Count(%q, 100) = %d", b[i:i+window], p) 412 } 413 pGeneric := CountGeneric(b[i:i+window], []byte{100}) 414 if pGeneric != j+1 { 415 t.Errorf("TestCountByte.CountGeneric(%q, 100) = %d", b[i:i+window], p) 416 } 417 } 418 } 419 420 maxWnd := windows[len(windows)-1] 421 422 for i := 0; i <= 2*maxWnd; i++ { 423 for _, window := range windows { 424 if window > len(b[i:]) { 425 window = len(b[i:]) 426 } 427 testCountWindow(i, window) 428 for j := 0; j < window; j++ { 429 b[i+j] = byte(0) 430 } 431 } 432 } 433 for i := 4096 - (maxWnd + 1); i < len(b); i++ { 434 for _, window := range windows { 435 if window > len(b[i:]) { 436 window = len(b[i:]) 437 } 438 testCountWindow(i, window) 439 for j := 0; j < window; j++ { 440 b[i+j] = byte(0) 441 } 442 } 443 } 444} 445 446// Make sure we don't count bytes outside our window 447func TestCountByteNoMatch(t *testing.T) { 448 b := make([]byte, 5015) 449 windows := []int{1, 2, 3, 4, 15, 16, 17, 31, 32, 33, 63, 64, 65, 128} 450 for i := 0; i <= len(b); i++ { 451 for _, window := range windows { 452 if window > len(b[i:]) { 453 window = len(b[i:]) 454 } 455 // Fill the window with non-match 456 for j := 0; j < window; j++ { 457 b[i+j] = byte(100) 458 } 459 // Try to find something that doesn't exist 460 p := Count(b[i:i+window], []byte{0}) 461 if p != 0 { 462 t.Errorf("TestCountByteNoMatch(%q, 0) = %d", b[i:i+window], p) 463 } 464 pGeneric := CountGeneric(b[i:i+window], []byte{0}) 465 if pGeneric != 0 { 466 t.Errorf("TestCountByteNoMatch.CountGeneric(%q, 100) = %d", b[i:i+window], p) 467 } 468 for j := 0; j < window; j++ { 469 b[i+j] = byte(0) 470 } 471 } 472 } 473} 474 475var bmbuf []byte 476 477func valName(x int) string { 478 if s := x >> 20; s<<20 == x { 479 return fmt.Sprintf("%dM", s) 480 } 481 if s := x >> 10; s<<10 == x { 482 return fmt.Sprintf("%dK", s) 483 } 484 return fmt.Sprint(x) 485} 486 487func benchBytes(b *testing.B, sizes []int, f func(b *testing.B, n int)) { 488 for _, n := range sizes { 489 if isRaceBuilder && n > 4<<10 { 490 continue 491 } 492 b.Run(valName(n), func(b *testing.B) { 493 if len(bmbuf) < n { 494 bmbuf = make([]byte, n) 495 } 496 b.SetBytes(int64(n)) 497 f(b, n) 498 }) 499 } 500} 501 502var indexSizes = []int{10, 32, 4 << 10, 4 << 20, 64 << 20} 503 504var isRaceBuilder = strings.HasSuffix(testenv.Builder(), "-race") 505 506func BenchmarkIndexByte(b *testing.B) { 507 benchBytes(b, indexSizes, bmIndexByte(IndexByte)) 508} 509 510func BenchmarkIndexBytePortable(b *testing.B) { 511 benchBytes(b, indexSizes, bmIndexByte(IndexBytePortable)) 512} 513 514func bmIndexByte(index func([]byte, byte) int) func(b *testing.B, n int) { 515 return func(b *testing.B, n int) { 516 buf := bmbuf[0:n] 517 buf[n-1] = 'x' 518 for i := 0; i < b.N; i++ { 519 j := index(buf, 'x') 520 if j != n-1 { 521 b.Fatal("bad index", j) 522 } 523 } 524 buf[n-1] = '\x00' 525 } 526} 527 528func BenchmarkIndexRune(b *testing.B) { 529 benchBytes(b, indexSizes, bmIndexRune(IndexRune)) 530} 531 532func BenchmarkIndexRuneASCII(b *testing.B) { 533 benchBytes(b, indexSizes, bmIndexRuneASCII(IndexRune)) 534} 535 536func bmIndexRuneASCII(index func([]byte, rune) int) func(b *testing.B, n int) { 537 return func(b *testing.B, n int) { 538 buf := bmbuf[0:n] 539 buf[n-1] = 'x' 540 for i := 0; i < b.N; i++ { 541 j := index(buf, 'x') 542 if j != n-1 { 543 b.Fatal("bad index", j) 544 } 545 } 546 buf[n-1] = '\x00' 547 } 548} 549 550func bmIndexRune(index func([]byte, rune) int) func(b *testing.B, n int) { 551 return func(b *testing.B, n int) { 552 buf := bmbuf[0:n] 553 utf8.EncodeRune(buf[n-3:], '世') 554 for i := 0; i < b.N; i++ { 555 j := index(buf, '世') 556 if j != n-3 { 557 b.Fatal("bad index", j) 558 } 559 } 560 buf[n-3] = '\x00' 561 buf[n-2] = '\x00' 562 buf[n-1] = '\x00' 563 } 564} 565 566func BenchmarkEqual(b *testing.B) { 567 b.Run("0", func(b *testing.B) { 568 var buf [4]byte 569 buf1 := buf[0:0] 570 buf2 := buf[1:1] 571 for i := 0; i < b.N; i++ { 572 eq := Equal(buf1, buf2) 573 if !eq { 574 b.Fatal("bad equal") 575 } 576 } 577 }) 578 579 sizes := []int{1, 6, 9, 15, 16, 20, 32, 4 << 10, 4 << 20, 64 << 20} 580 benchBytes(b, sizes, bmEqual(Equal)) 581} 582 583func BenchmarkEqualPort(b *testing.B) { 584 sizes := []int{1, 6, 32, 4 << 10, 4 << 20, 64 << 20} 585 benchBytes(b, sizes, bmEqual(EqualPortable)) 586} 587 588func bmEqual(equal func([]byte, []byte) bool) func(b *testing.B, n int) { 589 return func(b *testing.B, n int) { 590 if len(bmbuf) < 2*n { 591 bmbuf = make([]byte, 2*n) 592 } 593 buf1 := bmbuf[0:n] 594 buf2 := bmbuf[n : 2*n] 595 buf1[n-1] = 'x' 596 buf2[n-1] = 'x' 597 for i := 0; i < b.N; i++ { 598 eq := equal(buf1, buf2) 599 if !eq { 600 b.Fatal("bad equal") 601 } 602 } 603 buf1[n-1] = '\x00' 604 buf2[n-1] = '\x00' 605 } 606} 607 608func BenchmarkIndex(b *testing.B) { 609 benchBytes(b, indexSizes, func(b *testing.B, n int) { 610 buf := bmbuf[0:n] 611 buf[n-1] = 'x' 612 for i := 0; i < b.N; i++ { 613 j := Index(buf, buf[n-7:]) 614 if j != n-7 { 615 b.Fatal("bad index", j) 616 } 617 } 618 buf[n-1] = '\x00' 619 }) 620} 621 622func BenchmarkIndexEasy(b *testing.B) { 623 benchBytes(b, indexSizes, func(b *testing.B, n int) { 624 buf := bmbuf[0:n] 625 buf[n-1] = 'x' 626 buf[n-7] = 'x' 627 for i := 0; i < b.N; i++ { 628 j := Index(buf, buf[n-7:]) 629 if j != n-7 { 630 b.Fatal("bad index", j) 631 } 632 } 633 buf[n-1] = '\x00' 634 buf[n-7] = '\x00' 635 }) 636} 637 638func BenchmarkCount(b *testing.B) { 639 benchBytes(b, indexSizes, func(b *testing.B, n int) { 640 buf := bmbuf[0:n] 641 buf[n-1] = 'x' 642 for i := 0; i < b.N; i++ { 643 j := Count(buf, buf[n-7:]) 644 if j != 1 { 645 b.Fatal("bad count", j) 646 } 647 } 648 buf[n-1] = '\x00' 649 }) 650} 651 652func BenchmarkCountEasy(b *testing.B) { 653 benchBytes(b, indexSizes, func(b *testing.B, n int) { 654 buf := bmbuf[0:n] 655 buf[n-1] = 'x' 656 buf[n-7] = 'x' 657 for i := 0; i < b.N; i++ { 658 j := Count(buf, buf[n-7:]) 659 if j != 1 { 660 b.Fatal("bad count", j) 661 } 662 } 663 buf[n-1] = '\x00' 664 buf[n-7] = '\x00' 665 }) 666} 667 668func BenchmarkCountSingle(b *testing.B) { 669 benchBytes(b, indexSizes, func(b *testing.B, n int) { 670 buf := bmbuf[0:n] 671 step := 8 672 for i := 0; i < len(buf); i += step { 673 buf[i] = 1 674 } 675 expect := (len(buf) + (step - 1)) / step 676 for i := 0; i < b.N; i++ { 677 j := Count(buf, []byte{1}) 678 if j != expect { 679 b.Fatal("bad count", j, expect) 680 } 681 } 682 for i := 0; i < len(buf); i++ { 683 buf[i] = 0 684 } 685 }) 686} 687 688type ExplodeTest struct { 689 s string 690 n int 691 a []string 692} 693 694var explodetests = []ExplodeTest{ 695 {"", -1, []string{}}, 696 {abcd, -1, []string{"a", "b", "c", "d"}}, 697 {faces, -1, []string{"☺", "☻", "☹"}}, 698 {abcd, 2, []string{"a", "bcd"}}, 699} 700 701func TestExplode(t *testing.T) { 702 for _, tt := range explodetests { 703 a := SplitN([]byte(tt.s), nil, tt.n) 704 result := sliceOfString(a) 705 if !eq(result, tt.a) { 706 t.Errorf(`Explode("%s", %d) = %v; want %v`, tt.s, tt.n, result, tt.a) 707 continue 708 } 709 s := Join(a, []byte{}) 710 if string(s) != tt.s { 711 t.Errorf(`Join(Explode("%s", %d), "") = "%s"`, tt.s, tt.n, s) 712 } 713 } 714} 715 716type SplitTest struct { 717 s string 718 sep string 719 n int 720 a []string 721} 722 723var splittests = []SplitTest{ 724 {abcd, "a", 0, nil}, 725 {abcd, "a", -1, []string{"", "bcd"}}, 726 {abcd, "z", -1, []string{"abcd"}}, 727 {abcd, "", -1, []string{"a", "b", "c", "d"}}, 728 {commas, ",", -1, []string{"1", "2", "3", "4"}}, 729 {dots, "...", -1, []string{"1", ".2", ".3", ".4"}}, 730 {faces, "☹", -1, []string{"☺☻", ""}}, 731 {faces, "~", -1, []string{faces}}, 732 {faces, "", -1, []string{"☺", "☻", "☹"}}, 733 {"1 2 3 4", " ", 3, []string{"1", "2", "3 4"}}, 734 {"1 2", " ", 3, []string{"1", "2"}}, 735 {"123", "", 2, []string{"1", "23"}}, 736 {"123", "", 17, []string{"1", "2", "3"}}, 737} 738 739func TestSplit(t *testing.T) { 740 for _, tt := range splittests { 741 a := SplitN([]byte(tt.s), []byte(tt.sep), tt.n) 742 743 // Appending to the results should not change future results. 744 var x []byte 745 for _, v := range a { 746 x = append(v, 'z') 747 } 748 749 result := sliceOfString(a) 750 if !eq(result, tt.a) { 751 t.Errorf(`Split(%q, %q, %d) = %v; want %v`, tt.s, tt.sep, tt.n, result, tt.a) 752 continue 753 } 754 if tt.n == 0 { 755 continue 756 } 757 758 if want := tt.a[len(tt.a)-1] + "z"; string(x) != want { 759 t.Errorf("last appended result was %s; want %s", x, want) 760 } 761 762 s := Join(a, []byte(tt.sep)) 763 if string(s) != tt.s { 764 t.Errorf(`Join(Split(%q, %q, %d), %q) = %q`, tt.s, tt.sep, tt.n, tt.sep, s) 765 } 766 if tt.n < 0 { 767 b := Split([]byte(tt.s), []byte(tt.sep)) 768 if !reflect.DeepEqual(a, b) { 769 t.Errorf("Split disagrees withSplitN(%q, %q, %d) = %v; want %v", tt.s, tt.sep, tt.n, b, a) 770 } 771 } 772 if len(a) > 0 { 773 in, out := a[0], s 774 if cap(in) == cap(out) && &in[:1][0] == &out[:1][0] { 775 t.Errorf("Join(%#v, %q) didn't copy", a, tt.sep) 776 } 777 } 778 } 779} 780 781var splitaftertests = []SplitTest{ 782 {abcd, "a", -1, []string{"a", "bcd"}}, 783 {abcd, "z", -1, []string{"abcd"}}, 784 {abcd, "", -1, []string{"a", "b", "c", "d"}}, 785 {commas, ",", -1, []string{"1,", "2,", "3,", "4"}}, 786 {dots, "...", -1, []string{"1...", ".2...", ".3...", ".4"}}, 787 {faces, "☹", -1, []string{"☺☻☹", ""}}, 788 {faces, "~", -1, []string{faces}}, 789 {faces, "", -1, []string{"☺", "☻", "☹"}}, 790 {"1 2 3 4", " ", 3, []string{"1 ", "2 ", "3 4"}}, 791 {"1 2 3", " ", 3, []string{"1 ", "2 ", "3"}}, 792 {"1 2", " ", 3, []string{"1 ", "2"}}, 793 {"123", "", 2, []string{"1", "23"}}, 794 {"123", "", 17, []string{"1", "2", "3"}}, 795} 796 797func TestSplitAfter(t *testing.T) { 798 for _, tt := range splitaftertests { 799 a := SplitAfterN([]byte(tt.s), []byte(tt.sep), tt.n) 800 801 // Appending to the results should not change future results. 802 var x []byte 803 for _, v := range a { 804 x = append(v, 'z') 805 } 806 807 result := sliceOfString(a) 808 if !eq(result, tt.a) { 809 t.Errorf(`Split(%q, %q, %d) = %v; want %v`, tt.s, tt.sep, tt.n, result, tt.a) 810 continue 811 } 812 813 if want := tt.a[len(tt.a)-1] + "z"; string(x) != want { 814 t.Errorf("last appended result was %s; want %s", x, want) 815 } 816 817 s := Join(a, nil) 818 if string(s) != tt.s { 819 t.Errorf(`Join(Split(%q, %q, %d), %q) = %q`, tt.s, tt.sep, tt.n, tt.sep, s) 820 } 821 if tt.n < 0 { 822 b := SplitAfter([]byte(tt.s), []byte(tt.sep)) 823 if !reflect.DeepEqual(a, b) { 824 t.Errorf("SplitAfter disagrees withSplitAfterN(%q, %q, %d) = %v; want %v", tt.s, tt.sep, tt.n, b, a) 825 } 826 } 827 } 828} 829 830type FieldsTest struct { 831 s string 832 a []string 833} 834 835var fieldstests = []FieldsTest{ 836 {"", []string{}}, 837 {" ", []string{}}, 838 {" \t ", []string{}}, 839 {" abc ", []string{"abc"}}, 840 {"1 2 3 4", []string{"1", "2", "3", "4"}}, 841 {"1 2 3 4", []string{"1", "2", "3", "4"}}, 842 {"1\t\t2\t\t3\t4", []string{"1", "2", "3", "4"}}, 843 {"1\u20002\u20013\u20024", []string{"1", "2", "3", "4"}}, 844 {"\u2000\u2001\u2002", []string{}}, 845 {"\n™\t™\n", []string{"™", "™"}}, 846 {faces, []string{faces}}, 847} 848 849func TestFields(t *testing.T) { 850 for _, tt := range fieldstests { 851 b := []byte(tt.s) 852 a := Fields(b) 853 854 // Appending to the results should not change future results. 855 var x []byte 856 for _, v := range a { 857 x = append(v, 'z') 858 } 859 860 result := sliceOfString(a) 861 if !eq(result, tt.a) { 862 t.Errorf("Fields(%q) = %v; want %v", tt.s, a, tt.a) 863 continue 864 } 865 866 if string(b) != tt.s { 867 t.Errorf("slice changed to %s; want %s", string(b), tt.s) 868 } 869 if len(tt.a) > 0 { 870 if want := tt.a[len(tt.a)-1] + "z"; string(x) != want { 871 t.Errorf("last appended result was %s; want %s", x, want) 872 } 873 } 874 } 875} 876 877func TestFieldsFunc(t *testing.T) { 878 for _, tt := range fieldstests { 879 a := FieldsFunc([]byte(tt.s), unicode.IsSpace) 880 result := sliceOfString(a) 881 if !eq(result, tt.a) { 882 t.Errorf("FieldsFunc(%q, unicode.IsSpace) = %v; want %v", tt.s, a, tt.a) 883 continue 884 } 885 } 886 pred := func(c rune) bool { return c == 'X' } 887 var fieldsFuncTests = []FieldsTest{ 888 {"", []string{}}, 889 {"XX", []string{}}, 890 {"XXhiXXX", []string{"hi"}}, 891 {"aXXbXXXcX", []string{"a", "b", "c"}}, 892 } 893 for _, tt := range fieldsFuncTests { 894 b := []byte(tt.s) 895 a := FieldsFunc(b, pred) 896 897 // Appending to the results should not change future results. 898 var x []byte 899 for _, v := range a { 900 x = append(v, 'z') 901 } 902 903 result := sliceOfString(a) 904 if !eq(result, tt.a) { 905 t.Errorf("FieldsFunc(%q) = %v, want %v", tt.s, a, tt.a) 906 } 907 908 if string(b) != tt.s { 909 t.Errorf("slice changed to %s; want %s", b, tt.s) 910 } 911 if len(tt.a) > 0 { 912 if want := tt.a[len(tt.a)-1] + "z"; string(x) != want { 913 t.Errorf("last appended result was %s; want %s", x, want) 914 } 915 } 916 } 917} 918 919// Test case for any function which accepts and returns a byte slice. 920// For ease of creation, we write the byte slices as strings. 921type StringTest struct { 922 in, out string 923} 924 925var upperTests = []StringTest{ 926 {"", ""}, 927 {"abc", "ABC"}, 928 {"AbC123", "ABC123"}, 929 {"azAZ09_", "AZAZ09_"}, 930 {"\u0250\u0250\u0250\u0250\u0250", "\u2C6F\u2C6F\u2C6F\u2C6F\u2C6F"}, // grows one byte per char 931} 932 933var lowerTests = []StringTest{ 934 {"", ""}, 935 {"abc", "abc"}, 936 {"AbC123", "abc123"}, 937 {"azAZ09_", "azaz09_"}, 938 {"\u2C6D\u2C6D\u2C6D\u2C6D\u2C6D", "\u0251\u0251\u0251\u0251\u0251"}, // shrinks one byte per char 939} 940 941const space = "\t\v\r\f\n\u0085\u00a0\u2000\u3000" 942 943var trimSpaceTests = []StringTest{ 944 {"", ""}, 945 {"abc", "abc"}, 946 {space + "abc" + space, "abc"}, 947 {" ", ""}, 948 {" \t\r\n \t\t\r\r\n\n ", ""}, 949 {" \t\r\n x\t\t\r\r\n\n ", "x"}, 950 {" \u2000\t\r\n x\t\t\r\r\ny\n \u3000", "x\t\t\r\r\ny"}, 951 {"1 \t\r\n2", "1 \t\r\n2"}, 952 {" x\x80", "x\x80"}, 953 {" x\xc0", "x\xc0"}, 954 {"x \xc0\xc0 ", "x \xc0\xc0"}, 955 {"x \xc0", "x \xc0"}, 956 {"x \xc0 ", "x \xc0"}, 957 {"x \xc0\xc0 ", "x \xc0\xc0"}, 958 {"x ☺\xc0\xc0 ", "x ☺\xc0\xc0"}, 959 {"x ☺ ", "x ☺"}, 960} 961 962// Execute f on each test case. funcName should be the name of f; it's used 963// in failure reports. 964func runStringTests(t *testing.T, f func([]byte) []byte, funcName string, testCases []StringTest) { 965 for _, tc := range testCases { 966 actual := string(f([]byte(tc.in))) 967 if actual != tc.out { 968 t.Errorf("%s(%q) = %q; want %q", funcName, tc.in, actual, tc.out) 969 } 970 } 971} 972 973func tenRunes(r rune) string { 974 runes := make([]rune, 10) 975 for i := range runes { 976 runes[i] = r 977 } 978 return string(runes) 979} 980 981// User-defined self-inverse mapping function 982func rot13(r rune) rune { 983 const step = 13 984 if r >= 'a' && r <= 'z' { 985 return ((r - 'a' + step) % 26) + 'a' 986 } 987 if r >= 'A' && r <= 'Z' { 988 return ((r - 'A' + step) % 26) + 'A' 989 } 990 return r 991} 992 993func TestMap(t *testing.T) { 994 // Run a couple of awful growth/shrinkage tests 995 a := tenRunes('a') 996 997 // 1. Grow. This triggers two reallocations in Map. 998 maxRune := func(r rune) rune { return unicode.MaxRune } 999 m := Map(maxRune, []byte(a)) 1000 expect := tenRunes(unicode.MaxRune) 1001 if string(m) != expect { 1002 t.Errorf("growing: expected %q got %q", expect, m) 1003 } 1004 1005 // 2. Shrink 1006 minRune := func(r rune) rune { return 'a' } 1007 m = Map(minRune, []byte(tenRunes(unicode.MaxRune))) 1008 expect = a 1009 if string(m) != expect { 1010 t.Errorf("shrinking: expected %q got %q", expect, m) 1011 } 1012 1013 // 3. Rot13 1014 m = Map(rot13, []byte("a to zed")) 1015 expect = "n gb mrq" 1016 if string(m) != expect { 1017 t.Errorf("rot13: expected %q got %q", expect, m) 1018 } 1019 1020 // 4. Rot13^2 1021 m = Map(rot13, Map(rot13, []byte("a to zed"))) 1022 expect = "a to zed" 1023 if string(m) != expect { 1024 t.Errorf("rot13: expected %q got %q", expect, m) 1025 } 1026 1027 // 5. Drop 1028 dropNotLatin := func(r rune) rune { 1029 if unicode.Is(unicode.Latin, r) { 1030 return r 1031 } 1032 return -1 1033 } 1034 m = Map(dropNotLatin, []byte("Hello, 세계")) 1035 expect = "Hello" 1036 if string(m) != expect { 1037 t.Errorf("drop: expected %q got %q", expect, m) 1038 } 1039 1040 // 6. Invalid rune 1041 invalidRune := func(r rune) rune { 1042 return utf8.MaxRune + 1 1043 } 1044 m = Map(invalidRune, []byte("x")) 1045 expect = "\uFFFD" 1046 if string(m) != expect { 1047 t.Errorf("invalidRune: expected %q got %q", expect, m) 1048 } 1049} 1050 1051func TestToUpper(t *testing.T) { runStringTests(t, ToUpper, "ToUpper", upperTests) } 1052 1053func TestToLower(t *testing.T) { runStringTests(t, ToLower, "ToLower", lowerTests) } 1054 1055func TestTrimSpace(t *testing.T) { runStringTests(t, TrimSpace, "TrimSpace", trimSpaceTests) } 1056 1057type RepeatTest struct { 1058 in, out string 1059 count int 1060} 1061 1062var RepeatTests = []RepeatTest{ 1063 {"", "", 0}, 1064 {"", "", 1}, 1065 {"", "", 2}, 1066 {"-", "", 0}, 1067 {"-", "-", 1}, 1068 {"-", "----------", 10}, 1069 {"abc ", "abc abc abc ", 3}, 1070} 1071 1072func TestRepeat(t *testing.T) { 1073 for _, tt := range RepeatTests { 1074 tin := []byte(tt.in) 1075 tout := []byte(tt.out) 1076 a := Repeat(tin, tt.count) 1077 if !Equal(a, tout) { 1078 t.Errorf("Repeat(%q, %d) = %q; want %q", tin, tt.count, a, tout) 1079 continue 1080 } 1081 } 1082} 1083 1084func repeat(b []byte, count int) (err error) { 1085 defer func() { 1086 if r := recover(); r != nil { 1087 switch v := r.(type) { 1088 case error: 1089 err = v 1090 default: 1091 err = fmt.Errorf("%s", v) 1092 } 1093 } 1094 }() 1095 1096 Repeat(b, count) 1097 1098 return 1099} 1100 1101// See Issue golang.org/issue/16237 1102func TestRepeatCatchesOverflow(t *testing.T) { 1103 tests := [...]struct { 1104 s string 1105 count int 1106 errStr string 1107 }{ 1108 0: {"--", -2147483647, "negative"}, 1109 1: {"", int(^uint(0) >> 1), ""}, 1110 2: {"-", 10, ""}, 1111 3: {"gopher", 0, ""}, 1112 4: {"-", -1, "negative"}, 1113 5: {"--", -102, "negative"}, 1114 6: {string(make([]byte, 255)), int((^uint(0))/255 + 1), "overflow"}, 1115 } 1116 1117 for i, tt := range tests { 1118 err := repeat([]byte(tt.s), tt.count) 1119 if tt.errStr == "" { 1120 if err != nil { 1121 t.Errorf("#%d panicked %v", i, err) 1122 } 1123 continue 1124 } 1125 1126 if err == nil || !strings.Contains(err.Error(), tt.errStr) { 1127 t.Errorf("#%d expected %q got %q", i, tt.errStr, err) 1128 } 1129 } 1130} 1131 1132func runesEqual(a, b []rune) bool { 1133 if len(a) != len(b) { 1134 return false 1135 } 1136 for i, r := range a { 1137 if r != b[i] { 1138 return false 1139 } 1140 } 1141 return true 1142} 1143 1144type RunesTest struct { 1145 in string 1146 out []rune 1147 lossy bool 1148} 1149 1150var RunesTests = []RunesTest{ 1151 {"", []rune{}, false}, 1152 {" ", []rune{32}, false}, 1153 {"ABC", []rune{65, 66, 67}, false}, 1154 {"abc", []rune{97, 98, 99}, false}, 1155 {"\u65e5\u672c\u8a9e", []rune{26085, 26412, 35486}, false}, 1156 {"ab\x80c", []rune{97, 98, 0xFFFD, 99}, true}, 1157 {"ab\xc0c", []rune{97, 98, 0xFFFD, 99}, true}, 1158} 1159 1160func TestRunes(t *testing.T) { 1161 for _, tt := range RunesTests { 1162 tin := []byte(tt.in) 1163 a := Runes(tin) 1164 if !runesEqual(a, tt.out) { 1165 t.Errorf("Runes(%q) = %v; want %v", tin, a, tt.out) 1166 continue 1167 } 1168 if !tt.lossy { 1169 // can only test reassembly if we didn't lose information 1170 s := string(a) 1171 if s != tt.in { 1172 t.Errorf("string(Runes(%q)) = %x; want %x", tin, s, tin) 1173 } 1174 } 1175 } 1176} 1177 1178type TrimTest struct { 1179 f string 1180 in, arg, out string 1181} 1182 1183var trimTests = []TrimTest{ 1184 {"Trim", "abba", "a", "bb"}, 1185 {"Trim", "abba", "ab", ""}, 1186 {"TrimLeft", "abba", "ab", ""}, 1187 {"TrimRight", "abba", "ab", ""}, 1188 {"TrimLeft", "abba", "a", "bba"}, 1189 {"TrimRight", "abba", "a", "abb"}, 1190 {"Trim", "<tag>", "<>", "tag"}, 1191 {"Trim", "* listitem", " *", "listitem"}, 1192 {"Trim", `"quote"`, `"`, "quote"}, 1193 {"Trim", "\u2C6F\u2C6F\u0250\u0250\u2C6F\u2C6F", "\u2C6F", "\u0250\u0250"}, 1194 {"Trim", "\x80test\xff", "\xff", "test"}, 1195 {"Trim", " Ġ ", " ", "Ġ"}, 1196 {"Trim", " Ġİ0", "0 ", "Ġİ"}, 1197 //empty string tests 1198 {"Trim", "abba", "", "abba"}, 1199 {"Trim", "", "123", ""}, 1200 {"Trim", "", "", ""}, 1201 {"TrimLeft", "abba", "", "abba"}, 1202 {"TrimLeft", "", "123", ""}, 1203 {"TrimLeft", "", "", ""}, 1204 {"TrimRight", "abba", "", "abba"}, 1205 {"TrimRight", "", "123", ""}, 1206 {"TrimRight", "", "", ""}, 1207 {"TrimRight", "☺\xc0", "☺", "☺\xc0"}, 1208 {"TrimPrefix", "aabb", "a", "abb"}, 1209 {"TrimPrefix", "aabb", "b", "aabb"}, 1210 {"TrimSuffix", "aabb", "a", "aabb"}, 1211 {"TrimSuffix", "aabb", "b", "aab"}, 1212} 1213 1214func TestTrim(t *testing.T) { 1215 for _, tc := range trimTests { 1216 name := tc.f 1217 var f func([]byte, string) []byte 1218 var fb func([]byte, []byte) []byte 1219 switch name { 1220 case "Trim": 1221 f = Trim 1222 case "TrimLeft": 1223 f = TrimLeft 1224 case "TrimRight": 1225 f = TrimRight 1226 case "TrimPrefix": 1227 fb = TrimPrefix 1228 case "TrimSuffix": 1229 fb = TrimSuffix 1230 default: 1231 t.Errorf("Undefined trim function %s", name) 1232 } 1233 var actual string 1234 if f != nil { 1235 actual = string(f([]byte(tc.in), tc.arg)) 1236 } else { 1237 actual = string(fb([]byte(tc.in), []byte(tc.arg))) 1238 } 1239 if actual != tc.out { 1240 t.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.arg, actual, tc.out) 1241 } 1242 } 1243} 1244 1245type predicate struct { 1246 f func(r rune) bool 1247 name string 1248} 1249 1250var isSpace = predicate{unicode.IsSpace, "IsSpace"} 1251var isDigit = predicate{unicode.IsDigit, "IsDigit"} 1252var isUpper = predicate{unicode.IsUpper, "IsUpper"} 1253var isValidRune = predicate{ 1254 func(r rune) bool { 1255 return r != utf8.RuneError 1256 }, 1257 "IsValidRune", 1258} 1259 1260type TrimFuncTest struct { 1261 f predicate 1262 in, out string 1263} 1264 1265func not(p predicate) predicate { 1266 return predicate{ 1267 func(r rune) bool { 1268 return !p.f(r) 1269 }, 1270 "not " + p.name, 1271 } 1272} 1273 1274var trimFuncTests = []TrimFuncTest{ 1275 {isSpace, space + " hello " + space, "hello"}, 1276 {isDigit, "\u0e50\u0e5212hello34\u0e50\u0e51", "hello"}, 1277 {isUpper, "\u2C6F\u2C6F\u2C6F\u2C6FABCDhelloEF\u2C6F\u2C6FGH\u2C6F\u2C6F", "hello"}, 1278 {not(isSpace), "hello" + space + "hello", space}, 1279 {not(isDigit), "hello\u0e50\u0e521234\u0e50\u0e51helo", "\u0e50\u0e521234\u0e50\u0e51"}, 1280 {isValidRune, "ab\xc0a\xc0cd", "\xc0a\xc0"}, 1281 {not(isValidRune), "\xc0a\xc0", "a"}, 1282} 1283 1284func TestTrimFunc(t *testing.T) { 1285 for _, tc := range trimFuncTests { 1286 actual := string(TrimFunc([]byte(tc.in), tc.f.f)) 1287 if actual != tc.out { 1288 t.Errorf("TrimFunc(%q, %q) = %q; want %q", tc.in, tc.f.name, actual, tc.out) 1289 } 1290 } 1291} 1292 1293type IndexFuncTest struct { 1294 in string 1295 f predicate 1296 first, last int 1297} 1298 1299var indexFuncTests = []IndexFuncTest{ 1300 {"", isValidRune, -1, -1}, 1301 {"abc", isDigit, -1, -1}, 1302 {"0123", isDigit, 0, 3}, 1303 {"a1b", isDigit, 1, 1}, 1304 {space, isSpace, 0, len(space) - 3}, // last rune in space is 3 bytes 1305 {"\u0e50\u0e5212hello34\u0e50\u0e51", isDigit, 0, 18}, 1306 {"\u2C6F\u2C6F\u2C6F\u2C6FABCDhelloEF\u2C6F\u2C6FGH\u2C6F\u2C6F", isUpper, 0, 34}, 1307 {"12\u0e50\u0e52hello34\u0e50\u0e51", not(isDigit), 8, 12}, 1308 1309 // tests of invalid UTF-8 1310 {"\x801", isDigit, 1, 1}, 1311 {"\x80abc", isDigit, -1, -1}, 1312 {"\xc0a\xc0", isValidRune, 1, 1}, 1313 {"\xc0a\xc0", not(isValidRune), 0, 2}, 1314 {"\xc0☺\xc0", not(isValidRune), 0, 4}, 1315 {"\xc0☺\xc0\xc0", not(isValidRune), 0, 5}, 1316 {"ab\xc0a\xc0cd", not(isValidRune), 2, 4}, 1317 {"a\xe0\x80cd", not(isValidRune), 1, 2}, 1318} 1319 1320func TestIndexFunc(t *testing.T) { 1321 for _, tc := range indexFuncTests { 1322 first := IndexFunc([]byte(tc.in), tc.f.f) 1323 if first != tc.first { 1324 t.Errorf("IndexFunc(%q, %s) = %d; want %d", tc.in, tc.f.name, first, tc.first) 1325 } 1326 last := LastIndexFunc([]byte(tc.in), tc.f.f) 1327 if last != tc.last { 1328 t.Errorf("LastIndexFunc(%q, %s) = %d; want %d", tc.in, tc.f.name, last, tc.last) 1329 } 1330 } 1331} 1332 1333type ReplaceTest struct { 1334 in string 1335 old, new string 1336 n int 1337 out string 1338} 1339 1340var ReplaceTests = []ReplaceTest{ 1341 {"hello", "l", "L", 0, "hello"}, 1342 {"hello", "l", "L", -1, "heLLo"}, 1343 {"hello", "x", "X", -1, "hello"}, 1344 {"", "x", "X", -1, ""}, 1345 {"radar", "r", "<r>", -1, "<r>ada<r>"}, 1346 {"", "", "<>", -1, "<>"}, 1347 {"banana", "a", "<>", -1, "b<>n<>n<>"}, 1348 {"banana", "a", "<>", 1, "b<>nana"}, 1349 {"banana", "a", "<>", 1000, "b<>n<>n<>"}, 1350 {"banana", "an", "<>", -1, "b<><>a"}, 1351 {"banana", "ana", "<>", -1, "b<>na"}, 1352 {"banana", "", "<>", -1, "<>b<>a<>n<>a<>n<>a<>"}, 1353 {"banana", "", "<>", 10, "<>b<>a<>n<>a<>n<>a<>"}, 1354 {"banana", "", "<>", 6, "<>b<>a<>n<>a<>n<>a"}, 1355 {"banana", "", "<>", 5, "<>b<>a<>n<>a<>na"}, 1356 {"banana", "", "<>", 1, "<>banana"}, 1357 {"banana", "a", "a", -1, "banana"}, 1358 {"banana", "a", "a", 1, "banana"}, 1359 {"☺☻☹", "", "<>", -1, "<>☺<>☻<>☹<>"}, 1360} 1361 1362func TestReplace(t *testing.T) { 1363 for _, tt := range ReplaceTests { 1364 in := append([]byte(tt.in), "<spare>"...) 1365 in = in[:len(tt.in)] 1366 out := Replace(in, []byte(tt.old), []byte(tt.new), tt.n) 1367 if s := string(out); s != tt.out { 1368 t.Errorf("Replace(%q, %q, %q, %d) = %q, want %q", tt.in, tt.old, tt.new, tt.n, s, tt.out) 1369 } 1370 if cap(in) == cap(out) && &in[:1][0] == &out[:1][0] { 1371 t.Errorf("Replace(%q, %q, %q, %d) didn't copy", tt.in, tt.old, tt.new, tt.n) 1372 } 1373 } 1374} 1375 1376type TitleTest struct { 1377 in, out string 1378} 1379 1380var TitleTests = []TitleTest{ 1381 {"", ""}, 1382 {"a", "A"}, 1383 {" aaa aaa aaa ", " Aaa Aaa Aaa "}, 1384 {" Aaa Aaa Aaa ", " Aaa Aaa Aaa "}, 1385 {"123a456", "123a456"}, 1386 {"double-blind", "Double-Blind"}, 1387 {"ÿøû", "Ÿøû"}, 1388 {"with_underscore", "With_underscore"}, 1389 {"unicode \xe2\x80\xa8 line separator", "Unicode \xe2\x80\xa8 Line Separator"}, 1390} 1391 1392func TestTitle(t *testing.T) { 1393 for _, tt := range TitleTests { 1394 if s := string(Title([]byte(tt.in))); s != tt.out { 1395 t.Errorf("Title(%q) = %q, want %q", tt.in, s, tt.out) 1396 } 1397 } 1398} 1399 1400var ToTitleTests = []TitleTest{ 1401 {"", ""}, 1402 {"a", "A"}, 1403 {" aaa aaa aaa ", " AAA AAA AAA "}, 1404 {" Aaa Aaa Aaa ", " AAA AAA AAA "}, 1405 {"123a456", "123A456"}, 1406 {"double-blind", "DOUBLE-BLIND"}, 1407 {"ÿøû", "ŸØÛ"}, 1408} 1409 1410func TestToTitle(t *testing.T) { 1411 for _, tt := range ToTitleTests { 1412 if s := string(ToTitle([]byte(tt.in))); s != tt.out { 1413 t.Errorf("ToTitle(%q) = %q, want %q", tt.in, s, tt.out) 1414 } 1415 } 1416} 1417 1418var EqualFoldTests = []struct { 1419 s, t string 1420 out bool 1421}{ 1422 {"abc", "abc", true}, 1423 {"ABcd", "ABcd", true}, 1424 {"123abc", "123ABC", true}, 1425 {"αβδ", "ΑΒΔ", true}, 1426 {"abc", "xyz", false}, 1427 {"abc", "XYZ", false}, 1428 {"abcdefghijk", "abcdefghijX", false}, 1429 {"abcdefghijk", "abcdefghij\u212A", true}, 1430 {"abcdefghijK", "abcdefghij\u212A", true}, 1431 {"abcdefghijkz", "abcdefghij\u212Ay", false}, 1432 {"abcdefghijKz", "abcdefghij\u212Ay", false}, 1433} 1434 1435func TestEqualFold(t *testing.T) { 1436 for _, tt := range EqualFoldTests { 1437 if out := EqualFold([]byte(tt.s), []byte(tt.t)); out != tt.out { 1438 t.Errorf("EqualFold(%#q, %#q) = %v, want %v", tt.s, tt.t, out, tt.out) 1439 } 1440 if out := EqualFold([]byte(tt.t), []byte(tt.s)); out != tt.out { 1441 t.Errorf("EqualFold(%#q, %#q) = %v, want %v", tt.t, tt.s, out, tt.out) 1442 } 1443 } 1444} 1445 1446func TestBufferGrowNegative(t *testing.T) { 1447 defer func() { 1448 if err := recover(); err == nil { 1449 t.Fatal("Grow(-1) should have panicked") 1450 } 1451 }() 1452 var b Buffer 1453 b.Grow(-1) 1454} 1455 1456func TestBufferTruncateNegative(t *testing.T) { 1457 defer func() { 1458 if err := recover(); err == nil { 1459 t.Fatal("Truncate(-1) should have panicked") 1460 } 1461 }() 1462 var b Buffer 1463 b.Truncate(-1) 1464} 1465 1466func TestBufferTruncateOutOfRange(t *testing.T) { 1467 defer func() { 1468 if err := recover(); err == nil { 1469 t.Fatal("Truncate(20) should have panicked") 1470 } 1471 }() 1472 var b Buffer 1473 b.Write(make([]byte, 10)) 1474 b.Truncate(20) 1475} 1476 1477var containsTests = []struct { 1478 b, subslice []byte 1479 want bool 1480}{ 1481 {[]byte("hello"), []byte("hel"), true}, 1482 {[]byte("日本語"), []byte("日本"), true}, 1483 {[]byte("hello"), []byte("Hello, world"), false}, 1484 {[]byte("東京"), []byte("京東"), false}, 1485} 1486 1487func TestContains(t *testing.T) { 1488 for _, tt := range containsTests { 1489 if got := Contains(tt.b, tt.subslice); got != tt.want { 1490 t.Errorf("Contains(%q, %q) = %v, want %v", tt.b, tt.subslice, got, tt.want) 1491 } 1492 } 1493} 1494 1495var ContainsAnyTests = []struct { 1496 b []byte 1497 substr string 1498 expected bool 1499}{ 1500 {[]byte(""), "", false}, 1501 {[]byte(""), "a", false}, 1502 {[]byte(""), "abc", false}, 1503 {[]byte("a"), "", false}, 1504 {[]byte("a"), "a", true}, 1505 {[]byte("aaa"), "a", true}, 1506 {[]byte("abc"), "xyz", false}, 1507 {[]byte("abc"), "xcz", true}, 1508 {[]byte("a☺b☻c☹d"), "uvw☻xyz", true}, 1509 {[]byte("aRegExp*"), ".(|)*+?^$[]", true}, 1510 {[]byte(dots + dots + dots), " ", false}, 1511} 1512 1513func TestContainsAny(t *testing.T) { 1514 for _, ct := range ContainsAnyTests { 1515 if ContainsAny(ct.b, ct.substr) != ct.expected { 1516 t.Errorf("ContainsAny(%s, %s) = %v, want %v", 1517 ct.b, ct.substr, !ct.expected, ct.expected) 1518 } 1519 } 1520} 1521 1522var ContainsRuneTests = []struct { 1523 b []byte 1524 r rune 1525 expected bool 1526}{ 1527 {[]byte(""), 'a', false}, 1528 {[]byte("a"), 'a', true}, 1529 {[]byte("aaa"), 'a', true}, 1530 {[]byte("abc"), 'y', false}, 1531 {[]byte("abc"), 'c', true}, 1532 {[]byte("a☺b☻c☹d"), 'x', false}, 1533 {[]byte("a☺b☻c☹d"), '☻', true}, 1534 {[]byte("aRegExp*"), '*', true}, 1535} 1536 1537func TestContainsRune(t *testing.T) { 1538 for _, ct := range ContainsRuneTests { 1539 if ContainsRune(ct.b, ct.r) != ct.expected { 1540 t.Errorf("ContainsRune(%q, %q) = %v, want %v", 1541 ct.b, ct.r, !ct.expected, ct.expected) 1542 } 1543 } 1544} 1545 1546var makeFieldsInput = func() []byte { 1547 x := make([]byte, 1<<20) 1548 // Input is ~10% space, ~10% 2-byte UTF-8, rest ASCII non-space. 1549 for i := range x { 1550 switch rand.Intn(10) { 1551 case 0: 1552 x[i] = ' ' 1553 case 1: 1554 if i > 0 && x[i-1] == 'x' { 1555 copy(x[i-1:], "χ") 1556 break 1557 } 1558 fallthrough 1559 default: 1560 x[i] = 'x' 1561 } 1562 } 1563 return x 1564} 1565 1566var makeFieldsInputASCII = func() []byte { 1567 x := make([]byte, 1<<20) 1568 // Input is ~10% space, rest ASCII non-space. 1569 for i := range x { 1570 if rand.Intn(10) == 0 { 1571 x[i] = ' ' 1572 } else { 1573 x[i] = 'x' 1574 } 1575 } 1576 return x 1577} 1578 1579var bytesdata = []struct { 1580 name string 1581 data []byte 1582}{ 1583 {"ASCII", makeFieldsInputASCII()}, 1584 {"Mixed", makeFieldsInput()}, 1585} 1586 1587func BenchmarkFields(b *testing.B) { 1588 for _, sd := range bytesdata { 1589 b.Run(sd.name, func(b *testing.B) { 1590 for j := 1 << 4; j <= 1<<20; j <<= 4 { 1591 b.Run(fmt.Sprintf("%d", j), func(b *testing.B) { 1592 b.ReportAllocs() 1593 b.SetBytes(int64(j)) 1594 data := sd.data[:j] 1595 for i := 0; i < b.N; i++ { 1596 Fields(data) 1597 } 1598 }) 1599 } 1600 }) 1601 } 1602} 1603 1604func BenchmarkFieldsFunc(b *testing.B) { 1605 for _, sd := range bytesdata { 1606 b.Run(sd.name, func(b *testing.B) { 1607 for j := 1 << 4; j <= 1<<20; j <<= 4 { 1608 b.Run(fmt.Sprintf("%d", j), func(b *testing.B) { 1609 b.ReportAllocs() 1610 b.SetBytes(int64(j)) 1611 data := sd.data[:j] 1612 for i := 0; i < b.N; i++ { 1613 FieldsFunc(data, unicode.IsSpace) 1614 } 1615 }) 1616 } 1617 }) 1618 } 1619} 1620 1621func BenchmarkTrimSpace(b *testing.B) { 1622 s := []byte(" Some text. \n") 1623 for i := 0; i < b.N; i++ { 1624 TrimSpace(s) 1625 } 1626} 1627 1628func makeBenchInputHard() []byte { 1629 tokens := [...]string{ 1630 "<a>", "<p>", "<b>", "<strong>", 1631 "</a>", "</p>", "</b>", "</strong>", 1632 "hello", "world", 1633 } 1634 x := make([]byte, 0, 1<<20) 1635 for { 1636 i := rand.Intn(len(tokens)) 1637 if len(x)+len(tokens[i]) >= 1<<20 { 1638 break 1639 } 1640 x = append(x, tokens[i]...) 1641 } 1642 return x 1643} 1644 1645var benchInputHard = makeBenchInputHard() 1646 1647func BenchmarkSplitEmptySeparator(b *testing.B) { 1648 for i := 0; i < b.N; i++ { 1649 Split(benchInputHard, nil) 1650 } 1651} 1652 1653func BenchmarkSplitSingleByteSeparator(b *testing.B) { 1654 sep := []byte("/") 1655 for i := 0; i < b.N; i++ { 1656 Split(benchInputHard, sep) 1657 } 1658} 1659 1660func BenchmarkSplitMultiByteSeparator(b *testing.B) { 1661 sep := []byte("hello") 1662 for i := 0; i < b.N; i++ { 1663 Split(benchInputHard, sep) 1664 } 1665} 1666 1667func BenchmarkSplitNSingleByteSeparator(b *testing.B) { 1668 sep := []byte("/") 1669 for i := 0; i < b.N; i++ { 1670 SplitN(benchInputHard, sep, 10) 1671 } 1672} 1673 1674func BenchmarkSplitNMultiByteSeparator(b *testing.B) { 1675 sep := []byte("hello") 1676 for i := 0; i < b.N; i++ { 1677 SplitN(benchInputHard, sep, 10) 1678 } 1679} 1680 1681func BenchmarkRepeat(b *testing.B) { 1682 for i := 0; i < b.N; i++ { 1683 Repeat([]byte("-"), 80) 1684 } 1685} 1686 1687func BenchmarkBytesCompare(b *testing.B) { 1688 for n := 1; n <= 2048; n <<= 1 { 1689 b.Run(fmt.Sprint(n), func(b *testing.B) { 1690 var x = make([]byte, n) 1691 var y = make([]byte, n) 1692 1693 for i := 0; i < n; i++ { 1694 x[i] = 'a' 1695 } 1696 1697 for i := 0; i < n; i++ { 1698 y[i] = 'a' 1699 } 1700 1701 b.ResetTimer() 1702 for i := 0; i < b.N; i++ { 1703 Compare(x, y) 1704 } 1705 }) 1706 } 1707} 1708 1709func BenchmarkIndexAnyASCII(b *testing.B) { 1710 x := Repeat([]byte{'#'}, 4096) // Never matches set 1711 cs := "0123456789abcdef" 1712 for k := 1; k <= 4096; k <<= 4 { 1713 for j := 1; j <= 16; j <<= 1 { 1714 b.Run(fmt.Sprintf("%d:%d", k, j), func(b *testing.B) { 1715 for i := 0; i < b.N; i++ { 1716 IndexAny(x[:k], cs[:j]) 1717 } 1718 }) 1719 } 1720 } 1721} 1722 1723func BenchmarkTrimASCII(b *testing.B) { 1724 cs := "0123456789abcdef" 1725 for k := 1; k <= 4096; k <<= 4 { 1726 for j := 1; j <= 16; j <<= 1 { 1727 b.Run(fmt.Sprintf("%d:%d", k, j), func(b *testing.B) { 1728 x := Repeat([]byte(cs[:j]), k) // Always matches set 1729 for i := 0; i < b.N; i++ { 1730 Trim(x[:k], cs[:j]) 1731 } 1732 }) 1733 } 1734 } 1735} 1736 1737func BenchmarkIndexPeriodic(b *testing.B) { 1738 key := []byte{1, 1} 1739 for _, skip := range [...]int{2, 4, 8, 16, 32, 64} { 1740 b.Run(fmt.Sprintf("IndexPeriodic%d", skip), func(b *testing.B) { 1741 buf := make([]byte, 1<<16) 1742 for i := 0; i < len(buf); i += skip { 1743 buf[i] = 1 1744 } 1745 for i := 0; i < b.N; i++ { 1746 Index(buf, key) 1747 } 1748 }) 1749 } 1750} 1751