github.com/decred/dcrlnd@v0.7.6/channeldb/reject_cache_test.go (about) 1 package channeldb 2 3 import ( 4 "reflect" 5 "testing" 6 ) 7 8 // TestRejectCache checks the behavior of the rejectCache with respect to insertion, 9 // eviction, and removal of cache entries. 10 func TestRejectCache(t *testing.T) { 11 const cacheSize = 100 12 13 // Create a new reject cache with the configured max size. 14 c := newRejectCache(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("reject 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, entryForInt(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 assertHasEntries(t, c, 0, cacheSize) 31 32 // Now, insert a new element that causes the cache to evict an element. 33 c.insert(cacheSize, entryForInt(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 assertHasEntries(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, entryForInt(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 assertHasEntries(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, entryForInt(i)) 73 } 74 assertHasEntries(t, c, 0, cacheSize) 75 76 } 77 78 // assertHasEntries queries the reject 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 assertHasEntries(t *testing.T, c *rejectCache, 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("reject cache should contain chan %d", i) 88 } 89 90 expEntry := entryForInt(i) 91 if !reflect.DeepEqual(entry, expEntry) { 92 t.Fatalf("entry mismatch, want: %v, got: %v", 93 expEntry, entry) 94 } 95 } 96 } 97 98 // entryForInt generates a unique rejectCacheEntry given an integer. 99 func entryForInt(i uint64) rejectCacheEntry { 100 exists := i%2 == 0 101 isZombie := i%3 == 0 102 return rejectCacheEntry{ 103 upd1Time: int64(2 * i), 104 upd2Time: int64(2*i + 1), 105 flags: packRejectFlags(exists, isZombie), 106 } 107 }