package gcache import ( "bytes" "encoding/gob" "sync" "sync/atomic" "testing" "time" ) func TestLoaderFunc(t *testing.T) { size := 2 var testCaches = []*CacheBuilder[int, int64]{ New[int, int64](size).Simple(), New[int, int64](size).LRU(), New[int, int64](size).LFU(), New[int, int64](size).ARC(), } for _, builder := range testCaches { var testCounter int64 counter := 1000 cache := builder. LoaderFunc(func(key int) (int64, error) { time.Sleep(10 * time.Millisecond) return atomic.AddInt64(&testCounter, 1), nil }). EvictedFunc(func(key int, value int64) { panic(key) }).Build() var wg sync.WaitGroup for i := 0; i < counter; i++ { wg.Add(1) go func() { defer wg.Done() _, err := cache.Get(0) if err != nil { t.Error(err) } }() } wg.Wait() if testCounter != 1 { t.Errorf("testCounter != %v", testCounter) } } } func TestLoaderExpireFuncWithoutExpire(t *testing.T) { size := 2 var testCaches = []*CacheBuilder[int, int64]{ New[int, int64](size).Simple(), New[int, int64](size).LRU(), New[int, int64](size).LFU(), New[int, int64](size).ARC(), } for _, builder := range testCaches { var testCounter int64 counter := 1000 cache := builder. LoaderExpireFunc(func(key int) (int64, *time.Duration, error) { return atomic.AddInt64(&testCounter, 1), nil, nil }). EvictedFunc(func(key int, value int64) { panic(key) }).Build() var wg sync.WaitGroup for i := 0; i < counter; i++ { wg.Add(1) go func() { defer wg.Done() _, err := cache.Get(0) if err != nil { t.Error(err) } }() } wg.Wait() if testCounter != 1 { t.Errorf("testCounter != %v", testCounter) } } } func TestLoaderExpireFuncWithExpire(t *testing.T) { size := 2 var testCaches = []*CacheBuilder[int, int64]{ New[int, int64](size).Simple(), New[int, int64](size).LRU(), New[int, int64](size).LFU(), New[int, int64](size).ARC(), } for _, builder := range testCaches { var testCounter int64 counter := 1000 expire := 200 * time.Millisecond cache := builder. LoaderExpireFunc(func(key int) (int64, *time.Duration, error) { return atomic.AddInt64(&testCounter, 1), &expire, nil }). Build() var wg sync.WaitGroup for i := 0; i < counter; i++ { wg.Add(1) go func() { defer wg.Done() _, err := cache.Get(0) if err != nil { t.Error(err) } }() } time.Sleep(expire) // Waiting for key expiration for i := 0; i < counter; i++ { wg.Add(1) go func() { defer wg.Done() _, err := cache.Get(0) if err != nil { t.Error(err) } }() } wg.Wait() if testCounter != 2 { t.Errorf("testCounter != %v", testCounter) } } } func TestLoaderPurgeVisitorFunc(t *testing.T) { size := 7 tests := []struct { name string cacheBuilder *CacheBuilder[int, int64] }{ { name: "simple", cacheBuilder: New[int, int64](size).Simple(), }, { name: "lru", cacheBuilder: New[int, int64](size).LRU(), }, { name: "lfu", cacheBuilder: New[int, int64](size).LFU(), }, { name: "arc", cacheBuilder: New[int, int64](size).ARC(), }, } for _, test := range tests { var purgeCounter, evictCounter, loaderCounter int64 counter := 1000 cache := test.cacheBuilder. LoaderFunc(func(key int) (int64, error) { return atomic.AddInt64(&loaderCounter, 1), nil }). EvictedFunc(func(key int, value int64) { atomic.AddInt64(&evictCounter, 1) }). PurgeVisitorFunc(func(k int, v int64) { atomic.AddInt64(&purgeCounter, 1) }). Build() var wg sync.WaitGroup for i := 0; i < counter; i++ { i := i wg.Add(1) go func() { defer wg.Done() _, err := cache.Get(i) if err != nil { t.Error(err) } }() } wg.Wait() if loaderCounter != int64(counter) { t.Errorf("%s: loaderCounter != %v", test.name, loaderCounter) } cache.Purge() if evictCounter+purgeCounter != loaderCounter { t.Logf("%s: evictCounter: %d", test.name, evictCounter) t.Logf("%s: purgeCounter: %d", test.name, purgeCounter) t.Logf("%s: loaderCounter: %d", test.name, loaderCounter) t.Errorf("%s: load != evict+purge", test.name) } } } func TestDeserializeFunc(t *testing.T) { var cases = []struct { tp string }{ {TYPE_SIMPLE}, {TYPE_LRU}, {TYPE_LFU}, {TYPE_ARC}, } for _, cs := range cases { key1, value1 := "key1", "value1" key2, value2 := "key2", "value2" cc := New[string, any](32). EvictType(cs.tp). LoaderFunc(func(k string) (any, error) { return value1, nil }). DeserializeFunc(func(k string, v any) (any, error) { dec := gob.NewDecoder(bytes.NewBuffer(v.([]byte))) var str string err := dec.Decode(&str) if err != nil { return nil, err } return str, nil }). SerializeFunc(func(k string, v any) (any, error) { buf := new(bytes.Buffer) enc := gob.NewEncoder(buf) err := enc.Encode(v) return buf.Bytes(), err }). Build() v, err := cc.Get(key1) if err != nil { t.Fatal(err) } if v != value1 { t.Errorf("%v != %v", v, value1) } v, err = cc.Get(key1) if err != nil { t.Fatal(err) } if v != value1 { t.Errorf("%v != %v", v, value1) } if err := cc.Set(key2, value2); err != nil { t.Error(err) } v, err = cc.Get(key2) if err != nil { t.Error(err) } if v != value2 { t.Errorf("%v != %v", v, value2) } } } func TestExpiredItems(t *testing.T) { var tps = []string{ TYPE_SIMPLE, TYPE_LRU, TYPE_LFU, TYPE_ARC, } for _, tp := range tps { t.Run(tp, func(t *testing.T) { testExpiredItems(t, tp) }) } }