github.com/mjibson/goon@v1.1.0/cache_test.go (about)

     1  /*
     2   * Copyright (c) 2012 The Goon Authors
     3   *
     4   * Permission to use, copy, modify, and distribute this software for any
     5   * purpose with or without fee is hereby granted, provided that the above
     6   * copyright notice and this permission notice appear in all copies.
     7   *
     8   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     9   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    10   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    11   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    12   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
    13   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    14   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    15   */
    16  
    17  package goon
    18  
    19  import (
    20  	"bytes"
    21  	"reflect"
    22  	"testing"
    23  	"unsafe"
    24  )
    25  
    26  func TestCacheBasics(t *testing.T) {
    27  	items := []*cacheItem{}
    28  	items = append(items, &cacheItem{key: "foo", value: []byte{1, 2, 3}})
    29  	items = append(items, &cacheItem{key: "bar", value: []byte{4, 5, 6}})
    30  
    31  	keys := make([]string, 0, len(items))
    32  	for _, item := range items {
    33  		keys = append(keys, item.key)
    34  	}
    35  
    36  	c := newCache(defaultCacheLimit)
    37  
    38  	for i := range items {
    39  		if v := c.Get(items[i].key); v != nil {
    40  			t.Fatalf("Expected nil for items[%d] but got %v", i, v)
    41  		}
    42  
    43  		c.Set(items[i])
    44  
    45  		if v := c.Get(items[i].key); !bytes.Equal(v, items[i].value) {
    46  			t.Fatalf("Invalid bytes for items[%d]! %x vs %x", i, v, items[i].value)
    47  		}
    48  
    49  		c.Delete(items[i].key)
    50  
    51  		if v := c.Get(items[i].key); v != nil {
    52  			t.Fatalf("Expected nil for items[%d] but got %v", i, v)
    53  		}
    54  
    55  		if c.size != 0 {
    56  			t.Fatalf("Expected size to be zero, but got %v", c.size)
    57  		}
    58  	}
    59  
    60  	if vs := c.GetMulti(keys); !reflect.DeepEqual(vs, [][]byte{nil, nil}) {
    61  		t.Fatalf("Expected nils but got %+v", vs)
    62  	}
    63  
    64  	c.SetMulti(items)
    65  
    66  	if vs := c.GetMulti(keys); !reflect.DeepEqual(vs, [][]byte{items[0].value, items[1].value}) {
    67  		t.Fatalf("Invalid bytes for items! %+v", vs)
    68  	}
    69  
    70  	c.DeleteMulti(keys)
    71  
    72  	if vs := c.GetMulti(keys); !reflect.DeepEqual(vs, [][]byte{nil, nil}) {
    73  		t.Fatalf("Expected nils but got %+v", vs)
    74  	}
    75  
    76  	if c.size != 0 {
    77  		t.Fatalf("Expected size to be zero, but got %v", c.size)
    78  	}
    79  
    80  	c.Set(items[0])
    81  	c.Flush()
    82  	if v := c.Get(items[0].key); v != nil {
    83  		t.Fatalf("Expected nil after flush but got %v", v)
    84  	}
    85  
    86  	c.Set(items[0])
    87  	c.Set(&cacheItem{key: items[0].key, value: []byte{7, 7, 7}})
    88  	if v := c.Get(items[0].key); !bytes.Equal(v, []byte{7, 7, 7}) {
    89  		t.Fatalf("Invalid bytes for value change! Got %x", v)
    90  	}
    91  }
    92  
    93  func TestCacheKeyLeak(t *testing.T) {
    94  	ak, bk := string([]byte{'f', 'o', 'o'}), string([]byte{'f', 'o', 'o'})
    95  	av, bv := []byte{1, 2, 3}, []byte{4, 5, 6}
    96  
    97  	c := newCache(defaultCacheLimit)
    98  
    99  	// Set the original value
   100  	c.Set(&cacheItem{key: ak, value: av})
   101  	if v := c.Get(ak); !bytes.Equal(v, av) {
   102  		t.Fatalf("Invalid bytes! %v", v)
   103  	}
   104  
   105  	// Rewrite it with a different value, and also a different key but same key contents
   106  	c.Set(&cacheItem{key: bk, value: bv})
   107  	if v := c.Get(bk); !bytes.Equal(v, bv) {
   108  		t.Fatalf("Invalid bytes! %v", v)
   109  	}
   110  
   111  	// Modify the new key contents without changing the pointer
   112  	*(*byte)(unsafe.Pointer(*(*uintptr)(unsafe.Pointer(&bk)))) = 'g'
   113  	if bk != "goo" {
   114  		t.Fatalf("Expected key to be 'goo' but it's %v", bk)
   115  	}
   116  
   117  	// Make sure that we can no longer retrieve the value with the new key,
   118  	// as that will only be possible via pointer equality, which means that
   119  	// the cache is still holding on to the new key, which doubles key storage
   120  	if v := c.Get(bk); v != nil {
   121  		t.Fatalf("Cache is leaking memory by keeping around the new key pointer! %v", v)
   122  	}
   123  	// Also make sure that we can retrieve the correct new value
   124  	// by using an unrelated key pointer that just matches the key contents
   125  	if v := c.Get("foo"); !bytes.Equal(v, bv) {
   126  		t.Fatalf("Invalid bytes! %v", v)
   127  	}
   128  
   129  	// Inspect the internals of the cache too, which contains only a single entry with ak as key
   130  	keyAddr := *(*uintptr)(unsafe.Pointer(&ak))
   131  	for key, elem := range c.elements {
   132  		if ka := *(*uintptr)(unsafe.Pointer(&key)); ka != keyAddr {
   133  			t.Fatalf("map key has wrong pointer! %x vs %x", ka, keyAddr)
   134  		}
   135  		if ka := *(*uintptr)(unsafe.Pointer(&(elem.Value.(*cacheItem)).key)); ka != keyAddr {
   136  			t.Fatalf("element key has wrong pointer! %x vs %x", ka, keyAddr)
   137  		}
   138  	}
   139  }
   140  
   141  func TestCacheLimit(t *testing.T) {
   142  	c := newCache(defaultCacheLimit)
   143  
   144  	items := []*cacheItem{}
   145  	items = append(items, &cacheItem{key: "foo", value: []byte{1, 2, 3}})
   146  	items = append(items, &cacheItem{key: "bar", value: []byte{4, 5, 6}})
   147  
   148  	keys := make([]string, 0, len(items))
   149  	for _, item := range items {
   150  		keys = append(keys, item.key)
   151  	}
   152  
   153  	if vs := c.GetMulti(keys); !reflect.DeepEqual(vs, [][]byte{nil, nil}) {
   154  		t.Fatalf("Expected nils but got %+v", vs)
   155  	}
   156  
   157  	c.SetMulti(items)
   158  
   159  	if vs := c.GetMulti(keys); !reflect.DeepEqual(vs, [][]byte{items[0].value, items[1].value}) {
   160  		t.Fatalf("Invalid bytes for items! %+v", vs)
   161  	}
   162  
   163  	c.setLimit(0)
   164  
   165  	if vs := c.GetMulti(keys); !reflect.DeepEqual(vs, [][]byte{nil, nil}) {
   166  		t.Fatalf("Expected nils but got %+v", vs)
   167  	}
   168  
   169  	if c.size != 0 {
   170  		t.Fatalf("Expected size to be zero, but got %v", c.size)
   171  	}
   172  
   173  	c.setLimit(cachedValueOverhead + len(items[1].key) + cap(items[1].value))
   174  
   175  	c.SetMulti(items)
   176  
   177  	if vs := c.GetMulti(keys); !reflect.DeepEqual(vs, [][]byte{nil, items[1].value}) {
   178  		t.Fatalf("Invalid bytes for items! %+v", vs)
   179  	}
   180  }