/** * Copyright 2016 Yahoo Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.bookkeeper.mledger.util; import static org.testng.Assert.assertEquals; import static org.testng.Assert.fail; import org.testng.annotations.Test; import com.google.common.collect.Lists; import io.netty.util.AbstractReferenceCounted; import io.netty.util.ReferenceCounted; @Test public class RangeCacheTest { class RefString extends AbstractReferenceCounted implements ReferenceCounted { final String s; RefString(String s) { super(); this.s = s; setRefCnt(1); } @Override protected void deallocate() { // no-op } @Override public boolean equals(Object obj) { if (obj instanceof RefString) { return this.s.equals(((RefString) obj).s); } else if (obj instanceof String) { return this.s.equals((String) obj); } return false; } } @Test void simple() { RangeCache<Integer, RefString> cache = new RangeCache<>(); cache.put(0, new RefString("0")); cache.put(1, new RefString("1")); assertEquals(cache.getSize(), 2); assertEquals(cache.getNumberOfEntries(), 2); RefString s = cache.get(0); assertEquals(s.s, "0"); assertEquals(s.refCnt(), 2); s.release(); RefString s1 = cache.get(0); RefString s2 = cache.get(0); assertEquals(s1, s2); assertEquals(s1.refCnt(), 3); s1.release(); s2.release(); assertEquals(cache.get(2), null); cache.put(2, new RefString("2")); cache.put(8, new RefString("8")); cache.put(11, new RefString("11")); assertEquals(cache.getSize(), 5); assertEquals(cache.getNumberOfEntries(), 5); cache.removeRange(1, 5, true); assertEquals(cache.getSize(), 3); assertEquals(cache.getNumberOfEntries(), 3); cache.removeRange(2, 8, false); assertEquals(cache.getSize(), 3); assertEquals(cache.getNumberOfEntries(), 3); cache.removeRange(0, 100, false); assertEquals(cache.getSize(), 0); assertEquals(cache.getNumberOfEntries(), 0); cache.removeRange(0, 100, false); assertEquals(cache.getSize(), 0); assertEquals(cache.getNumberOfEntries(), 0); } @Test void customWeighter() { RangeCache<Integer, RefString> cache = new RangeCache<>(value -> value.s.length()); cache.put(0, new RefString("zero")); cache.put(1, new RefString("one")); assertEquals(cache.getSize(), 7); assertEquals(cache.getNumberOfEntries(), 2); } @Test void doubleInsert() { RangeCache<Integer, RefString> cache = new RangeCache<>(); RefString s0 = new RefString("zero"); assertEquals(s0.refCnt(), 1); assertEquals(cache.put(0, s0), true); assertEquals(s0.refCnt(), 1); cache.put(1, new RefString("one")); assertEquals(cache.getSize(), 2); assertEquals(cache.getNumberOfEntries(), 2); RefString s = cache.get(1); assertEquals(s.s, "one"); assertEquals(s.refCnt(), 2); RefString s1 = new RefString("uno"); assertEquals(s1.refCnt(), 1); assertEquals(cache.put(1, s1), false); assertEquals(s1.refCnt(), 1); s1.release(); // Should not have been overridden in cache assertEquals(cache.getSize(), 2); assertEquals(cache.getNumberOfEntries(), 2); assertEquals(cache.get(1).s, "one"); } @Test void getRange() { RangeCache<Integer, RefString> cache = new RangeCache<>(); cache.put(0, new RefString("0")); cache.put(1, new RefString("1")); cache.put(3, new RefString("3")); cache.put(5, new RefString("5")); assertEquals(cache.getRange(1, 8), Lists.newArrayList(new RefString("1"), new RefString("3"), new RefString("5"))); cache.put(8, new RefString("8")); assertEquals(cache.getRange(1, 8), Lists.newArrayList(new RefString("1"), new RefString("3"), new RefString("5"), new RefString("8"))); cache.clear(); assertEquals(cache.getSize(), 0); assertEquals(cache.getNumberOfEntries(), 0); } @Test void eviction() { RangeCache<Integer, RefString> cache = new RangeCache<>(value -> value.s.length()); cache.put(0, new RefString("zero")); cache.put(1, new RefString("one")); cache.put(2, new RefString("two")); cache.put(3, new RefString("three")); // This should remove the LRU entries: 0, 1 whose combined size is 7 assertEquals(cache.evictLeastAccessedEntries(5), Pair.create(2, (long) 7)); assertEquals(cache.getNumberOfEntries(), 2); assertEquals(cache.getSize(), 8); assertEquals(cache.get(0), null); assertEquals(cache.get(1), null); assertEquals(cache.get(2).s, "two"); assertEquals(cache.get(3).s, "three"); assertEquals(cache.evictLeastAccessedEntries(100), Pair.create(2, (long) 8)); assertEquals(cache.getNumberOfEntries(), 0); assertEquals(cache.getSize(), 0); assertEquals(cache.get(0), null); assertEquals(cache.get(1), null); assertEquals(cache.get(2), null); assertEquals(cache.get(3), null); try { cache.evictLeastAccessedEntries(0); fail("should throw exception"); } catch (IllegalArgumentException e) { // ok } try { cache.evictLeastAccessedEntries(-1); fail("should throw exception"); } catch (IllegalArgumentException e) { // ok } } @Test void evictions() { RangeCache<Integer, RefString> cache = new RangeCache<>(); for (int i = 0; i < 100; i++) { cache.put(i, new RefString(Integer.toString(i))); } assertEquals(cache.getSize(), 100); Pair<Integer, Long> res = cache.evictLeastAccessedEntries(1); assertEquals((int) res.first, 1); assertEquals((long) res.second, 1); assertEquals(cache.getSize(), 99); res = cache.evictLeastAccessedEntries(10); assertEquals((int) res.first, 10); assertEquals((long) res.second, 10); assertEquals(cache.getSize(), 89); res = cache.evictLeastAccessedEntries(100); assertEquals((int) res.first, 89); assertEquals((long) res.second, 89); assertEquals(cache.getSize(), 0); for (int i = 0; i < 100; i++) { cache.put(i, new RefString(Integer.toString(i))); } assertEquals(cache.getSize(), 100); res = cache.removeRange(10, 20, false); assertEquals((int) res.first, 10); assertEquals((long) res.second, 10); assertEquals(cache.getSize(), 90); } }