/* * Copyright 2005-2010 the original author or authors. * * 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.wamblee.cache; import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; import java.io.IOException; import junit.framework.TestCase; import net.sf.ehcache.CacheException; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.wamblee.io.TestResource; import org.wamblee.test.TimingUtils; /** * Cached object test. * * @author Erik Brakkee */ public class CachedObjectTest extends TestCase { private static final String EHCACHE_CONFIG = "ehcache.xml"; private static final int OBJECT_KEY = 10; private CachedObject.Computation computation; private int ncomputations; private synchronized void incrementComputations() { ncomputations++; } private synchronized int getComputationCount() { return ncomputations; } /* * (non-Javadoc) * * @see junit.framework.TestCase#setUp() */ @Override protected void setUp() throws Exception { super.setUp(); computation = mock(CachedObject.Computation.class); when(computation.getObject(anyInt())).thenAnswer(new Answer() { public Integer answer( org.mockito.invocation.InvocationOnMock aInvocation) throws Throwable { ncomputations++; return compute((Integer) aInvocation.getArguments()[0]); } }); ncomputations = 0; } private int compute(int aValue) { return aValue + 10; } private CachedObject createCached( Cache aCache) { return new CachedObject(aCache, OBJECT_KEY, computation); } /** * Verifies that upon first use, the cached object uses the computation to * retrieve the object. * */ public void testComputation() { CachedObject cached = createCached(new ZeroCache()); int value = cached.get(); assertEquals(compute(OBJECT_KEY), value); assertEquals(1, ncomputations); } public void testInvalidateCache() { CachedObject cached = createCached(new ForeverCache()); int value = cached.get(); assertEquals(compute(OBJECT_KEY), value); assertEquals(1, ncomputations); cached.invalidate(); value = cached.get(); assertEquals(compute(OBJECT_KEY), value); assertEquals(2, ncomputations); } public void testBehaviorEhCache() throws CacheException, IOException { Cache cache = new EhCache( new TestResource(CachedObjectTest.class, EHCACHE_CONFIG), "test"); CachedObject cached = createCached(cache); assertTrue(cache == cached.getCache()); int value = cached.get(); assertEquals(compute(OBJECT_KEY), value); assertEquals(1, getComputationCount()); // The value must still be cached. value = cached.get(); assertEquals(compute(OBJECT_KEY), value); assertEquals(1, getComputationCount()); // Cache expiry. TimingUtils.sleep(6000); value = cached.get(); assertEquals(compute(OBJECT_KEY), value); assertEquals(2, getComputationCount()); // Should still be cached now. value = cached.get(); assertEquals(compute(OBJECT_KEY), value); assertEquals(2, getComputationCount()); // explicit invalidation. cached.invalidate(); value = cached.get(); assertEquals(compute(OBJECT_KEY), value); assertEquals(3, getComputationCount()); } public void testBehaviorEhCacheDefault() throws CacheException, IOException { Cache cache = new EhCache( new TestResource(CachedObjectTest.class, EHCACHE_CONFIG), "undefined"); CachedObject cached = createCached(cache); assertTrue(cache == cached.getCache()); int value = cached.get(); assertEquals(compute(OBJECT_KEY), value); assertEquals(1, getComputationCount()); // The value must still be cached. value = cached.get(); assertEquals(compute(OBJECT_KEY), value); assertEquals(1, getComputationCount()); // Cache expiry. TimingUtils.sleep(6000); value = cached.get(); assertEquals(compute(OBJECT_KEY), value); assertEquals(2, getComputationCount()); // Should still be cached now. value = cached.get(); assertEquals(compute(OBJECT_KEY), value); assertEquals(2, getComputationCount()); } public void testBehaviorForeverCache() { CachedObject cached = createCached(new ForeverCache()); int value = cached.get(); assertEquals(compute(OBJECT_KEY), value); assertEquals(1, getComputationCount()); for (int ncomp = 2; ncomp <= 100; ncomp++) { value = cached.get(); assertEquals(compute(OBJECT_KEY), value); assertEquals(1, getComputationCount()); } } public void testBehaviorZeroCache() { CachedObject cached = createCached(new ZeroCache()); int value = cached.get(); assertEquals(compute(OBJECT_KEY), value); assertEquals(1, getComputationCount()); for (int ncomp = 2; ncomp <= 100; ncomp++) { value = cached.get(); assertEquals(compute(OBJECT_KEY), value); assertEquals(ncomp, getComputationCount()); } cached.invalidate(); value = cached.get(); assertEquals(compute(OBJECT_KEY), value); assertEquals(101, getComputationCount()); } public void testPreviousValueWhileComputationBeingDone() throws Exception { final CachedObject cached = createCached(new ZeroCache()); reset(computation); when(computation.getObject(anyInt())).thenReturn(1).thenAnswer( new Answer() { @Override public Integer answer(InvocationOnMock aInvocation) throws Throwable { TimingUtils.sleep(1000); return 2; } }).thenReturn(3); assertEquals(1, cached.get().intValue()); Thread recompute = new Thread(new Runnable() { @Override public void run() { // will sleep for 1 second. assertEquals(2, cached.get().intValue()); } }); recompute.start(); // Now asking the value should not recompute but should return the old // value. TimingUtils.sleep(500); assertEquals(1, cached.get().intValue()); recompute.join(); } public void testNullValueWhenComputationReturnsNull() throws Exception { final CachedObject cached = createCached(new ZeroCache()); reset(computation); when(computation.getObject(anyInt())).thenReturn(1).thenReturn(null) .thenReturn(2); assertEquals(1, cached.get().intValue()); assertNull(cached.get()); assertEquals(2, cached.get().intValue()); } public void testPreviousValueWhenComputationThrowsException() throws Exception { final CachedObject cached = createCached(new ZeroCache()); reset(computation); when(computation.getObject(anyInt())).thenReturn(1).thenThrow( new RuntimeException()).thenReturn(2); assertEquals(1, cached.get().intValue()); assertEquals(1, cached.get().intValue()); assertEquals(2, cached.get().intValue()); } }