(no commit message)
[utils] / support / general / src / test / java / org / wamblee / cache / CachedObjectTest.java
1 /*
2  * Copyright 2005-2010 the original author or authors.
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  * 
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  * 
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.wamblee.cache;
17
18 import static org.mockito.Matchers.*;
19 import static org.mockito.Mockito.*;
20
21 import java.io.IOException;
22
23 import junit.framework.TestCase;
24 import net.sf.ehcache.CacheException;
25
26 import org.mockito.invocation.InvocationOnMock;
27 import org.mockito.stubbing.Answer;
28 import org.wamblee.io.TestResource;
29 import org.wamblee.test.TimingUtils;
30
31 /**
32  * Cached object test.
33  * 
34  * @author Erik Brakkee
35  */
36 public class CachedObjectTest extends TestCase {
37     private static final String EHCACHE_CONFIG = "ehcache.xml";
38     private static final int OBJECT_KEY = 10;
39     private CachedObject.Computation<Integer, Integer> computation;
40     private int ncomputations;
41
42     private synchronized void incrementComputations() {
43         ncomputations++;
44     }
45
46     private synchronized int getComputationCount() {
47         return ncomputations;
48     }
49
50     /*
51      * (non-Javadoc)
52      * 
53      * @see junit.framework.TestCase#setUp()
54      */
55     @Override
56     protected void setUp() throws Exception {
57         super.setUp();
58         computation = mock(CachedObject.Computation.class);
59         when(computation.getObject(anyInt())).thenAnswer(new Answer<Integer>() {
60             public Integer answer(
61                 org.mockito.invocation.InvocationOnMock aInvocation)
62                 throws Throwable {
63                 ncomputations++;
64                 return compute((Integer) aInvocation.getArguments()[0]);
65             }
66         });
67         ncomputations = 0;
68     }
69
70     private int compute(int aValue) {
71         return aValue + 10;
72     }
73
74     private CachedObject<Integer, Integer> createCached(
75         Cache<Integer, Integer> aCache) {
76         return new CachedObject<Integer, Integer>(aCache, OBJECT_KEY,
77             computation);
78     }
79
80     /**
81      * Verifies that upon first use, the cached object uses the computation to
82      * retrieve the object.
83      * 
84      */
85     public void testComputation() {
86         CachedObject<Integer, Integer> cached = createCached(new ZeroCache<Integer, Integer>());
87         int value = cached.get();
88         assertEquals(compute(OBJECT_KEY), value);
89         assertEquals(1, ncomputations);
90     }
91
92     public void testInvalidateCache() {
93         CachedObject<Integer, Integer> cached = createCached(new ForeverCache<Integer, Integer>());
94         int value = cached.get();
95         assertEquals(compute(OBJECT_KEY), value);
96         assertEquals(1, ncomputations);
97         cached.invalidate();
98         value = cached.get();
99         assertEquals(compute(OBJECT_KEY), value);
100         assertEquals(2, ncomputations);
101     }
102
103     public void testBehaviorEhCache() throws CacheException, IOException {
104         Cache<Integer, Integer> cache = new EhCache<Integer, Integer>(
105             new TestResource(CachedObjectTest.class, EHCACHE_CONFIG), "test");
106         CachedObject<Integer, Integer> cached = createCached(cache);
107
108         assertTrue(cache == cached.getCache());
109
110         int value = cached.get();
111         assertEquals(compute(OBJECT_KEY), value);
112         assertEquals(1, getComputationCount());
113         // The value must still be cached.
114         value = cached.get();
115         assertEquals(compute(OBJECT_KEY), value);
116         assertEquals(1, getComputationCount());
117
118         // Cache expiry.
119         TimingUtils.sleep(6000);
120         value = cached.get();
121         assertEquals(compute(OBJECT_KEY), value);
122         assertEquals(2, getComputationCount());
123
124         // Should still be cached now.
125         value = cached.get();
126         assertEquals(compute(OBJECT_KEY), value);
127         assertEquals(2, getComputationCount());
128
129         // explicit invalidation.
130         cached.invalidate();
131         value = cached.get();
132         assertEquals(compute(OBJECT_KEY), value);
133         assertEquals(3, getComputationCount());
134     }
135
136     public void testBehaviorEhCacheDefault() throws CacheException, IOException {
137         Cache<Integer, Integer> cache = new EhCache<Integer, Integer>(
138             new TestResource(CachedObjectTest.class, EHCACHE_CONFIG),
139             "undefined");
140         CachedObject<Integer, Integer> cached = createCached(cache);
141
142         assertTrue(cache == cached.getCache());
143
144         int value = cached.get();
145         assertEquals(compute(OBJECT_KEY), value);
146         assertEquals(1, getComputationCount());
147         // The value must still be cached.
148         value = cached.get();
149         assertEquals(compute(OBJECT_KEY), value);
150         assertEquals(1, getComputationCount());
151
152         // Cache expiry.
153         TimingUtils.sleep(6000);
154         value = cached.get();
155         assertEquals(compute(OBJECT_KEY), value);
156         assertEquals(2, getComputationCount());
157
158         // Should still be cached now.
159         value = cached.get();
160         assertEquals(compute(OBJECT_KEY), value);
161         assertEquals(2, getComputationCount());
162     }
163
164     public void testBehaviorForeverCache() {
165         CachedObject<Integer, Integer> cached = createCached(new ForeverCache<Integer, Integer>());
166         int value = cached.get();
167         assertEquals(compute(OBJECT_KEY), value);
168         assertEquals(1, getComputationCount());
169
170         for (int ncomp = 2; ncomp <= 100; ncomp++) {
171             value = cached.get();
172             assertEquals(compute(OBJECT_KEY), value);
173             assertEquals(1, getComputationCount());
174         }
175     }
176
177     public void testBehaviorZeroCache() {
178         CachedObject<Integer, Integer> cached = createCached(new ZeroCache<Integer, Integer>());
179         int value = cached.get();
180         assertEquals(compute(OBJECT_KEY), value);
181         assertEquals(1, getComputationCount());
182
183         for (int ncomp = 2; ncomp <= 100; ncomp++) {
184             value = cached.get();
185             assertEquals(compute(OBJECT_KEY), value);
186             assertEquals(ncomp, getComputationCount());
187         }
188
189         cached.invalidate();
190         value = cached.get();
191         assertEquals(compute(OBJECT_KEY), value);
192         assertEquals(101, getComputationCount());
193     }
194
195     public void testPreviousValueWhileComputationBeingDone() throws Exception {
196         final CachedObject<Integer, Integer> cached = createCached(new ZeroCache<Integer, Integer>());
197         reset(computation);
198         when(computation.getObject(anyInt())).thenReturn(1).thenAnswer(
199             new Answer<Integer>() {
200                 @Override
201                 public Integer answer(InvocationOnMock aInvocation)
202                     throws Throwable {
203                     TimingUtils.sleep(1000);
204                     return 2;
205                 }
206             }).thenReturn(3);
207
208         assertEquals(1, cached.get().intValue());
209         Thread recompute = new Thread(new Runnable() {
210
211             @Override
212             public void run() {
213                 // will sleep for 1 second.
214                 assertEquals(2, cached.get().intValue());
215             }
216         });
217         recompute.start();
218         // Now asking the value should not recompute but should return the old
219         // value.
220         TimingUtils.sleep(500);
221         assertEquals(1, cached.get().intValue());
222         recompute.join();
223     }
224
225     public void testNullValueWhenComputationReturnsNull() throws Exception {
226         final CachedObject<Integer, Integer> cached = createCached(new ZeroCache<Integer, Integer>());
227         reset(computation);
228         when(computation.getObject(anyInt())).thenReturn(1).thenReturn(null)
229             .thenReturn(2);
230
231         assertEquals(1, cached.get().intValue());
232         assertNull(cached.get());
233         assertEquals(2, cached.get().intValue());
234     }
235
236     public void testPreviousValueWhenComputationThrowsException()
237         throws Exception {
238         final CachedObject<Integer, Integer> cached = createCached(new ZeroCache<Integer, Integer>());
239         reset(computation);
240         when(computation.getObject(anyInt())).thenReturn(1).thenThrow(
241             new RuntimeException()).thenReturn(2);
242
243         assertEquals(1, cached.get().intValue());
244         assertEquals(1, cached.get().intValue());
245         assertEquals(2, cached.get().intValue());
246     }
247 }