package leftright import ( "math" "runtime" "sync/atomic" "testing" ) func TestHundred(t *testing.T) { testHundred(t) } func BenchmarkTestHundred(b *testing.B) { for i := 0; i < b.N; i++ { testHundred(b) } } func testHundred(t testing.TB) { lrm := New[int, int]() rh := lrm.NewReadHandler() defer rh.Close() for i := 1; i < 100; i += 2 { k, v := i, i lrm.Set(k, v) } lrm.Commit() for i := 1; i < 100; i += 2 { k, v := i, i rh.Enter() if _v := rh.Get(k); _v != v { t.Errorf("Get(%d) want %d, got %d", k, v, _v) } rh.Leave() } for i := 0; i < 100; i += 2 { k, v := i, i lrm.Set(k, v) } lrm.Commit() for i := 0; i < 100; i++ { rh.Enter() k, v := i, i if _v := rh.Get(k); _v != v { t.Errorf("Get(%d) want %d, got %d", k, v, _v) } rh.Leave() } lrm.Commit() rh.Enter() for i := 0; i < 100; i++ { k, v := i, i if _v := rh.Get(k); _v != v { t.Errorf("Get(%d) want %d, got %d", k, v, _v) } } rh.Leave() for i := 0; i < 100; i += 2 { k := i lrm.Delete(k) } lrm.Commit() rh.Enter() for i := 0; i < 100; i++ { k, v := i, i _v, ok := rh.GetOK(k) if i%2 == 0 { if ok || _v != 0 { t.Errorf("GetOK(%d), want (0, false), got (%d, %t)", i, _v, ok) } } else { if !ok || _v != v { t.Errorf("GetOK(%d), want (%d, true), got (%d, %t)", i, v, _v, ok) } } } rh.Leave() lrm.Commit() rh.Enter() for i := 0; i < 100; i++ { k, v := i, i _v, ok := rh.GetOK(k) if i%2 == 0 { if ok || _v != 0 { t.Errorf("GetOK(%d), want (0, false), got (%d, %t)", i, _v, ok) } } else { if !ok || _v != v { t.Errorf("GetOK(%d), want (%d, true), got (%d, %t)", i, v, _v, ok) } } } rh.Leave() } func TestSerialOverflow(t *testing.T) { var maxEven uint64 = math.MaxUint64 - 1 t.Logf("maxEven(%d)", maxEven) if maxEven%2 == 1 { t.Errorf("maxEven(%d) is odd", maxEven) } maxOdd := maxEven + 1 t.Logf("maxOdd(%d)", maxOdd) if maxOdd%2 == 0 { t.Errorf("maxOdd(%d) is even", maxOdd) } overflow := maxOdd + 1 t.Logf("overflow(%d)", overflow) if overflow%2 == 1 { t.Errorf("overflow(%d) is odd", overflow) } } func TestCleanup(t *testing.T) { const n = 1000 lrmap := New[int, int]() lrmap.recordMetrics() readHandlers := make([]*ReadHandler[int, int], n) for i := 0; i < n; i++ { readHandlers[i] = lrmap.NewReadHandler() } runtime.GC() if created, cleanedUp := atomic.LoadUint64(&lrmap.metrics.readHandlersCreated), atomic.LoadUint64(&lrmap.metrics.readHandlersCleanedUp); created != n || cleanedUp != 0 { t.Errorf("created %d read handlers, cleaned up %d read handlers", created, cleanedUp) } lrmap.mu.Lock() if nn := len(lrmap.readHandlers); nn != n { t.Errorf("expected %d read handlers, got %d", n, nn) } lrmap.mu.Unlock() for i := range readHandlers { readHandlers[i] = nil } check := func() (uint64, uint64) { return atomic.LoadUint64(&lrmap.metrics.readHandlersCreated), atomic.LoadUint64(&lrmap.metrics.readHandlersCleanedUp) } Retry: for i := 0; i < 100; i++ { runtime.GC() if created, cleanedUp := check(); created == n && cleanedUp == n { break Retry } } if created, cleanedUp := check(); created != n || cleanedUp != n { t.Errorf("created %d read handlers, cleaned up %d read handlers", created, cleanedUp) } lrmap.mu.Lock() if nn := len(lrmap.readHandlers); nn != 0 { t.Errorf("expected no read handlers, got %d", nn) } lrmap.mu.Unlock() }