-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcache_utils_test.go
More file actions
147 lines (124 loc) · 4.25 KB
/
cache_utils_test.go
File metadata and controls
147 lines (124 loc) · 4.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package loadingcache_test
import (
"context"
"fmt"
"hash/fnv"
"testing"
"github.com/benbjohnson/clock"
"github.com/pkg/errors"
"go.uber.org/goleak"
"github.com/devzero-inc/loadingcache"
)
type testRemovalListener[K comparable, V any] struct {
lastRemovalNotification loadingcache.RemovalNotification[K, V]
}
func (t *testRemovalListener[K, V]) Listener(notification loadingcache.RemovalNotification[K, V]) {
t.lastRemovalNotification = notification
}
// testLoadFunc provides a configurable loading function that may fail
type testLoadFunc[K comparable, V ~string] struct {
fail bool
}
func (t *testLoadFunc[K, V]) LoadFunc(_ context.Context, key K) (V, error) {
var Nil V
if t.fail {
return Nil, errors.New("failing on request")
}
return V(fmt.Sprint(key)), nil
}
// stringHashCodeFunc is a test hash code function for strings which uses fnv.New32a
func stringHashCodeFunc[Key ~string]() func(k Key) uint64 {
return func(k Key) uint64 {
h := fnv.New32a()
if _, err := h.Write([]byte(string(k))); err != nil {
panic(err)
}
return uint64(h.Sum32())
}
}
// uintHashCodeFunc is a test hash code function for ints which just passes them through
func uintHashCodeFunc[Key ~int]() func(k Key) uint64 {
return func(k Key) uint64 {
return uint64(k)
}
}
func matrixBenchmark[K ~int, V any](b *testing.B,
options []loadingcache.CacheOption[K, V],
setupFunc matrixBenchmarkSetupFunc[K, V],
testFunc matrixBenchmarkFunc[K, V],
) {
matrixOptions := cacheMatrixOptions(options)
b.ResetTimer()
for name := range matrixOptions {
cache := loadingcache.New(matrixOptions[name]...)
setupFunc(b, cache)
b.Run(name, func(b *testing.B) {
b.ResetTimer()
testFunc(b, cache)
})
}
}
type matrixBenchmarkSetupFunc[K comparable, V any] func(b *testing.B, cache loadingcache.Cache[K, V])
type matrixBenchmarkFunc[K comparable, V any] func(b *testing.B, cache loadingcache.Cache[K, V])
func noopBenchmarkSetupFunc[K comparable, V any]() matrixBenchmarkSetupFunc[K, V] {
return func(b *testing.B, cache loadingcache.Cache[K, V]) {}
}
func matrixTest[K ~int, V any](t *testing.T, options matrixTestOptions[K, V], testFunc matrixTestFunc[K, V]) {
defer goleak.VerifyNone(t)
matrixOptions := cacheMatrixOptions(options.cacheOptions)
for name := range matrixOptions {
utils := &matrixTestUtils{}
cacheOptions := matrixOptions[name]
mockClock := clock.NewMock()
utils.clock = mockClock
// Place our clock first, allowing the user to override if necessary
cacheOptions = append([]loadingcache.CacheOption[K, V]{
loadingcache.WithClock[K, V](mockClock),
}, cacheOptions...)
ctx := put(context.Background(), utils)
cache := loadingcache.New(cacheOptions...)
if options.setupFunc != nil {
options.setupFunc(t, cache)
}
t.Run(name, func(t *testing.T) {
defer cache.Close()
testFunc(t, ctx, cache)
})
}
}
type matrixTestOptions[K comparable, V any] struct {
cacheOptions []loadingcache.CacheOption[K, V]
setupFunc func(t *testing.T, cache loadingcache.Cache[K, V])
}
type matrixTestUtils struct {
clock *clock.Mock
}
type utilsKey struct{}
func put(ctx context.Context, utils *matrixTestUtils) context.Context {
return context.WithValue(ctx, utilsKey{}, utils)
}
func get(ctx context.Context) *matrixTestUtils {
val := ctx.Value(utilsKey{})
if val == nil {
panic("could not find utils in context")
}
return val.(*matrixTestUtils)
}
type matrixTestFunc[K comparable, V any] func(t *testing.T, ctx context.Context, cache loadingcache.Cache[K, V])
func cacheMatrixOptions[K ~int, V any](baseOptions []loadingcache.CacheOption[K, V]) map[string][]loadingcache.CacheOption[K, V] {
matrix := map[string][]loadingcache.CacheOption[K, V]{}
var simpleOptions []loadingcache.CacheOption[K, V]
simpleOptions = append(simpleOptions, baseOptions...)
simpleOptions = append(simpleOptions, loadingcache.WithShardCount[K, V](1))
matrix["Simple"] = simpleOptions
for _, shardCount := range []int{2, 3, 16, 32} {
var shardedOptions []loadingcache.CacheOption[K, V]
shardedOptions = append(shardedOptions, baseOptions...)
shardedOptions = append(shardedOptions,
loadingcache.WithShardCount[K, V](shardCount),
loadingcache.WithHashCodeFunc[K, V](uintHashCodeFunc[K]()),
)
matrix[fmt.Sprintf("Sharded (%d)", shardCount)] = shardedOptions
}
return matrix
}