github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/slices/slices_test.go (about) 1 // Copyright 2021 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 slices_test 6 7 import ( 8 "cmp" 9 "internal/race" 10 "internal/testenv" 11 "math" 12 . "slices" 13 "strings" 14 "testing" 15 ) 16 17 var equalIntTests = []struct { 18 s1, s2 []int 19 want bool 20 }{ 21 { 22 []int{1}, 23 nil, 24 false, 25 }, 26 { 27 []int{}, 28 nil, 29 true, 30 }, 31 { 32 []int{1, 2, 3}, 33 []int{1, 2, 3}, 34 true, 35 }, 36 { 37 []int{1, 2, 3}, 38 []int{1, 2, 3, 4}, 39 false, 40 }, 41 } 42 43 var equalFloatTests = []struct { 44 s1, s2 []float64 45 wantEqual bool 46 wantEqualNaN bool 47 }{ 48 { 49 []float64{1, 2}, 50 []float64{1, 2}, 51 true, 52 true, 53 }, 54 { 55 []float64{1, 2, math.NaN()}, 56 []float64{1, 2, math.NaN()}, 57 false, 58 true, 59 }, 60 } 61 62 func TestEqual(t *testing.T) { 63 for _, test := range equalIntTests { 64 if got := Equal(test.s1, test.s2); got != test.want { 65 t.Errorf("Equal(%v, %v) = %t, want %t", test.s1, test.s2, got, test.want) 66 } 67 } 68 for _, test := range equalFloatTests { 69 if got := Equal(test.s1, test.s2); got != test.wantEqual { 70 t.Errorf("Equal(%v, %v) = %t, want %t", test.s1, test.s2, got, test.wantEqual) 71 } 72 } 73 } 74 75 // equal is simply ==. 76 func equal[T comparable](v1, v2 T) bool { 77 return v1 == v2 78 } 79 80 // equalNaN is like == except that all NaNs are equal. 81 func equalNaN[T comparable](v1, v2 T) bool { 82 isNaN := func(f T) bool { return f != f } 83 return v1 == v2 || (isNaN(v1) && isNaN(v2)) 84 } 85 86 // offByOne returns true if integers v1 and v2 differ by 1. 87 func offByOne(v1, v2 int) bool { 88 return v1 == v2+1 || v1 == v2-1 89 } 90 91 func TestEqualFunc(t *testing.T) { 92 for _, test := range equalIntTests { 93 if got := EqualFunc(test.s1, test.s2, equal[int]); got != test.want { 94 t.Errorf("EqualFunc(%v, %v, equal[int]) = %t, want %t", test.s1, test.s2, got, test.want) 95 } 96 } 97 for _, test := range equalFloatTests { 98 if got := EqualFunc(test.s1, test.s2, equal[float64]); got != test.wantEqual { 99 t.Errorf("Equal(%v, %v, equal[float64]) = %t, want %t", test.s1, test.s2, got, test.wantEqual) 100 } 101 if got := EqualFunc(test.s1, test.s2, equalNaN[float64]); got != test.wantEqualNaN { 102 t.Errorf("Equal(%v, %v, equalNaN[float64]) = %t, want %t", test.s1, test.s2, got, test.wantEqualNaN) 103 } 104 } 105 106 s1 := []int{1, 2, 3} 107 s2 := []int{2, 3, 4} 108 if EqualFunc(s1, s1, offByOne) { 109 t.Errorf("EqualFunc(%v, %v, offByOne) = true, want false", s1, s1) 110 } 111 if !EqualFunc(s1, s2, offByOne) { 112 t.Errorf("EqualFunc(%v, %v, offByOne) = false, want true", s1, s2) 113 } 114 115 s3 := []string{"a", "b", "c"} 116 s4 := []string{"A", "B", "C"} 117 if !EqualFunc(s3, s4, strings.EqualFold) { 118 t.Errorf("EqualFunc(%v, %v, strings.EqualFold) = false, want true", s3, s4) 119 } 120 121 cmpIntString := func(v1 int, v2 string) bool { 122 return string(rune(v1)-1+'a') == v2 123 } 124 if !EqualFunc(s1, s3, cmpIntString) { 125 t.Errorf("EqualFunc(%v, %v, cmpIntString) = false, want true", s1, s3) 126 } 127 } 128 129 func BenchmarkEqualFunc_Large(b *testing.B) { 130 type Large [4 * 1024]byte 131 132 xs := make([]Large, 1024) 133 ys := make([]Large, 1024) 134 for i := 0; i < b.N; i++ { 135 _ = EqualFunc(xs, ys, func(x, y Large) bool { return x == y }) 136 } 137 } 138 139 var compareIntTests = []struct { 140 s1, s2 []int 141 want int 142 }{ 143 { 144 []int{1}, 145 []int{1}, 146 0, 147 }, 148 { 149 []int{1}, 150 []int{}, 151 1, 152 }, 153 { 154 []int{}, 155 []int{1}, 156 -1, 157 }, 158 { 159 []int{}, 160 []int{}, 161 0, 162 }, 163 { 164 []int{1, 2, 3}, 165 []int{1, 2, 3}, 166 0, 167 }, 168 { 169 []int{1, 2, 3}, 170 []int{1, 2, 3, 4}, 171 -1, 172 }, 173 { 174 []int{1, 2, 3, 4}, 175 []int{1, 2, 3}, 176 +1, 177 }, 178 { 179 []int{1, 2, 3}, 180 []int{1, 4, 3}, 181 -1, 182 }, 183 { 184 []int{1, 4, 3}, 185 []int{1, 2, 3}, 186 +1, 187 }, 188 { 189 []int{1, 4, 3}, 190 []int{1, 2, 3, 8, 9}, 191 +1, 192 }, 193 } 194 195 var compareFloatTests = []struct { 196 s1, s2 []float64 197 want int 198 }{ 199 { 200 []float64{}, 201 []float64{}, 202 0, 203 }, 204 { 205 []float64{1}, 206 []float64{1}, 207 0, 208 }, 209 { 210 []float64{math.NaN()}, 211 []float64{math.NaN()}, 212 0, 213 }, 214 { 215 []float64{1, 2, math.NaN()}, 216 []float64{1, 2, math.NaN()}, 217 0, 218 }, 219 { 220 []float64{1, math.NaN(), 3}, 221 []float64{1, math.NaN(), 4}, 222 -1, 223 }, 224 { 225 []float64{1, math.NaN(), 3}, 226 []float64{1, 2, 4}, 227 -1, 228 }, 229 { 230 []float64{1, math.NaN(), 3}, 231 []float64{1, 2, math.NaN()}, 232 -1, 233 }, 234 { 235 []float64{1, 2, 3}, 236 []float64{1, 2, math.NaN()}, 237 +1, 238 }, 239 { 240 []float64{1, 2, 3}, 241 []float64{1, math.NaN(), 3}, 242 +1, 243 }, 244 { 245 []float64{1, math.NaN(), 3, 4}, 246 []float64{1, 2, math.NaN()}, 247 -1, 248 }, 249 } 250 251 func TestCompare(t *testing.T) { 252 intWant := func(want bool) string { 253 if want { 254 return "0" 255 } 256 return "!= 0" 257 } 258 for _, test := range equalIntTests { 259 if got := Compare(test.s1, test.s2); (got == 0) != test.want { 260 t.Errorf("Compare(%v, %v) = %d, want %s", test.s1, test.s2, got, intWant(test.want)) 261 } 262 } 263 for _, test := range equalFloatTests { 264 if got := Compare(test.s1, test.s2); (got == 0) != test.wantEqualNaN { 265 t.Errorf("Compare(%v, %v) = %d, want %s", test.s1, test.s2, got, intWant(test.wantEqualNaN)) 266 } 267 } 268 269 for _, test := range compareIntTests { 270 if got := Compare(test.s1, test.s2); got != test.want { 271 t.Errorf("Compare(%v, %v) = %d, want %d", test.s1, test.s2, got, test.want) 272 } 273 } 274 for _, test := range compareFloatTests { 275 if got := Compare(test.s1, test.s2); got != test.want { 276 t.Errorf("Compare(%v, %v) = %d, want %d", test.s1, test.s2, got, test.want) 277 } 278 } 279 } 280 281 func equalToCmp[T comparable](eq func(T, T) bool) func(T, T) int { 282 return func(v1, v2 T) int { 283 if eq(v1, v2) { 284 return 0 285 } 286 return 1 287 } 288 } 289 290 func TestCompareFunc(t *testing.T) { 291 intWant := func(want bool) string { 292 if want { 293 return "0" 294 } 295 return "!= 0" 296 } 297 for _, test := range equalIntTests { 298 if got := CompareFunc(test.s1, test.s2, equalToCmp(equal[int])); (got == 0) != test.want { 299 t.Errorf("CompareFunc(%v, %v, equalToCmp(equal[int])) = %d, want %s", test.s1, test.s2, got, intWant(test.want)) 300 } 301 } 302 for _, test := range equalFloatTests { 303 if got := CompareFunc(test.s1, test.s2, equalToCmp(equal[float64])); (got == 0) != test.wantEqual { 304 t.Errorf("CompareFunc(%v, %v, equalToCmp(equal[float64])) = %d, want %s", test.s1, test.s2, got, intWant(test.wantEqual)) 305 } 306 } 307 308 for _, test := range compareIntTests { 309 if got := CompareFunc(test.s1, test.s2, cmp.Compare[int]); got != test.want { 310 t.Errorf("CompareFunc(%v, %v, cmp[int]) = %d, want %d", test.s1, test.s2, got, test.want) 311 } 312 } 313 for _, test := range compareFloatTests { 314 if got := CompareFunc(test.s1, test.s2, cmp.Compare[float64]); got != test.want { 315 t.Errorf("CompareFunc(%v, %v, cmp[float64]) = %d, want %d", test.s1, test.s2, got, test.want) 316 } 317 } 318 319 s1 := []int{1, 2, 3} 320 s2 := []int{2, 3, 4} 321 if got := CompareFunc(s1, s2, equalToCmp(offByOne)); got != 0 { 322 t.Errorf("CompareFunc(%v, %v, offByOne) = %d, want 0", s1, s2, got) 323 } 324 325 s3 := []string{"a", "b", "c"} 326 s4 := []string{"A", "B", "C"} 327 if got := CompareFunc(s3, s4, strings.Compare); got != 1 { 328 t.Errorf("CompareFunc(%v, %v, strings.Compare) = %d, want 1", s3, s4, got) 329 } 330 331 compareLower := func(v1, v2 string) int { 332 return strings.Compare(strings.ToLower(v1), strings.ToLower(v2)) 333 } 334 if got := CompareFunc(s3, s4, compareLower); got != 0 { 335 t.Errorf("CompareFunc(%v, %v, compareLower) = %d, want 0", s3, s4, got) 336 } 337 338 cmpIntString := func(v1 int, v2 string) int { 339 return strings.Compare(string(rune(v1)-1+'a'), v2) 340 } 341 if got := CompareFunc(s1, s3, cmpIntString); got != 0 { 342 t.Errorf("CompareFunc(%v, %v, cmpIntString) = %d, want 0", s1, s3, got) 343 } 344 } 345 346 var indexTests = []struct { 347 s []int 348 v int 349 want int 350 }{ 351 { 352 nil, 353 0, 354 -1, 355 }, 356 { 357 []int{}, 358 0, 359 -1, 360 }, 361 { 362 []int{1, 2, 3}, 363 2, 364 1, 365 }, 366 { 367 []int{1, 2, 2, 3}, 368 2, 369 1, 370 }, 371 { 372 []int{1, 2, 3, 2}, 373 2, 374 1, 375 }, 376 } 377 378 func TestIndex(t *testing.T) { 379 for _, test := range indexTests { 380 if got := Index(test.s, test.v); got != test.want { 381 t.Errorf("Index(%v, %v) = %d, want %d", test.s, test.v, got, test.want) 382 } 383 } 384 } 385 386 func equalToIndex[T any](f func(T, T) bool, v1 T) func(T) bool { 387 return func(v2 T) bool { 388 return f(v1, v2) 389 } 390 } 391 392 func BenchmarkIndex_Large(b *testing.B) { 393 type Large [4 * 1024]byte 394 395 ss := make([]Large, 1024) 396 for i := 0; i < b.N; i++ { 397 _ = Index(ss, Large{1}) 398 } 399 } 400 401 func TestIndexFunc(t *testing.T) { 402 for _, test := range indexTests { 403 if got := IndexFunc(test.s, equalToIndex(equal[int], test.v)); got != test.want { 404 t.Errorf("IndexFunc(%v, equalToIndex(equal[int], %v)) = %d, want %d", test.s, test.v, got, test.want) 405 } 406 } 407 408 s1 := []string{"hi", "HI"} 409 if got := IndexFunc(s1, equalToIndex(equal[string], "HI")); got != 1 { 410 t.Errorf("IndexFunc(%v, equalToIndex(equal[string], %q)) = %d, want %d", s1, "HI", got, 1) 411 } 412 if got := IndexFunc(s1, equalToIndex(strings.EqualFold, "HI")); got != 0 { 413 t.Errorf("IndexFunc(%v, equalToIndex(strings.EqualFold, %q)) = %d, want %d", s1, "HI", got, 0) 414 } 415 } 416 417 func BenchmarkIndexFunc_Large(b *testing.B) { 418 type Large [4 * 1024]byte 419 420 ss := make([]Large, 1024) 421 for i := 0; i < b.N; i++ { 422 _ = IndexFunc(ss, func(e Large) bool { 423 return e == Large{1} 424 }) 425 } 426 } 427 428 func TestContains(t *testing.T) { 429 for _, test := range indexTests { 430 if got := Contains(test.s, test.v); got != (test.want != -1) { 431 t.Errorf("Contains(%v, %v) = %t, want %t", test.s, test.v, got, test.want != -1) 432 } 433 } 434 } 435 436 func TestContainsFunc(t *testing.T) { 437 for _, test := range indexTests { 438 if got := ContainsFunc(test.s, equalToIndex(equal[int], test.v)); got != (test.want != -1) { 439 t.Errorf("ContainsFunc(%v, equalToIndex(equal[int], %v)) = %t, want %t", test.s, test.v, got, test.want != -1) 440 } 441 } 442 443 s1 := []string{"hi", "HI"} 444 if got := ContainsFunc(s1, equalToIndex(equal[string], "HI")); got != true { 445 t.Errorf("ContainsFunc(%v, equalToContains(equal[string], %q)) = %t, want %t", s1, "HI", got, true) 446 } 447 if got := ContainsFunc(s1, equalToIndex(equal[string], "hI")); got != false { 448 t.Errorf("ContainsFunc(%v, equalToContains(strings.EqualFold, %q)) = %t, want %t", s1, "hI", got, false) 449 } 450 if got := ContainsFunc(s1, equalToIndex(strings.EqualFold, "hI")); got != true { 451 t.Errorf("ContainsFunc(%v, equalToContains(strings.EqualFold, %q)) = %t, want %t", s1, "hI", got, true) 452 } 453 } 454 455 var insertTests = []struct { 456 s []int 457 i int 458 add []int 459 want []int 460 }{ 461 { 462 []int{1, 2, 3}, 463 0, 464 []int{4}, 465 []int{4, 1, 2, 3}, 466 }, 467 { 468 []int{1, 2, 3}, 469 1, 470 []int{4}, 471 []int{1, 4, 2, 3}, 472 }, 473 { 474 []int{1, 2, 3}, 475 3, 476 []int{4}, 477 []int{1, 2, 3, 4}, 478 }, 479 { 480 []int{1, 2, 3}, 481 2, 482 []int{4, 5}, 483 []int{1, 2, 4, 5, 3}, 484 }, 485 } 486 487 func TestInsert(t *testing.T) { 488 s := []int{1, 2, 3} 489 if got := Insert(s, 0); !Equal(got, s) { 490 t.Errorf("Insert(%v, 0) = %v, want %v", s, got, s) 491 } 492 for _, test := range insertTests { 493 copy := Clone(test.s) 494 if got := Insert(copy, test.i, test.add...); !Equal(got, test.want) { 495 t.Errorf("Insert(%v, %d, %v...) = %v, want %v", test.s, test.i, test.add, got, test.want) 496 } 497 } 498 499 if !testenv.OptimizationOff() && !race.Enabled { 500 // Allocations should be amortized. 501 const count = 50 502 n := testing.AllocsPerRun(10, func() { 503 s := []int{1, 2, 3} 504 for i := 0; i < count; i++ { 505 s = Insert(s, 0, 1) 506 } 507 }) 508 if n > count/2 { 509 t.Errorf("too many allocations inserting %d elements: got %v, want less than %d", count, n, count/2) 510 } 511 } 512 } 513 514 func TestInsertOverlap(t *testing.T) { 515 const N = 10 516 a := make([]int, N) 517 want := make([]int, 2*N) 518 for n := 0; n <= N; n++ { // length 519 for i := 0; i <= n; i++ { // insertion point 520 for x := 0; x <= N; x++ { // start of inserted data 521 for y := x; y <= N; y++ { // end of inserted data 522 for k := 0; k < N; k++ { 523 a[k] = k 524 } 525 want = want[:0] 526 want = append(want, a[:i]...) 527 want = append(want, a[x:y]...) 528 want = append(want, a[i:n]...) 529 got := Insert(a[:n], i, a[x:y]...) 530 if !Equal(got, want) { 531 t.Errorf("Insert with overlap failed n=%d i=%d x=%d y=%d, got %v want %v", n, i, x, y, got, want) 532 } 533 } 534 } 535 } 536 } 537 } 538 539 func TestInsertPanics(t *testing.T) { 540 a := [3]int{} 541 b := [1]int{} 542 for _, test := range []struct { 543 name string 544 s []int 545 i int 546 v []int 547 }{ 548 // There are no values. 549 {"with negative index", a[:1:1], -1, nil}, 550 {"with out-of-bounds index and > cap", a[:1:1], 2, nil}, 551 {"with out-of-bounds index and = cap", a[:1:2], 2, nil}, 552 {"with out-of-bounds index and < cap", a[:1:3], 2, nil}, 553 554 // There are values. 555 {"with negative index", a[:1:1], -1, b[:]}, 556 {"with out-of-bounds index and > cap", a[:1:1], 2, b[:]}, 557 {"with out-of-bounds index and = cap", a[:1:2], 2, b[:]}, 558 {"with out-of-bounds index and < cap", a[:1:3], 2, b[:]}, 559 } { 560 if !panics(func() { _ = Insert(test.s, test.i, test.v...) }) { 561 t.Errorf("Insert %s: got no panic, want panic", test.name) 562 } 563 } 564 } 565 566 var deleteTests = []struct { 567 s []int 568 i, j int 569 want []int 570 }{ 571 { 572 []int{1, 2, 3}, 573 0, 574 0, 575 []int{1, 2, 3}, 576 }, 577 { 578 []int{1, 2, 3}, 579 0, 580 1, 581 []int{2, 3}, 582 }, 583 { 584 []int{1, 2, 3}, 585 3, 586 3, 587 []int{1, 2, 3}, 588 }, 589 { 590 []int{1, 2, 3}, 591 0, 592 2, 593 []int{3}, 594 }, 595 { 596 []int{1, 2, 3}, 597 0, 598 3, 599 []int{}, 600 }, 601 } 602 603 func TestDelete(t *testing.T) { 604 for _, test := range deleteTests { 605 copy := Clone(test.s) 606 if got := Delete(copy, test.i, test.j); !Equal(got, test.want) { 607 t.Errorf("Delete(%v, %d, %d) = %v, want %v", test.s, test.i, test.j, got, test.want) 608 } 609 } 610 } 611 612 var deleteFuncTests = []struct { 613 s []int 614 fn func(int) bool 615 want []int 616 }{ 617 { 618 nil, 619 func(int) bool { return true }, 620 nil, 621 }, 622 { 623 []int{1, 2, 3}, 624 func(int) bool { return true }, 625 nil, 626 }, 627 { 628 []int{1, 2, 3}, 629 func(int) bool { return false }, 630 []int{1, 2, 3}, 631 }, 632 { 633 []int{1, 2, 3}, 634 func(i int) bool { return i > 2 }, 635 []int{1, 2}, 636 }, 637 { 638 []int{1, 2, 3}, 639 func(i int) bool { return i < 2 }, 640 []int{2, 3}, 641 }, 642 { 643 []int{10, 2, 30}, 644 func(i int) bool { return i >= 10 }, 645 []int{2}, 646 }, 647 } 648 649 func TestDeleteFunc(t *testing.T) { 650 for i, test := range deleteFuncTests { 651 copy := Clone(test.s) 652 if got := DeleteFunc(copy, test.fn); !Equal(got, test.want) { 653 t.Errorf("DeleteFunc case %d: got %v, want %v", i, got, test.want) 654 } 655 } 656 } 657 658 func panics(f func()) (b bool) { 659 defer func() { 660 if x := recover(); x != nil { 661 b = true 662 } 663 }() 664 f() 665 return false 666 } 667 668 func TestDeletePanics(t *testing.T) { 669 s := []int{0, 1, 2, 3, 4} 670 s = s[0:2] 671 _ = s[0:4] // this is a valid slice of s 672 673 for _, test := range []struct { 674 name string 675 s []int 676 i, j int 677 }{ 678 {"with negative first index", []int{42}, -2, 1}, 679 {"with negative second index", []int{42}, 1, -1}, 680 {"with out-of-bounds first index", []int{42}, 2, 3}, 681 {"with out-of-bounds second index", []int{42}, 0, 2}, 682 {"with out-of-bounds both indexes", []int{42}, 2, 2}, 683 {"with invalid i>j", []int{42}, 1, 0}, 684 {"s[i:j] is valid and j > len(s)", s, 0, 4}, 685 {"s[i:j] is valid and i == j > len(s)", s, 3, 3}, 686 } { 687 if !panics(func() { _ = Delete(test.s, test.i, test.j) }) { 688 t.Errorf("Delete %s: got no panic, want panic", test.name) 689 } 690 } 691 } 692 693 func TestDeleteClearTail(t *testing.T) { 694 mem := []*int{new(int), new(int), new(int), new(int), new(int), new(int)} 695 s := mem[0:5] // there is 1 element beyond len(s), within cap(s) 696 697 s = Delete(s, 2, 4) 698 699 if mem[3] != nil || mem[4] != nil { 700 // Check that potential memory leak is avoided 701 t.Errorf("Delete: want nil discarded elements, got %v, %v", mem[3], mem[4]) 702 } 703 if mem[5] == nil { 704 t.Errorf("Delete: want unchanged elements beyond original len, got nil") 705 } 706 } 707 708 func TestDeleteFuncClearTail(t *testing.T) { 709 mem := []*int{new(int), new(int), new(int), new(int), new(int), new(int)} 710 *mem[2], *mem[3] = 42, 42 711 s := mem[0:5] // there is 1 element beyond len(s), within cap(s) 712 713 s = DeleteFunc(s, func(i *int) bool { 714 return i != nil && *i == 42 715 }) 716 717 if mem[3] != nil || mem[4] != nil { 718 // Check that potential memory leak is avoided 719 t.Errorf("DeleteFunc: want nil discarded elements, got %v, %v", mem[3], mem[4]) 720 } 721 if mem[5] == nil { 722 t.Errorf("DeleteFunc: want unchanged elements beyond original len, got nil") 723 } 724 } 725 726 func TestClone(t *testing.T) { 727 s1 := []int{1, 2, 3} 728 s2 := Clone(s1) 729 if !Equal(s1, s2) { 730 t.Errorf("Clone(%v) = %v, want %v", s1, s2, s1) 731 } 732 s1[0] = 4 733 want := []int{1, 2, 3} 734 if !Equal(s2, want) { 735 t.Errorf("Clone(%v) changed unexpectedly to %v", want, s2) 736 } 737 if got := Clone([]int(nil)); got != nil { 738 t.Errorf("Clone(nil) = %#v, want nil", got) 739 } 740 if got := Clone(s1[:0]); got == nil || len(got) != 0 { 741 t.Errorf("Clone(%v) = %#v, want %#v", s1[:0], got, s1[:0]) 742 } 743 } 744 745 var compactTests = []struct { 746 name string 747 s []int 748 want []int 749 }{ 750 { 751 "nil", 752 nil, 753 nil, 754 }, 755 { 756 "one", 757 []int{1}, 758 []int{1}, 759 }, 760 { 761 "sorted", 762 []int{1, 2, 3}, 763 []int{1, 2, 3}, 764 }, 765 { 766 "1 item", 767 []int{1, 1, 2}, 768 []int{1, 2}, 769 }, 770 { 771 "unsorted", 772 []int{1, 2, 1}, 773 []int{1, 2, 1}, 774 }, 775 { 776 "many", 777 []int{1, 2, 2, 3, 3, 4}, 778 []int{1, 2, 3, 4}, 779 }, 780 } 781 782 func TestCompact(t *testing.T) { 783 for _, test := range compactTests { 784 copy := Clone(test.s) 785 if got := Compact(copy); !Equal(got, test.want) { 786 t.Errorf("Compact(%v) = %v, want %v", test.s, got, test.want) 787 } 788 } 789 } 790 791 func BenchmarkCompact(b *testing.B) { 792 for _, c := range compactTests { 793 b.Run(c.name, func(b *testing.B) { 794 ss := make([]int, 0, 64) 795 for k := 0; k < b.N; k++ { 796 ss = ss[:0] 797 ss = append(ss, c.s...) 798 _ = Compact(ss) 799 } 800 }) 801 } 802 } 803 804 func BenchmarkCompact_Large(b *testing.B) { 805 type Large [4 * 1024]byte 806 807 ss := make([]Large, 1024) 808 for i := 0; i < b.N; i++ { 809 _ = Compact(ss) 810 } 811 } 812 813 func TestCompactFunc(t *testing.T) { 814 for _, test := range compactTests { 815 copy := Clone(test.s) 816 if got := CompactFunc(copy, equal[int]); !Equal(got, test.want) { 817 t.Errorf("CompactFunc(%v, equal[int]) = %v, want %v", test.s, got, test.want) 818 } 819 } 820 821 s1 := []string{"a", "a", "A", "B", "b"} 822 copy := Clone(s1) 823 want := []string{"a", "B"} 824 if got := CompactFunc(copy, strings.EqualFold); !Equal(got, want) { 825 t.Errorf("CompactFunc(%v, strings.EqualFold) = %v, want %v", s1, got, want) 826 } 827 } 828 829 func TestCompactClearTail(t *testing.T) { 830 one, two, three, four := 1, 2, 3, 4 831 mem := []*int{&one, &one, &two, &two, &three, &four} 832 s := mem[0:5] // there is 1 element beyond len(s), within cap(s) 833 copy := Clone(s) 834 835 s = Compact(s) 836 837 if want := []*int{&one, &two, &three}; !Equal(s, want) { 838 t.Errorf("Compact(%v) = %v, want %v", copy, s, want) 839 } 840 841 if mem[3] != nil || mem[4] != nil { 842 // Check that potential memory leak is avoided 843 t.Errorf("Compact: want nil discarded elements, got %v, %v", mem[3], mem[4]) 844 } 845 if mem[5] != &four { 846 t.Errorf("Compact: want unchanged element beyond original len, got %v", mem[5]) 847 } 848 } 849 850 func TestCompactFuncClearTail(t *testing.T) { 851 a, b, c, d, e, f := 1, 1, 2, 2, 3, 4 852 mem := []*int{&a, &b, &c, &d, &e, &f} 853 s := mem[0:5] // there is 1 element beyond len(s), within cap(s) 854 copy := Clone(s) 855 856 s = CompactFunc(s, func(x, y *int) bool { 857 if x == nil || y == nil { 858 return x == y 859 } 860 return *x == *y 861 }) 862 863 if want := []*int{&a, &c, &e}; !Equal(s, want) { 864 t.Errorf("CompactFunc(%v) = %v, want %v", copy, s, want) 865 } 866 867 if mem[3] != nil || mem[4] != nil { 868 // Check that potential memory leak is avoided 869 t.Errorf("CompactFunc: want nil discarded elements, got %v, %v", mem[3], mem[4]) 870 } 871 if mem[5] != &f { 872 t.Errorf("CompactFunc: want unchanged elements beyond original len, got %v", mem[5]) 873 } 874 } 875 876 func BenchmarkCompactFunc_Large(b *testing.B) { 877 type Large [4 * 1024]byte 878 879 ss := make([]Large, 1024) 880 for i := 0; i < b.N; i++ { 881 _ = CompactFunc(ss, func(a, b Large) bool { return a == b }) 882 } 883 } 884 885 func TestGrow(t *testing.T) { 886 s1 := []int{1, 2, 3} 887 888 copy := Clone(s1) 889 s2 := Grow(copy, 1000) 890 if !Equal(s1, s2) { 891 t.Errorf("Grow(%v) = %v, want %v", s1, s2, s1) 892 } 893 if cap(s2) < 1000+len(s1) { 894 t.Errorf("after Grow(%v) cap = %d, want >= %d", s1, cap(s2), 1000+len(s1)) 895 } 896 897 // Test mutation of elements between length and capacity. 898 copy = Clone(s1) 899 s3 := Grow(copy[:1], 2)[:3] 900 if !Equal(s1, s3) { 901 t.Errorf("Grow should not mutate elements between length and capacity") 902 } 903 s3 = Grow(copy[:1], 1000)[:3] 904 if !Equal(s1, s3) { 905 t.Errorf("Grow should not mutate elements between length and capacity") 906 } 907 908 // Test number of allocations. 909 if n := testing.AllocsPerRun(100, func() { _ = Grow(s2, cap(s2)-len(s2)) }); n != 0 { 910 t.Errorf("Grow should not allocate when given sufficient capacity; allocated %v times", n) 911 } 912 if n := testing.AllocsPerRun(100, func() { _ = Grow(s2, cap(s2)-len(s2)+1) }); n != 1 { 913 errorf := t.Errorf 914 if race.Enabled || testenv.OptimizationOff() { 915 errorf = t.Logf // this allocates multiple times in race detector mode 916 } 917 errorf("Grow should allocate once when given insufficient capacity; allocated %v times", n) 918 } 919 920 // Test for negative growth sizes. 921 var gotPanic bool 922 func() { 923 defer func() { gotPanic = recover() != nil }() 924 _ = Grow(s1, -1) 925 }() 926 if !gotPanic { 927 t.Errorf("Grow(-1) did not panic; expected a panic") 928 } 929 } 930 931 func TestClip(t *testing.T) { 932 s1 := []int{1, 2, 3, 4, 5, 6}[:3] 933 orig := Clone(s1) 934 if len(s1) != 3 { 935 t.Errorf("len(%v) = %d, want 3", s1, len(s1)) 936 } 937 if cap(s1) < 6 { 938 t.Errorf("cap(%v[:3]) = %d, want >= 6", orig, cap(s1)) 939 } 940 s2 := Clip(s1) 941 if !Equal(s1, s2) { 942 t.Errorf("Clip(%v) = %v, want %v", s1, s2, s1) 943 } 944 if cap(s2) != 3 { 945 t.Errorf("cap(Clip(%v)) = %d, want 3", orig, cap(s2)) 946 } 947 } 948 949 func TestReverse(t *testing.T) { 950 even := []int{3, 1, 4, 1, 5, 9} // len = 6 951 Reverse(even) 952 if want := []int{9, 5, 1, 4, 1, 3}; !Equal(even, want) { 953 t.Errorf("Reverse(even) = %v, want %v", even, want) 954 } 955 956 odd := []int{3, 1, 4, 1, 5, 9, 2} // len = 7 957 Reverse(odd) 958 if want := []int{2, 9, 5, 1, 4, 1, 3}; !Equal(odd, want) { 959 t.Errorf("Reverse(odd) = %v, want %v", odd, want) 960 } 961 962 words := strings.Fields("one two three") 963 Reverse(words) 964 if want := strings.Fields("three two one"); !Equal(words, want) { 965 t.Errorf("Reverse(words) = %v, want %v", words, want) 966 } 967 968 singleton := []string{"one"} 969 Reverse(singleton) 970 if want := []string{"one"}; !Equal(singleton, want) { 971 t.Errorf("Reverse(singeleton) = %v, want %v", singleton, want) 972 } 973 974 Reverse[[]string](nil) 975 } 976 977 // naiveReplace is a baseline implementation to the Replace function. 978 func naiveReplace[S ~[]E, E any](s S, i, j int, v ...E) S { 979 s = Delete(s, i, j) 980 s = Insert(s, i, v...) 981 return s 982 } 983 984 func TestReplace(t *testing.T) { 985 for _, test := range []struct { 986 s, v []int 987 i, j int 988 }{ 989 {}, // all zero value 990 { 991 s: []int{1, 2, 3, 4}, 992 v: []int{5}, 993 i: 1, 994 j: 2, 995 }, 996 { 997 s: []int{1, 2, 3, 4}, 998 v: []int{5, 6, 7, 8}, 999 i: 1, 1000 j: 2, 1001 }, 1002 { 1003 s: func() []int { 1004 s := make([]int, 3, 20) 1005 s[0] = 0 1006 s[1] = 1 1007 s[2] = 2 1008 return s 1009 }(), 1010 v: []int{3, 4, 5, 6, 7}, 1011 i: 0, 1012 j: 1, 1013 }, 1014 } { 1015 ss, vv := Clone(test.s), Clone(test.v) 1016 want := naiveReplace(ss, test.i, test.j, vv...) 1017 got := Replace(test.s, test.i, test.j, test.v...) 1018 if !Equal(got, want) { 1019 t.Errorf("Replace(%v, %v, %v, %v) = %v, want %v", test.s, test.i, test.j, test.v, got, want) 1020 } 1021 } 1022 } 1023 1024 func TestReplacePanics(t *testing.T) { 1025 s := []int{0, 1, 2, 3, 4} 1026 s = s[0:2] 1027 _ = s[0:4] // this is a valid slice of s 1028 1029 for _, test := range []struct { 1030 name string 1031 s, v []int 1032 i, j int 1033 }{ 1034 {"indexes out of order", []int{1, 2}, []int{3}, 2, 1}, 1035 {"large index", []int{1, 2}, []int{3}, 1, 10}, 1036 {"negative index", []int{1, 2}, []int{3}, -1, 2}, 1037 {"s[i:j] is valid and j > len(s)", s, nil, 0, 4}, 1038 } { 1039 ss, vv := Clone(test.s), Clone(test.v) 1040 if !panics(func() { _ = Replace(ss, test.i, test.j, vv...) }) { 1041 t.Errorf("Replace %s: should have panicked", test.name) 1042 } 1043 } 1044 } 1045 1046 func TestReplaceGrow(t *testing.T) { 1047 // When Replace needs to allocate a new slice, we want the original slice 1048 // to not be changed. 1049 a, b, c, d, e, f := 1, 2, 3, 4, 5, 6 1050 mem := []*int{&a, &b, &c, &d, &e, &f} 1051 memcopy := Clone(mem) 1052 s := mem[0:5] // there is 1 element beyond len(s), within cap(s) 1053 copy := Clone(s) 1054 original := s 1055 1056 // The new elements don't fit within cap(s), so Replace will allocate. 1057 z := 99 1058 s = Replace(s, 1, 3, &z, &z, &z, &z) 1059 1060 if want := []*int{&a, &z, &z, &z, &z, &d, &e}; !Equal(s, want) { 1061 t.Errorf("Replace(%v, 1, 3, %v, %v, %v, %v) = %v, want %v", copy, &z, &z, &z, &z, s, want) 1062 } 1063 1064 if !Equal(original, copy) { 1065 t.Errorf("original slice has changed, got %v, want %v", original, copy) 1066 } 1067 1068 if !Equal(mem, memcopy) { 1069 // Changing the original tail s[len(s):cap(s)] is unwanted 1070 t.Errorf("original backing memory has changed, got %v, want %v", mem, memcopy) 1071 } 1072 } 1073 1074 func TestReplaceClearTail(t *testing.T) { 1075 a, b, c, d, e, f := 1, 2, 3, 4, 5, 6 1076 mem := []*int{&a, &b, &c, &d, &e, &f} 1077 s := mem[0:5] // there is 1 element beyond len(s), within cap(s) 1078 copy := Clone(s) 1079 1080 y, z := 8, 9 1081 s = Replace(s, 1, 4, &y, &z) 1082 1083 if want := []*int{&a, &y, &z, &e}; !Equal(s, want) { 1084 t.Errorf("Replace(%v) = %v, want %v", copy, s, want) 1085 } 1086 1087 if mem[4] != nil { 1088 // Check that potential memory leak is avoided 1089 t.Errorf("Replace: want nil discarded element, got %v", mem[4]) 1090 } 1091 if mem[5] != &f { 1092 t.Errorf("Replace: want unchanged elements beyond original len, got %v", mem[5]) 1093 } 1094 } 1095 1096 func TestReplaceOverlap(t *testing.T) { 1097 const N = 10 1098 a := make([]int, N) 1099 want := make([]int, 2*N) 1100 for n := 0; n <= N; n++ { // length 1101 for i := 0; i <= n; i++ { // insertion point 1 1102 for j := i; j <= n; j++ { // insertion point 2 1103 for x := 0; x <= N; x++ { // start of inserted data 1104 for y := x; y <= N; y++ { // end of inserted data 1105 for k := 0; k < N; k++ { 1106 a[k] = k 1107 } 1108 want = want[:0] 1109 want = append(want, a[:i]...) 1110 want = append(want, a[x:y]...) 1111 want = append(want, a[j:n]...) 1112 got := Replace(a[:n], i, j, a[x:y]...) 1113 if !Equal(got, want) { 1114 t.Errorf("Insert with overlap failed n=%d i=%d j=%d x=%d y=%d, got %v want %v", n, i, j, x, y, got, want) 1115 } 1116 } 1117 } 1118 } 1119 } 1120 } 1121 } 1122 1123 func TestReplaceEndClearTail(t *testing.T) { 1124 s := []int{11, 22, 33} 1125 v := []int{99} 1126 // case when j == len(s) 1127 i, j := 1, 3 1128 s = Replace(s, i, j, v...) 1129 1130 x := s[:3][2] 1131 if want := 0; x != want { 1132 t.Errorf("TestReplaceEndClearTail: obsolete element is %d, want %d", x, want) 1133 } 1134 } 1135 1136 func BenchmarkReplace(b *testing.B) { 1137 cases := []struct { 1138 name string 1139 s, v func() []int 1140 i, j int 1141 }{ 1142 { 1143 name: "fast", 1144 s: func() []int { 1145 return make([]int, 100) 1146 }, 1147 v: func() []int { 1148 return make([]int, 20) 1149 }, 1150 i: 10, 1151 j: 40, 1152 }, 1153 { 1154 name: "slow", 1155 s: func() []int { 1156 return make([]int, 100) 1157 }, 1158 v: func() []int { 1159 return make([]int, 20) 1160 }, 1161 i: 0, 1162 j: 2, 1163 }, 1164 } 1165 1166 for _, c := range cases { 1167 b.Run("naive-"+c.name, func(b *testing.B) { 1168 for k := 0; k < b.N; k++ { 1169 s := c.s() 1170 v := c.v() 1171 _ = naiveReplace(s, c.i, c.j, v...) 1172 } 1173 }) 1174 b.Run("optimized-"+c.name, func(b *testing.B) { 1175 for k := 0; k < b.N; k++ { 1176 s := c.s() 1177 v := c.v() 1178 _ = Replace(s, c.i, c.j, v...) 1179 } 1180 }) 1181 } 1182 1183 } 1184 1185 func TestInsertGrowthRate(t *testing.T) { 1186 b := make([]byte, 1) 1187 maxCap := cap(b) 1188 nGrow := 0 1189 const N = 1e6 1190 for i := 0; i < N; i++ { 1191 b = Insert(b, len(b)-1, 0) 1192 if cap(b) > maxCap { 1193 maxCap = cap(b) 1194 nGrow++ 1195 } 1196 } 1197 want := int(math.Log(N) / math.Log(1.25)) // 1.25 == growth rate for large slices 1198 if nGrow > want { 1199 t.Errorf("too many grows. got:%d want:%d", nGrow, want) 1200 } 1201 } 1202 1203 func TestReplaceGrowthRate(t *testing.T) { 1204 b := make([]byte, 2) 1205 maxCap := cap(b) 1206 nGrow := 0 1207 const N = 1e6 1208 for i := 0; i < N; i++ { 1209 b = Replace(b, len(b)-2, len(b)-1, 0, 0) 1210 if cap(b) > maxCap { 1211 maxCap = cap(b) 1212 nGrow++ 1213 } 1214 } 1215 want := int(math.Log(N) / math.Log(1.25)) // 1.25 == growth rate for large slices 1216 if nGrow > want { 1217 t.Errorf("too many grows. got:%d want:%d", nGrow, want) 1218 } 1219 } 1220 1221 func apply[T any](v T, f func(T)) { 1222 f(v) 1223 } 1224 1225 // Test type inference with a named slice type. 1226 func TestInference(t *testing.T) { 1227 s1 := []int{1, 2, 3} 1228 apply(s1, Reverse) 1229 if want := []int{3, 2, 1}; !Equal(s1, want) { 1230 t.Errorf("Reverse(%v) = %v, want %v", []int{1, 2, 3}, s1, want) 1231 } 1232 1233 type S []int 1234 s2 := S{4, 5, 6} 1235 apply(s2, Reverse) 1236 if want := (S{6, 5, 4}); !Equal(s2, want) { 1237 t.Errorf("Reverse(%v) = %v, want %v", S{4, 5, 6}, s2, want) 1238 } 1239 } 1240 1241 func TestConcat(t *testing.T) { 1242 cases := []struct { 1243 s [][]int 1244 want []int 1245 }{ 1246 { 1247 s: [][]int{nil}, 1248 want: nil, 1249 }, 1250 { 1251 s: [][]int{{1}}, 1252 want: []int{1}, 1253 }, 1254 { 1255 s: [][]int{{1}, {2}}, 1256 want: []int{1, 2}, 1257 }, 1258 { 1259 s: [][]int{{1}, nil, {2}}, 1260 want: []int{1, 2}, 1261 }, 1262 } 1263 for _, tc := range cases { 1264 got := Concat(tc.s...) 1265 if !Equal(tc.want, got) { 1266 t.Errorf("Concat(%v) = %v, want %v", tc.s, got, tc.want) 1267 } 1268 var sink []int 1269 allocs := testing.AllocsPerRun(5, func() { 1270 sink = Concat(tc.s...) 1271 }) 1272 _ = sink 1273 if allocs > 1 { 1274 errorf := t.Errorf 1275 if testenv.OptimizationOff() || race.Enabled { 1276 errorf = t.Logf 1277 } 1278 errorf("Concat(%v) allocated %v times; want 1", tc.s, allocs) 1279 } 1280 } 1281 } 1282 1283 func TestConcat_too_large(t *testing.T) { 1284 // Use zero length element to minimize memory in testing 1285 type void struct{} 1286 cases := []struct { 1287 lengths []int 1288 shouldPanic bool 1289 }{ 1290 { 1291 lengths: []int{0, 0}, 1292 shouldPanic: false, 1293 }, 1294 { 1295 lengths: []int{math.MaxInt, 0}, 1296 shouldPanic: false, 1297 }, 1298 { 1299 lengths: []int{0, math.MaxInt}, 1300 shouldPanic: false, 1301 }, 1302 { 1303 lengths: []int{math.MaxInt - 1, 1}, 1304 shouldPanic: false, 1305 }, 1306 { 1307 lengths: []int{math.MaxInt - 1, 1, 1}, 1308 shouldPanic: true, 1309 }, 1310 { 1311 lengths: []int{math.MaxInt, 1}, 1312 shouldPanic: true, 1313 }, 1314 { 1315 lengths: []int{math.MaxInt, math.MaxInt}, 1316 shouldPanic: true, 1317 }, 1318 } 1319 for _, tc := range cases { 1320 var r any 1321 ss := make([][]void, 0, len(tc.lengths)) 1322 for _, l := range tc.lengths { 1323 s := make([]void, l) 1324 ss = append(ss, s) 1325 } 1326 func() { 1327 defer func() { 1328 r = recover() 1329 }() 1330 _ = Concat(ss...) 1331 }() 1332 if didPanic := r != nil; didPanic != tc.shouldPanic { 1333 t.Errorf("slices.Concat(lens(%v)) got panic == %v", 1334 tc.lengths, didPanic) 1335 } 1336 } 1337 } 1338 1339 func TestRepeat(t *testing.T) { 1340 // normal cases 1341 for _, tc := range []struct { 1342 x []int 1343 count int 1344 want []int 1345 }{ 1346 {x: []int(nil), count: 0, want: []int{}}, 1347 {x: []int(nil), count: 1, want: []int{}}, 1348 {x: []int(nil), count: math.MaxInt, want: []int{}}, 1349 {x: []int{}, count: 0, want: []int{}}, 1350 {x: []int{}, count: 1, want: []int{}}, 1351 {x: []int{}, count: math.MaxInt, want: []int{}}, 1352 {x: []int{0}, count: 0, want: []int{}}, 1353 {x: []int{0}, count: 1, want: []int{0}}, 1354 {x: []int{0}, count: 2, want: []int{0, 0}}, 1355 {x: []int{0}, count: 3, want: []int{0, 0, 0}}, 1356 {x: []int{0}, count: 4, want: []int{0, 0, 0, 0}}, 1357 {x: []int{0, 1}, count: 0, want: []int{}}, 1358 {x: []int{0, 1}, count: 1, want: []int{0, 1}}, 1359 {x: []int{0, 1}, count: 2, want: []int{0, 1, 0, 1}}, 1360 {x: []int{0, 1}, count: 3, want: []int{0, 1, 0, 1, 0, 1}}, 1361 {x: []int{0, 1}, count: 4, want: []int{0, 1, 0, 1, 0, 1, 0, 1}}, 1362 {x: []int{0, 1, 2}, count: 0, want: []int{}}, 1363 {x: []int{0, 1, 2}, count: 1, want: []int{0, 1, 2}}, 1364 {x: []int{0, 1, 2}, count: 2, want: []int{0, 1, 2, 0, 1, 2}}, 1365 {x: []int{0, 1, 2}, count: 3, want: []int{0, 1, 2, 0, 1, 2, 0, 1, 2}}, 1366 {x: []int{0, 1, 2}, count: 4, want: []int{0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2}}, 1367 } { 1368 if got := Repeat(tc.x, tc.count); got == nil || cap(got) != cap(tc.want) || !Equal(got, tc.want) { 1369 t.Errorf("Repeat(%v, %v): got: %v, want: %v, (got == nil): %v, cap(got): %v, cap(want): %v", 1370 tc.x, tc.count, got, tc.want, got == nil, cap(got), cap(tc.want)) 1371 } 1372 } 1373 1374 // big slices 1375 for _, tc := range []struct { 1376 x []struct{} 1377 count int 1378 want []struct{} 1379 }{ 1380 {x: make([]struct{}, math.MaxInt/1-0), count: 1, want: make([]struct{}, 1*(math.MaxInt/1-0))}, 1381 {x: make([]struct{}, math.MaxInt/2-1), count: 2, want: make([]struct{}, 2*(math.MaxInt/2-1))}, 1382 {x: make([]struct{}, math.MaxInt/3-2), count: 3, want: make([]struct{}, 3*(math.MaxInt/3-2))}, 1383 {x: make([]struct{}, math.MaxInt/4-3), count: 4, want: make([]struct{}, 4*(math.MaxInt/4-3))}, 1384 {x: make([]struct{}, math.MaxInt/5-4), count: 5, want: make([]struct{}, 5*(math.MaxInt/5-4))}, 1385 {x: make([]struct{}, math.MaxInt/6-5), count: 6, want: make([]struct{}, 6*(math.MaxInt/6-5))}, 1386 {x: make([]struct{}, math.MaxInt/7-6), count: 7, want: make([]struct{}, 7*(math.MaxInt/7-6))}, 1387 {x: make([]struct{}, math.MaxInt/8-7), count: 8, want: make([]struct{}, 8*(math.MaxInt/8-7))}, 1388 {x: make([]struct{}, math.MaxInt/9-8), count: 9, want: make([]struct{}, 9*(math.MaxInt/9-8))}, 1389 } { 1390 if got := Repeat(tc.x, tc.count); got == nil || len(got) != len(tc.want) || cap(got) != cap(tc.want) { 1391 t.Errorf("Repeat(make([]struct{}, %v), %v): (got == nil): %v, len(got): %v, len(want): %v, cap(got): %v, cap(want): %v", 1392 len(tc.x), tc.count, got == nil, len(got), len(tc.want), cap(got), cap(tc.want)) 1393 } 1394 } 1395 } 1396 1397 func TestRepeatPanics(t *testing.T) { 1398 for _, test := range []struct { 1399 name string 1400 x []struct{} 1401 count int 1402 }{ 1403 {name: "cannot be negative", x: make([]struct{}, 0), count: -1}, 1404 {name: "the result of (len(x) * count) overflows, hi > 0", x: make([]struct{}, 3), count: math.MaxInt}, 1405 {name: "the result of (len(x) * count) overflows, lo > maxInt", x: make([]struct{}, 2), count: 1 + math.MaxInt/2}, 1406 } { 1407 if !panics(func() { _ = Repeat(test.x, test.count) }) { 1408 t.Errorf("Repeat %s: got no panic, want panic", test.name) 1409 } 1410 } 1411 }