/*
- * 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.
* 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.
*/
package org.wamblee.cache;
-import junit.framework.TestCase;
+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;
-import java.io.IOException;
-
-
/**
* 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;
- /* (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) {
- ncomputations++;
-
- return compute(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((Integer) invocation.getArguments()[0]);
+ }
+ });
ncomputations = 0;
}
/**
* 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 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());
int value = cached.get();
assertEquals(compute(OBJECT_KEY), value);
- assertEquals(1, ncomputations);
- // The value must still be cached.
+ assertEquals(1, getComputationCount());
+ // The value must still be cached.
value = cached.get();
assertEquals(compute(OBJECT_KEY), value);
- assertEquals(1, ncomputations);
+ assertEquals(1, getComputationCount());
- // Cache expiry.
+ // 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();
assertEquals(compute(OBJECT_KEY), value);
- assertEquals(2, ncomputations);
+ assertEquals(2, getComputationCount());
// explicit invalidation.
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());
int value = cached.get();
assertEquals(compute(OBJECT_KEY), value);
- assertEquals(1, ncomputations);
- // The value must still be cached.
+ assertEquals(1, getComputationCount());
+ // The value must still be cached.
value = cached.get();
assertEquals(compute(OBJECT_KEY), value);
- assertEquals(1, ncomputations);
+ assertEquals(1, getComputationCount());
- // Cache expiry.
+ // 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();
assertEquals(compute(OBJECT_KEY), value);
- assertEquals(2, ncomputations);
+ assertEquals(2, getComputationCount());
}
public void testBehaviorForeverCache() {
CachedObject<Integer, Integer> cached = createCached(new ForeverCache<Integer, Integer>());
int value = cached.get();
assertEquals(compute(OBJECT_KEY), value);
- assertEquals(1, ncomputations);
+ assertEquals(1, getComputationCount());
for (int ncomp = 2; ncomp <= 100; ncomp++) {
value = cached.get();
assertEquals(compute(OBJECT_KEY), value);
- assertEquals(1, ncomputations);
+ assertEquals(1, getComputationCount());
}
}
CachedObject<Integer, Integer> cached = createCached(new ZeroCache<Integer, Integer>());
int value = cached.get();
assertEquals(compute(OBJECT_KEY), value);
- assertEquals(1, ncomputations);
+ 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();
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());
}
}