All checks were successful
CI / Check PR Title (pull_request) Successful in 32s
CI / Go Lint (pull_request) Successful in 54s
CI / Markdown Lint (pull_request) Successful in 34s
CI / Makefile Lint (pull_request) Successful in 54s
CI / Unit Tests (pull_request) Successful in 54s
CI / Fuzz Tests (pull_request) Successful in 1m25s
CI / Mutation Tests (pull_request) Successful in 1m8s
94 lines
2.0 KiB
Go
94 lines
2.0 KiB
Go
package cuckoo_test
|
|
|
|
import (
|
|
"fmt"
|
|
"maps"
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
go_fuzz_utils "github.com/trailofbits/go-fuzz-utils"
|
|
|
|
"git.maximhutz.com/tools/go-cuckoo"
|
|
)
|
|
|
|
func offsetHash(seed uint32) cuckoo.Hash[uint32] {
|
|
return func(x uint32) uint64 {
|
|
v := uint64(x) ^ uint64(seed)
|
|
v = (v ^ (v >> 30)) * 0xbf58476d1ce4e5b9
|
|
v = (v ^ (v >> 27)) * 0x94d049bb133111eb
|
|
return v ^ (v >> 31)
|
|
}
|
|
}
|
|
|
|
type fuzzStep struct {
|
|
drop bool
|
|
key, value uint32
|
|
}
|
|
|
|
type fuzzScenario struct {
|
|
seedA, seedB uint32
|
|
capacity, growthFactor uint8
|
|
steps []fuzzStep
|
|
}
|
|
|
|
func FuzzInsertLookup(f *testing.F) {
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
var scenario fuzzScenario
|
|
assert := assert.New(t)
|
|
|
|
if tp, err := go_fuzz_utils.NewTypeProvider(data); err != nil {
|
|
return
|
|
} else if err := tp.Fill(&scenario); err != nil {
|
|
return
|
|
}
|
|
|
|
seedA, seedB := scenario.seedA, scenario.seedB
|
|
growthFactor := max(2, int(scenario.growthFactor))
|
|
capacity := int(scenario.capacity)
|
|
|
|
// If they are the same number, the hashes will clash, always causing an
|
|
// error.
|
|
if seedA == seedB {
|
|
t.Skip()
|
|
}
|
|
|
|
fmt.Fprintf(os.Stderr, "seedA=%d seedB=%d capacity=%d growthFactor=%d\n",
|
|
seedA, seedB, capacity, growthFactor)
|
|
|
|
actual := cuckoo.NewCustom[uint32, uint32](
|
|
offsetHash(seedA),
|
|
offsetHash(seedB),
|
|
func(a, b uint32) bool { return a == b },
|
|
cuckoo.Capacity(capacity),
|
|
cuckoo.GrowthFactor(growthFactor),
|
|
)
|
|
|
|
expected := map[uint32]uint32{}
|
|
|
|
for _, step := range scenario.steps {
|
|
if step.drop {
|
|
ok := actual.Drop(step.key)
|
|
_, has := expected[step.key]
|
|
assert.Equal(ok, has)
|
|
|
|
delete(expected, step.key)
|
|
|
|
_, ok = actual.Get(step.key)
|
|
assert.False(ok)
|
|
} else {
|
|
err := actual.Put(step.key, step.value)
|
|
assert.NoError(err)
|
|
|
|
expected[step.key] = step.value
|
|
|
|
found, ok := actual.Get(step.key)
|
|
assert.True(ok)
|
|
assert.Equal(step.value, found)
|
|
}
|
|
|
|
assert.Equal(expected, maps.Collect(actual.Entries()))
|
|
}
|
|
})
|
|
}
|