4 | 4 |
"testing"
|
5 | 5 |
"time"
|
6 | 6 |
|
|
7 |
"go.uber.org/goleak"
|
|
8 |
|
7 | 9 |
"fmt"
|
8 | |
"log"
|
9 | 10 |
"sync"
|
10 | 11 |
|
11 | 12 |
"github.com/stretchr/testify/assert"
|
12 | 13 |
)
|
13 | 14 |
|
14 | |
// test for Feature request in issue #12
|
15 | |
//
|
16 | |
func TestCache_SkipTtlExtensionOnHit(t *testing.T) {
|
17 | |
cache := NewCache()
|
|
15 |
func TestMain(m *testing.M) {
|
|
16 |
goleak.VerifyTestMain(m)
|
|
17 |
}
|
|
18 |
|
|
19 |
// Issue #23: Goroutine leak on closing. When adding a close method i would like to see
|
|
20 |
// that it can be called in a repeated way without problems.
|
|
21 |
func TestCache_MultipleCloseCalls(t *testing.T) {
|
|
22 |
cache := NewCache()
|
|
23 |
|
18 | 24 |
cache.SetTTL(time.Millisecond * 100)
|
19 | 25 |
|
20 | 26 |
cache.SkipTtlExtensionOnHit(false)
|
|
27 | 33 |
|
28 | 34 |
}
|
29 | 35 |
|
|
36 |
cache.Close()
|
|
37 |
cache.Close()
|
|
38 |
cache.Close()
|
|
39 |
cache.Close()
|
|
40 |
}
|
|
41 |
|
|
42 |
// test for Feature request in issue #12
|
|
43 |
//
|
|
44 |
func TestCache_SkipTtlExtensionOnHit(t *testing.T) {
|
|
45 |
cache := NewCache()
|
|
46 |
defer cache.Close()
|
|
47 |
|
|
48 |
cache.SetTTL(time.Millisecond * 100)
|
|
49 |
|
|
50 |
cache.SkipTtlExtensionOnHit(false)
|
|
51 |
cache.Set("test", "!")
|
|
52 |
startTime := time.Now()
|
|
53 |
for now := time.Now(); now.Before(startTime.Add(time.Second * 3)); now = time.Now() {
|
|
54 |
if _, found := cache.Get("test"); !found {
|
|
55 |
t.Errorf("Item was not found, even though it should not expire.")
|
|
56 |
}
|
|
57 |
|
|
58 |
}
|
|
59 |
|
30 | 60 |
cache.SkipTtlExtensionOnHit(true)
|
31 | 61 |
cache.Set("expireTest", "!")
|
32 | 62 |
// will loop if item does not expire
|
|
36 | 66 |
|
37 | 67 |
func TestCache_ForRacesAcrossGoroutines(t *testing.T) {
|
38 | 68 |
cache := NewCache()
|
|
69 |
defer cache.Close()
|
|
70 |
|
39 | 71 |
cache.SetTTL(time.Minute * 1)
|
40 | 72 |
cache.SkipTtlExtensionOnHit(false)
|
41 | 73 |
|
|
51 | 83 |
go func(i int) {
|
52 | 84 |
time.Sleep(time.Nanosecond * time.Duration(rand.Int63n(1000000)))
|
53 | 85 |
if i%2 == 0 {
|
54 | |
cache.Set(fmt.Sprintf("test%d", i /10), false)
|
|
86 |
cache.Set(fmt.Sprintf("test%d", i/10), false)
|
55 | 87 |
} else {
|
56 | |
cache.SetWithTTL(fmt.Sprintf("test%d", i /10), false, time.Second*59)
|
|
88 |
cache.SetWithTTL(fmt.Sprintf("test%d", i/10), false, time.Second*59)
|
57 | 89 |
}
|
58 | 90 |
wgSet.Done()
|
59 | 91 |
}(i)
|
|
67 | 99 |
|
68 | 100 |
go func(i int) {
|
69 | 101 |
time.Sleep(time.Nanosecond * time.Duration(rand.Int63n(1000000)))
|
70 | |
cache.Get(fmt.Sprintf("test%d", i /10))
|
|
102 |
cache.Get(fmt.Sprintf("test%d", i/10))
|
71 | 103 |
wgGet.Done()
|
72 | 104 |
}(i)
|
73 | 105 |
}
|
|
80 | 112 |
|
81 | 113 |
func TestCache_SkipTtlExtensionOnHit_ForRacesAcrossGoroutines(t *testing.T) {
|
82 | 114 |
cache := NewCache()
|
|
115 |
defer cache.Close()
|
|
116 |
|
83 | 117 |
cache.SetTTL(time.Minute * 1)
|
84 | 118 |
cache.SkipTtlExtensionOnHit(true)
|
85 | 119 |
|
|
95 | 129 |
go func(i int) {
|
96 | 130 |
time.Sleep(time.Nanosecond * time.Duration(rand.Int63n(1000000)))
|
97 | 131 |
if i%2 == 0 {
|
98 | |
cache.Set(fmt.Sprintf("test%d", i /10), false)
|
|
132 |
cache.Set(fmt.Sprintf("test%d", i/10), false)
|
99 | 133 |
} else {
|
100 | |
cache.SetWithTTL(fmt.Sprintf("test%d", i /10), false, time.Second*59)
|
|
134 |
cache.SetWithTTL(fmt.Sprintf("test%d", i/10), false, time.Second*59)
|
101 | 135 |
}
|
102 | 136 |
wgSet.Done()
|
103 | 137 |
}(i)
|
|
111 | 145 |
|
112 | 146 |
go func(i int) {
|
113 | 147 |
time.Sleep(time.Nanosecond * time.Duration(rand.Int63n(1000000)))
|
114 | |
cache.Get(fmt.Sprintf("test%d", i /10))
|
|
148 |
cache.Get(fmt.Sprintf("test%d", i/10))
|
115 | 149 |
wgGet.Done()
|
116 | 150 |
}(i)
|
117 | 151 |
}
|
|
129 | 163 |
ch := make(chan struct{})
|
130 | 164 |
|
131 | 165 |
cacheAD := NewCache()
|
|
166 |
defer cacheAD.Close()
|
|
167 |
|
132 | 168 |
cacheAD.SetTTL(time.Millisecond)
|
133 | 169 |
cacheAD.SetCheckExpirationCallback(func(key string, value interface{}) bool {
|
134 | 170 |
v := value.(*int)
|
135 | |
log.Printf("key=%v, value=%d\n", key, *v)
|
|
171 |
t.Logf("key=%v, value=%d\n", key, *v)
|
136 | 172 |
iterated++
|
137 | 173 |
if iterated == 1 {
|
138 | 174 |
// this is the breaking test case for issue #14
|
|
160 | 196 |
|
161 | 197 |
// Setup the TTL cache
|
162 | 198 |
cache := NewCache()
|
|
199 |
defer cache.Close()
|
|
200 |
|
163 | 201 |
cache.SetTTL(time.Second * 1)
|
164 | 202 |
cache.SetExpirationCallback(func(key string, value interface{}) {
|
165 | |
fmt.Printf("This key(%s) has expired\n", key)
|
|
203 |
t.Logf("This key(%s) has expired\n", key)
|
166 | 204 |
})
|
167 | 205 |
for i := 0; i < 1024; i++ {
|
168 | 206 |
cache.Set(fmt.Sprintf("item_%d", i), A{})
|
169 | 207 |
time.Sleep(time.Millisecond * 10)
|
170 | |
fmt.Printf("Cache size: %d\n", cache.Count())
|
|
208 |
t.Logf("Cache size: %d\n", cache.Count())
|
171 | 209 |
}
|
172 | 210 |
|
173 | 211 |
if cache.Count() > 100 {
|
|
178 | 216 |
// test github issue #4
|
179 | 217 |
func TestRemovalAndCountDoesNotPanic(t *testing.T) {
|
180 | 218 |
cache := NewCache()
|
|
219 |
defer cache.Close()
|
|
220 |
|
181 | 221 |
cache.Set("key", "value")
|
182 | 222 |
cache.Remove("key")
|
183 | 223 |
count := cache.Count()
|
|
187 | 227 |
// test github issue #3
|
188 | 228 |
func TestRemovalWithTtlDoesNotPanic(t *testing.T) {
|
189 | 229 |
cache := NewCache()
|
|
230 |
defer cache.Close()
|
|
231 |
|
190 | 232 |
cache.SetExpirationCallback(func(key string, value interface{}) {
|
191 | 233 |
t.Logf("This key(%s) has expired\n", key)
|
192 | 234 |
})
|
|
216 | 258 |
|
217 | 259 |
func TestCacheIndividualExpirationBiggerThanGlobal(t *testing.T) {
|
218 | 260 |
cache := NewCache()
|
|
261 |
defer cache.Close()
|
|
262 |
|
219 | 263 |
cache.SetTTL(time.Duration(50 * time.Millisecond))
|
220 | 264 |
cache.SetWithTTL("key", "value", time.Duration(100*time.Millisecond))
|
221 | 265 |
<-time.After(150 * time.Millisecond)
|
|
226 | 270 |
|
227 | 271 |
func TestCacheGlobalExpirationByGlobal(t *testing.T) {
|
228 | 272 |
cache := NewCache()
|
|
273 |
defer cache.Close()
|
|
274 |
|
229 | 275 |
cache.Set("key", "value")
|
230 | 276 |
<-time.After(50 * time.Millisecond)
|
231 | 277 |
data, exists := cache.Get("key")
|
|
245 | 291 |
|
246 | 292 |
func TestCacheGlobalExpiration(t *testing.T) {
|
247 | 293 |
cache := NewCache()
|
|
294 |
defer cache.Close()
|
|
295 |
|
248 | 296 |
cache.SetTTL(time.Duration(100 * time.Millisecond))
|
249 | 297 |
cache.Set("key_1", "value")
|
250 | 298 |
cache.Set("key_2", "value")
|
|
255 | 303 |
|
256 | 304 |
func TestCacheMixedExpirations(t *testing.T) {
|
257 | 305 |
cache := NewCache()
|
|
306 |
defer cache.Close()
|
|
307 |
|
258 | 308 |
cache.SetExpirationCallback(func(key string, value interface{}) {
|
259 | 309 |
t.Logf("expired: %s", key)
|
260 | 310 |
})
|
|
267 | 317 |
|
268 | 318 |
func TestCacheIndividualExpiration(t *testing.T) {
|
269 | 319 |
cache := NewCache()
|
|
320 |
defer cache.Close()
|
|
321 |
|
270 | 322 |
cache.SetWithTTL("key", "value", time.Duration(100*time.Millisecond))
|
271 | 323 |
cache.SetWithTTL("key2", "value", time.Duration(100*time.Millisecond))
|
272 | 324 |
cache.SetWithTTL("key3", "value", time.Duration(100*time.Millisecond))
|
|
283 | 335 |
|
284 | 336 |
func TestCacheGet(t *testing.T) {
|
285 | 337 |
cache := NewCache()
|
|
338 |
defer cache.Close()
|
|
339 |
|
286 | 340 |
data, exists := cache.Get("hello")
|
287 | 341 |
assert.Equal(t, exists, false, "Expected empty cache to return no data")
|
288 | 342 |
assert.Nil(t, data, "Expected data to be empty")
|
|
299 | 353 |
var lock sync.Mutex
|
300 | 354 |
|
301 | 355 |
cache := NewCache()
|
|
356 |
defer cache.Close()
|
|
357 |
|
302 | 358 |
cache.SetTTL(time.Duration(500 * time.Millisecond))
|
303 | 359 |
cache.SetExpirationCallback(func(key string, value interface{}) {
|
304 | 360 |
lock.Lock()
|
|
321 | 377 |
var lock sync.Mutex
|
322 | 378 |
|
323 | 379 |
cache := NewCache()
|
|
380 |
defer cache.Close()
|
|
381 |
|
324 | 382 |
cache.SkipTtlExtensionOnHit(true)
|
325 | 383 |
cache.SetTTL(time.Duration(50 * time.Millisecond))
|
326 | 384 |
cache.SetCheckExpirationCallback(func(key string, value interface{}) bool {
|
|
348 | 406 |
func TestCacheNewItemCallbackFunction(t *testing.T) {
|
349 | 407 |
newItemCount := 0
|
350 | 408 |
cache := NewCache()
|
|
409 |
defer cache.Close()
|
|
410 |
|
351 | 411 |
cache.SetTTL(time.Duration(50 * time.Millisecond))
|
352 | 412 |
cache.SetNewItemCallback(func(key string, value interface{}) {
|
353 | 413 |
newItemCount = newItemCount + 1
|
|
361 | 421 |
|
362 | 422 |
func TestCacheRemove(t *testing.T) {
|
363 | 423 |
cache := NewCache()
|
|
424 |
defer cache.Close()
|
|
425 |
|
364 | 426 |
cache.SetTTL(time.Duration(50 * time.Millisecond))
|
365 | 427 |
cache.SetWithTTL("key", "value", time.Duration(100*time.Millisecond))
|
366 | 428 |
cache.Set("key_2", "value")
|
|
373 | 435 |
|
374 | 436 |
func TestCacheSetWithTTLExistItem(t *testing.T) {
|
375 | 437 |
cache := NewCache()
|
|
438 |
defer cache.Close()
|
|
439 |
|
376 | 440 |
cache.SetTTL(time.Duration(100 * time.Millisecond))
|
377 | 441 |
cache.SetWithTTL("key", "value", time.Duration(50*time.Millisecond))
|
378 | 442 |
<-time.After(30 * time.Millisecond)
|
|
384 | 448 |
|
385 | 449 |
func TestCache_Purge(t *testing.T) {
|
386 | 450 |
cache := NewCache()
|
|
451 |
defer cache.Close()
|
|
452 |
|
387 | 453 |
cache.SetTTL(time.Duration(100 * time.Millisecond))
|
388 | 454 |
|
389 | 455 |
for i := 0; i < 5; i++ {
|
|
398 | 464 |
}
|
399 | 465 |
|
400 | 466 |
}
|
401 | |
|
402 | |
func BenchmarkCacheSetWithoutTTL(b *testing.B) {
|
403 | |
cache := NewCache()
|
404 | |
for n := 0; n < b.N; n++ {
|
405 | |
cache.Set(string(n), "value")
|
406 | |
}
|
407 | |
}
|
408 | |
|
409 | |
func BenchmarkCacheSetWithGlobalTTL(b *testing.B) {
|
410 | |
cache := NewCache()
|
411 | |
cache.SetTTL(time.Duration(50 * time.Millisecond))
|
412 | |
for n := 0; n < b.N; n++ {
|
413 | |
cache.Set(string(n), "value")
|
414 | |
}
|
415 | |
}
|
416 | |
|
417 | |
func BenchmarkCacheSetWithTTL(b *testing.B) {
|
418 | |
cache := NewCache()
|
419 | |
for n := 0; n < b.N; n++ {
|
420 | |
cache.SetWithTTL(string(n), "value", time.Duration(50*time.Millisecond))
|
421 | |
}
|
422 | |
}
|