github.com/decred/dcrlnd@v0.7.6/channeldb/channel_cache_test.go (about)

     1  package channeldb
     2  
     3  import (
     4  	"reflect"
     5  	"testing"
     6  )
     7  
     8  // TestChannelCache checks the behavior of the channelCache with respect to
     9  // insertion, eviction, and removal of cache entries.
    10  func TestChannelCache(t *testing.T) {
    11  	const cacheSize = 100
    12  
    13  	// Create a new channel cache with the configured max size.
    14  	c := newChannelCache(cacheSize)
    15  
    16  	// As a sanity check, assert that querying the empty cache does not
    17  	// return an entry.
    18  	_, ok := c.get(0)
    19  	if ok {
    20  		t.Fatalf("channel cache should be empty")
    21  	}
    22  
    23  	// Now, fill up the cache entirely.
    24  	for i := uint64(0); i < cacheSize; i++ {
    25  		c.insert(i, channelForInt(i))
    26  	}
    27  
    28  	// Assert that the cache has all of the entries just inserted, since no
    29  	// eviction should occur until we try to surpass the max size.
    30  	assertHasChanEntries(t, c, 0, cacheSize)
    31  
    32  	// Now, insert a new element that causes the cache to evict an element.
    33  	c.insert(cacheSize, channelForInt(cacheSize))
    34  
    35  	// Assert that the cache has this last entry, as the cache should evict
    36  	// some prior element and not the newly inserted one.
    37  	assertHasChanEntries(t, c, cacheSize, cacheSize)
    38  
    39  	// Iterate over all inserted elements and construct a set of the evicted
    40  	// elements.
    41  	evicted := make(map[uint64]struct{})
    42  	for i := uint64(0); i < cacheSize+1; i++ {
    43  		_, ok := c.get(i)
    44  		if !ok {
    45  			evicted[i] = struct{}{}
    46  		}
    47  	}
    48  
    49  	// Assert that exactly one element has been evicted.
    50  	numEvicted := len(evicted)
    51  	if numEvicted != 1 {
    52  		t.Fatalf("expected one evicted entry, got: %d", numEvicted)
    53  	}
    54  
    55  	// Remove the highest item which initially caused the eviction and
    56  	// reinsert the element that was evicted prior.
    57  	c.remove(cacheSize)
    58  	for i := range evicted {
    59  		c.insert(i, channelForInt(i))
    60  	}
    61  
    62  	// Since the removal created an extra slot, the last insertion should
    63  	// not have caused an eviction and the entries for all channels in the
    64  	// original set that filled the cache should be present.
    65  	assertHasChanEntries(t, c, 0, cacheSize)
    66  
    67  	// Finally, reinsert the existing set back into the cache and test that
    68  	// the cache still has all the entries. If the randomized eviction were
    69  	// happening on inserts for existing cache items, we expect this to fail
    70  	// with high probability.
    71  	for i := uint64(0); i < cacheSize; i++ {
    72  		c.insert(i, channelForInt(i))
    73  	}
    74  	assertHasChanEntries(t, c, 0, cacheSize)
    75  
    76  }
    77  
    78  // assertHasEntries queries the edge cache for all channels in the range [start,
    79  // end), asserting that they exist and their value matches the entry produced by
    80  // entryForInt.
    81  func assertHasChanEntries(t *testing.T, c *channelCache, start, end uint64) {
    82  	t.Helper()
    83  
    84  	for i := start; i < end; i++ {
    85  		entry, ok := c.get(i)
    86  		if !ok {
    87  			t.Fatalf("channel cache should contain chan %d", i)
    88  		}
    89  
    90  		expEntry := channelForInt(i)
    91  		if !reflect.DeepEqual(entry, expEntry) {
    92  			t.Fatalf("entry mismatch, want: %v, got: %v",
    93  				expEntry, entry)
    94  		}
    95  	}
    96  }
    97  
    98  // channelForInt generates a unique ChannelEdge given an integer.
    99  func channelForInt(i uint64) ChannelEdge {
   100  	return ChannelEdge{
   101  		Info: &ChannelEdgeInfo{
   102  			ChannelID: i,
   103  		},
   104  	}
   105  }