-
Notifications
You must be signed in to change notification settings - Fork 2.3k
/
Copy pathlru_test.go
154 lines (141 loc) · 3.32 KB
/
lru_test.go
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
148
149
150
151
152
153
154
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package lru_test
import (
"bytes"
cryptorand "crypto/rand"
"fmt"
"log"
mathrand "math/rand"
"strings"
"testing"
"golang.org/x/sync/errgroup"
"golang.org/x/tools/internal/lru"
)
func TestCache(t *testing.T) {
type get struct {
key string
want any
}
type set struct {
key, value string
}
tests := []struct {
label string
steps []any
}{
{"empty cache", []any{
get{"a", nil},
get{"b", nil},
}},
{"zero-length string", []any{
set{"a", ""},
get{"a", ""},
}},
{"under capacity", []any{
set{"a", "123"},
set{"b", "456"},
get{"a", "123"},
get{"b", "456"},
}},
{"over capacity", []any{
set{"a", "123"},
set{"b", "456"},
set{"c", "78901"},
get{"a", nil},
get{"b", "456"},
get{"c", "78901"},
}},
{"access ordering", []any{
set{"a", "123"},
set{"b", "456"},
get{"a", "123"},
set{"c", "78901"},
get{"a", "123"},
get{"b", nil},
get{"c", "78901"},
}},
}
for _, test := range tests {
t.Run(test.label, func(t *testing.T) {
c := lru.New(10)
for i, step := range test.steps {
switch step := step.(type) {
case get:
if got := c.Get(step.key); got != step.want {
t.Errorf("#%d: c.Get(%q) = %q, want %q", i, step.key, got, step.want)
}
case set:
c.Set(step.key, step.value, len(step.value))
}
}
})
}
}
// TestConcurrency exercises concurrent access to the same entry.
//
// It is a copy of TestConcurrency from the filecache package.
func TestConcurrency(t *testing.T) {
key := uniqueKey()
const N = 100 // concurrency level
// Construct N distinct values, each larger
// than a typical 4KB OS file buffer page.
var values [N][8192]byte
for i := range values {
if _, err := mathrand.Read(values[i][:]); err != nil {
t.Fatalf("rand: %v", err)
}
}
cache := lru.New(100 * 1e6) // 100MB cache
// get calls Get and verifies that the cache entry
// matches one of the values passed to Set.
get := func(mustBeFound bool) error {
got := cache.Get(key)
if got == nil {
if !mustBeFound {
return nil
}
return fmt.Errorf("Get did not return a value")
}
gotBytes := got.([]byte)
for _, want := range values {
if bytes.Equal(want[:], gotBytes) {
return nil // a match
}
}
return fmt.Errorf("Get returned a value that was never Set")
}
// Perform N concurrent calls to Set and Get.
// All sets must succeed.
// All gets must return nothing, or one of the Set values;
// there is no third possibility.
var group errgroup.Group
for i := range values {
i := i
v := values[i][:]
group.Go(func() error {
cache.Set(key, v, len(v))
return nil
})
group.Go(func() error { return get(false) })
}
if err := group.Wait(); err != nil {
if strings.Contains(err.Error(), "operation not supported") ||
strings.Contains(err.Error(), "not implemented") {
t.Skipf("skipping: %v", err)
}
t.Fatal(err)
}
// A final Get must report one of the values that was Set.
if err := get(true); err != nil {
t.Fatalf("final Get failed: %v", err)
}
}
// uniqueKey returns a key that has never been used before.
func uniqueKey() (key [32]byte) {
if _, err := cryptorand.Read(key[:]); err != nil {
log.Fatalf("rand: %v", err)
}
return
}