1 package com.atlassian.cache;
2
3 import java.util.concurrent.CountDownLatch;
4 import java.util.concurrent.TimeUnit;
5 import java.util.concurrent.atomic.AtomicInteger;
6
7 import javax.annotation.Nonnull;
8 import javax.annotation.Nullable;
9
10 import com.google.common.base.Function;
11
12 import org.junit.Test;
13
14 import static com.atlassian.cache.TestThread.runTest;
15 import static org.hamcrest.Matchers.is;
16 import static org.hamcrest.Matchers.isOneOf;
17 import static org.hamcrest.core.IsEqual.equalTo;
18 import static org.hamcrest.core.IsInstanceOf.instanceOf;
19 import static org.junit.Assert.assertNotSame;
20 import static org.junit.Assert.assertThat;
21 import static org.junit.Assert.fail;
22
23
24
25
26
27
28 public abstract class AbstractCacheLazyTest extends AbstractCacheTest
29 {
30
31 @Test
32 public void testGetName() throws Exception
33 {
34 Cache<String, Long> cache = makeUnexpiringCache();
35
36 assertThat(cache.getName(), equalTo("mycache"));
37 }
38
39 @Test
40 public void testFactoryGeneratedName() throws Exception
41 {
42 Cache<String, Long> cache = factory.getCache(Object.class, "mycache");
43
44 assertThat(cache.getName(), equalTo("java.lang.Object.mycache"));
45 }
46
47 @Test
48 public void testGetKeys() throws Exception
49 {
50 Cache<String, Long> cache = makeUnexpiringCache();
51
52 cache.get("1");
53 cache.get("2");
54 cache.get("3");
55 cache.get("4");
56 assertThat(cache.get("1"), equalTo(1L));
57 assertThat(cache.get("2"), equalTo(2L));
58 assertThat(cache.get("3"), equalTo(3L));
59 assertThat(cache.get("4"), equalTo(4L));
60 assertSize(cache, 4);
61 }
62
63 @Test
64 public void testConstructExpiringCache() throws Exception
65 {
66 Cache<String, Long> cache = makeExpiringCache();
67
68 cache.get("1");
69 cache.get("2");
70 cache.get("3");
71 cache.get("4");
72 assertThat(cache.get("1"), equalTo(1L));
73 assertThat(cache.get("2"), equalTo(2L));
74 assertThat(cache.get("3"), equalTo(3L));
75 assertThat(cache.get("4"), equalTo(4L));
76 assertSize(cache, 4);
77 }
78
79 @Test
80 public void testPut() throws Exception
81 {
82 Cache<String, Long> cache = makeUnexpiringCache();
83
84 cache.put("1", 11L);
85 cache.put("2", 12L);
86 cache.put("3", 13L);
87 cache.put("4", 14L);
88 assertThat(cache.get("1"), equalTo(11L));
89 assertThat(cache.get("2"), equalTo(12L));
90 assertThat(cache.get("3"), equalTo(13L));
91 assertThat(cache.get("4"), equalTo(14L));
92 assertSize(cache, (4));
93 }
94
95 @Test
96 public void testGet() throws Exception
97 {
98 Cache<String, Long> cache = makeUnexpiringCache();
99
100 cache.put("1", 11L);
101 cache.put("2", 12L);
102 cache.put("3", 13L);
103 cache.put("4", 14L);
104 assertThat(cache.get("1"), equalTo(11L));
105 assertThat(cache.get("2"), equalTo(12L));
106 assertThat(cache.get("3"), equalTo(13L));
107 assertThat(cache.get("4"), equalTo(14L));
108 assertThat(cache.get("5"), equalTo(5L));
109 assertThat(cache.get("6"), equalTo(6L));
110 assertThat(cache.get("7"), equalTo(7L));
111 assertSize(cache, (7));
112 }
113
114 @Test
115 public void testRemove() throws Exception
116 {
117 Cache<String, Long> cache = makeUnexpiringCache();
118
119 cache.put("1", 11L);
120 cache.put("2", 12L);
121 cache.put("3", 13L);
122 cache.put("4", 14L);
123 assertThat(cache.get("1"), equalTo(11L));
124 assertThat(cache.get("2"), equalTo(12L));
125 assertThat(cache.get("3"), equalTo(13L));
126 assertThat(cache.get("4"), equalTo(14L));
127 assertSize(cache, (4));
128
129 cache.remove("1");
130 cache.remove("2");
131 cache.remove("3");
132 assertSize(cache, (1));
133
134
135 assertThat(cache.get("1"), equalTo(1L));
136 assertThat(cache.get("2"), equalTo(2L));
137 assertThat(cache.get("3"), equalTo(3L));
138 assertThat(cache.get("4"), equalTo(14L));
139 assertSize(cache, (4));
140 }
141
142 @Test
143 public void testRemoveTwice() throws Exception
144 {
145 Cache<String, Long> cache = makeUnexpiringCache();
146
147
148 cache.put("1", 11L);
149 cache.put("2", 12L);
150 cache.put("3", 13L);
151 cache.put("4", 14L);
152 assertThat(cache.get("1"), equalTo(11L));
153 assertThat(cache.get("2"), equalTo(12L));
154 assertThat(cache.get("3"), equalTo(13L));
155 assertThat(cache.get("4"), equalTo(14L));
156 assertSize(cache, (4));
157
158 cache.remove("1");
159
160 cache.remove("1");
161 assertSize(cache, (3));
162
163
164 assertThat(cache.get("1"), equalTo(1L));
165 assertThat(cache.get("2"), equalTo(12L));
166 assertThat(cache.get("3"), equalTo(13L));
167 assertThat(cache.get("4"), equalTo(14L));
168 assertSize(cache, (4));
169 }
170
171 @Test
172 public void testRemoveAll() throws Exception
173 {
174 Cache<String, Long> cache = makeUnexpiringCache();
175
176 cache.put("1", 11L);
177 cache.put("2", 12L);
178 cache.put("3", 13L);
179 cache.put("4", 14L);
180 assertThat(cache.get("1"), equalTo(11L));
181 assertThat(cache.get("2"), equalTo(12L));
182 assertThat(cache.get("3"), equalTo(13L));
183 assertThat(cache.get("4"), equalTo(14L));
184 assertSize(cache, (4));
185
186 cache.removeAll();
187 assertEmpty(cache);
188
189
190 assertThat(cache.get("1"), equalTo(1L));
191 assertThat(cache.get("2"), equalTo(2L));
192 assertThat(cache.get("3"), equalTo(3L));
193 assertThat(cache.get("4"), equalTo(4L));
194 assertSize(cache, (4));
195 }
196
197 @Test
198 public void testClear() throws Exception
199 {
200 Cache<String, Long> cache = makeUnexpiringCache();
201
202 cache.put("1", 11L);
203 cache.put("2", 12L);
204 cache.put("3", 13L);
205 cache.put("4", 14L);
206 assertThat(cache.get("1"), equalTo(11L));
207 assertThat(cache.get("2"), equalTo(12L));
208 assertThat(cache.get("3"), equalTo(13L));
209 assertThat(cache.get("4"), equalTo(14L));
210 assertSize(cache, (4));
211
212 ((ManagedCache) cache).clear();
213 assertEmpty(cache);
214
215
216 assertThat(cache.get("1"), equalTo(1L));
217 assertThat(cache.get("2"), equalTo(2L));
218 assertThat(cache.get("3"), equalTo(3L));
219 assertThat(cache.get("4"), equalTo(4L));
220 assertSize(cache, (4));
221 }
222
223 @Test (expected = CacheException.class)
224 public void testNullKey() throws Exception
225 {
226 makeNullReturningCache().get(null);
227 }
228
229 @Test (expected = CacheException.class)
230 public void testNullValue() throws Exception
231 {
232 Cache<String, Long> cache = makeNullReturningCache();
233
234
235 assertThat(cache.get("1"), equalTo(1L));
236 assertThat(cache.get("2"), equalTo(2L));
237 assertThat(cache.get("3"), equalTo(3L));
238
239 cache.get("George");
240 }
241
242 @Test
243 public void testMaxEntries() throws Exception
244 {
245 final AtomicInteger loadCounter = new AtomicInteger();
246 Cache<String, Long> cache = makeSizeLimitedCache(3, loadCounter);
247
248
249 assertThat(cache.get("1"), equalTo(1L));
250 assertThat(cache.get("1"), equalTo(1L));
251 assertThat(cache.get("2"), equalTo(2L));
252 assertThat(cache.get("3"), equalTo(3L));
253 assertThat("loadCounter", loadCounter.get(), equalTo(3));
254 assertSize(cache, (3));
255 assertThat(cache.get("3"), equalTo(3L));
256 assertThat(cache.get("3"), equalTo(3L));
257 assertThat("loadCounter", loadCounter.get(), equalTo(3));
258 assertSize(cache, (3));
259 assertThat(cache.get("4"), equalTo(4L));
260 assertThat("loadCounter", loadCounter.get(), equalTo(4));
261 assertSize(cache, (3));
262 }
263
264 @Test
265 public void testExceptionHandling() throws Exception
266 {
267 Cache<String, Long> cache = makeExceptionalCache();
268
269
270 assertThat(cache.get("1"), equalTo(1L));
271 assertThat(cache.get("2"), equalTo(2L));
272 assertThat(cache.get("3"), equalTo(3L));
273 try
274 {
275 cache.get("George");
276 fail("Should throw CacheException when the loader throws an exception");
277 }
278 catch (CacheException ex)
279 {
280 assertThat("This exception should wrap the original Exception", ex.getCause(), instanceOf(NumberFormatException.class));
281 }
282 try
283 {
284 Long v = cache.get(null);
285 fail("Should throw CacheException when the Key is a null");
286 }
287 catch (CacheException ex)
288 {
289 assertThat("This exception should wrap the original Exception", ex.getCause(), instanceOf(NullPointerException.class));
290 }
291 assertSize(cache, (3));
292 }
293
294 @Test
295 public void testNewInstanceForEveryGet()
296 {
297 Cache<String, Long> cache1 = makeUnexpiringCache();
298 Cache<String, Long> cache2 = makeUnexpiringCache();
299
300 assertNotSame("The cache manager should not return the same cache twice", cache1, cache2);
301 }
302
303 protected static final Function<Cache<Integer, Integer>, Void> REMOVE_0 = new Function<Cache<Integer, Integer>, Void>()
304 {
305 @Nullable
306 @Override
307 public Void apply(Cache<Integer, Integer> input)
308 {
309 input.remove(0);
310 return null;
311 }
312 };
313
314 protected static final Function<Cache<Integer, Integer>, Void> REMOVE_ALL = new Function<Cache<Integer, Integer>, Void>()
315 {
316 @Nullable
317 @Override
318 public Void apply(Cache<Integer, Integer> input)
319 {
320 input.removeAll();
321 return null;
322 }
323 };
324
325 @Test
326 public void testRemoveConcurrentWithLoadLocal()
327 {
328 removeConcurrentWithLoad(factory, null, REMOVE_0);
329 }
330
331 @Test
332 public void testRemoveAllConcurrentWithLoadLocal()
333 {
334 removeConcurrentWithLoad(factory, null, REMOVE_ALL);
335 }
336
337 protected void removeConcurrentWithLoad(final CacheFactory factory1, @Nullable final CacheFactory factory2,
338 final Function<Cache<Integer,Integer>,Void> removeFn)
339 {
340 final Barrier doUpdate = new Barrier();
341 final CountDownLatch removeCalled = new CountDownLatch(1);
342 final Barrier afterGet = new Barrier();
343 final AtomicInteger dbValue = new AtomicInteger(1);
344
345 final CacheLoader<Integer,Integer> loader = new CacheLoader<Integer, Integer>()
346 {
347 @Nonnull
348 @Override
349 public Integer load(@Nonnull Integer key)
350 {
351 final int value = dbValue.get();
352 doUpdate.trySignal();
353 try
354 {
355
356 removeCalled.await(250L, TimeUnit.MILLISECONDS);
357 }
358 catch (InterruptedException ie)
359 {
360 throw new AssertionError(ie);
361 }
362 afterGet.trySignal();
363 return value;
364 }
365 };
366
367 final Cache<Integer,Integer> cache1 = factory1.getCache("removeConcurrentWithLoad", loader,
368 settingsBuilder().build());
369
370 final Cache<Integer,Integer> cache2 = (factory2 != null)
371 ? factory2.getCache("removeConcurrentWithLoad", loader, settingsBuilder().build())
372 : cache1;
373
374
375 cache1.removeAll();
376 cache2.removeAll();
377
378 final TestThread blockingGet = new TestThread("blockingGet")
379 {
380 @Override
381 protected void go() throws Exception
382 {
383 assertThat(cache1.get(0), isOneOf(1, 2));
384 removeCalled.await();
385 assertThat(cache1.get(0), is(2));
386 }
387 };
388
389 final TestThread modifyRemoveGet = new TestThread("modifyRemoveGet")
390 {
391 @Override
392 protected void go() throws Exception
393 {
394 doUpdate.await();
395 dbValue.set(2);
396 removeFn.apply(cache2);
397 removeCalled.countDown();
398 assertThat(cache2.get(0), is(2));
399 }
400 };
401
402 runTest(blockingGet, modifyRemoveGet);
403 }
404 }