1 package ccache
2
3 import (
4 "sort"
5 "strconv"
6 "sync/atomic"
7 "testing"
8 "time"
9
10 . "github.com/karlseguin/expect"
11 )
12
13 type CacheTests struct{}
14
15 func Test_Cache(t *testing.T) {
16 Expectify(new(CacheTests), t)
17 }
18
19 func (_ CacheTests) DeletesAValue() {
20 cache := New(Configure())
21 Expect(cache.ItemCount()).To.Equal(0)
22
23 cache.Set("spice", "flow", time.Minute)
24 cache.Set("worm", "sand", time.Minute)
25 Expect(cache.ItemCount()).To.Equal(2)
26
27 cache.Delete("spice")
28 Expect(cache.Get("spice")).To.Equal(nil)
29 Expect(cache.Get("worm").Value()).To.Equal("sand")
30 Expect(cache.ItemCount()).To.Equal(1)
31 }
32
33 func (_ CacheTests) DeletesAPrefix() {
34 cache := New(Configure())
35 Expect(cache.ItemCount()).To.Equal(0)
36
37 cache.Set("aaa", "1", time.Minute)
38 cache.Set("aab", "2", time.Minute)
39 cache.Set("aac", "3", time.Minute)
40 cache.Set("ac", "4", time.Minute)
41 cache.Set("z5", "7", time.Minute)
42 Expect(cache.ItemCount()).To.Equal(5)
43
44 Expect(cache.DeletePrefix("9a")).To.Equal(0)
45 Expect(cache.ItemCount()).To.Equal(5)
46
47 Expect(cache.DeletePrefix("aa")).To.Equal(3)
48 Expect(cache.Get("aaa")).To.Equal(nil)
49 Expect(cache.Get("aab")).To.Equal(nil)
50 Expect(cache.Get("aac")).To.Equal(nil)
51 Expect(cache.Get("ac").Value()).To.Equal("4")
52 Expect(cache.Get("z5").Value()).To.Equal("7")
53 Expect(cache.ItemCount()).To.Equal(2)
54 }
55
56 func (_ CacheTests) DeletesAFunc() {
57 cache := New(Configure())
58 Expect(cache.ItemCount()).To.Equal(0)
59
60 cache.Set("a", 1, time.Minute)
61 cache.Set("b", 2, time.Minute)
62 cache.Set("c", 3, time.Minute)
63 cache.Set("d", 4, time.Minute)
64 cache.Set("e", 5, time.Minute)
65 cache.Set("f", 6, time.Minute)
66 Expect(cache.ItemCount()).To.Equal(6)
67
68 Expect(cache.DeleteFunc(func(key string, item *Item) bool {
69 return false
70 })).To.Equal(0)
71 Expect(cache.ItemCount()).To.Equal(6)
72
73 Expect(cache.DeleteFunc(func(key string, item *Item) bool {
74 return item.Value().(int) < 4
75 })).To.Equal(3)
76 Expect(cache.ItemCount()).To.Equal(3)
77
78 Expect(cache.DeleteFunc(func(key string, item *Item) bool {
79 return key == "d"
80 })).To.Equal(1)
81 Expect(cache.ItemCount()).To.Equal(2)
82
83 }
84
85 func (_ CacheTests) OnDeleteCallbackCalled() {
86 onDeleteFnCalled := int32(0)
87 onDeleteFn := func(item *Item) {
88 if item.key == "spice" {
89 atomic.AddInt32(&onDeleteFnCalled, 1)
90 }
91 }
92
93 cache := New(Configure().OnDelete(onDeleteFn))
94 cache.Set("spice", "flow", time.Minute)
95 cache.Set("worm", "sand", time.Minute)
96
97 time.Sleep(time.Millisecond * 10)
98 cache.Delete("spice")
99 time.Sleep(time.Millisecond * 10)
100
101 Expect(cache.Get("spice")).To.Equal(nil)
102 Expect(cache.Get("worm").Value()).To.Equal("sand")
103 Expect(atomic.LoadInt32(&onDeleteFnCalled)).To.Eql(1)
104 }
105
106 func (_ CacheTests) FetchesExpiredItems() {
107 cache := New(Configure())
108 fn := func() (interface{}, error) { return "moo-moo", nil }
109
110 cache.Set("beef", "moo", time.Second*-1)
111 Expect(cache.Get("beef").Value()).To.Equal("moo")
112
113 out, _ := cache.Fetch("beef", time.Second, fn)
114 Expect(out.Value()).To.Equal("moo-moo")
115 }
116
117 func (_ CacheTests) GCsTheOldestItems() {
118 cache := New(Configure().ItemsToPrune(10))
119 for i := 0; i < 500; i++ {
120 cache.Set(strconv.Itoa(i), i, time.Minute)
121 }
122
123 time.Sleep(time.Millisecond * 10)
124 gcCache(cache)
125 Expect(cache.Get("9")).To.Equal(nil)
126 Expect(cache.Get("10").Value()).To.Equal(10)
127 Expect(cache.ItemCount()).To.Equal(490)
128 }
129
130 func (_ CacheTests) PromotedItemsDontGetPruned() {
131 cache := New(Configure().ItemsToPrune(10).GetsPerPromote(1))
132 for i := 0; i < 500; i++ {
133 cache.Set(strconv.Itoa(i), i, time.Minute)
134 }
135 time.Sleep(time.Millisecond * 10)
136 cache.Get("9")
137 time.Sleep(time.Millisecond * 10)
138 gcCache(cache)
139 Expect(cache.Get("9").Value()).To.Equal(9)
140 Expect(cache.Get("10")).To.Equal(nil)
141 Expect(cache.Get("11").Value()).To.Equal(11)
142 }
143
144 func (_ CacheTests) TrackerDoesNotCleanupHeldInstance() {
145 cache := New(Configure().ItemsToPrune(11).Track())
146 item0 := cache.TrackingSet("0", 0, time.Minute)
147 for i := 1; i < 11; i++ {
148 cache.Set(strconv.Itoa(i), i, time.Minute)
149 }
150 item1 := cache.TrackingGet("1")
151 time.Sleep(time.Millisecond * 10)
152 gcCache(cache)
153 Expect(cache.Get("0").Value()).To.Equal(0)
154 Expect(cache.Get("1").Value()).To.Equal(1)
155 item0.Release()
156 item1.Release()
157 gcCache(cache)
158 Expect(cache.Get("0")).To.Equal(nil)
159 Expect(cache.Get("1")).To.Equal(nil)
160 }
161
162 func (_ CacheTests) RemovesOldestItemWhenFull() {
163 onDeleteFnCalled := false
164 onDeleteFn := func(item *Item) {
165 if item.key == "0" {
166 onDeleteFnCalled = true
167 }
168 }
169
170 cache := New(Configure().MaxSize(5).ItemsToPrune(1).OnDelete(onDeleteFn))
171 for i := 0; i < 7; i++ {
172 cache.Set(strconv.Itoa(i), i, time.Minute)
173 }
174 time.Sleep(time.Millisecond * 10)
175 Expect(cache.Get("0")).To.Equal(nil)
176 Expect(cache.Get("1")).To.Equal(nil)
177 Expect(cache.Get("2").Value()).To.Equal(2)
178 Expect(onDeleteFnCalled).To.Equal(true)
179 Expect(cache.ItemCount()).To.Equal(5)
180 }
181
182 func (_ CacheTests) RemovesOldestItemWhenFullBySizer() {
183 cache := New(Configure().MaxSize(9).ItemsToPrune(2))
184 for i := 0; i < 7; i++ {
185 cache.Set(strconv.Itoa(i), &SizedItem{i, 2}, time.Minute)
186 }
187 time.Sleep(time.Millisecond * 10)
188 Expect(cache.Get("0")).To.Equal(nil)
189 Expect(cache.Get("1")).To.Equal(nil)
190 Expect(cache.Get("2")).To.Equal(nil)
191 Expect(cache.Get("3")).To.Equal(nil)
192 Expect(cache.Get("4").Value().(*SizedItem).id).To.Equal(4)
193 Expect(cache.GetDropped()).To.Equal(4)
194 Expect(cache.GetDropped()).To.Equal(0)
195 }
196
197 func (_ CacheTests) SetUpdatesSizeOnDelta() {
198 cache := New(Configure())
199 cache.Set("a", &SizedItem{0, 2}, time.Minute)
200 cache.Set("b", &SizedItem{0, 3}, time.Minute)
201 time.Sleep(time.Millisecond * 5)
202 checkSize(cache, 5)
203 cache.Set("b", &SizedItem{0, 3}, time.Minute)
204 time.Sleep(time.Millisecond * 5)
205 checkSize(cache, 5)
206 cache.Set("b", &SizedItem{0, 4}, time.Minute)
207 time.Sleep(time.Millisecond * 5)
208 checkSize(cache, 6)
209 cache.Set("b", &SizedItem{0, 2}, time.Minute)
210 time.Sleep(time.Millisecond * 5)
211 checkSize(cache, 4)
212 cache.Delete("b")
213 time.Sleep(time.Millisecond * 100)
214 checkSize(cache, 2)
215 }
216
217 func (_ CacheTests) ReplaceDoesNotchangeSizeIfNotSet() {
218 cache := New(Configure())
219 cache.Set("1", &SizedItem{1, 2}, time.Minute)
220 cache.Set("2", &SizedItem{1, 2}, time.Minute)
221 cache.Set("3", &SizedItem{1, 2}, time.Minute)
222 cache.Replace("4", &SizedItem{1, 2})
223 time.Sleep(time.Millisecond * 5)
224 checkSize(cache, 6)
225 }
226
227 func (_ CacheTests) ReplaceChangesSize() {
228 cache := New(Configure())
229 cache.Set("1", &SizedItem{1, 2}, time.Minute)
230 cache.Set("2", &SizedItem{1, 2}, time.Minute)
231
232 cache.Replace("2", &SizedItem{1, 2})
233 time.Sleep(time.Millisecond * 5)
234 checkSize(cache, 4)
235
236 cache.Replace("2", &SizedItem{1, 1})
237 time.Sleep(time.Millisecond * 5)
238 checkSize(cache, 3)
239
240 cache.Replace("2", &SizedItem{1, 3})
241 time.Sleep(time.Millisecond * 5)
242 checkSize(cache, 5)
243 }
244
245 func (_ CacheTests) ResizeOnTheFly() {
246 cache := New(Configure().MaxSize(9).ItemsToPrune(1))
247 for i := 0; i < 5; i++ {
248 cache.Set(strconv.Itoa(i), i, time.Minute)
249 }
250 cache.SetMaxSize(3)
251 time.Sleep(time.Millisecond * 10)
252 Expect(cache.GetDropped()).To.Equal(2)
253 Expect(cache.Get("0")).To.Equal(nil)
254 Expect(cache.Get("1")).To.Equal(nil)
255 Expect(cache.Get("2").Value()).To.Equal(2)
256 Expect(cache.Get("3").Value()).To.Equal(3)
257 Expect(cache.Get("4").Value()).To.Equal(4)
258
259 cache.Set("5", 5, time.Minute)
260 time.Sleep(time.Millisecond * 5)
261 Expect(cache.GetDropped()).To.Equal(1)
262 Expect(cache.Get("2")).To.Equal(nil)
263 Expect(cache.Get("3").Value()).To.Equal(3)
264 Expect(cache.Get("4").Value()).To.Equal(4)
265 Expect(cache.Get("5").Value()).To.Equal(5)
266
267 cache.SetMaxSize(10)
268 cache.Set("6", 6, time.Minute)
269 time.Sleep(time.Millisecond * 10)
270 Expect(cache.GetDropped()).To.Equal(0)
271 Expect(cache.Get("3").Value()).To.Equal(3)
272 Expect(cache.Get("4").Value()).To.Equal(4)
273 Expect(cache.Get("5").Value()).To.Equal(5)
274 Expect(cache.Get("6").Value()).To.Equal(6)
275 }
276
277 func (_ CacheTests) ForEachFunc() {
278 cache := New(Configure().MaxSize(3).ItemsToPrune(1))
279 Expect(forEachKeys(cache)).To.Equal([]string{})
280
281 cache.Set("1", 1, time.Minute)
282 Expect(forEachKeys(cache)).To.Equal([]string{"1"})
283
284 cache.Set("2", 2, time.Minute)
285 time.Sleep(time.Millisecond * 10)
286 Expect(forEachKeys(cache)).To.Equal([]string{"1", "2"})
287
288 cache.Set("3", 3, time.Minute)
289 time.Sleep(time.Millisecond * 10)
290 Expect(forEachKeys(cache)).To.Equal([]string{"1", "2", "3"})
291
292 cache.Set("4", 4, time.Minute)
293 time.Sleep(time.Millisecond * 10)
294 Expect(forEachKeys(cache)).To.Equal([]string{"2", "3", "4"})
295
296 cache.Set("stop", 5, time.Minute)
297 time.Sleep(time.Millisecond * 10)
298 Expect(forEachKeys(cache)).Not.To.Contain("stop")
299
300 cache.Set("6", 6, time.Minute)
301 time.Sleep(time.Millisecond * 10)
302 Expect(forEachKeys(cache)).Not.To.Contain("stop")
303 }
304
305 type SizedItem struct {
306 id int
307 s int64
308 }
309
310 func (s *SizedItem) Size() int64 {
311 return s.s
312 }
313
314 func checkSize(cache *Cache, sz int64) {
315 cache.Stop()
316 Expect(cache.size).To.Equal(sz)
317 cache.restart()
318 }
319
320 func gcCache(cache *Cache) {
321 cache.Stop()
322 cache.gc()
323 cache.restart()
324 }
325
326 func forEachKeys(cache *Cache) []string {
327 keys := make([]string, 0, 10)
328 cache.ForEachFunc(func(key string, i *Item) bool {
329 if key == "stop" {
330 return false
331 }
332 keys = append(keys, key)
333 return true
334 })
335 sort.Strings(keys)
336 return keys
337 }
338
View as plain text