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