github.com/bhojpur/cache@v0.0.4/pkg/engine/lru_cache_test.go (about)

     1  package engine
     2  
     3  // Copyright (c) 2018 Bhojpur Consulting Private Limited, India. All rights reserved.
     4  
     5  // Permission is hereby granted, free of charge, to any person obtaining a copy
     6  // of this software and associated documentation files (the "Software"), to deal
     7  // in the Software without restriction, including without limitation the rights
     8  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     9  // copies of the Software, and to permit persons to whom the Software is
    10  // furnished to do so, subject to the following conditions:
    11  
    12  // The above copyright notice and this permission notice shall be included in
    13  // all copies or substantial portions of the Software.
    14  
    15  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    16  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    17  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    18  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    19  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    20  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    21  // THE SOFTWARE.
    22  
    23  import (
    24  	"testing"
    25  )
    26  
    27  type CacheValue struct {
    28  	size int64
    29  }
    30  
    31  func cacheValueSize(val interface{}) int64 {
    32  	return val.(*CacheValue).size
    33  }
    34  
    35  func TestInitialState(t *testing.T) {
    36  	cache := NewLRUCache(5, cacheValueSize)
    37  	l, sz, c, e := cache.Len(), cache.UsedCapacity(), cache.MaxCapacity(), cache.Evictions()
    38  	if l != 0 {
    39  		t.Errorf("length = %v, want 0", l)
    40  	}
    41  	if sz != 0 {
    42  		t.Errorf("size = %v, want 0", sz)
    43  	}
    44  	if c != 5 {
    45  		t.Errorf("capacity = %v, want 5", c)
    46  	}
    47  	if e != 0 {
    48  		t.Errorf("evictions = %v, want 0", c)
    49  	}
    50  }
    51  
    52  func TestSetInsertsValue(t *testing.T) {
    53  	cache := NewLRUCache(100, cacheValueSize)
    54  	data := &CacheValue{0}
    55  	key := "key"
    56  	cache.Set(key, data)
    57  
    58  	v, ok := cache.Get(key)
    59  	if !ok || v.(*CacheValue) != data {
    60  		t.Errorf("Cache has incorrect value: %v != %v", data, v)
    61  	}
    62  
    63  	values := cache.Items()
    64  	if len(values) != 1 || values[0].Key != key {
    65  		t.Errorf("Cache.Values() returned incorrect values: %v", values)
    66  	}
    67  }
    68  
    69  func TestGetValueWithMultipleTypes(t *testing.T) {
    70  	cache := NewLRUCache(100, cacheValueSize)
    71  	data := &CacheValue{0}
    72  	key := "key"
    73  	cache.Set(key, data)
    74  
    75  	v, ok := cache.Get("key")
    76  	if !ok || v.(*CacheValue) != data {
    77  		t.Errorf("Cache has incorrect value for \"key\": %v != %v", data, v)
    78  	}
    79  
    80  	v, ok = cache.Get(string([]byte{'k', 'e', 'y'}))
    81  	if !ok || v.(*CacheValue) != data {
    82  		t.Errorf("Cache has incorrect value for []byte {'k','e','y'}: %v != %v", data, v)
    83  	}
    84  }
    85  
    86  func TestSetUpdatesSize(t *testing.T) {
    87  	cache := NewLRUCache(100, cacheValueSize)
    88  	emptyValue := &CacheValue{0}
    89  	key := "key1"
    90  	cache.Set(key, emptyValue)
    91  	if sz := cache.UsedCapacity(); sz != 0 {
    92  		t.Errorf("cache.UsedCapacity() = %v, expected 0", sz)
    93  	}
    94  	someValue := &CacheValue{20}
    95  	key = "key2"
    96  	cache.Set(key, someValue)
    97  	if sz := cache.UsedCapacity(); sz != 20 {
    98  		t.Errorf("cache.UsedCapacity() = %v, expected 20", sz)
    99  	}
   100  }
   101  
   102  func TestSetWithOldKeyUpdatesValue(t *testing.T) {
   103  	cache := NewLRUCache(100, cacheValueSize)
   104  	emptyValue := &CacheValue{0}
   105  	key := "key1"
   106  	cache.Set(key, emptyValue)
   107  	someValue := &CacheValue{20}
   108  	cache.Set(key, someValue)
   109  
   110  	v, ok := cache.Get(key)
   111  	if !ok || v.(*CacheValue) != someValue {
   112  		t.Errorf("Cache has incorrect value: %v != %v", someValue, v)
   113  	}
   114  }
   115  
   116  func TestSetWithOldKeyUpdatesSize(t *testing.T) {
   117  	cache := NewLRUCache(100, cacheValueSize)
   118  	emptyValue := &CacheValue{0}
   119  	key := "key1"
   120  	cache.Set(key, emptyValue)
   121  
   122  	if sz := cache.UsedCapacity(); sz != 0 {
   123  		t.Errorf("cache.UsedCapacity() = %v, expected %v", sz, 0)
   124  	}
   125  
   126  	someValue := &CacheValue{20}
   127  	cache.Set(key, someValue)
   128  	expected := int64(someValue.size)
   129  	if sz := cache.UsedCapacity(); sz != expected {
   130  		t.Errorf("cache.UsedCapacity() = %v, expected %v", sz, expected)
   131  	}
   132  }
   133  
   134  func TestGetNonExistent(t *testing.T) {
   135  	cache := NewLRUCache(100, cacheValueSize)
   136  
   137  	if _, ok := cache.Get("notthere"); ok {
   138  		t.Error("Cache returned a notthere value after no inserts.")
   139  	}
   140  }
   141  
   142  func TestDelete(t *testing.T) {
   143  	cache := NewLRUCache(100, cacheValueSize)
   144  	value := &CacheValue{1}
   145  	key := "key"
   146  
   147  	cache.Delete(key)
   148  	cache.Set(key, value)
   149  	cache.Delete(key)
   150  
   151  	if sz := cache.UsedCapacity(); sz != 0 {
   152  		t.Errorf("cache.UsedCapacity() = %v, expected 0", sz)
   153  	}
   154  
   155  	if _, ok := cache.Get(key); ok {
   156  		t.Error("Cache returned a value after deletion.")
   157  	}
   158  }
   159  
   160  func TestClear(t *testing.T) {
   161  	cache := NewLRUCache(100, cacheValueSize)
   162  	value := &CacheValue{1}
   163  	key := "key"
   164  
   165  	cache.Set(key, value)
   166  	cache.Clear()
   167  
   168  	if sz := cache.UsedCapacity(); sz != 0 {
   169  		t.Errorf("cache.UsedCapacity() = %v, expected 0 after Clear()", sz)
   170  	}
   171  }
   172  
   173  func TestCapacityIsObeyed(t *testing.T) {
   174  	size := int64(3)
   175  	cache := NewLRUCache(100, cacheValueSize)
   176  	cache.SetCapacity(size)
   177  	value := &CacheValue{1}
   178  
   179  	// Insert up to the cache's capacity.
   180  	cache.Set("key1", value)
   181  	cache.Set("key2", value)
   182  	cache.Set("key3", value)
   183  	if sz := cache.UsedCapacity(); sz != size {
   184  		t.Errorf("cache.UsedCapacity() = %v, expected %v", sz, size)
   185  	}
   186  	// Insert one more; something should be evicted to make room.
   187  	cache.Set("key4", value)
   188  	sz, evictions := cache.UsedCapacity(), cache.Evictions()
   189  	if sz != size {
   190  		t.Errorf("post-evict cache.UsedCapacity() = %v, expected %v", sz, size)
   191  	}
   192  	if evictions != 1 {
   193  		t.Errorf("post-evict cache.Evictions() = %v, expected 1", evictions)
   194  	}
   195  
   196  	// Check various other stats
   197  	if l := cache.Len(); int64(l) != size {
   198  		t.Errorf("cache.Len() returned bad length: %v", l)
   199  	}
   200  	if s := cache.UsedCapacity(); s != size {
   201  		t.Errorf("cache.UsedCapacity() returned bad size: %v", s)
   202  	}
   203  	if c := cache.MaxCapacity(); c != size {
   204  		t.Errorf("cache.UsedCapacity() returned bad length: %v", c)
   205  	}
   206  }
   207  
   208  func TestLRUIsEvicted(t *testing.T) {
   209  	size := int64(3)
   210  	cache := NewLRUCache(size, cacheValueSize)
   211  
   212  	cache.Set("key1", &CacheValue{1})
   213  	cache.Set("key2", &CacheValue{1})
   214  	cache.Set("key3", &CacheValue{1})
   215  	// lru: [key3, key2, key1]
   216  
   217  	// Look up the elements. This will rearrange the LRU ordering.
   218  	cache.Get("key3")
   219  	cache.Get("key2")
   220  	cache.Get("key1")
   221  	// lru: [key1, key2, key3]
   222  
   223  	cache.Set("key0", &CacheValue{1})
   224  	// lru: [key0, key1, key2]
   225  
   226  	// The least recently used one should have been evicted.
   227  	if _, ok := cache.Get("key3"); ok {
   228  		t.Error("Least recently used element was not evicted.")
   229  	}
   230  
   231  	if e, want := cache.Evictions(), int64(1); e != want {
   232  		t.Errorf("evictions: %d, want: %d", e, want)
   233  	}
   234  }