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
|
package gocache
import (
"fmt"
"testing"
"time"
)
func TestCache_StartJanitor(t *testing.T) {
cache := NewCache()
cache.SetWithTTL("1", "1", time.Nanosecond)
if cacheSize := cache.Count(); cacheSize != 1 {
t.Errorf("expected cacheSize to be 1, but was %d", cacheSize)
}
err := cache.StartJanitor()
if err != nil {
t.Fatal(err)
}
defer cache.StopJanitor()
time.Sleep(JanitorMinShiftBackOff * 2)
if cacheSize := cache.Count(); cacheSize != 0 {
t.Errorf("expected cacheSize to be 0, but was %d", cacheSize)
}
}
func TestCache_StartJanitorWhenAlreadyStarted(t *testing.T) {
cache := NewCache()
if err := cache.StartJanitor(); err != nil {
t.Fatal(err)
}
if err := cache.StartJanitor(); err == nil {
t.Fatal("expected StartJanitor to return an error, because the janitor is already started")
}
cache.StopJanitor()
}
func TestCache_StopJanitor(t *testing.T) {
cache := NewCache()
_ = cache.StartJanitor()
if cache.stopJanitor == nil {
t.Error("starting the janitor should've initialized cache.stopJanitor")
}
cache.StopJanitor()
if cache.stopJanitor != nil {
t.Error("stopping the janitor should've set cache.stopJanitor to nil")
}
// Check if stopping the janitor even though it's already stopped causes a panic
cache.StopJanitor()
}
func TestJanitor(t *testing.T) {
cache := NewCache().WithMaxSize(3 * JanitorMaxIterationsPerShift)
defer cache.Clear()
for i := 0; i < 3*JanitorMaxIterationsPerShift; i++ {
if i < JanitorMaxIterationsPerShift && i%2 == 0 {
cache.SetWithTTL(fmt.Sprintf("%d", i), "value", time.Millisecond)
} else {
cache.SetWithTTL(fmt.Sprintf("%d", i), "value", time.Hour)
}
}
cacheSize := cache.Count()
err := cache.StartJanitor()
if err != nil {
t.Fatal(err)
}
defer cache.StopJanitor()
time.Sleep(JanitorMinShiftBackOff * 4)
if cacheSize <= cache.Count() {
t.Error("The janitor should be deleting expired cache entries")
}
cacheSize = cache.Count()
time.Sleep(JanitorMinShiftBackOff * 4)
if cacheSize <= cache.Count() {
t.Error("The janitor should be deleting expired cache entries")
}
cacheSize = cache.Count()
time.Sleep(JanitorMinShiftBackOff * 4)
if cacheSize <= cache.Count() {
t.Error("The janitor should be deleting expired cache entries")
}
}
func TestJanitorIsLoopingProperly(t *testing.T) {
cache := NewCache().WithMaxSize(JanitorMaxIterationsPerShift + 3)
defer cache.Clear()
for i := 0; i < JanitorMaxIterationsPerShift; i++ {
cache.SetWithTTL(fmt.Sprintf("%d", i), "value", time.Hour)
}
cache.SetWithTTL("key-to-expire-1", "value", JanitorMinShiftBackOff*2)
cache.SetWithTTL("key-to-expire-2", "value", JanitorMinShiftBackOff*2)
cache.SetWithTTL("key-to-expire-3", "value", JanitorMinShiftBackOff*2)
err := cache.StartJanitor()
if err != nil {
t.Fatal(err)
}
defer cache.StopJanitor()
if cache.Count() != JanitorMaxIterationsPerShift+3 {
t.Error("The janitor shouldn't have had enough time to remove anything from the cache yet", cache.Count())
}
const timeout = JanitorMinShiftBackOff * 20
threeKeysExpiredWithinOneSecond := false
for start := time.Now(); time.Since(start) < timeout; {
if cache.Stats().ExpiredKeys == 3 {
threeKeysExpiredWithinOneSecond = true
break
}
time.Sleep(JanitorMinShiftBackOff)
}
if !threeKeysExpiredWithinOneSecond {
t.Error("expected 3 keys to expire within 1 second")
}
if cache.Count() != JanitorMaxIterationsPerShift {
t.Error("The janitor should've deleted 3 entries")
}
}
func TestJanitorDoesNotThrowATantrumWhenThereIsNothingToClean(t *testing.T) {
cache := NewCache()
start := time.Now()
_ = cache.StartJanitor()
defer cache.StopJanitor()
time.Sleep(JanitorMaxShiftBackOff * 3)
// Technically, if the janitor doesn't backoff properly, the sleep above is likely to take more time than the sleep
// below because it would be eating up the CPU.
// This is a far-fetched test, but it's a good sanity check.
if time.Since(start) > JanitorMaxShiftBackOff*4 {
t.Error("The janitor should've backed off and prevented CPU usage from throttling the application")
}
}
|