github.com/songzhibin97/go-baseutils@v0.0.2-0.20240302024150-487d8ce9c082/base/bslice/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 bslice 6 7 import ( 8 "github.com/songzhibin97/go-baseutils/base/btype" 9 "math" 10 "strings" 11 "testing" 12 ) 13 14 var raceEnabled bool 15 16 var equalIntTests = []struct { 17 s1, s2 []int 18 want bool 19 }{ 20 { 21 []int{1}, 22 nil, 23 false, 24 }, 25 { 26 []int{}, 27 nil, 28 true, 29 }, 30 { 31 []int{1, 2, 3}, 32 []int{1, 2, 3}, 33 true, 34 }, 35 { 36 []int{1, 2, 3}, 37 []int{1, 2, 3, 4}, 38 false, 39 }, 40 } 41 42 var equalFloatTests = []struct { 43 s1, s2 []float64 44 wantEqual bool 45 wantEqualNaN bool 46 }{ 47 { 48 []float64{1, 2}, 49 []float64{1, 2}, 50 true, 51 true, 52 }, 53 { 54 []float64{1, 2, math.NaN()}, 55 []float64{1, 2, math.NaN()}, 56 false, 57 true, 58 }, 59 } 60 61 func TestEqual(t *testing.T) { 62 for _, test := range equalIntTests { 63 if got := Equal(test.s1, test.s2); got != test.want { 64 t.Errorf("Equal(%v, %v) = %t, want %t", test.s1, test.s2, got, test.want) 65 } 66 } 67 for _, test := range equalFloatTests { 68 if got := Equal(test.s1, test.s2); got != test.wantEqual { 69 t.Errorf("Equal(%v, %v) = %t, want %t", test.s1, test.s2, got, test.wantEqual) 70 } 71 } 72 } 73 74 // equal is simply ==. 75 func equal[T comparable](v1, v2 T) bool { 76 return v1 == v2 77 } 78 79 // equalNaN is like == except that all NaNs are equal. 80 func equalNaN[T comparable](v1, v2 T) bool { 81 isNaN := func(f T) bool { return f != f } 82 return v1 == v2 || (isNaN(v1) && isNaN(v2)) 83 } 84 85 // offByOne returns true if integers v1 and v2 differ by 1. 86 func offByOne[E btype.Integer](v1, v2 E) bool { 87 return v1 == v2+1 || v1 == v2-1 88 } 89 90 func TestEqualFunc(t *testing.T) { 91 for _, test := range equalIntTests { 92 if got := EqualFunc(test.s1, test.s2, equal[int]); got != test.want { 93 t.Errorf("EqualFunc(%v, %v, equal[int]) = %t, want %t", test.s1, test.s2, got, test.want) 94 } 95 } 96 for _, test := range equalFloatTests { 97 if got := EqualFunc(test.s1, test.s2, equal[float64]); got != test.wantEqual { 98 t.Errorf("Equal(%v, %v, equal[float64]) = %t, want %t", test.s1, test.s2, got, test.wantEqual) 99 } 100 if got := EqualFunc(test.s1, test.s2, equalNaN[float64]); got != test.wantEqualNaN { 101 t.Errorf("Equal(%v, %v, equalNaN[float64]) = %t, want %t", test.s1, test.s2, got, test.wantEqualNaN) 102 } 103 } 104 105 s1 := []int{1, 2, 3} 106 s2 := []int{2, 3, 4} 107 if EqualFunc(s1, s1, offByOne[int]) { 108 t.Errorf("EqualFunc(%v, %v, offByOne) = true, want false", s1, s1) 109 } 110 if !EqualFunc(s1, s2, offByOne[int]) { 111 t.Errorf("EqualFunc(%v, %v, offByOne) = false, want true", s1, s2) 112 } 113 114 s3 := []string{"a", "b", "c"} 115 s4 := []string{"A", "B", "C"} 116 if !EqualFunc(s3, s4, strings.EqualFold) { 117 t.Errorf("EqualFunc(%v, %v, strings.EqualFold) = false, want true", s3, s4) 118 } 119 120 cmpIntString := func(v1 int, v2 string) bool { 121 return string(rune(v1)-1+'a') == v2 122 } 123 if !EqualFunc(s1, s3, cmpIntString) { 124 t.Errorf("EqualFunc(%v, %v, cmpIntString) = false, want true", s1, s3) 125 } 126 } 127 128 func BenchmarkEqualFunc_Large(b *testing.B) { 129 type Large [4 * 1024]byte 130 131 xs := make([]Large, 1024) 132 ys := make([]Large, 1024) 133 for i := 0; i < b.N; i++ { 134 _ = EqualFunc(xs, ys, func(x, y Large) bool { return x == y }) 135 } 136 } 137 138 var compareIntTests = []struct { 139 s1, s2 []int 140 want int 141 }{ 142 { 143 []int{1, 2, 3}, 144 []int{1, 2, 3, 4}, 145 -1, 146 }, 147 { 148 []int{1, 2, 3, 4}, 149 []int{1, 2, 3}, 150 +1, 151 }, 152 { 153 []int{1, 2, 3}, 154 []int{1, 4, 3}, 155 -1, 156 }, 157 { 158 []int{1, 4, 3}, 159 []int{1, 2, 3}, 160 +1, 161 }, 162 } 163 164 var compareFloatTests = []struct { 165 s1, s2 []float64 166 want int 167 }{ 168 { 169 []float64{1, 2, math.NaN()}, 170 []float64{1, 2, math.NaN()}, 171 0, 172 }, 173 { 174 []float64{1, math.NaN(), 3}, 175 []float64{1, math.NaN(), 4}, 176 -1, 177 }, 178 { 179 []float64{1, math.NaN(), 3}, 180 []float64{1, 2, 4}, 181 -1, 182 }, 183 { 184 []float64{1, math.NaN(), 3}, 185 []float64{1, 2, math.NaN()}, 186 0, 187 }, 188 { 189 []float64{1, math.NaN(), 3, 4}, 190 []float64{1, 2, math.NaN()}, 191 +1, 192 }, 193 } 194 195 func TestCompare(t *testing.T) { 196 intWant := func(want bool) string { 197 if want { 198 return "0" 199 } 200 return "!= 0" 201 } 202 for _, test := range equalIntTests { 203 if got := Compare(test.s1, test.s2); (got == 0) != test.want { 204 t.Errorf("Compare(%v, %v) = %d, want %s", test.s1, test.s2, got, intWant(test.want)) 205 } 206 } 207 for _, test := range equalFloatTests { 208 if got := Compare(test.s1, test.s2); (got == 0) != test.wantEqualNaN { 209 t.Errorf("Compare(%v, %v) = %d, want %s", test.s1, test.s2, got, intWant(test.wantEqualNaN)) 210 } 211 } 212 213 for _, test := range compareIntTests { 214 if got := Compare(test.s1, test.s2); got != test.want { 215 t.Errorf("Compare(%v, %v) = %d, want %d", test.s1, test.s2, got, test.want) 216 } 217 } 218 for _, test := range compareFloatTests { 219 if got := Compare(test.s1, test.s2); got != test.want { 220 t.Errorf("Compare(%v, %v) = %d, want %d", test.s1, test.s2, got, test.want) 221 } 222 } 223 } 224 225 func equalToCmp[T comparable](eq func(T, T) bool) func(T, T) int { 226 return func(v1, v2 T) int { 227 if eq(v1, v2) { 228 return 0 229 } 230 return 1 231 } 232 } 233 234 func cmp[T btype.Ordered](v1, v2 T) int { 235 if v1 < v2 { 236 return -1 237 } else if v1 > v2 { 238 return 1 239 } else { 240 return 0 241 } 242 } 243 244 func TestCompareFunc(t *testing.T) { 245 intWant := func(want bool) string { 246 if want { 247 return "0" 248 } 249 return "!= 0" 250 } 251 for _, test := range equalIntTests { 252 if got := CompareFunc(test.s1, test.s2, equalToCmp(equal[int])); (got == 0) != test.want { 253 t.Errorf("CompareFunc(%v, %v, equalToCmp(equal[int])) = %d, want %s", test.s1, test.s2, got, intWant(test.want)) 254 } 255 } 256 for _, test := range equalFloatTests { 257 if got := CompareFunc(test.s1, test.s2, equalToCmp(equal[float64])); (got == 0) != test.wantEqual { 258 t.Errorf("CompareFunc(%v, %v, equalToCmp(equal[float64])) = %d, want %s", test.s1, test.s2, got, intWant(test.wantEqual)) 259 } 260 } 261 262 for _, test := range compareIntTests { 263 if got := CompareFunc(test.s1, test.s2, cmp[int]); got != test.want { 264 t.Errorf("CompareFunc(%v, %v, cmp[int]) = %d, want %d", test.s1, test.s2, got, test.want) 265 } 266 } 267 for _, test := range compareFloatTests { 268 if got := CompareFunc(test.s1, test.s2, cmp[float64]); got != test.want { 269 t.Errorf("CompareFunc(%v, %v, cmp[float64]) = %d, want %d", test.s1, test.s2, got, test.want) 270 } 271 } 272 273 s1 := []int{1, 2, 3} 274 s2 := []int{2, 3, 4} 275 if got := CompareFunc(s1, s2, equalToCmp(offByOne[int])); got != 0 { 276 t.Errorf("CompareFunc(%v, %v, offByOne) = %d, want 0", s1, s2, got) 277 } 278 279 s3 := []string{"a", "b", "c"} 280 s4 := []string{"A", "B", "C"} 281 if got := CompareFunc(s3, s4, strings.Compare); got != 1 { 282 t.Errorf("CompareFunc(%v, %v, strings.Compare) = %d, want 1", s3, s4, got) 283 } 284 285 compareLower := func(v1, v2 string) int { 286 return strings.Compare(strings.ToLower(v1), strings.ToLower(v2)) 287 } 288 if got := CompareFunc(s3, s4, compareLower); got != 0 { 289 t.Errorf("CompareFunc(%v, %v, compareLower) = %d, want 0", s3, s4, got) 290 } 291 292 cmpIntString := func(v1 int, v2 string) int { 293 return strings.Compare(string(rune(v1)-1+'a'), v2) 294 } 295 if got := CompareFunc(s1, s3, cmpIntString); got != 0 { 296 t.Errorf("CompareFunc(%v, %v, cmpIntString) = %d, want 0", s1, s3, got) 297 } 298 } 299 300 var indexTests = []struct { 301 s []int 302 v int 303 want int 304 }{ 305 { 306 nil, 307 0, 308 -1, 309 }, 310 { 311 []int{}, 312 0, 313 -1, 314 }, 315 { 316 []int{1, 2, 3}, 317 2, 318 1, 319 }, 320 { 321 []int{1, 2, 2, 3}, 322 2, 323 1, 324 }, 325 { 326 []int{1, 2, 3, 2}, 327 2, 328 1, 329 }, 330 } 331 332 func TestIndex(t *testing.T) { 333 for _, test := range indexTests { 334 if got := Index(test.s, test.v); got != test.want { 335 t.Errorf("Index(%v, %v) = %d, want %d", test.s, test.v, got, test.want) 336 } 337 } 338 } 339 340 func equalToIndex[T any](f func(T, T) bool, v1 T) func(T) bool { 341 return func(v2 T) bool { 342 return f(v1, v2) 343 } 344 } 345 346 func BenchmarkIndex_Large(b *testing.B) { 347 type Large [4 * 1024]byte 348 349 ss := make([]Large, 1024) 350 for i := 0; i < b.N; i++ { 351 _ = Index(ss, Large{1}) 352 } 353 } 354 355 func TestIndexFunc(t *testing.T) { 356 for _, test := range indexTests { 357 if got := IndexFunc(test.s, equalToIndex(equal[int], test.v)); got != test.want { 358 t.Errorf("IndexFunc(%v, equalToIndex(equal[int], %v)) = %d, want %d", test.s, test.v, got, test.want) 359 } 360 } 361 362 s1 := []string{"hi", "HI"} 363 if got := IndexFunc(s1, equalToIndex(equal[string], "HI")); got != 1 { 364 t.Errorf("IndexFunc(%v, equalToIndex(equal[string], %q)) = %d, want %d", s1, "HI", got, 1) 365 } 366 if got := IndexFunc(s1, equalToIndex(strings.EqualFold, "HI")); got != 0 { 367 t.Errorf("IndexFunc(%v, equalToIndex(strings.EqualFold, %q)) = %d, want %d", s1, "HI", got, 0) 368 } 369 } 370 371 func BenchmarkIndexFunc_Large(b *testing.B) { 372 type Large [4 * 1024]byte 373 374 ss := make([]Large, 1024) 375 for i := 0; i < b.N; i++ { 376 _ = IndexFunc(ss, func(e Large) bool { 377 return e == Large{1} 378 }) 379 } 380 } 381 382 func TestContains(t *testing.T) { 383 for _, test := range indexTests { 384 if got := Contains(test.s, test.v); got != (test.want != -1) { 385 t.Errorf("Contains(%v, %v) = %t, want %t", test.s, test.v, got, test.want != -1) 386 } 387 } 388 } 389 390 func TestContainsFunc(t *testing.T) { 391 for _, test := range indexTests { 392 if got := ContainsFunc(test.s, equalToIndex(equal[int], test.v)); got != (test.want != -1) { 393 t.Errorf("ContainsFunc(%v, equalToIndex(equal[int], %v)) = %t, want %t", test.s, test.v, got, test.want != -1) 394 } 395 } 396 397 s1 := []string{"hi", "HI"} 398 if got := ContainsFunc(s1, equalToIndex(equal[string], "HI")); got != true { 399 t.Errorf("ContainsFunc(%v, equalToContains(equal[string], %q)) = %t, want %t", s1, "HI", got, true) 400 } 401 if got := ContainsFunc(s1, equalToIndex(equal[string], "hI")); got != false { 402 t.Errorf("ContainsFunc(%v, equalToContains(strings.EqualFold, %q)) = %t, want %t", s1, "hI", got, false) 403 } 404 if got := ContainsFunc(s1, equalToIndex(strings.EqualFold, "hI")); got != true { 405 t.Errorf("ContainsFunc(%v, equalToContains(strings.EqualFold, %q)) = %t, want %t", s1, "hI", got, true) 406 } 407 } 408 409 var insertTests = []struct { 410 s []int 411 i int 412 add []int 413 want []int 414 }{ 415 { 416 []int{1, 2, 3}, 417 0, 418 []int{4}, 419 []int{4, 1, 2, 3}, 420 }, 421 { 422 []int{1, 2, 3}, 423 1, 424 []int{4}, 425 []int{1, 4, 2, 3}, 426 }, 427 { 428 []int{1, 2, 3}, 429 3, 430 []int{4}, 431 []int{1, 2, 3, 4}, 432 }, 433 { 434 []int{1, 2, 3}, 435 2, 436 []int{4, 5}, 437 []int{1, 2, 4, 5, 3}, 438 }, 439 } 440 441 func TestInsert(t *testing.T) { 442 s := []int{1, 2, 3} 443 if got := Insert(s, 0); !Equal(got, s) { 444 t.Errorf("Insert(%v, 0) = %v, want %v", s, got, s) 445 } 446 for _, test := range insertTests { 447 copy := Clone(test.s) 448 if got := Insert(copy, test.i, test.add...); !Equal(got, test.want) { 449 t.Errorf("Insert(%v, %d, %v...) = %v, want %v", test.s, test.i, test.add, got, test.want) 450 } 451 } 452 } 453 454 var deleteTests = []struct { 455 s []int 456 i, j int 457 want []int 458 }{ 459 { 460 []int{1, 2, 3}, 461 0, 462 0, 463 []int{1, 2, 3}, 464 }, 465 { 466 []int{1, 2, 3}, 467 0, 468 1, 469 []int{2, 3}, 470 }, 471 { 472 []int{1, 2, 3}, 473 3, 474 3, 475 []int{1, 2, 3}, 476 }, 477 { 478 []int{1, 2, 3}, 479 0, 480 2, 481 []int{3}, 482 }, 483 { 484 []int{1, 2, 3}, 485 0, 486 3, 487 []int{}, 488 }, 489 } 490 491 func TestDelete(t *testing.T) { 492 for _, test := range deleteTests { 493 copy := Clone(test.s) 494 if got := Delete(copy, test.i, test.j); !Equal(got, test.want) { 495 t.Errorf("Delete(%v, %d, %d) = %v, want %v", test.s, test.i, test.j, got, test.want) 496 } 497 } 498 } 499 500 func panics(f func()) (b bool) { 501 defer func() { 502 if x := recover(); x != nil { 503 b = true 504 } 505 }() 506 f() 507 return false 508 } 509 510 func TestDeletePanics(t *testing.T) { 511 for _, test := range []struct { 512 name string 513 s []int 514 i, j int 515 }{ 516 {"with negative first index", []int{42}, -2, 1}, 517 {"with negative second index", []int{42}, 1, -1}, 518 {"with out-of-bounds first index", []int{42}, 2, 3}, 519 {"with out-of-bounds second index", []int{42}, 0, 2}, 520 {"with invalid i>j", []int{42}, 1, 0}, 521 } { 522 if !panics(func() { Delete(test.s, test.i, test.j) }) { 523 t.Errorf("Delete %s: got no panic, want panic", test.name) 524 } 525 } 526 } 527 528 func TestClone(t *testing.T) { 529 s1 := []int{1, 2, 3} 530 s2 := Clone(s1) 531 if !Equal(s1, s2) { 532 t.Errorf("Clone(%v) = %v, want %v", s1, s2, s1) 533 } 534 s1[0] = 4 535 want := []int{1, 2, 3} 536 if !Equal(s2, want) { 537 t.Errorf("Clone(%v) changed unexpectedly to %v", want, s2) 538 } 539 if got := Clone([]int(nil)); got != nil { 540 t.Errorf("Clone(nil) = %#v, want nil", got) 541 } 542 if got := Clone(s1[:0]); got == nil || len(got) != 0 { 543 t.Errorf("Clone(%v) = %#v, want %#v", s1[:0], got, s1[:0]) 544 } 545 } 546 547 var compactTests = []struct { 548 name string 549 s []int 550 want []int 551 }{ 552 { 553 "nil", 554 nil, 555 nil, 556 }, 557 { 558 "one", 559 []int{1}, 560 []int{1}, 561 }, 562 { 563 "sorted", 564 []int{1, 2, 3}, 565 []int{1, 2, 3}, 566 }, 567 { 568 "1 item", 569 []int{1, 1, 2}, 570 []int{1, 2}, 571 }, 572 { 573 "unsorted", 574 []int{1, 2, 1}, 575 []int{1, 2, 1}, 576 }, 577 { 578 "many", 579 []int{1, 2, 2, 3, 3, 4}, 580 []int{1, 2, 3, 4}, 581 }, 582 } 583 584 func TestCompact(t *testing.T) { 585 for _, test := range compactTests { 586 copy := Clone(test.s) 587 if got := Compact(copy); !Equal(got, test.want) { 588 t.Errorf("Compact(%v) = %v, want %v", test.s, got, test.want) 589 } 590 } 591 } 592 593 func BenchmarkCompact(b *testing.B) { 594 for _, c := range compactTests { 595 b.Run(c.name, func(b *testing.B) { 596 ss := make([]int, 0, 64) 597 for k := 0; k < b.N; k++ { 598 ss = ss[:0] 599 ss = append(ss, c.s...) 600 _ = Compact(ss) 601 } 602 }) 603 } 604 } 605 606 func BenchmarkCompact_Large(b *testing.B) { 607 type Large [4 * 1024]byte 608 609 ss := make([]Large, 1024) 610 for i := 0; i < b.N; i++ { 611 _ = Compact(ss) 612 } 613 } 614 615 func TestCompactFunc(t *testing.T) { 616 for _, test := range compactTests { 617 copy := Clone(test.s) 618 if got := CompactFunc(copy, equal[int]); !Equal(got, test.want) { 619 t.Errorf("CompactFunc(%v, equal[int]) = %v, want %v", test.s, got, test.want) 620 } 621 } 622 623 s1 := []string{"a", "a", "A", "B", "b"} 624 copy := Clone(s1) 625 want := []string{"a", "B"} 626 if got := CompactFunc(copy, strings.EqualFold); !Equal(got, want) { 627 t.Errorf("CompactFunc(%v, strings.EqualFold) = %v, want %v", s1, got, want) 628 } 629 } 630 631 func BenchmarkCompactFunc_Large(b *testing.B) { 632 type Large [4 * 1024]byte 633 634 ss := make([]Large, 1024) 635 for i := 0; i < b.N; i++ { 636 _ = CompactFunc(ss, func(a, b Large) bool { return a == b }) 637 } 638 } 639 640 func TestGrow(t *testing.T) { 641 s1 := []int{1, 2, 3} 642 643 copy := Clone(s1) 644 s2 := Grow(copy, 1000) 645 if !Equal(s1, s2) { 646 t.Errorf("Grow(%v) = %v, want %v", s1, s2, s1) 647 } 648 if cap(s2) < 1000+len(s1) { 649 t.Errorf("after Grow(%v) cap = %d, want >= %d", s1, cap(s2), 1000+len(s1)) 650 } 651 652 // Test mutation of elements between length and capacity. 653 copy = Clone(s1) 654 s3 := Grow(copy[:1], 2)[:3] 655 if !Equal(s1, s3) { 656 t.Errorf("Grow should not mutate elements between length and capacity") 657 } 658 s3 = Grow(copy[:1], 1000)[:3] 659 if !Equal(s1, s3) { 660 t.Errorf("Grow should not mutate elements between length and capacity") 661 } 662 663 // Test number of allocations. 664 if n := testing.AllocsPerRun(100, func() { Grow(s2, cap(s2)-len(s2)) }); n != 0 { 665 t.Errorf("Grow should not allocate when given sufficient capacity; allocated %v times", n) 666 } 667 if n := testing.AllocsPerRun(100, func() { Grow(s2, cap(s2)-len(s2)+1) }); n != 1 { 668 errorf := t.Errorf 669 if raceEnabled { 670 errorf = t.Logf // this allocates multiple times in race detector mode 671 } 672 errorf("Grow should allocate once when given insufficient capacity; allocated %v times", n) 673 } 674 675 // Test for negative growth sizes. 676 var gotPanic bool 677 func() { 678 defer func() { gotPanic = recover() != nil }() 679 Grow(s1, -1) 680 }() 681 if !gotPanic { 682 t.Errorf("Grow(-1) did not panic; expected a panic") 683 } 684 } 685 686 func TestClip(t *testing.T) { 687 s1 := []int{1, 2, 3, 4, 5, 6}[:3] 688 orig := Clone(s1) 689 if len(s1) != 3 { 690 t.Errorf("len(%v) = %d, want 3", s1, len(s1)) 691 } 692 if cap(s1) < 6 { 693 t.Errorf("cap(%v[:3]) = %d, want >= 6", orig, cap(s1)) 694 } 695 s2 := Clip(s1) 696 if !Equal(s1, s2) { 697 t.Errorf("Clip(%v) = %v, want %v", s1, s2, s1) 698 } 699 if cap(s2) != 3 { 700 t.Errorf("cap(Clip(%v)) = %d, want 3", orig, cap(s2)) 701 } 702 } 703 704 // naiveReplace is a baseline implementation to the Replace function. 705 func naiveReplace[S ~[]E, E any](s S, i, j int, v ...E) S { 706 s = Delete(s, i, j) 707 s = Insert(s, i, v...) 708 return s 709 } 710 711 func TestReplace(t *testing.T) { 712 for _, test := range []struct { 713 s, v []int 714 i, j int 715 }{ 716 {}, // all zero value 717 { 718 s: []int{1, 2, 3, 4}, 719 v: []int{5}, 720 i: 1, 721 j: 2, 722 }, 723 { 724 s: []int{1, 2, 3, 4}, 725 v: []int{5, 6, 7, 8}, 726 i: 1, 727 j: 2, 728 }, 729 { 730 s: func() []int { 731 s := make([]int, 3, 20) 732 s[0] = 0 733 s[1] = 1 734 s[2] = 2 735 return s 736 }(), 737 v: []int{3, 4, 5, 6, 7}, 738 i: 0, 739 j: 1, 740 }, 741 } { 742 ss, vv := Clone(test.s), Clone(test.v) 743 want := naiveReplace(ss, test.i, test.j, vv...) 744 got := Replace(test.s, test.i, test.j, test.v...) 745 if !Equal(got, want) { 746 t.Errorf("Replace(%v, %v, %v, %v) = %v, want %v", test.s, test.i, test.j, test.v, got, want) 747 } 748 } 749 } 750 751 func TestReplacePanics(t *testing.T) { 752 for _, test := range []struct { 753 name string 754 s, v []int 755 i, j int 756 }{ 757 {"indexes out of order", []int{1, 2}, []int{3}, 2, 1}, 758 {"large index", []int{1, 2}, []int{3}, 1, 10}, 759 {"negative index", []int{1, 2}, []int{3}, -1, 2}, 760 } { 761 ss, vv := Clone(test.s), Clone(test.v) 762 if !panics(func() { Replace(ss, test.i, test.j, vv...) }) { 763 t.Errorf("Replace %s: should have panicked", test.name) 764 } 765 } 766 } 767 768 func BenchmarkReplace(b *testing.B) { 769 cases := []struct { 770 name string 771 s, v func() []int 772 i, j int 773 }{ 774 { 775 name: "fast", 776 s: func() []int { 777 return make([]int, 100) 778 }, 779 v: func() []int { 780 return make([]int, 20) 781 }, 782 i: 10, 783 j: 40, 784 }, 785 { 786 name: "slow", 787 s: func() []int { 788 return make([]int, 100) 789 }, 790 v: func() []int { 791 return make([]int, 20) 792 }, 793 i: 0, 794 j: 2, 795 }, 796 } 797 798 for _, c := range cases { 799 b.Run("naive-"+c.name, func(b *testing.B) { 800 for k := 0; k < b.N; k++ { 801 s := c.s() 802 v := c.v() 803 _ = naiveReplace(s, c.i, c.j, v...) 804 } 805 }) 806 b.Run("optimized-"+c.name, func(b *testing.B) { 807 for k := 0; k < b.N; k++ { 808 s := c.s() 809 v := c.v() 810 _ = Replace(s, c.i, c.j, v...) 811 } 812 }) 813 } 814 815 }