/** * */ package com.socialize.test.unit; import com.socialize.cache.ICacheableFactory; import com.socialize.cache.ISuicidal; import com.socialize.cache.Key; import com.socialize.cache.TTLCache; import com.socialize.cache.TTLObject; import com.socialize.test.SocializeUnitTest; import org.mockito.Mockito; import java.util.Collection; import java.util.TreeMap; /** * @author Jason */ public class TTLCacheTest extends SocializeUnitTest { private TTLCache<String, StringCacheable> cache; private CacheListenerTest listener; protected void setUp() throws Exception { super.setUp(); cache = new TTLCache<String, StringCacheable>(1, 2); cache.setDebug(true); cache.setMaxCapacityBytes(20); cache.setHardByteLimit(false); listener = new CacheListenerTest(); cache.setEventListener(listener); } @Override protected void tearDown() throws Exception { cache.clear(); super.tearDown(); } /** * Tests that an object put into cache can be retrieved. */ public void testSimpleCachePut() { StringCacheable testObj = new StringCacheable("test"); cache.put("testKey", testObj); StringCacheable fromCache = cache.get("testKey"); assertNotNull(fromCache); assertEquals("test", fromCache.getValue()); } public void testOnPutCalledOnPut() { StringCacheable testObj = new StringCacheable("test"); cache.put("testKey", testObj); assertEquals(1, testObj.getOnPutCount()); } public void testOnGetCalledOnGet() { StringCacheable testObj = new StringCacheable("test"); cache.put("testKey", testObj); cache.get("testKey"); assertEquals(1, testObj.getOnGetCount()); } /** * Tests that the reaper was executed when expected. */ public void testManualCacheReap() { assertTrue(cache.doReap()); assertEquals(1, listener.reapStarts); assertEquals(1, listener.reapEnds); } public void testScheduledCacheReap() { assertEquals(0, listener.reapStarts); assertEquals(0, listener.reapEnds); cache.setReapCycle(2000); try { Thread.sleep(2200); } catch (InterruptedException ignore) { ignore.printStackTrace(); } assertEquals(1, listener.reapStarts); assertEquals(1, listener.reapEnds); } /** * Tests that the onRemove method is called when an object is replaced. */ public void testOnRemoveNOTCalledOnReplace() { StringCacheable testObj = new StringCacheable("test"); StringCacheable testObj2 = new StringCacheable("test2"); cache.put("testKey", testObj); cache.put("testKey", testObj2); assertEquals(0, testObj.getOnRemoveCount()); } /** * Tests that an object expires after its ttl. */ public void testObjectExpiry() { StringCacheable testObj = new StringCacheable("test"); cache.put("testKey", testObj, 100); try { Thread.sleep(500); } catch (InterruptedException ignore) { ignore.printStackTrace(); } assertTrue(cache.doReap()); assertFalse(cache.exists("testKey")); assertNull(cache.get("testKey")); } /** * Tests that the onRemove method is called when an object is reaped. */ public void testOnRemoveCalledOnReap() { StringCacheable testObj = new StringCacheable("test"); cache.put("testKey", testObj, 100); try { Thread.sleep(500); } catch (InterruptedException ignore) { ignore.printStackTrace(); } assertTrue(cache.doReap()); assertEquals(1, testObj.getOnRemoveCount()); } /** * Tests that the onRemove method is called when an object is manually removed. */ public void testOnRemoveCalledOnRemove() { StringCacheable testObj = new StringCacheable("test"); cache.put("testKey", testObj); assertTrue(cache.exists("testKey")); cache.remove("testKey"); assertFalse(cache.exists("testKey")); assertEquals(1, testObj.getOnRemoveCount()); } /** * Tests that the onRemove method is called when an object is manually removed. */ public void testOnRemoveCalledOnClear() { StringCacheable testObj = new StringCacheable("test"); StringCacheable testObj2 = new StringCacheable("test2"); cache.put("testKey", testObj); cache.put("testKey2", testObj2); assertEquals(2, cache.size()); cache.clear(); assertEquals(1, testObj.getOnRemoveCount()); assertEquals(1, testObj2.getOnRemoveCount()); assertEquals(0, cache.size()); } public void testSizeInBytesIncreasedOnPut() { StringCacheable testObj = new StringCacheable("test1"); StringCacheable testObj2 = new StringCacheable("test2"); cache.put("testKey", testObj); cache.put("testKey2", testObj2); assertEquals(10, cache.sizeInBytes()); } public void testSizeInBytesCorrectOnReplace() { StringCacheable testObj = new StringCacheable("test1"); StringCacheable testObj2 = new StringCacheable("test2"); cache.put("testKey", testObj); cache.put("testKey", testObj2); assertEquals(5, cache.sizeInBytes()); } public void testSizeInBytesCorrectOnClear() { StringCacheable testObj = new StringCacheable("test1"); StringCacheable testObj2 = new StringCacheable("test2"); cache.put("testKey", testObj); cache.put("testKey2", testObj2); cache.clear(); assertEquals(0, cache.sizeInBytes()); } public void testSizeInBytesCorrectOnReap() { StringCacheable testObj = new StringCacheable("test1"); StringCacheable testObj2 = new StringCacheable("test2"); cache.put("testKey", testObj); cache.put("testKey2", testObj2, 100); try { Thread.sleep(500); } catch (InterruptedException ignore) { ignore.printStackTrace(); } assertTrue(cache.doReap()); assertEquals(0, testObj.getOnRemoveCount()); assertEquals(1, testObj2.getOnRemoveCount()); assertEquals(5, cache.sizeInBytes()); } public void testSizeInBytesCorrectOnRemove() { StringCacheable testObj = new StringCacheable("test1"); StringCacheable testObj2 = new StringCacheable("test2"); cache.put("testKey", testObj); cache.put("testKey2", testObj2); cache.remove("testKey"); assertEquals(5, cache.sizeInBytes()); } public void testTrimOnCountExceededAfterReap() { StringCacheable testObj = new StringCacheable("test1"); StringCacheable testObj2 = new StringCacheable("test2"); StringCacheable testObj3 = new StringCacheable("test3"); cache.put("testKey", testObj); cache.put("testKey2", testObj2); cache.put("testKey3", testObj3); assertEquals(3, cache.size()); assertTrue(cache.doReap()); assertEquals(2, cache.size()); } public void testTrimOnBytesExceededAfterReap() { StringCacheable testObj = new StringCacheable("0000000000"); StringCacheable testObj2 = new StringCacheable("00000000000"); cache.put("testKey", testObj); cache.put("testKey2", testObj2); assertEquals(2, cache.size()); assertTrue(cache.doReap()); assertEquals(1, cache.size()); } public void testPutFailOnBytesExceededWithHardLimit() { StringCacheable testObj = new StringCacheable("0000000000"); StringCacheable testObj2 = new StringCacheable("00000000000"); cache.setHardByteLimit(true); cache.put("testKey", testObj); assertFalse(cache.put("testKey2", testObj2)); assertEquals(1, cache.size()); } public void testOldestRemoveOnReap() { StringCacheable testObj = new StringCacheable("test1"); StringCacheable testObj2 = new StringCacheable("test2"); StringCacheable testObj3 = new StringCacheable("test3"); cache.put("testKey", testObj); cache.put("testKey2", testObj2); try { Thread.sleep(500); } catch (InterruptedException ignore) { ignore.printStackTrace(); } cache.put("testKey", testObj); // replace so we know it's not just first cache.put("testKey3", testObj3); assertEquals(3, cache.size()); assertTrue(cache.doReap()); assertEquals(2, cache.size()); assertTrue(cache.exists("testKey")); assertFalse(cache.exists("testKey2")); assertTrue(cache.exists("testKey3")); } public void testConstructorUsesDefaultCacheCount() { TTLCache<String, StringCacheable> cache = new TTLCache<String, StringCacheable>(1); assertEquals(TTLCache.DEFAULT_CACHE_COUNT, cache.getMaxCapacity()); } public void testPauseCallsStopOnReaper() { TTLCache<String, StringCacheable> cache = new TTLCache<String, StringCacheable>() { @Override protected synchronized void stopReaper() { addResult(true); } }; cache.pause(); Boolean result = getNextResult(); assertNotNull(result); assertTrue(result); } public void testResumeCallsStartOnReaper() { TTLCache<String, StringCacheable> cache = new TTLCache<String, StringCacheable>() { @Override protected synchronized void startReaper() { addResult(true); } }; cache.resume(); Boolean result = getNextResult(); assertNotNull(result); assertTrue(result); } public void testPutCallPutWithDefaultTTL() { TTLCache<String, StringCacheable> cache = new TTLCache<String, StringCacheable>() { @Override protected synchronized boolean put(String k, StringCacheable object, long ttl, boolean eternal) { addResult(ttl); return true; } }; cache.put("foobar", null); Long result = getNextResult(); assertNotNull(result); assertEquals(cache.getDefaultTTL(), result.longValue()); } @SuppressWarnings("unchecked") public void testGetRaw() { final TTLObject<String, StringCacheable> object = Mockito.mock(TTLObject.class); final StringCacheable cacheable = new StringCacheable(); Mockito.when(object.getObject()).thenReturn(cacheable); TTLCache<String, StringCacheable> cache = new TTLCache<String, StringCacheable>() { @Override protected TTLObject<String, StringCacheable> getTTLObject(String strKey) { return object; } @Override public boolean isExpired(TTLObject<String, StringCacheable> object) { return false; } }; assertSame(cacheable, cache.getRaw("foobar")); } @SuppressWarnings("unchecked") public void testCreateOnEmptyGet() { final String key = "foobar"; final TTLObject<String, StringCacheable> object = Mockito.mock(TTLObject.class); final ICacheableFactory<String, StringCacheable> factory = Mockito.mock(ICacheableFactory.class); final StringCacheable cacheable = new StringCacheable(); Mockito.when(object.getObject()).thenReturn(null); Mockito.when(factory.create(key)).thenReturn(cacheable); TTLCache<String, StringCacheable> cache = new TTLCache<String, StringCacheable>() { @Override protected TTLObject<String, StringCacheable> getTTLObject(String strKey) { return object; } @Override protected synchronized boolean put(String k, StringCacheable object, long ttl, boolean eternal) { addResult(true); return true; } }; cache.setObjectFactory(factory); assertSame(cacheable, cache.get(key)); Boolean result = getNextResult(); assertNotNull(result); assertTrue(result); } public void testValues() { final int expiredCount = 5; int totalCount = 10; TTLCache<String, StringCacheable> cache = new TTLCache<String, StringCacheable>() { @Override public boolean isExpired(TTLObject<String, StringCacheable> object) { Integer nextResult = getNextResult(); if(nextResult == null) { addResult(1); return true; } else if(nextResult >= expiredCount) { addResult(++nextResult); return false; } else { addResult(++nextResult); return true; } } }; for (int i = 0; i < totalCount; i++) { StringCacheable value = new StringCacheable(String.valueOf(i)); cache.put(String.valueOf(i), value); } Collection<StringCacheable> values = cache.values(); assertNotNull(values); assertEquals((totalCount - expiredCount), values.size()); } @SuppressWarnings("unchecked") public void testNotExpired() { TTLObject<String, StringCacheable> obj = Mockito.mock(TTLObject.class); Mockito.when(obj.getObject()).thenReturn(null); Mockito.when(obj.isEternal()).thenReturn(false); Mockito.when(obj.getLifeExpectancy()).thenReturn(Long.MAX_VALUE); assertFalse(cache.isExpired(obj)); } @SuppressWarnings("unchecked") public void testExpired() { TTLObject<String, StringCacheable> obj = Mockito.mock(TTLObject.class); Mockito.when(obj.getObject()).thenReturn(null); Mockito.when(obj.isEternal()).thenReturn(false); Mockito.when(obj.getLifeExpectancy()).thenReturn(0L); assertTrue(cache.isExpired(obj)); } @SuppressWarnings("unchecked") public void testExpiredSuicical() { TTLCache<String, ISuicidal<String>> cache = new TTLCache<String, ISuicidal<String>>(); TTLObject<String, ISuicidal<String>> obj = Mockito.mock(TTLObject.class); ISuicidal<String> s = Mockito.mock(ISuicidal.class); Mockito.when(obj.getObject()).thenReturn(s); Mockito.when(s.isDead()).thenReturn(true); assertTrue(cache.isExpired(obj)); } public void testKey() { Key<String> key1 = new Key<String>("foobar1", 0); Key<String> key2 = new Key<String>("foobar1", 0); Key<String> key3 = new Key<String>("foobar2", 0); assertFalse(key1.equals(key3)); assertTrue(key1.equals(key2)); } public void testKeyCompare() { Key<String> key1 = new Key<String>("foobar1", 100); Key<String> key2 = new Key<String>("foobar1", 200); // key1 is oldest TreeMap<Key<String>, String> sorted = new TreeMap<Key<String>, String>(); sorted.put(key1, "foo"); sorted.put(key2, "bar"); Key<String> firstKey = sorted.firstKey(); assertSame(key1, firstKey); TreeMap<Key<String>, String> sorted2 = new TreeMap<Key<String>, String>(); sorted2.put(key2, "bar"); sorted2.put(key1, "foo"); Key<String> firstKey2 = sorted2.firstKey(); assertSame(key1, firstKey2); } }