package cache import ( "container/heap" "testing" "time" "github.com/stretchr/testify/require" ) func TestExpiryHeap_impl(t *testing.T) { var _ heap.Interface = new(expiryHeap) } func TestExpiryHeap(t *testing.T) { require := require.New(t) now := time.Now() ch := make(chan struct{}, 10) // buffered to prevent blocking in tests h := &expiryHeap{NotifyCh: ch} // Init, shouldn't trigger anything heap.Init(h) testNoMessage(t, ch) // Push an initial value, expect one message entry := &cacheEntryExpiry{Key: "foo", HeapIndex: -1, Expires: now.Add(100)} heap.Push(h, entry) require.Equal(0, entry.HeapIndex) testMessage(t, ch) testNoMessage(t, ch) // exactly one asserted above // Push another that goes earlier than entry entry2 := &cacheEntryExpiry{Key: "bar", HeapIndex: -1, Expires: now.Add(50)} heap.Push(h, entry2) require.Equal(0, entry2.HeapIndex) require.Equal(1, entry.HeapIndex) testMessage(t, ch) testNoMessage(t, ch) // exactly one asserted above // Push another that goes at the end entry3 := &cacheEntryExpiry{Key: "bar", HeapIndex: -1, Expires: now.Add(1000)} heap.Push(h, entry3) require.Equal(2, entry3.HeapIndex) testNoMessage(t, ch) // no notify cause index 0 stayed the same // Remove the first entry (not Pop, since we don't use Pop, but that works too) remove := h.Entries[0] heap.Remove(h, remove.HeapIndex) require.Equal(0, entry.HeapIndex) require.Equal(1, entry3.HeapIndex) testMessage(t, ch) testMessage(t, ch) // we have two because two swaps happen testNoMessage(t, ch) // Let's change entry 3 to be early, and fix it entry3.Expires = now.Add(10) h.Fix(entry3) require.Equal(1, entry.HeapIndex) require.Equal(0, entry3.HeapIndex) testMessage(t, ch) testNoMessage(t, ch) // Let's change entry 3 again, this is an edge case where if the 0th // element changed, we didn't trigger the channel. Our Fix func should. entry.Expires = now.Add(20) h.Fix(entry3) require.Equal(1, entry.HeapIndex) // no move require.Equal(0, entry3.HeapIndex) testMessage(t, ch) testNoMessage(t, ch) // one message } func testNoMessage(t *testing.T, ch <-chan struct{}) { t.Helper() select { case <-ch: t.Fatal("should not have a message") default: } } func testMessage(t *testing.T, ch <-chan struct{}) { t.Helper() select { case <-ch: default: t.Fatal("should have a message") } }