02d151cbd9126c75708d1c3c33d8583d6ac3970a
[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 invocation)
62                 throws Throwable {
63                 ncomputations++;
64                 return compute((Integer) invocation.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             }
207             ).thenReturn(3);
208         
209         assertEquals(1, cached.get().intValue());
210         Thread recompute = new Thread(new Runnable() {
211             
212             @Override
213             public void run() {
214                 // will sleep for 1 second. 
215                 assertEquals(2, cached.get().intValue());
216             }
217         });
218         recompute.start();
219         // Now asking the value should not recompute but should return the old 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).thenReturn(2);
229         
230         assertEquals(1, cached.get().intValue());
231         assertNull(cached.get());
232         assertEquals(2, cached.get().intValue());
233     }
234     
235     public void testPreviousValueWhenComputationThrowsException() throws Exception { 
236         final CachedObject<Integer,Integer> cached = createCached(new ZeroCache<Integer, Integer>());
237         reset(computation);
238         when(computation.getObject(anyInt())).thenReturn(1).thenThrow(new RuntimeException()).thenReturn(2);
239         
240         assertEquals(1, cached.get().intValue());
241         assertEquals(1, cached.get().intValue());
242         assertEquals(2, cached.get().intValue());
243     }
244 }