1
2
3
4
5 package cache
6
7 import (
8 "bytes"
9 "encoding/binary"
10 "fmt"
11 "os"
12 "path/filepath"
13 "testing"
14 "time"
15 )
16
17 func init() {
18 verify = false
19 }
20
21 func TestBasic(t *testing.T) {
22 dir, err := os.MkdirTemp("", "cachetest-")
23 if err != nil {
24 t.Fatal(err)
25 }
26 defer os.RemoveAll(dir)
27 _, err = Open(filepath.Join(dir, "notexist"))
28 if err == nil {
29 t.Fatal(`Open("tmp/notexist") succeeded, want failure`)
30 }
31
32 cdir := filepath.Join(dir, "c1")
33 if err := os.Mkdir(cdir, 0777); err != nil {
34 t.Fatal(err)
35 }
36
37 c1, err := Open(cdir)
38 if err != nil {
39 t.Fatalf("Open(c1) (create): %v", err)
40 }
41 if err := c1.putIndexEntry(dummyID(1), dummyID(12), 13, true); err != nil {
42 t.Fatalf("addIndexEntry: %v", err)
43 }
44 if err := c1.putIndexEntry(dummyID(1), dummyID(2), 3, true); err != nil {
45 t.Fatalf("addIndexEntry: %v", err)
46 }
47 if entry, err := c1.Get(dummyID(1)); err != nil || entry.OutputID != dummyID(2) || entry.Size != 3 {
48 t.Fatalf("c1.Get(1) = %x, %v, %v, want %x, %v, nil", entry.OutputID, entry.Size, err, dummyID(2), 3)
49 }
50
51 c2, err := Open(cdir)
52 if err != nil {
53 t.Fatalf("Open(c2) (reuse): %v", err)
54 }
55 if entry, err := c2.Get(dummyID(1)); err != nil || entry.OutputID != dummyID(2) || entry.Size != 3 {
56 t.Fatalf("c2.Get(1) = %x, %v, %v, want %x, %v, nil", entry.OutputID, entry.Size, err, dummyID(2), 3)
57 }
58 if err := c2.putIndexEntry(dummyID(2), dummyID(3), 4, true); err != nil {
59 t.Fatalf("addIndexEntry: %v", err)
60 }
61 if entry, err := c1.Get(dummyID(2)); err != nil || entry.OutputID != dummyID(3) || entry.Size != 4 {
62 t.Fatalf("c1.Get(2) = %x, %v, %v, want %x, %v, nil", entry.OutputID, entry.Size, err, dummyID(3), 4)
63 }
64 }
65
66 func TestGrowth(t *testing.T) {
67 dir, err := os.MkdirTemp("", "cachetest-")
68 if err != nil {
69 t.Fatal(err)
70 }
71 defer os.RemoveAll(dir)
72
73 c, err := Open(dir)
74 if err != nil {
75 t.Fatalf("Open: %v", err)
76 }
77
78 n := 10000
79 if testing.Short() {
80 n = 10
81 }
82
83 for i := 0; i < n; i++ {
84 if err := c.putIndexEntry(dummyID(i), dummyID(i*99), int64(i)*101, true); err != nil {
85 t.Fatalf("addIndexEntry: %v", err)
86 }
87 id := ActionID(dummyID(i))
88 entry, err := c.Get(id)
89 if err != nil {
90 t.Fatalf("Get(%x): %v", id, err)
91 }
92 if entry.OutputID != dummyID(i*99) || entry.Size != int64(i)*101 {
93 t.Errorf("Get(%x) = %x, %d, want %x, %d", id, entry.OutputID, entry.Size, dummyID(i*99), int64(i)*101)
94 }
95 }
96 for i := 0; i < n; i++ {
97 id := ActionID(dummyID(i))
98 entry, err := c.Get(id)
99 if err != nil {
100 t.Fatalf("Get2(%x): %v", id, err)
101 }
102 if entry.OutputID != dummyID(i*99) || entry.Size != int64(i)*101 {
103 t.Errorf("Get2(%x) = %x, %d, want %x, %d", id, entry.OutputID, entry.Size, dummyID(i*99), int64(i)*101)
104 }
105 }
106 }
107
108 func TestVerifyPanic(t *testing.T) {
109 os.Setenv("GODEBUG", "gocacheverify=1")
110 initEnv()
111 defer func() {
112 os.Unsetenv("GODEBUG")
113 verify = false
114 }()
115
116 if !verify {
117 t.Fatal("initEnv did not set verify")
118 }
119
120 dir, err := os.MkdirTemp("", "cachetest-")
121 if err != nil {
122 t.Fatal(err)
123 }
124 defer os.RemoveAll(dir)
125
126 c, err := Open(dir)
127 if err != nil {
128 t.Fatalf("Open: %v", err)
129 }
130
131 id := ActionID(dummyID(1))
132 if err := c.PutBytes(id, []byte("abc")); err != nil {
133 t.Fatal(err)
134 }
135
136 defer func() {
137 if err := recover(); err != nil {
138 t.Log(err)
139 return
140 }
141 }()
142 c.PutBytes(id, []byte("def"))
143 t.Fatal("mismatched Put did not panic in verify mode")
144 }
145
146 func dummyID(x int) [HashSize]byte {
147 var out [HashSize]byte
148 binary.LittleEndian.PutUint64(out[:], uint64(x))
149 return out
150 }
151
152 func TestCacheTrim(t *testing.T) {
153 dir, err := os.MkdirTemp("", "cachetest-")
154 if err != nil {
155 t.Fatal(err)
156 }
157 defer os.RemoveAll(dir)
158
159 c, err := Open(dir)
160 if err != nil {
161 t.Fatalf("Open: %v", err)
162 }
163 const start = 1000000000
164 now := int64(start)
165 c.now = func() time.Time { return time.Unix(now, 0) }
166
167 checkTime := func(name string, mtime int64) {
168 t.Helper()
169 file := filepath.Join(c.dir, name[:2], name)
170 info, err := os.Stat(file)
171 if err != nil {
172 t.Fatal(err)
173 }
174 if info.ModTime().Unix() != mtime {
175 t.Fatalf("%s mtime = %d, want %d", name, info.ModTime().Unix(), mtime)
176 }
177 }
178
179 id := ActionID(dummyID(1))
180 c.PutBytes(id, []byte("abc"))
181 entry, _ := c.Get(id)
182 c.PutBytes(ActionID(dummyID(2)), []byte("def"))
183 mtime := now
184 checkTime(fmt.Sprintf("%x-a", id), mtime)
185 checkTime(fmt.Sprintf("%x-d", entry.OutputID), mtime)
186
187
188 now = start + 10
189 c.Get(id)
190 checkTime(fmt.Sprintf("%x-a", id), mtime)
191 checkTime(fmt.Sprintf("%x-d", entry.OutputID), mtime)
192
193
194 now = start + 5000
195 mtime2 := now
196 if _, err := c.Get(id); err != nil {
197 t.Fatal(err)
198 }
199 c.OutputFile(entry.OutputID)
200 checkTime(fmt.Sprintf("%x-a", id), mtime2)
201 checkTime(fmt.Sprintf("%x-d", entry.OutputID), mtime2)
202
203
204 c.Trim()
205 if _, err := c.Get(id); err != nil {
206 t.Fatal(err)
207 }
208 c.OutputFile(entry.OutputID)
209 data, err := os.ReadFile(filepath.Join(dir, "trim.txt"))
210 if err != nil {
211 t.Fatal(err)
212 }
213 checkTime(fmt.Sprintf("%x-a", dummyID(2)), start)
214
215
216 now = start + 80000
217 c.Trim()
218 if _, err := c.Get(id); err != nil {
219 t.Fatal(err)
220 }
221 c.OutputFile(entry.OutputID)
222 data2, err := os.ReadFile(filepath.Join(dir, "trim.txt"))
223 if err != nil {
224 t.Fatal(err)
225 }
226 if !bytes.Equal(data, data2) {
227 t.Fatalf("second trim did work: %q -> %q", data, data2)
228 }
229
230
231
232
233
234
235 now += 5 * 86400
236 checkTime(fmt.Sprintf("%x-a", dummyID(2)), start)
237 c.Trim()
238 if _, err := c.Get(id); err != nil {
239 t.Fatal(err)
240 }
241 c.OutputFile(entry.OutputID)
242 mtime3 := now
243 if _, err := c.Get(dummyID(2)); err == nil {
244 t.Fatalf("Trim did not remove dummyID(2)")
245 }
246
247
248
249
250 now += 5 * 86400
251 c.Trim()
252 checkTime(fmt.Sprintf("%x-a", id), mtime3)
253 checkTime(fmt.Sprintf("%x-d", entry.OutputID), mtime3)
254
255
256
257
258 now += 86400 / 2
259 c.Trim()
260 checkTime(fmt.Sprintf("%x-a", id), mtime3)
261 checkTime(fmt.Sprintf("%x-d", entry.OutputID), mtime3)
262
263
264 now += 86400/2 + 1
265 c.Trim()
266 if _, err := c.Get(dummyID(1)); err == nil {
267 t.Fatal("Trim did not remove dummyID(1)")
268 }
269 }
270
View as plain text