|
6 | 6 | "math/rand"
|
7 | 7 | "sort"
|
8 | 8 | "strconv"
|
| 9 | + "strings" |
9 | 10 | "testing"
|
10 | 11 |
|
11 | 12 | "github.com/stretchr/testify/assert"
|
@@ -482,3 +483,128 @@ func TestPatternIPRange(t *testing.T) {
|
482 | 483 |
|
483 | 484 | testAll(t, tp, tests)
|
484 | 485 | }
|
| 486 | + |
| 487 | +func testFindSequence(a *assert.Assertions, cnt int, needles []string, haystack string) { |
| 488 | + var needlesB [][]byte |
| 489 | + for _, needle := range needles { |
| 490 | + needlesB = append(needlesB, []byte(needle)) |
| 491 | + } |
| 492 | + res := findSequence([]byte(haystack), needlesB) |
| 493 | + a.Equal(cnt, res, "wrong total number of matches") |
| 494 | +} |
| 495 | + |
| 496 | +func TestFindSequence(t *testing.T) { |
| 497 | + a := assert.New(t) |
| 498 | + |
| 499 | + testFindSequence(a, 2, []string{"abra", "ada"}, "abracadabra") |
| 500 | + testFindSequence(a, 2, []string{"aba", "aba"}, "abacaba") |
| 501 | + testFindSequence(a, 2, []string{"aba", "caba"}, "abacaba") |
| 502 | + testFindSequence(a, 1, []string{"abacaba"}, "abacaba") |
| 503 | + testFindSequence(a, 0, []string{"abacaba"}, "aba") |
| 504 | + testFindSequence(a, 1, []string{"aba"}, "abacaba") |
| 505 | + testFindSequence(a, 0, []string{"dad"}, "abacaba") |
| 506 | + testFindSequence(a, 1, []string{"aba", "dad"}, "abacaba") |
| 507 | + testFindSequence(a, 0, []string{"dad", "aba"}, "abacaba") |
| 508 | + |
| 509 | + testFindSequence(a, 2, []string{"needle", "haystack"}, "can you find a needle in a haystack?") |
| 510 | + testFindSequence(a, 2, []string{"k8s_pod", "_prod"}, "\"k8s_pod\":{\"main_prod\"}") |
| 511 | + |
| 512 | + testFindSequence(a, 2, []string{"!13", "37#"}, "woah!13@37#test") |
| 513 | + |
| 514 | + testFindSequence(a, 1, []string{"abc"}, strings.Repeat("ab", 1024)+"c") |
| 515 | +} |
| 516 | + |
| 517 | +func BenchmarkFindSequence_Deterministic(b *testing.B) { |
| 518 | + type testCase struct { |
| 519 | + haystack []byte |
| 520 | + needles [][]byte |
| 521 | + } |
| 522 | + |
| 523 | + type namedTestCase struct { |
| 524 | + name string |
| 525 | + cases []testCase |
| 526 | + } |
| 527 | + |
| 528 | + testCases := []namedTestCase{ |
| 529 | + { |
| 530 | + name: "regular-cases", |
| 531 | + cases: []testCase{ |
| 532 | + {bb("Hello, world!"), [][]byte{bb("orl")}}, |
| 533 | + {bb("some-k8s-service"), [][]byte{bb("k8s")}}, |
| 534 | + }, |
| 535 | + }, |
| 536 | + { |
| 537 | + name: "corner-cases", |
| 538 | + cases: []testCase{ |
| 539 | + {bb(strings.Repeat("ab", 32) + "c"), [][]byte{bb("abc")}}, |
| 540 | + {bb(strings.Repeat("ab", 64) + "c"), [][]byte{bb("abc")}}, |
| 541 | + {bb(strings.Repeat("ab", 1024) + "c"), [][]byte{bb("abc")}}, |
| 542 | + {bb(strings.Repeat("ab", 16384) + "c"), [][]byte{bb("abc")}}, |
| 543 | + }, |
| 544 | + }, |
| 545 | + } |
| 546 | + |
| 547 | + for _, tc := range testCases { |
| 548 | + for i, c := range tc.cases { |
| 549 | + b.Run(tc.name+"-"+strconv.Itoa(i), func(b *testing.B) { |
| 550 | + for b.Loop() { |
| 551 | + findSequence([]byte(c.haystack), c.needles) |
| 552 | + } |
| 553 | + }) |
| 554 | + } |
| 555 | + } |
| 556 | +} |
| 557 | + |
| 558 | +func BenchmarkFindSequence_Random(b *testing.B) { |
| 559 | + sizes := []struct { |
| 560 | + name string |
| 561 | + haystackSize int |
| 562 | + needleSize int |
| 563 | + needleCount int |
| 564 | + }{ |
| 565 | + {"tiny", 64, 3, 2}, |
| 566 | + {"small", 256, 10, 3}, |
| 567 | + {"medium", 1024, 50, 5}, |
| 568 | + {"large", 16384, 200, 10}, |
| 569 | + {"extra-large", 1048576, 1024, 100}, |
| 570 | + } |
| 571 | + |
| 572 | + for _, size := range sizes { |
| 573 | + b.Run(size.name, func(b *testing.B) { |
| 574 | + haystack, needles := generateTestData( |
| 575 | + size.haystackSize, size.needleSize, size.needleCount, 256, |
| 576 | + ) |
| 577 | + b.ResetTimer() |
| 578 | + for b.Loop() { |
| 579 | + findSequence(haystack, needles) |
| 580 | + b.SetBytes(int64(len(haystack))) |
| 581 | + } |
| 582 | + }) |
| 583 | + } |
| 584 | +} |
| 585 | + |
| 586 | +func generateTestData(haystackSize, needleSize, needleCount, charset int) ([]byte, [][]byte) { |
| 587 | + haystack := generateRandomBytes(haystackSize, charset) |
| 588 | + |
| 589 | + needles := make([][]byte, needleCount) |
| 590 | + for i := range needleCount { |
| 591 | + pattern := generateRandomBytes(needleSize, charset) |
| 592 | + pos := rand.Intn(len(haystack) - needleSize) |
| 593 | + copy(haystack[pos:], pattern) |
| 594 | + needles[i] = pattern |
| 595 | + } |
| 596 | + |
| 597 | + return haystack, needles |
| 598 | +} |
| 599 | + |
| 600 | +func generateRandomBytes(size, charset int) []byte { |
| 601 | + b := make([]byte, size) |
| 602 | + for i := range b { |
| 603 | + b[i] = byte(rand.Intn(charset)) |
| 604 | + } |
| 605 | + return b |
| 606 | +} |
| 607 | + |
| 608 | +func bb(s string) []byte { |
| 609 | + return []byte(s) |
| 610 | +} |
0 commit comments