// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You // may not use this file except in compliance with the License. A copy of // the License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF // ANY KIND, either express or implied. See the License for the specific // language governing permissions and limitations under the License. package secretcache import ( "strconv" "testing" ) func TestPutIfAbsent(t *testing.T) { lruCache := newLRUCache(DefaultMaxCacheSize) key := "some-key" data := 42 addedToCache := lruCache.putIfAbsent(key, data) if !addedToCache { t.Fatalf("Failed initial add to cache") } addedToCache = lruCache.putIfAbsent(key, data*2) if addedToCache { t.Fatalf("Should have failed second add to cache") } retrievedItem, found := lruCache.cacheMap[key] if !found { t.Fatalf("Did not find expected entry in cache") } if (*retrievedItem).data != data { t.Fatalf("Expected data %d did not match retrieved data %d", data, (*retrievedItem).data) } if lruCache.cacheSize != 1 { t.Fatalf("Expected cache size to be 1") } } func TestGet(t *testing.T) { lruCache := newLRUCache(DefaultMaxCacheSize) key := "some-key" data := 42 _, found := lruCache.get(key) if found { t.Fatalf("Did not expect entry in cache") } lruCache.putIfAbsent(key, data) retrievedData, found := lruCache.get(key) if !found { t.Fatalf("Did not find expected entry in cache") } if retrievedData != data { t.Fatalf("Expected data %d did not match retrieved data %d", data, retrievedData) } } func TestLRUCacheMax(t *testing.T) { lruCache := newLRUCache(10) for i := 0; i <= 100; i++ { lruCache.putIfAbsent(strconv.Itoa(i), i) } for i := 0; i <= 90; i++ { if _, found := lruCache.get(strconv.Itoa(i)); found { t.Fatalf("Found unexpected val in cache - %d", i) } } for i := 91; i <= 100; i++ { if val, found := lruCache.get(strconv.Itoa(i)); !found || i != val.(int) { t.Fatalf("Expected to find val in cache - %d", i) } } } func TestLRUCacheEmpty(t *testing.T) { lruCache := newLRUCache(10) _, found := lruCache.get("some-key") if found { t.Fatalf("Found unexpected val in cache") } } func TestLRUCacheRecent(t *testing.T) { lruCache := newLRUCache(10) for i := 0; i <= 100; i++ { lruCache.putIfAbsent(strconv.Itoa(i), i) lruCache.get("0") } for i := 1; i <= 91; i++ { if _, found := lruCache.get(strconv.Itoa(i)); found { t.Fatalf("Found unexpected val in cache - %d", i) } } for i := 92; i <= 100; i++ { if val, found := lruCache.get(strconv.Itoa(i)); !found || i != val.(int) { t.Fatalf("Expected to find val in cache - %d", i) } } if val, found := lruCache.get("0"); !found || val.(int) != 0 { t.Fatalf("Expected to find val in cache - %d", 0) } } func TestLRUCacheZero(t *testing.T) { lruCache := newLRUCache(0) for i := 0; i <= 100; i++ { strI := strconv.Itoa(i) lruCache.putIfAbsent(strI, i) if _, found := lruCache.get(strI); found { t.Fatalf("Found unexpected val in cache - %d", i) } } for i := 0; i <= 100; i++ { if _, found := lruCache.get(strconv.Itoa(i)); found { t.Fatalf("Found unexpected val in cache - %d", i) } } } func TestLRUCacheOne(t *testing.T) { lruCache := newLRUCache(1) for i := 0; i <= 100; i++ { strI := strconv.Itoa(i) lruCache.putIfAbsent(strI, i) if val, found := lruCache.get(strI); !found || i != val.(int) { t.Fatalf("Expected to find val in cache - %d", i) } } for i := 0; i <= 99; i++ { if _, found := lruCache.get(strconv.Itoa(i)); found { t.Fatalf("Found unexpected val in cache - %d", i) } } } func TestConcurrentAccess(t *testing.T) { cache := newLRUCache(1) cache.putIfAbsent("key", "value") failed := make(chan bool) go accessor(cache, 500, "key", "value", failed) go accessor(cache, 400, "key", "value", failed) go accessor(cache, 300, "key", "value", failed) go accessor(cache, 600, "key", "value", failed) for i := 0; i < 4; i++ { if <-failed { t.Fatalf("Expected value not found") } } } func accessor(cache *lruCache, n int, key string, value string, failed chan bool) { for i := 0; i < n; i++ { if val, found := cache.get(key); !found || val != value { failed <- true } } failed <- false } func TestConcurrentMutations(t *testing.T) { cache := newLRUCache(1) failed := make(chan bool) go mutator(cache, 500, "key") go mutator(cache, 400, "key") go accessor(cache, 300, "key", "value", failed) go accessor(cache, 600, "key", "value", failed) } func mutator(cache *lruCache, n int, key string) { for i := 0; i < n; i++ { cache.putIfAbsent(key, i) } }