codeberg.org/gruf/go-cache/v3@v3.5.7/ttl/ttl_test.go (about)

     1  package ttl_test
     2  
     3  import (
     4  	"net/url"
     5  	"reflect"
     6  	"testing"
     7  	"time"
     8  	"unsafe"
     9  
    10  	"codeberg.org/gruf/go-cache/v3/ttl"
    11  	"github.com/google/go-cmp/cmp"
    12  )
    13  
    14  var testEntries = map[string]interface{}{
    15  	"key1":  "value1",
    16  	"key2":  2,
    17  	"a":     'a',
    18  	"b":     '0',
    19  	"c":     []string{"a", "b", "c"},
    20  	"map":   map[string]string{"a": "a"},
    21  	"iface": interface{}(nil),
    22  	"weird": unsafe.Pointer(&ttl.Cache[string, string]{}),
    23  	"float": 2.4,
    24  	"url":   url.URL{},
    25  }
    26  
    27  func TestCache(t *testing.T) {
    28  	// Prepare cache
    29  	c := ttl.Cache[string, any]{}
    30  	c.Init(
    31  		len(testEntries),
    32  		len(testEntries)+1,
    33  		time.Second*5,
    34  	)
    35  
    36  	// Ensure we can start and stop it
    37  	if !c.Start(time.Second * 10) {
    38  		t.Fatal("failed to start cache eviction routine")
    39  	}
    40  
    41  	done := make(chan struct{})
    42  	go func() {
    43  		for {
    44  			// Return if done
    45  			select {
    46  			case <-done:
    47  				return
    48  			default:
    49  			}
    50  
    51  			// Continually loop checking keys
    52  			for key := range testEntries {
    53  				c.Has(key)
    54  			}
    55  		}
    56  	}()
    57  
    58  	// Track callbacks set
    59  	callbacks := map[string]interface{}{}
    60  	c.SetInvalidateCallback(func(key string, value interface{}) {
    61  		callbacks[key] = value
    62  	})
    63  
    64  	// Add all entries to cache
    65  	for key, val := range testEntries {
    66  		t.Logf("Cache.Add(%s, %v)", key, val)
    67  		if !c.Add(key, val) {
    68  			t.Fatalf("failed adding key to cache: %s", key)
    69  		}
    70  	}
    71  
    72  	// Ensure all entries are expected
    73  	for key, val := range testEntries {
    74  		check, ok := c.Get(key)
    75  		t.Logf("Cache.Get() => %s, %v", key, val)
    76  		if !ok {
    77  			t.Fatalf("key unexpectedly not found in cache: %s", key)
    78  		} else if !cmp.Equal(val, check) {
    79  			t.Fatalf("value not as expected for key in cache: %s", key)
    80  		}
    81  	}
    82  
    83  	// Update entries via CAS to ensure callback
    84  	for key, val := range testEntries {
    85  		t.Logf("Cache.CAS(%s, %v, nil)", key, val)
    86  		if !c.CAS(key, val, nil, reflect.DeepEqual) && val != nil {
    87  			t.Fatalf("CAS failed for: %s", key)
    88  		} else if _, ok := callbacks[key]; !ok {
    89  			t.Fatalf("invalidate callback not called for: %s", key)
    90  		}
    91  	}
    92  
    93  	// Check values were updated
    94  	for key := range testEntries {
    95  		check, ok := c.Get(key)
    96  		t.Logf("Cache.Get() => %s, %v", key, check)
    97  		if !ok {
    98  			t.Fatalf("key unexpectedly not found in cache: %s", key)
    99  		} else if check != nil {
   100  			t.Fatalf("value not as expected after update for key in cache: %s", key)
   101  		}
   102  	}
   103  
   104  	// Clear callbacks, force invalidate and recheck
   105  	callbacks = map[string]interface{}{}
   106  	for key := range testEntries {
   107  		t.Logf("Cache.Invalidate(%s)", key)
   108  		c.Invalidate(key)
   109  		if _, ok := callbacks[key]; !ok {
   110  			t.Fatalf("invalidate callback unexpectedly not called for: %s", key)
   111  		}
   112  	}
   113  
   114  	close(done) // stop the background loop
   115  	t.Log("Sleeping to give time for cache sweeps")
   116  	time.Sleep(time.Second * 11)
   117  
   118  	// Checking cache is off expected size
   119  	t.Logf("Checking cache is of expected size (0)")
   120  	if sz := c.Len(); sz != 0 {
   121  		t.Fatalf("unexpected cache size: %d", sz)
   122  	}
   123  
   124  	// Update cache TTL time to be
   125  	// sometime larger than sweep time.
   126  	c.SetTTL(time.Second*20, false)
   127  
   128  	// Restart the cache.
   129  	for c.Stop() {
   130  	}
   131  	for c.Start(time.Second * 10) {
   132  	}
   133  
   134  	// Add all entries to cache
   135  	for key, val := range testEntries {
   136  		t.Logf("Cache.Add(%s, %v)", key, val)
   137  		if !c.Add(key, val) {
   138  			t.Fatalf("failed adding key to cache: %s", key)
   139  		}
   140  	}
   141  
   142  	t.Log("Sleeping to give time for cache sweeps")
   143  	time.Sleep(time.Second * 15)
   144  
   145  	// Ensure all entries remain as expected
   146  	for key := range testEntries {
   147  		t.Logf("Cache.Has() => %s", key)
   148  		if !c.Has(key) {
   149  			t.Fatalf("key unexpectedly not found in cache: %s", key)
   150  		}
   151  	}
   152  
   153  	t.Log("Sleeping to give time for cache sweeps")
   154  	time.Sleep(time.Second * 11)
   155  
   156  	// Checking cache is off expected size
   157  	t.Logf("Checking cache is of expected size (0)")
   158  	if sz := c.Len(); sz != 0 {
   159  		t.Fatalf("unexpected cache size: %d", sz)
   160  	}
   161  }