/*
- * Copyright 2005 the original author or authors.
+ * 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.
* 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.
- *
+ * 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<Integer, Integer> computation;
+ private int ncomputations;
- private static final int OBJECT_KEY = 10;
-
- private CachedObject.Computation<Integer,Integer> computation;
- private int ncomputations;
-
- /* (non-Javadoc)
+ 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 = new CachedObject.Computation<Integer,Integer>() {
- public Integer getObject(Integer aObjectKey) {
+ computation = mock(CachedObject.Computation.class);
+ when(computation.getObject(anyInt())).thenAnswer(new Answer<Integer>() {
+ public Integer answer(
+ org.mockito.invocation.InvocationOnMock invocation)
+ throws Throwable {
ncomputations++;
- return compute(aObjectKey);
- };
- };
- ncomputations = 0;
+ return compute((Integer) invocation.getArguments()[0]);
+ }
+ });
+ ncomputations = 0;
}
-
- private int compute(int aValue) {
+
+ private int compute(int aValue) {
return aValue + 10;
}
-
- private CachedObject<Integer, Integer> createCached(Cache<Integer,Integer> aCache) {
- return new CachedObject<Integer, Integer>(aCache, OBJECT_KEY, computation);
+
+ private CachedObject<Integer, Integer> createCached(
+ Cache<Integer, Integer> aCache) {
+ return new CachedObject<Integer, Integer>(aCache, OBJECT_KEY,
+ computation);
}
/**
- * Verifies that upon first use, the cached object uses the computation to
- * retrieve the object.
- *
+ * Verifies that upon first use, the cached object uses the computation to
+ * retrieve the object.
+ *
*/
- public void testComputation() {
- CachedObject<Integer, Integer> cached = createCached(new ZeroCache<Integer,Integer>());
+ public void testComputation() {
+ CachedObject<Integer, Integer> cached = createCached(new ZeroCache<Integer, Integer>());
int value = cached.get();
assertEquals(compute(OBJECT_KEY), value);
- assertEquals(1, ncomputations);
+ assertEquals(1, ncomputations);
}
-
- public void testInvalidateCache() {
- CachedObject<Integer, Integer> cached = createCached(new ForeverCache<Integer,Integer>());
+
+ public void testInvalidateCache() {
+ CachedObject<Integer, Integer> cached = createCached(new ForeverCache<Integer, Integer>());
int value = cached.get();
assertEquals(compute(OBJECT_KEY), value);
assertEquals(1, ncomputations);
- cached.invalidate();
+ cached.invalidate();
value = cached.get();
assertEquals(compute(OBJECT_KEY), value);
assertEquals(2, ncomputations);
}
-
+
public void testBehaviorEhCache() throws CacheException, IOException {
- Cache<Integer,Integer> cache = new EhCache<Integer,Integer>(new TestResource(CachedObjectTest.class, EHCACHE_CONFIG), "test");
+ Cache<Integer, Integer> cache = new EhCache<Integer, Integer>(
+ new TestResource(CachedObjectTest.class, EHCACHE_CONFIG), "test");
CachedObject<Integer, Integer> cached = createCached(cache);
-
- assertTrue( cache == cached.getCache());
+
+ assertTrue(cache == cached.getCache());
+
int value = cached.get();
assertEquals(compute(OBJECT_KEY), value);
- assertEquals(1, ncomputations);
- // The value must still be cached.
- value = cached.get();
+ assertEquals(1, getComputationCount());
+ // The value must still be cached.
+ value = cached.get();
assertEquals(compute(OBJECT_KEY), value);
- assertEquals(1, ncomputations);
-
- // Cache expiry.
- TimingUtils.sleep(6000);
- value = cached.get();
+ assertEquals(1, getComputationCount());
+
+ // Cache expiry.
+ TimingUtils.sleep(6000);
+ value = cached.get();
assertEquals(compute(OBJECT_KEY), value);
- assertEquals(2, ncomputations);
-
+ assertEquals(2, getComputationCount());
+
// Should still be cached now.
- value = cached.get();
+ value = cached.get();
assertEquals(compute(OBJECT_KEY), value);
- assertEquals(2, ncomputations);
-
+ assertEquals(2, getComputationCount());
+
// explicit invalidation.
- cached.invalidate();
- value = cached.get();
+ cached.invalidate();
+ value = cached.get();
assertEquals(compute(OBJECT_KEY), value);
- assertEquals(3, ncomputations);
-
+ assertEquals(3, getComputationCount());
}
-
+
public void testBehaviorEhCacheDefault() throws CacheException, IOException {
- Cache<Integer,Integer> cache = new EhCache<Integer,Integer>(new TestResource(CachedObjectTest.class, EHCACHE_CONFIG), "undefined");
+ Cache<Integer, Integer> cache = new EhCache<Integer, Integer>(
+ new TestResource(CachedObjectTest.class, EHCACHE_CONFIG),
+ "undefined");
CachedObject<Integer, Integer> cached = createCached(cache);
-
- assertTrue( cache == cached.getCache());
+
+ assertTrue(cache == cached.getCache());
+
int value = cached.get();
assertEquals(compute(OBJECT_KEY), value);
- assertEquals(1, ncomputations);
- // The value must still be cached.
- value = cached.get();
+ assertEquals(1, getComputationCount());
+ // The value must still be cached.
+ value = cached.get();
assertEquals(compute(OBJECT_KEY), value);
- assertEquals(1, ncomputations);
-
- // Cache expiry.
- TimingUtils.sleep(6000);
- value = cached.get();
+ assertEquals(1, getComputationCount());
+
+ // Cache expiry.
+ TimingUtils.sleep(6000);
+ value = cached.get();
assertEquals(compute(OBJECT_KEY), value);
- assertEquals(2, ncomputations);
-
+ assertEquals(2, getComputationCount());
+
// Should still be cached now.
- value = cached.get();
+ value = cached.get();
assertEquals(compute(OBJECT_KEY), value);
- assertEquals(2, ncomputations);
-
+ assertEquals(2, getComputationCount());
}
-
-
- public void testBehaviorForeverCache() {
- CachedObject<Integer, Integer> cached = createCached(new ForeverCache<Integer,Integer>());
+
+ public void testBehaviorForeverCache() {
+ CachedObject<Integer, Integer> cached = createCached(new ForeverCache<Integer, Integer>());
int value = cached.get();
assertEquals(compute(OBJECT_KEY), value);
- assertEquals(1, ncomputations);
- for (int ncomp = 2; ncomp <= 100; ncomp++) {
- value = cached.get();
+ assertEquals(1, getComputationCount());
+
+ for (int ncomp = 2; ncomp <= 100; ncomp++) {
+ value = cached.get();
assertEquals(compute(OBJECT_KEY), value);
- assertEquals(1, ncomputations);
+ assertEquals(1, getComputationCount());
}
}
-
- public void testBehaviorZeroCache() {
- CachedObject<Integer, Integer> cached = createCached(new ZeroCache<Integer,Integer>());
+
+ public void testBehaviorZeroCache() {
+ CachedObject<Integer, Integer> cached = createCached(new ZeroCache<Integer, Integer>());
int value = cached.get();
assertEquals(compute(OBJECT_KEY), value);
- assertEquals(1, ncomputations);
- for (int ncomp = 2; ncomp <= 100; ncomp++) {
- value = cached.get();
+ assertEquals(1, getComputationCount());
+
+ for (int ncomp = 2; ncomp <= 100; ncomp++) {
+ value = cached.get();
assertEquals(compute(OBJECT_KEY), value);
- assertEquals(ncomp, ncomputations);
+ assertEquals(ncomp, getComputationCount());
}
- cached.invalidate();
- value = cached.get();
+
+ cached.invalidate();
+ value = cached.get();
assertEquals(compute(OBJECT_KEY), value);
- assertEquals(101, ncomputations);
+ assertEquals(101, getComputationCount());
+ }
+
+ public void testPreviousValueWhileComputationBeingDone() throws Exception {
+ final CachedObject<Integer,Integer> cached = createCached(new ZeroCache<Integer, Integer>());
+ reset(computation);
+ when(computation.getObject(anyInt())).thenReturn(1).thenAnswer(
+ new Answer<Integer>() {
+ @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<Integer,Integer> cached = createCached(new ZeroCache<Integer, Integer>());
+ 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<Integer,Integer> cached = createCached(new ZeroCache<Integer, Integer>());
+ 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());
}
}