1 /*
2  * Copyright (C) 2011 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 package com.google.common.cache;
16 
17 import static com.google.common.cache.TestingCacheLoaders.bulkLoader;
18 import static com.google.common.cache.TestingCacheLoaders.constantLoader;
19 import static com.google.common.cache.TestingCacheLoaders.errorLoader;
20 import static com.google.common.cache.TestingCacheLoaders.exceptionLoader;
21 import static com.google.common.cache.TestingCacheLoaders.identityLoader;
22 import static com.google.common.cache.TestingRemovalListeners.countingRemovalListener;
23 import static com.google.common.truth.Truth.assertThat;
24 import static java.lang.Thread.currentThread;
25 import static java.util.Arrays.asList;
26 import static java.util.concurrent.TimeUnit.MILLISECONDS;
27 
28 import com.google.common.cache.CacheLoader.InvalidCacheLoadException;
29 import com.google.common.cache.TestingCacheLoaders.CountingLoader;
30 import com.google.common.cache.TestingCacheLoaders.IdentityLoader;
31 import com.google.common.cache.TestingRemovalListeners.CountingRemovalListener;
32 import com.google.common.collect.ImmutableList;
33 import com.google.common.collect.ImmutableMap;
34 import com.google.common.collect.Lists;
35 import com.google.common.collect.Maps;
36 import com.google.common.testing.FakeTicker;
37 import com.google.common.testing.TestLogHandler;
38 import com.google.common.util.concurrent.Callables;
39 import com.google.common.util.concurrent.ExecutionError;
40 import com.google.common.util.concurrent.Futures;
41 import com.google.common.util.concurrent.ListenableFuture;
42 import com.google.common.util.concurrent.UncheckedExecutionException;
43 import java.io.IOException;
44 import java.lang.ref.WeakReference;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.Map.Entry;
48 import java.util.concurrent.Callable;
49 import java.util.concurrent.ConcurrentMap;
50 import java.util.concurrent.CountDownLatch;
51 import java.util.concurrent.ExecutionException;
52 import java.util.concurrent.TimeUnit;
53 import java.util.concurrent.atomic.AtomicInteger;
54 import java.util.concurrent.atomic.AtomicReferenceArray;
55 import java.util.logging.LogRecord;
56 import junit.framework.TestCase;
57 
58 /**
59  * Tests relating to cache loading: concurrent loading, exceptions during loading, etc.
60  *
61  * @author mike nonemacher
62  */
63 public class CacheLoadingTest extends TestCase {
64   TestLogHandler logHandler;
65 
66   @Override
setUp()67   public void setUp() throws Exception {
68     super.setUp();
69     logHandler = new TestLogHandler();
70     LocalCache.logger.addHandler(logHandler);
71   }
72 
73   @Override
tearDown()74   public void tearDown() throws Exception {
75     super.tearDown();
76     // TODO(cpovirk): run tests in other thread instead of messing with main thread interrupt status
77     currentThread().interrupted();
78     LocalCache.logger.removeHandler(logHandler);
79   }
80 
popLoggedThrowable()81   private Throwable popLoggedThrowable() {
82     List<LogRecord> logRecords = logHandler.getStoredLogRecords();
83     assertEquals(1, logRecords.size());
84     LogRecord logRecord = logRecords.get(0);
85     logHandler.clear();
86     return logRecord.getThrown();
87   }
88 
checkNothingLogged()89   private void checkNothingLogged() {
90     assertThat(logHandler.getStoredLogRecords()).isEmpty();
91   }
92 
checkLoggedCause(Throwable t)93   private void checkLoggedCause(Throwable t) {
94     assertThat(popLoggedThrowable()).hasCauseThat().isSameInstanceAs(t);
95   }
96 
checkLoggedInvalidLoad()97   private void checkLoggedInvalidLoad() {
98     assertThat(popLoggedThrowable()).isInstanceOf(InvalidCacheLoadException.class);
99   }
100 
testLoad()101   public void testLoad() throws ExecutionException {
102     LoadingCache<Object, Object> cache =
103         CacheBuilder.newBuilder().recordStats().build(identityLoader());
104     CacheStats stats = cache.stats();
105     assertEquals(0, stats.missCount());
106     assertEquals(0, stats.loadSuccessCount());
107     assertEquals(0, stats.loadExceptionCount());
108     assertEquals(0, stats.hitCount());
109 
110     Object key = new Object();
111     assertSame(key, cache.get(key));
112     stats = cache.stats();
113     assertEquals(1, stats.missCount());
114     assertEquals(1, stats.loadSuccessCount());
115     assertEquals(0, stats.loadExceptionCount());
116     assertEquals(0, stats.hitCount());
117 
118     key = new Object();
119     assertSame(key, cache.getUnchecked(key));
120     stats = cache.stats();
121     assertEquals(2, stats.missCount());
122     assertEquals(2, stats.loadSuccessCount());
123     assertEquals(0, stats.loadExceptionCount());
124     assertEquals(0, stats.hitCount());
125 
126     key = new Object();
127     cache.refresh(key);
128     checkNothingLogged();
129     stats = cache.stats();
130     assertEquals(2, stats.missCount());
131     assertEquals(3, stats.loadSuccessCount());
132     assertEquals(0, stats.loadExceptionCount());
133     assertEquals(0, stats.hitCount());
134 
135     assertSame(key, cache.get(key));
136     stats = cache.stats();
137     assertEquals(2, stats.missCount());
138     assertEquals(3, stats.loadSuccessCount());
139     assertEquals(0, stats.loadExceptionCount());
140     assertEquals(1, stats.hitCount());
141 
142     Object value = new Object();
143     // callable is not called
144     assertSame(key, cache.get(key, throwing(new Exception())));
145     stats = cache.stats();
146     assertEquals(2, stats.missCount());
147     assertEquals(3, stats.loadSuccessCount());
148     assertEquals(0, stats.loadExceptionCount());
149     assertEquals(2, stats.hitCount());
150 
151     key = new Object();
152     assertSame(value, cache.get(key, Callables.returning(value)));
153     stats = cache.stats();
154     assertEquals(3, stats.missCount());
155     assertEquals(4, stats.loadSuccessCount());
156     assertEquals(0, stats.loadExceptionCount());
157     assertEquals(2, stats.hitCount());
158   }
159 
testReload()160   public void testReload() throws ExecutionException {
161     final Object one = new Object();
162     final Object two = new Object();
163     CacheLoader<Object, Object> loader =
164         new CacheLoader<Object, Object>() {
165           @Override
166           public Object load(Object key) {
167             return one;
168           }
169 
170           @Override
171           public ListenableFuture<Object> reload(Object key, Object oldValue) {
172             return Futures.immediateFuture(two);
173           }
174         };
175 
176     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
177     Object key = new Object();
178     CacheStats stats = cache.stats();
179     assertEquals(0, stats.missCount());
180     assertEquals(0, stats.loadSuccessCount());
181     assertEquals(0, stats.loadExceptionCount());
182     assertEquals(0, stats.hitCount());
183 
184     assertSame(one, cache.getUnchecked(key));
185     stats = cache.stats();
186     assertEquals(1, stats.missCount());
187     assertEquals(1, stats.loadSuccessCount());
188     assertEquals(0, stats.loadExceptionCount());
189     assertEquals(0, stats.hitCount());
190 
191     cache.refresh(key);
192     checkNothingLogged();
193     stats = cache.stats();
194     assertEquals(1, stats.missCount());
195     assertEquals(2, stats.loadSuccessCount());
196     assertEquals(0, stats.loadExceptionCount());
197     assertEquals(0, stats.hitCount());
198 
199     assertSame(two, cache.getUnchecked(key));
200     stats = cache.stats();
201     assertEquals(1, stats.missCount());
202     assertEquals(2, stats.loadSuccessCount());
203     assertEquals(0, stats.loadExceptionCount());
204     assertEquals(1, stats.hitCount());
205   }
206 
testRefresh()207   public void testRefresh() {
208     final Object one = new Object();
209     final Object two = new Object();
210     FakeTicker ticker = new FakeTicker();
211     CacheLoader<Object, Object> loader =
212         new CacheLoader<Object, Object>() {
213           @Override
214           public Object load(Object key) {
215             return one;
216           }
217 
218           @Override
219           public ListenableFuture<Object> reload(Object key, Object oldValue) {
220             return Futures.immediateFuture(two);
221           }
222         };
223 
224     LoadingCache<Object, Object> cache =
225         CacheBuilder.newBuilder()
226             .recordStats()
227             .ticker(ticker)
228             .refreshAfterWrite(1, MILLISECONDS)
229             .build(loader);
230     Object key = new Object();
231     CacheStats stats = cache.stats();
232     assertEquals(0, stats.missCount());
233     assertEquals(0, stats.loadSuccessCount());
234     assertEquals(0, stats.loadExceptionCount());
235     assertEquals(0, stats.hitCount());
236 
237     assertSame(one, cache.getUnchecked(key));
238     stats = cache.stats();
239     assertEquals(1, stats.missCount());
240     assertEquals(1, stats.loadSuccessCount());
241     assertEquals(0, stats.loadExceptionCount());
242     assertEquals(0, stats.hitCount());
243 
244     ticker.advance(1, MILLISECONDS);
245     assertSame(one, cache.getUnchecked(key));
246     stats = cache.stats();
247     assertEquals(1, stats.missCount());
248     assertEquals(1, stats.loadSuccessCount());
249     assertEquals(0, stats.loadExceptionCount());
250     assertEquals(1, stats.hitCount());
251 
252     ticker.advance(1, MILLISECONDS);
253     assertSame(two, cache.getUnchecked(key));
254     stats = cache.stats();
255     assertEquals(1, stats.missCount());
256     assertEquals(2, stats.loadSuccessCount());
257     assertEquals(0, stats.loadExceptionCount());
258     assertEquals(2, stats.hitCount());
259 
260     ticker.advance(1, MILLISECONDS);
261     assertSame(two, cache.getUnchecked(key));
262     stats = cache.stats();
263     assertEquals(1, stats.missCount());
264     assertEquals(2, stats.loadSuccessCount());
265     assertEquals(0, stats.loadExceptionCount());
266     assertEquals(3, stats.hitCount());
267   }
268 
testRefresh_getIfPresent()269   public void testRefresh_getIfPresent() {
270     final Object one = new Object();
271     final Object two = new Object();
272     FakeTicker ticker = new FakeTicker();
273     CacheLoader<Object, Object> loader =
274         new CacheLoader<Object, Object>() {
275           @Override
276           public Object load(Object key) {
277             return one;
278           }
279 
280           @Override
281           public ListenableFuture<Object> reload(Object key, Object oldValue) {
282             return Futures.immediateFuture(two);
283           }
284         };
285 
286     LoadingCache<Object, Object> cache =
287         CacheBuilder.newBuilder()
288             .recordStats()
289             .ticker(ticker)
290             .refreshAfterWrite(1, MILLISECONDS)
291             .build(loader);
292     Object key = new Object();
293     CacheStats stats = cache.stats();
294     assertEquals(0, stats.missCount());
295     assertEquals(0, stats.loadSuccessCount());
296     assertEquals(0, stats.loadExceptionCount());
297     assertEquals(0, stats.hitCount());
298 
299     assertSame(one, cache.getUnchecked(key));
300     stats = cache.stats();
301     assertEquals(1, stats.missCount());
302     assertEquals(1, stats.loadSuccessCount());
303     assertEquals(0, stats.loadExceptionCount());
304     assertEquals(0, stats.hitCount());
305 
306     ticker.advance(1, MILLISECONDS);
307     assertSame(one, cache.getIfPresent(key));
308     stats = cache.stats();
309     assertEquals(1, stats.missCount());
310     assertEquals(1, stats.loadSuccessCount());
311     assertEquals(0, stats.loadExceptionCount());
312     assertEquals(1, stats.hitCount());
313 
314     ticker.advance(1, MILLISECONDS);
315     assertSame(two, cache.getIfPresent(key));
316     stats = cache.stats();
317     assertEquals(1, stats.missCount());
318     assertEquals(2, stats.loadSuccessCount());
319     assertEquals(0, stats.loadExceptionCount());
320     assertEquals(2, stats.hitCount());
321 
322     ticker.advance(1, MILLISECONDS);
323     assertSame(two, cache.getIfPresent(key));
324     stats = cache.stats();
325     assertEquals(1, stats.missCount());
326     assertEquals(2, stats.loadSuccessCount());
327     assertEquals(0, stats.loadExceptionCount());
328     assertEquals(3, stats.hitCount());
329   }
330 
testBulkLoad_default()331   public void testBulkLoad_default() throws ExecutionException {
332     LoadingCache<Integer, Integer> cache =
333         CacheBuilder.newBuilder()
334             .recordStats()
335             .build(TestingCacheLoaders.<Integer>identityLoader());
336     CacheStats stats = cache.stats();
337     assertEquals(0, stats.missCount());
338     assertEquals(0, stats.loadSuccessCount());
339     assertEquals(0, stats.loadExceptionCount());
340     assertEquals(0, stats.hitCount());
341 
342     assertEquals(ImmutableMap.of(), cache.getAll(ImmutableList.<Integer>of()));
343     assertEquals(0, stats.missCount());
344     assertEquals(0, stats.loadSuccessCount());
345     assertEquals(0, stats.loadExceptionCount());
346     assertEquals(0, stats.hitCount());
347 
348     assertEquals(ImmutableMap.of(1, 1), cache.getAll(asList(1)));
349     stats = cache.stats();
350     assertEquals(1, stats.missCount());
351     assertEquals(1, stats.loadSuccessCount());
352     assertEquals(0, stats.loadExceptionCount());
353     assertEquals(0, stats.hitCount());
354 
355     assertEquals(ImmutableMap.of(1, 1, 2, 2, 3, 3, 4, 4), cache.getAll(asList(1, 2, 3, 4)));
356     stats = cache.stats();
357     assertEquals(4, stats.missCount());
358     assertEquals(4, stats.loadSuccessCount());
359     assertEquals(0, stats.loadExceptionCount());
360     assertEquals(1, stats.hitCount());
361 
362     assertEquals(ImmutableMap.of(2, 2, 3, 3), cache.getAll(asList(2, 3)));
363     stats = cache.stats();
364     assertEquals(4, stats.missCount());
365     assertEquals(4, stats.loadSuccessCount());
366     assertEquals(0, stats.loadExceptionCount());
367     assertEquals(3, stats.hitCount());
368 
369     // duplicate keys are ignored, and don't impact stats
370     assertEquals(ImmutableMap.of(4, 4, 5, 5), cache.getAll(asList(4, 5)));
371     stats = cache.stats();
372     assertEquals(5, stats.missCount());
373     assertEquals(5, stats.loadSuccessCount());
374     assertEquals(0, stats.loadExceptionCount());
375     assertEquals(4, stats.hitCount());
376   }
377 
testBulkLoad_loadAll()378   public void testBulkLoad_loadAll() throws ExecutionException {
379     IdentityLoader<Integer> backingLoader = identityLoader();
380     CacheLoader<Integer, Integer> loader = bulkLoader(backingLoader);
381     LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder().recordStats().build(loader);
382     CacheStats stats = cache.stats();
383     assertEquals(0, stats.missCount());
384     assertEquals(0, stats.loadSuccessCount());
385     assertEquals(0, stats.loadExceptionCount());
386     assertEquals(0, stats.hitCount());
387 
388     assertEquals(ImmutableMap.of(), cache.getAll(ImmutableList.<Integer>of()));
389     assertEquals(0, stats.missCount());
390     assertEquals(0, stats.loadSuccessCount());
391     assertEquals(0, stats.loadExceptionCount());
392     assertEquals(0, stats.hitCount());
393 
394     assertEquals(ImmutableMap.of(1, 1), cache.getAll(asList(1)));
395     stats = cache.stats();
396     assertEquals(1, stats.missCount());
397     assertEquals(1, stats.loadSuccessCount());
398     assertEquals(0, stats.loadExceptionCount());
399     assertEquals(0, stats.hitCount());
400 
401     assertEquals(ImmutableMap.of(1, 1, 2, 2, 3, 3, 4, 4), cache.getAll(asList(1, 2, 3, 4)));
402     stats = cache.stats();
403     assertEquals(4, stats.missCount());
404     assertEquals(2, stats.loadSuccessCount());
405     assertEquals(0, stats.loadExceptionCount());
406     assertEquals(1, stats.hitCount());
407 
408     assertEquals(ImmutableMap.of(2, 2, 3, 3), cache.getAll(asList(2, 3)));
409     stats = cache.stats();
410     assertEquals(4, stats.missCount());
411     assertEquals(2, stats.loadSuccessCount());
412     assertEquals(0, stats.loadExceptionCount());
413     assertEquals(3, stats.hitCount());
414 
415     // duplicate keys are ignored, and don't impact stats
416     assertEquals(ImmutableMap.of(4, 4, 5, 5), cache.getAll(asList(4, 5)));
417     stats = cache.stats();
418     assertEquals(5, stats.missCount());
419     assertEquals(3, stats.loadSuccessCount());
420     assertEquals(0, stats.loadExceptionCount());
421     assertEquals(4, stats.hitCount());
422   }
423 
testBulkLoad_extra()424   public void testBulkLoad_extra() throws ExecutionException {
425     CacheLoader<Object, Object> loader =
426         new CacheLoader<Object, Object>() {
427           @Override
428           public Object load(Object key) throws Exception {
429             return new Object();
430           }
431 
432           @Override
433           public Map<Object, Object> loadAll(Iterable<?> keys) throws Exception {
434             Map<Object, Object> result = Maps.newHashMap();
435             for (Object key : keys) {
436               Object value = new Object();
437               result.put(key, value);
438               // add extra entries
439               result.put(value, key);
440             }
441             return result;
442           }
443         };
444     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().build(loader);
445 
446     Object[] lookupKeys = new Object[] {new Object(), new Object(), new Object()};
447     Map<Object, Object> result = cache.getAll(asList(lookupKeys));
448     assertThat(result.keySet()).containsExactlyElementsIn(asList(lookupKeys));
449     for (Entry<Object, Object> entry : result.entrySet()) {
450       Object key = entry.getKey();
451       Object value = entry.getValue();
452       assertSame(value, result.get(key));
453       assertNull(result.get(value));
454       assertSame(value, cache.asMap().get(key));
455       assertSame(key, cache.asMap().get(value));
456     }
457   }
458 
testBulkLoad_clobber()459   public void testBulkLoad_clobber() throws ExecutionException {
460     final Object extraKey = new Object();
461     final Object extraValue = new Object();
462     CacheLoader<Object, Object> loader =
463         new CacheLoader<Object, Object>() {
464           @Override
465           public Object load(Object key) throws Exception {
466             throw new AssertionError();
467           }
468 
469           @Override
470           public Map<Object, Object> loadAll(Iterable<?> keys) throws Exception {
471             Map<Object, Object> result = Maps.newHashMap();
472             for (Object key : keys) {
473               Object value = new Object();
474               result.put(key, value);
475             }
476             result.put(extraKey, extraValue);
477             return result;
478           }
479         };
480     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().build(loader);
481     cache.asMap().put(extraKey, extraKey);
482     assertSame(extraKey, cache.asMap().get(extraKey));
483 
484     Object[] lookupKeys = new Object[] {new Object(), new Object(), new Object()};
485     Map<Object, Object> result = cache.getAll(asList(lookupKeys));
486     assertThat(result.keySet()).containsExactlyElementsIn(asList(lookupKeys));
487     for (Entry<Object, Object> entry : result.entrySet()) {
488       Object key = entry.getKey();
489       Object value = entry.getValue();
490       assertSame(value, result.get(key));
491       assertSame(value, cache.asMap().get(key));
492     }
493     assertNull(result.get(extraKey));
494     assertSame(extraValue, cache.asMap().get(extraKey));
495   }
496 
testBulkLoad_clobberNullValue()497   public void testBulkLoad_clobberNullValue() throws ExecutionException {
498     final Object extraKey = new Object();
499     final Object extraValue = new Object();
500     CacheLoader<Object, Object> loader =
501         new CacheLoader<Object, Object>() {
502           @Override
503           public Object load(Object key) throws Exception {
504             throw new AssertionError();
505           }
506 
507           @Override
508           public Map<Object, Object> loadAll(Iterable<?> keys) throws Exception {
509             Map<Object, Object> result = Maps.newHashMap();
510             for (Object key : keys) {
511               Object value = new Object();
512               result.put(key, value);
513             }
514             result.put(extraKey, extraValue);
515             result.put(extraValue, null);
516             return result;
517           }
518         };
519     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().build(loader);
520     cache.asMap().put(extraKey, extraKey);
521     assertSame(extraKey, cache.asMap().get(extraKey));
522 
523     Object[] lookupKeys = new Object[] {new Object(), new Object(), new Object()};
524     try {
525       cache.getAll(asList(lookupKeys));
526       fail();
527     } catch (InvalidCacheLoadException expected) {
528     }
529 
530     for (Object key : lookupKeys) {
531       assertTrue(cache.asMap().containsKey(key));
532     }
533     assertSame(extraValue, cache.asMap().get(extraKey));
534     assertFalse(cache.asMap().containsKey(extraValue));
535   }
536 
testBulkLoad_clobberNullKey()537   public void testBulkLoad_clobberNullKey() throws ExecutionException {
538     final Object extraKey = new Object();
539     final Object extraValue = new Object();
540     CacheLoader<Object, Object> loader =
541         new CacheLoader<Object, Object>() {
542           @Override
543           public Object load(Object key) throws Exception {
544             throw new AssertionError();
545           }
546 
547           @Override
548           public Map<Object, Object> loadAll(Iterable<?> keys) throws Exception {
549             Map<Object, Object> result = Maps.newHashMap();
550             for (Object key : keys) {
551               Object value = new Object();
552               result.put(key, value);
553             }
554             result.put(extraKey, extraValue);
555             result.put(null, extraKey);
556             return result;
557           }
558         };
559     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().build(loader);
560     cache.asMap().put(extraKey, extraKey);
561     assertSame(extraKey, cache.asMap().get(extraKey));
562 
563     Object[] lookupKeys = new Object[] {new Object(), new Object(), new Object()};
564     try {
565       cache.getAll(asList(lookupKeys));
566       fail();
567     } catch (InvalidCacheLoadException expected) {
568     }
569 
570     for (Object key : lookupKeys) {
571       assertTrue(cache.asMap().containsKey(key));
572     }
573     assertSame(extraValue, cache.asMap().get(extraKey));
574     assertFalse(cache.asMap().containsValue(extraKey));
575   }
576 
testBulkLoad_partial()577   public void testBulkLoad_partial() throws ExecutionException {
578     final Object extraKey = new Object();
579     final Object extraValue = new Object();
580     CacheLoader<Object, Object> loader =
581         new CacheLoader<Object, Object>() {
582           @Override
583           public Object load(Object key) throws Exception {
584             throw new AssertionError();
585           }
586 
587           @Override
588           public Map<Object, Object> loadAll(Iterable<?> keys) throws Exception {
589             Map<Object, Object> result = Maps.newHashMap();
590             // ignore request keys
591             result.put(extraKey, extraValue);
592             return result;
593           }
594         };
595     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().build(loader);
596 
597     Object[] lookupKeys = new Object[] {new Object(), new Object(), new Object()};
598     try {
599       cache.getAll(asList(lookupKeys));
600       fail();
601     } catch (InvalidCacheLoadException expected) {
602     }
603     assertSame(extraValue, cache.asMap().get(extraKey));
604   }
605 
testLoadNull()606   public void testLoadNull() throws ExecutionException {
607     LoadingCache<Object, Object> cache =
608         CacheBuilder.newBuilder().recordStats().build(constantLoader(null));
609     CacheStats stats = cache.stats();
610     assertEquals(0, stats.missCount());
611     assertEquals(0, stats.loadSuccessCount());
612     assertEquals(0, stats.loadExceptionCount());
613     assertEquals(0, stats.hitCount());
614 
615     try {
616       cache.get(new Object());
617       fail();
618     } catch (InvalidCacheLoadException expected) {
619     }
620     stats = cache.stats();
621     assertEquals(1, stats.missCount());
622     assertEquals(0, stats.loadSuccessCount());
623     assertEquals(1, stats.loadExceptionCount());
624     assertEquals(0, stats.hitCount());
625 
626     try {
627       cache.getUnchecked(new Object());
628       fail();
629     } catch (InvalidCacheLoadException expected) {
630     }
631     stats = cache.stats();
632     assertEquals(2, stats.missCount());
633     assertEquals(0, stats.loadSuccessCount());
634     assertEquals(2, stats.loadExceptionCount());
635     assertEquals(0, stats.hitCount());
636 
637     cache.refresh(new Object());
638     checkLoggedInvalidLoad();
639     stats = cache.stats();
640     assertEquals(2, stats.missCount());
641     assertEquals(0, stats.loadSuccessCount());
642     assertEquals(3, stats.loadExceptionCount());
643     assertEquals(0, stats.hitCount());
644 
645     try {
646       cache.get(new Object(), Callables.returning(null));
647       fail();
648     } catch (InvalidCacheLoadException expected) {
649     }
650     stats = cache.stats();
651     assertEquals(3, stats.missCount());
652     assertEquals(0, stats.loadSuccessCount());
653     assertEquals(4, stats.loadExceptionCount());
654     assertEquals(0, stats.hitCount());
655 
656     try {
657       cache.getAll(asList(new Object()));
658       fail();
659     } catch (InvalidCacheLoadException expected) {
660     }
661     stats = cache.stats();
662     assertEquals(4, stats.missCount());
663     assertEquals(0, stats.loadSuccessCount());
664     assertEquals(5, stats.loadExceptionCount());
665     assertEquals(0, stats.hitCount());
666   }
667 
testReloadNull()668   public void testReloadNull() throws ExecutionException {
669     final Object one = new Object();
670     CacheLoader<Object, Object> loader =
671         new CacheLoader<Object, Object>() {
672           @Override
673           public Object load(Object key) {
674             return one;
675           }
676 
677           @Override
678           public ListenableFuture<Object> reload(Object key, Object oldValue) {
679             return null;
680           }
681         };
682 
683     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
684     Object key = new Object();
685     CacheStats stats = cache.stats();
686     assertEquals(0, stats.missCount());
687     assertEquals(0, stats.loadSuccessCount());
688     assertEquals(0, stats.loadExceptionCount());
689     assertEquals(0, stats.hitCount());
690 
691     assertSame(one, cache.getUnchecked(key));
692     stats = cache.stats();
693     assertEquals(1, stats.missCount());
694     assertEquals(1, stats.loadSuccessCount());
695     assertEquals(0, stats.loadExceptionCount());
696     assertEquals(0, stats.hitCount());
697 
698     cache.refresh(key);
699     checkLoggedInvalidLoad();
700     stats = cache.stats();
701     assertEquals(1, stats.missCount());
702     assertEquals(1, stats.loadSuccessCount());
703     assertEquals(1, stats.loadExceptionCount());
704     assertEquals(0, stats.hitCount());
705 
706     assertSame(one, cache.getUnchecked(key));
707     stats = cache.stats();
708     assertEquals(1, stats.missCount());
709     assertEquals(1, stats.loadSuccessCount());
710     assertEquals(1, stats.loadExceptionCount());
711     assertEquals(1, stats.hitCount());
712   }
713 
testReloadNullFuture()714   public void testReloadNullFuture() throws ExecutionException {
715     final Object one = new Object();
716     CacheLoader<Object, Object> loader =
717         new CacheLoader<Object, Object>() {
718           @Override
719           public Object load(Object key) {
720             return one;
721           }
722 
723           @Override
724           public ListenableFuture<Object> reload(Object key, Object oldValue) {
725             return Futures.immediateFuture(null);
726           }
727         };
728 
729     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
730     Object key = new Object();
731     CacheStats stats = cache.stats();
732     assertEquals(0, stats.missCount());
733     assertEquals(0, stats.loadSuccessCount());
734     assertEquals(0, stats.loadExceptionCount());
735     assertEquals(0, stats.hitCount());
736 
737     assertSame(one, cache.getUnchecked(key));
738     stats = cache.stats();
739     assertEquals(1, stats.missCount());
740     assertEquals(1, stats.loadSuccessCount());
741     assertEquals(0, stats.loadExceptionCount());
742     assertEquals(0, stats.hitCount());
743 
744     cache.refresh(key);
745     checkLoggedInvalidLoad();
746     stats = cache.stats();
747     assertEquals(1, stats.missCount());
748     assertEquals(1, stats.loadSuccessCount());
749     assertEquals(1, stats.loadExceptionCount());
750     assertEquals(0, stats.hitCount());
751 
752     assertSame(one, cache.getUnchecked(key));
753     stats = cache.stats();
754     assertEquals(1, stats.missCount());
755     assertEquals(1, stats.loadSuccessCount());
756     assertEquals(1, stats.loadExceptionCount());
757     assertEquals(1, stats.hitCount());
758   }
759 
testRefreshNull()760   public void testRefreshNull() {
761     final Object one = new Object();
762     FakeTicker ticker = new FakeTicker();
763     CacheLoader<Object, Object> loader =
764         new CacheLoader<Object, Object>() {
765           @Override
766           public Object load(Object key) {
767             return one;
768           }
769 
770           @Override
771           public ListenableFuture<Object> reload(Object key, Object oldValue) {
772             return Futures.immediateFuture(null);
773           }
774         };
775 
776     LoadingCache<Object, Object> cache =
777         CacheBuilder.newBuilder()
778             .recordStats()
779             .ticker(ticker)
780             .refreshAfterWrite(1, MILLISECONDS)
781             .build(loader);
782     Object key = new Object();
783     CacheStats stats = cache.stats();
784     assertEquals(0, stats.missCount());
785     assertEquals(0, stats.loadSuccessCount());
786     assertEquals(0, stats.loadExceptionCount());
787     assertEquals(0, stats.hitCount());
788 
789     assertSame(one, cache.getUnchecked(key));
790     stats = cache.stats();
791     assertEquals(1, stats.missCount());
792     assertEquals(1, stats.loadSuccessCount());
793     assertEquals(0, stats.loadExceptionCount());
794     assertEquals(0, stats.hitCount());
795 
796     ticker.advance(1, MILLISECONDS);
797     assertSame(one, cache.getUnchecked(key));
798     stats = cache.stats();
799     assertEquals(1, stats.missCount());
800     assertEquals(1, stats.loadSuccessCount());
801     assertEquals(0, stats.loadExceptionCount());
802     assertEquals(1, stats.hitCount());
803 
804     ticker.advance(1, MILLISECONDS);
805     assertSame(one, cache.getUnchecked(key));
806     // refreshed
807     stats = cache.stats();
808     assertEquals(1, stats.missCount());
809     assertEquals(1, stats.loadSuccessCount());
810     assertEquals(1, stats.loadExceptionCount());
811     assertEquals(2, stats.hitCount());
812 
813     ticker.advance(1, MILLISECONDS);
814     assertSame(one, cache.getUnchecked(key));
815     stats = cache.stats();
816     assertEquals(1, stats.missCount());
817     assertEquals(1, stats.loadSuccessCount());
818     assertEquals(2, stats.loadExceptionCount());
819     assertEquals(3, stats.hitCount());
820   }
821 
testBulkLoadNull()822   public void testBulkLoadNull() throws ExecutionException {
823     LoadingCache<Object, Object> cache =
824         CacheBuilder.newBuilder().recordStats().build(bulkLoader(constantLoader(null)));
825     CacheStats stats = cache.stats();
826     assertEquals(0, stats.missCount());
827     assertEquals(0, stats.loadSuccessCount());
828     assertEquals(0, stats.loadExceptionCount());
829     assertEquals(0, stats.hitCount());
830 
831     try {
832       cache.getAll(asList(new Object()));
833       fail();
834     } catch (InvalidCacheLoadException expected) {
835     }
836     stats = cache.stats();
837     assertEquals(1, stats.missCount());
838     assertEquals(0, stats.loadSuccessCount());
839     assertEquals(1, stats.loadExceptionCount());
840     assertEquals(0, stats.hitCount());
841   }
842 
testBulkLoadNullMap()843   public void testBulkLoadNullMap() throws ExecutionException {
844     LoadingCache<Object, Object> cache =
845         CacheBuilder.newBuilder()
846             .recordStats()
847             .build(
848                 new CacheLoader<Object, Object>() {
849                   @Override
850                   public Object load(Object key) {
851                     throw new AssertionError();
852                   }
853 
854                   @Override
855                   public Map<Object, Object> loadAll(Iterable<?> keys) {
856                     return null;
857                   }
858                 });
859 
860     CacheStats stats = cache.stats();
861     assertEquals(0, stats.missCount());
862     assertEquals(0, stats.loadSuccessCount());
863     assertEquals(0, stats.loadExceptionCount());
864     assertEquals(0, stats.hitCount());
865 
866     try {
867       cache.getAll(asList(new Object()));
868       fail();
869     } catch (InvalidCacheLoadException expected) {
870     }
871     stats = cache.stats();
872     assertEquals(1, stats.missCount());
873     assertEquals(0, stats.loadSuccessCount());
874     assertEquals(1, stats.loadExceptionCount());
875     assertEquals(0, stats.hitCount());
876   }
877 
testLoadError()878   public void testLoadError() throws ExecutionException {
879     Error e = new Error();
880     CacheLoader<Object, Object> loader = errorLoader(e);
881     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
882     CacheStats stats = cache.stats();
883     assertEquals(0, stats.missCount());
884     assertEquals(0, stats.loadSuccessCount());
885     assertEquals(0, stats.loadExceptionCount());
886     assertEquals(0, stats.hitCount());
887 
888     try {
889       cache.get(new Object());
890       fail();
891     } catch (ExecutionError expected) {
892       assertThat(expected).hasCauseThat().isSameInstanceAs(e);
893     }
894     stats = cache.stats();
895     assertEquals(1, stats.missCount());
896     assertEquals(0, stats.loadSuccessCount());
897     assertEquals(1, stats.loadExceptionCount());
898     assertEquals(0, stats.hitCount());
899 
900     try {
901       cache.getUnchecked(new Object());
902       fail();
903     } catch (ExecutionError expected) {
904       assertThat(expected).hasCauseThat().isSameInstanceAs(e);
905     }
906     stats = cache.stats();
907     assertEquals(2, stats.missCount());
908     assertEquals(0, stats.loadSuccessCount());
909     assertEquals(2, stats.loadExceptionCount());
910     assertEquals(0, stats.hitCount());
911 
912     cache.refresh(new Object());
913     checkLoggedCause(e);
914     stats = cache.stats();
915     assertEquals(2, stats.missCount());
916     assertEquals(0, stats.loadSuccessCount());
917     assertEquals(3, stats.loadExceptionCount());
918     assertEquals(0, stats.hitCount());
919 
920     final Error callableError = new Error();
921     try {
922       cache.get(
923           new Object(),
924           new Callable<Object>() {
925             @Override
926             public Object call() {
927               throw callableError;
928             }
929           });
930       fail();
931     } catch (ExecutionError expected) {
932       assertThat(expected).hasCauseThat().isSameInstanceAs(callableError);
933     }
934     stats = cache.stats();
935     assertEquals(3, stats.missCount());
936     assertEquals(0, stats.loadSuccessCount());
937     assertEquals(4, stats.loadExceptionCount());
938     assertEquals(0, stats.hitCount());
939 
940     try {
941       cache.getAll(asList(new Object()));
942       fail();
943     } catch (ExecutionError expected) {
944       assertThat(expected).hasCauseThat().isSameInstanceAs(e);
945     }
946     stats = cache.stats();
947     assertEquals(4, stats.missCount());
948     assertEquals(0, stats.loadSuccessCount());
949     assertEquals(5, stats.loadExceptionCount());
950     assertEquals(0, stats.hitCount());
951   }
952 
testReloadError()953   public void testReloadError() throws ExecutionException {
954     final Object one = new Object();
955     final Error e = new Error();
956     CacheLoader<Object, Object> loader =
957         new CacheLoader<Object, Object>() {
958           @Override
959           public Object load(Object key) {
960             return one;
961           }
962 
963           @Override
964           public ListenableFuture<Object> reload(Object key, Object oldValue) {
965             throw e;
966           }
967         };
968 
969     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
970     Object key = new Object();
971     CacheStats stats = cache.stats();
972     assertEquals(0, stats.missCount());
973     assertEquals(0, stats.loadSuccessCount());
974     assertEquals(0, stats.loadExceptionCount());
975     assertEquals(0, stats.hitCount());
976 
977     assertSame(one, cache.getUnchecked(key));
978     stats = cache.stats();
979     assertEquals(1, stats.missCount());
980     assertEquals(1, stats.loadSuccessCount());
981     assertEquals(0, stats.loadExceptionCount());
982     assertEquals(0, stats.hitCount());
983 
984     cache.refresh(key);
985     checkLoggedCause(e);
986     stats = cache.stats();
987     assertEquals(1, stats.missCount());
988     assertEquals(1, stats.loadSuccessCount());
989     assertEquals(1, stats.loadExceptionCount());
990     assertEquals(0, stats.hitCount());
991 
992     assertSame(one, cache.getUnchecked(key));
993     stats = cache.stats();
994     assertEquals(1, stats.missCount());
995     assertEquals(1, stats.loadSuccessCount());
996     assertEquals(1, stats.loadExceptionCount());
997     assertEquals(1, stats.hitCount());
998   }
999 
testReloadFutureError()1000   public void testReloadFutureError() throws ExecutionException {
1001     final Object one = new Object();
1002     final Error e = new Error();
1003     CacheLoader<Object, Object> loader =
1004         new CacheLoader<Object, Object>() {
1005           @Override
1006           public Object load(Object key) {
1007             return one;
1008           }
1009 
1010           @Override
1011           public ListenableFuture<Object> reload(Object key, Object oldValue) {
1012             return Futures.immediateFailedFuture(e);
1013           }
1014         };
1015 
1016     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
1017     Object key = new Object();
1018     CacheStats stats = cache.stats();
1019     assertEquals(0, stats.missCount());
1020     assertEquals(0, stats.loadSuccessCount());
1021     assertEquals(0, stats.loadExceptionCount());
1022     assertEquals(0, stats.hitCount());
1023 
1024     assertSame(one, cache.getUnchecked(key));
1025     stats = cache.stats();
1026     assertEquals(1, stats.missCount());
1027     assertEquals(1, stats.loadSuccessCount());
1028     assertEquals(0, stats.loadExceptionCount());
1029     assertEquals(0, stats.hitCount());
1030 
1031     cache.refresh(key);
1032     checkLoggedCause(e);
1033     stats = cache.stats();
1034     assertEquals(1, stats.missCount());
1035     assertEquals(1, stats.loadSuccessCount());
1036     assertEquals(1, stats.loadExceptionCount());
1037     assertEquals(0, stats.hitCount());
1038 
1039     assertSame(one, cache.getUnchecked(key));
1040     stats = cache.stats();
1041     assertEquals(1, stats.missCount());
1042     assertEquals(1, stats.loadSuccessCount());
1043     assertEquals(1, stats.loadExceptionCount());
1044     assertEquals(1, stats.hitCount());
1045   }
1046 
testRefreshError()1047   public void testRefreshError() {
1048     final Object one = new Object();
1049     final Error e = new Error();
1050     FakeTicker ticker = new FakeTicker();
1051     CacheLoader<Object, Object> loader =
1052         new CacheLoader<Object, Object>() {
1053           @Override
1054           public Object load(Object key) {
1055             return one;
1056           }
1057 
1058           @Override
1059           public ListenableFuture<Object> reload(Object key, Object oldValue) {
1060             return Futures.immediateFailedFuture(e);
1061           }
1062         };
1063 
1064     LoadingCache<Object, Object> cache =
1065         CacheBuilder.newBuilder()
1066             .recordStats()
1067             .ticker(ticker)
1068             .refreshAfterWrite(1, MILLISECONDS)
1069             .build(loader);
1070     Object key = new Object();
1071     CacheStats stats = cache.stats();
1072     assertEquals(0, stats.missCount());
1073     assertEquals(0, stats.loadSuccessCount());
1074     assertEquals(0, stats.loadExceptionCount());
1075     assertEquals(0, stats.hitCount());
1076 
1077     assertSame(one, cache.getUnchecked(key));
1078     stats = cache.stats();
1079     assertEquals(1, stats.missCount());
1080     assertEquals(1, stats.loadSuccessCount());
1081     assertEquals(0, stats.loadExceptionCount());
1082     assertEquals(0, stats.hitCount());
1083 
1084     ticker.advance(1, MILLISECONDS);
1085     assertSame(one, cache.getUnchecked(key));
1086     stats = cache.stats();
1087     assertEquals(1, stats.missCount());
1088     assertEquals(1, stats.loadSuccessCount());
1089     assertEquals(0, stats.loadExceptionCount());
1090     assertEquals(1, stats.hitCount());
1091 
1092     ticker.advance(1, MILLISECONDS);
1093     assertSame(one, cache.getUnchecked(key));
1094     // refreshed
1095     stats = cache.stats();
1096     assertEquals(1, stats.missCount());
1097     assertEquals(1, stats.loadSuccessCount());
1098     assertEquals(1, stats.loadExceptionCount());
1099     assertEquals(2, stats.hitCount());
1100 
1101     ticker.advance(1, MILLISECONDS);
1102     assertSame(one, cache.getUnchecked(key));
1103     stats = cache.stats();
1104     assertEquals(1, stats.missCount());
1105     assertEquals(1, stats.loadSuccessCount());
1106     assertEquals(2, stats.loadExceptionCount());
1107     assertEquals(3, stats.hitCount());
1108   }
1109 
testBulkLoadError()1110   public void testBulkLoadError() throws ExecutionException {
1111     Error e = new Error();
1112     CacheLoader<Object, Object> loader = errorLoader(e);
1113     LoadingCache<Object, Object> cache =
1114         CacheBuilder.newBuilder().recordStats().build(bulkLoader(loader));
1115     CacheStats stats = cache.stats();
1116     assertEquals(0, stats.missCount());
1117     assertEquals(0, stats.loadSuccessCount());
1118     assertEquals(0, stats.loadExceptionCount());
1119     assertEquals(0, stats.hitCount());
1120 
1121     try {
1122       cache.getAll(asList(new Object()));
1123       fail();
1124     } catch (ExecutionError expected) {
1125       assertThat(expected).hasCauseThat().isSameInstanceAs(e);
1126     }
1127     stats = cache.stats();
1128     assertEquals(1, stats.missCount());
1129     assertEquals(0, stats.loadSuccessCount());
1130     assertEquals(1, stats.loadExceptionCount());
1131     assertEquals(0, stats.hitCount());
1132   }
1133 
testLoadCheckedException()1134   public void testLoadCheckedException() {
1135     Exception e = new Exception();
1136     CacheLoader<Object, Object> loader = exceptionLoader(e);
1137     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
1138     CacheStats stats = cache.stats();
1139     assertEquals(0, stats.missCount());
1140     assertEquals(0, stats.loadSuccessCount());
1141     assertEquals(0, stats.loadExceptionCount());
1142     assertEquals(0, stats.hitCount());
1143 
1144     try {
1145       cache.get(new Object());
1146       fail();
1147     } catch (ExecutionException expected) {
1148       assertThat(expected).hasCauseThat().isSameInstanceAs(e);
1149     }
1150     stats = cache.stats();
1151     assertEquals(1, stats.missCount());
1152     assertEquals(0, stats.loadSuccessCount());
1153     assertEquals(1, stats.loadExceptionCount());
1154     assertEquals(0, stats.hitCount());
1155 
1156     try {
1157       cache.getUnchecked(new Object());
1158       fail();
1159     } catch (UncheckedExecutionException expected) {
1160       assertThat(expected).hasCauseThat().isSameInstanceAs(e);
1161     }
1162     stats = cache.stats();
1163     assertEquals(2, stats.missCount());
1164     assertEquals(0, stats.loadSuccessCount());
1165     assertEquals(2, stats.loadExceptionCount());
1166     assertEquals(0, stats.hitCount());
1167 
1168     cache.refresh(new Object());
1169     checkLoggedCause(e);
1170     stats = cache.stats();
1171     assertEquals(2, stats.missCount());
1172     assertEquals(0, stats.loadSuccessCount());
1173     assertEquals(3, stats.loadExceptionCount());
1174     assertEquals(0, stats.hitCount());
1175 
1176     Exception callableException = new Exception();
1177     try {
1178       cache.get(new Object(), throwing(callableException));
1179       fail();
1180     } catch (ExecutionException expected) {
1181       assertThat(expected).hasCauseThat().isSameInstanceAs(callableException);
1182     }
1183     stats = cache.stats();
1184     assertEquals(3, stats.missCount());
1185     assertEquals(0, stats.loadSuccessCount());
1186     assertEquals(4, stats.loadExceptionCount());
1187     assertEquals(0, stats.hitCount());
1188 
1189     try {
1190       cache.getAll(asList(new Object()));
1191       fail();
1192     } catch (ExecutionException expected) {
1193       assertThat(expected).hasCauseThat().isSameInstanceAs(e);
1194     }
1195     stats = cache.stats();
1196     assertEquals(4, stats.missCount());
1197     assertEquals(0, stats.loadSuccessCount());
1198     assertEquals(5, stats.loadExceptionCount());
1199     assertEquals(0, stats.hitCount());
1200   }
1201 
testLoadInterruptedException()1202   public void testLoadInterruptedException() {
1203     Exception e = new InterruptedException();
1204     CacheLoader<Object, Object> loader = exceptionLoader(e);
1205     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
1206     CacheStats stats = cache.stats();
1207     assertEquals(0, stats.missCount());
1208     assertEquals(0, stats.loadSuccessCount());
1209     assertEquals(0, stats.loadExceptionCount());
1210     assertEquals(0, stats.hitCount());
1211 
1212     // Sanity check:
1213     assertFalse(currentThread().interrupted());
1214 
1215     try {
1216       cache.get(new Object());
1217       fail();
1218     } catch (ExecutionException expected) {
1219       assertThat(expected).hasCauseThat().isSameInstanceAs(e);
1220     }
1221     assertTrue(currentThread().interrupted());
1222     stats = cache.stats();
1223     assertEquals(1, stats.missCount());
1224     assertEquals(0, stats.loadSuccessCount());
1225     assertEquals(1, stats.loadExceptionCount());
1226     assertEquals(0, stats.hitCount());
1227 
1228     try {
1229       cache.getUnchecked(new Object());
1230       fail();
1231     } catch (UncheckedExecutionException expected) {
1232       assertThat(expected).hasCauseThat().isSameInstanceAs(e);
1233     }
1234     assertTrue(currentThread().interrupted());
1235     stats = cache.stats();
1236     assertEquals(2, stats.missCount());
1237     assertEquals(0, stats.loadSuccessCount());
1238     assertEquals(2, stats.loadExceptionCount());
1239     assertEquals(0, stats.hitCount());
1240 
1241     cache.refresh(new Object());
1242     assertTrue(currentThread().interrupted());
1243     checkLoggedCause(e);
1244     stats = cache.stats();
1245     assertEquals(2, stats.missCount());
1246     assertEquals(0, stats.loadSuccessCount());
1247     assertEquals(3, stats.loadExceptionCount());
1248     assertEquals(0, stats.hitCount());
1249 
1250     Exception callableException = new InterruptedException();
1251     try {
1252       cache.get(new Object(), throwing(callableException));
1253       fail();
1254     } catch (ExecutionException expected) {
1255       assertThat(expected).hasCauseThat().isSameInstanceAs(callableException);
1256     }
1257     assertTrue(currentThread().interrupted());
1258     stats = cache.stats();
1259     assertEquals(3, stats.missCount());
1260     assertEquals(0, stats.loadSuccessCount());
1261     assertEquals(4, stats.loadExceptionCount());
1262     assertEquals(0, stats.hitCount());
1263 
1264     try {
1265       cache.getAll(asList(new Object()));
1266       fail();
1267     } catch (ExecutionException expected) {
1268       assertThat(expected).hasCauseThat().isSameInstanceAs(e);
1269     }
1270     assertTrue(currentThread().interrupted());
1271     stats = cache.stats();
1272     assertEquals(4, stats.missCount());
1273     assertEquals(0, stats.loadSuccessCount());
1274     assertEquals(5, stats.loadExceptionCount());
1275     assertEquals(0, stats.hitCount());
1276   }
1277 
testReloadCheckedException()1278   public void testReloadCheckedException() {
1279     final Object one = new Object();
1280     final Exception e = new Exception();
1281     CacheLoader<Object, Object> loader =
1282         new CacheLoader<Object, Object>() {
1283           @Override
1284           public Object load(Object key) {
1285             return one;
1286           }
1287 
1288           @Override
1289           public ListenableFuture<Object> reload(Object key, Object oldValue) throws Exception {
1290             throw e;
1291           }
1292         };
1293 
1294     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
1295     Object key = new Object();
1296     CacheStats stats = cache.stats();
1297     assertEquals(0, stats.missCount());
1298     assertEquals(0, stats.loadSuccessCount());
1299     assertEquals(0, stats.loadExceptionCount());
1300     assertEquals(0, stats.hitCount());
1301 
1302     assertSame(one, cache.getUnchecked(key));
1303     stats = cache.stats();
1304     assertEquals(1, stats.missCount());
1305     assertEquals(1, stats.loadSuccessCount());
1306     assertEquals(0, stats.loadExceptionCount());
1307     assertEquals(0, stats.hitCount());
1308 
1309     cache.refresh(key);
1310     checkLoggedCause(e);
1311     stats = cache.stats();
1312     assertEquals(1, stats.missCount());
1313     assertEquals(1, stats.loadSuccessCount());
1314     assertEquals(1, stats.loadExceptionCount());
1315     assertEquals(0, stats.hitCount());
1316 
1317     assertSame(one, cache.getUnchecked(key));
1318     stats = cache.stats();
1319     assertEquals(1, stats.missCount());
1320     assertEquals(1, stats.loadSuccessCount());
1321     assertEquals(1, stats.loadExceptionCount());
1322     assertEquals(1, stats.hitCount());
1323   }
1324 
testReloadFutureCheckedException()1325   public void testReloadFutureCheckedException() {
1326     final Object one = new Object();
1327     final Exception e = new Exception();
1328     CacheLoader<Object, Object> loader =
1329         new CacheLoader<Object, Object>() {
1330           @Override
1331           public Object load(Object key) {
1332             return one;
1333           }
1334 
1335           @Override
1336           public ListenableFuture<Object> reload(Object key, Object oldValue) {
1337             return Futures.immediateFailedFuture(e);
1338           }
1339         };
1340 
1341     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
1342     Object key = new Object();
1343     CacheStats stats = cache.stats();
1344     assertEquals(0, stats.missCount());
1345     assertEquals(0, stats.loadSuccessCount());
1346     assertEquals(0, stats.loadExceptionCount());
1347     assertEquals(0, stats.hitCount());
1348 
1349     assertSame(one, cache.getUnchecked(key));
1350     stats = cache.stats();
1351     assertEquals(1, stats.missCount());
1352     assertEquals(1, stats.loadSuccessCount());
1353     assertEquals(0, stats.loadExceptionCount());
1354     assertEquals(0, stats.hitCount());
1355 
1356     cache.refresh(key);
1357     checkLoggedCause(e);
1358     stats = cache.stats();
1359     assertEquals(1, stats.missCount());
1360     assertEquals(1, stats.loadSuccessCount());
1361     assertEquals(1, stats.loadExceptionCount());
1362     assertEquals(0, stats.hitCount());
1363 
1364     assertSame(one, cache.getUnchecked(key));
1365     stats = cache.stats();
1366     assertEquals(1, stats.missCount());
1367     assertEquals(1, stats.loadSuccessCount());
1368     assertEquals(1, stats.loadExceptionCount());
1369     assertEquals(1, stats.hitCount());
1370   }
1371 
testRefreshCheckedException()1372   public void testRefreshCheckedException() {
1373     final Object one = new Object();
1374     final Exception e = new Exception();
1375     FakeTicker ticker = new FakeTicker();
1376     CacheLoader<Object, Object> loader =
1377         new CacheLoader<Object, Object>() {
1378           @Override
1379           public Object load(Object key) {
1380             return one;
1381           }
1382 
1383           @Override
1384           public ListenableFuture<Object> reload(Object key, Object oldValue) {
1385             return Futures.immediateFailedFuture(e);
1386           }
1387         };
1388 
1389     LoadingCache<Object, Object> cache =
1390         CacheBuilder.newBuilder()
1391             .recordStats()
1392             .ticker(ticker)
1393             .refreshAfterWrite(1, MILLISECONDS)
1394             .build(loader);
1395     Object key = new Object();
1396     CacheStats stats = cache.stats();
1397     assertEquals(0, stats.missCount());
1398     assertEquals(0, stats.loadSuccessCount());
1399     assertEquals(0, stats.loadExceptionCount());
1400     assertEquals(0, stats.hitCount());
1401 
1402     assertSame(one, cache.getUnchecked(key));
1403     stats = cache.stats();
1404     assertEquals(1, stats.missCount());
1405     assertEquals(1, stats.loadSuccessCount());
1406     assertEquals(0, stats.loadExceptionCount());
1407     assertEquals(0, stats.hitCount());
1408 
1409     ticker.advance(1, MILLISECONDS);
1410     assertSame(one, cache.getUnchecked(key));
1411     stats = cache.stats();
1412     assertEquals(1, stats.missCount());
1413     assertEquals(1, stats.loadSuccessCount());
1414     assertEquals(0, stats.loadExceptionCount());
1415     assertEquals(1, stats.hitCount());
1416 
1417     ticker.advance(1, MILLISECONDS);
1418     assertSame(one, cache.getUnchecked(key));
1419     // refreshed
1420     stats = cache.stats();
1421     assertEquals(1, stats.missCount());
1422     assertEquals(1, stats.loadSuccessCount());
1423     assertEquals(1, stats.loadExceptionCount());
1424     assertEquals(2, stats.hitCount());
1425 
1426     ticker.advance(1, MILLISECONDS);
1427     assertSame(one, cache.getUnchecked(key));
1428     stats = cache.stats();
1429     assertEquals(1, stats.missCount());
1430     assertEquals(1, stats.loadSuccessCount());
1431     assertEquals(2, stats.loadExceptionCount());
1432     assertEquals(3, stats.hitCount());
1433   }
1434 
testBulkLoadCheckedException()1435   public void testBulkLoadCheckedException() {
1436     Exception e = new Exception();
1437     CacheLoader<Object, Object> loader = exceptionLoader(e);
1438     LoadingCache<Object, Object> cache =
1439         CacheBuilder.newBuilder().recordStats().build(bulkLoader(loader));
1440     CacheStats stats = cache.stats();
1441     assertEquals(0, stats.missCount());
1442     assertEquals(0, stats.loadSuccessCount());
1443     assertEquals(0, stats.loadExceptionCount());
1444     assertEquals(0, stats.hitCount());
1445 
1446     try {
1447       cache.getAll(asList(new Object()));
1448       fail();
1449     } catch (ExecutionException expected) {
1450       assertThat(expected).hasCauseThat().isSameInstanceAs(e);
1451     }
1452     stats = cache.stats();
1453     assertEquals(1, stats.missCount());
1454     assertEquals(0, stats.loadSuccessCount());
1455     assertEquals(1, stats.loadExceptionCount());
1456     assertEquals(0, stats.hitCount());
1457   }
1458 
testBulkLoadInterruptedException()1459   public void testBulkLoadInterruptedException() {
1460     Exception e = new InterruptedException();
1461     CacheLoader<Object, Object> loader = exceptionLoader(e);
1462     LoadingCache<Object, Object> cache =
1463         CacheBuilder.newBuilder().recordStats().build(bulkLoader(loader));
1464     CacheStats stats = cache.stats();
1465     assertEquals(0, stats.missCount());
1466     assertEquals(0, stats.loadSuccessCount());
1467     assertEquals(0, stats.loadExceptionCount());
1468     assertEquals(0, stats.hitCount());
1469 
1470     try {
1471       cache.getAll(asList(new Object()));
1472       fail();
1473     } catch (ExecutionException expected) {
1474       assertThat(expected).hasCauseThat().isSameInstanceAs(e);
1475     }
1476     assertTrue(currentThread().interrupted());
1477     stats = cache.stats();
1478     assertEquals(1, stats.missCount());
1479     assertEquals(0, stats.loadSuccessCount());
1480     assertEquals(1, stats.loadExceptionCount());
1481     assertEquals(0, stats.hitCount());
1482   }
1483 
testLoadUncheckedException()1484   public void testLoadUncheckedException() throws ExecutionException {
1485     Exception e = new RuntimeException();
1486     CacheLoader<Object, Object> loader = exceptionLoader(e);
1487     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
1488     CacheStats stats = cache.stats();
1489     assertEquals(0, stats.missCount());
1490     assertEquals(0, stats.loadSuccessCount());
1491     assertEquals(0, stats.loadExceptionCount());
1492     assertEquals(0, stats.hitCount());
1493 
1494     try {
1495       cache.get(new Object());
1496       fail();
1497     } catch (UncheckedExecutionException expected) {
1498       assertThat(expected).hasCauseThat().isSameInstanceAs(e);
1499     }
1500     stats = cache.stats();
1501     assertEquals(1, stats.missCount());
1502     assertEquals(0, stats.loadSuccessCount());
1503     assertEquals(1, stats.loadExceptionCount());
1504     assertEquals(0, stats.hitCount());
1505 
1506     try {
1507       cache.getUnchecked(new Object());
1508       fail();
1509     } catch (UncheckedExecutionException expected) {
1510       assertThat(expected).hasCauseThat().isSameInstanceAs(e);
1511     }
1512     stats = cache.stats();
1513     assertEquals(2, stats.missCount());
1514     assertEquals(0, stats.loadSuccessCount());
1515     assertEquals(2, stats.loadExceptionCount());
1516     assertEquals(0, stats.hitCount());
1517 
1518     cache.refresh(new Object());
1519     checkLoggedCause(e);
1520     stats = cache.stats();
1521     assertEquals(2, stats.missCount());
1522     assertEquals(0, stats.loadSuccessCount());
1523     assertEquals(3, stats.loadExceptionCount());
1524     assertEquals(0, stats.hitCount());
1525 
1526     Exception callableException = new RuntimeException();
1527     try {
1528       cache.get(new Object(), throwing(callableException));
1529       fail();
1530     } catch (UncheckedExecutionException expected) {
1531       assertThat(expected).hasCauseThat().isSameInstanceAs(callableException);
1532     }
1533     stats = cache.stats();
1534     assertEquals(3, stats.missCount());
1535     assertEquals(0, stats.loadSuccessCount());
1536     assertEquals(4, stats.loadExceptionCount());
1537     assertEquals(0, stats.hitCount());
1538 
1539     try {
1540       cache.getAll(asList(new Object()));
1541       fail();
1542     } catch (UncheckedExecutionException expected) {
1543       assertThat(expected).hasCauseThat().isSameInstanceAs(e);
1544     }
1545     stats = cache.stats();
1546     assertEquals(4, stats.missCount());
1547     assertEquals(0, stats.loadSuccessCount());
1548     assertEquals(5, stats.loadExceptionCount());
1549     assertEquals(0, stats.hitCount());
1550   }
1551 
testReloadUncheckedException()1552   public void testReloadUncheckedException() throws ExecutionException {
1553     final Object one = new Object();
1554     final Exception e = new RuntimeException();
1555     CacheLoader<Object, Object> loader =
1556         new CacheLoader<Object, Object>() {
1557           @Override
1558           public Object load(Object key) {
1559             return one;
1560           }
1561 
1562           @Override
1563           public ListenableFuture<Object> reload(Object key, Object oldValue) throws Exception {
1564             throw e;
1565           }
1566         };
1567 
1568     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
1569     Object key = new Object();
1570     CacheStats stats = cache.stats();
1571     assertEquals(0, stats.missCount());
1572     assertEquals(0, stats.loadSuccessCount());
1573     assertEquals(0, stats.loadExceptionCount());
1574     assertEquals(0, stats.hitCount());
1575 
1576     assertSame(one, cache.getUnchecked(key));
1577     stats = cache.stats();
1578     assertEquals(1, stats.missCount());
1579     assertEquals(1, stats.loadSuccessCount());
1580     assertEquals(0, stats.loadExceptionCount());
1581     assertEquals(0, stats.hitCount());
1582 
1583     cache.refresh(key);
1584     checkLoggedCause(e);
1585     stats = cache.stats();
1586     assertEquals(1, stats.missCount());
1587     assertEquals(1, stats.loadSuccessCount());
1588     assertEquals(1, stats.loadExceptionCount());
1589     assertEquals(0, stats.hitCount());
1590 
1591     assertSame(one, cache.getUnchecked(key));
1592     stats = cache.stats();
1593     assertEquals(1, stats.missCount());
1594     assertEquals(1, stats.loadSuccessCount());
1595     assertEquals(1, stats.loadExceptionCount());
1596     assertEquals(1, stats.hitCount());
1597   }
1598 
testReloadFutureUncheckedException()1599   public void testReloadFutureUncheckedException() throws ExecutionException {
1600     final Object one = new Object();
1601     final Exception e = new RuntimeException();
1602     CacheLoader<Object, Object> loader =
1603         new CacheLoader<Object, Object>() {
1604           @Override
1605           public Object load(Object key) {
1606             return one;
1607           }
1608 
1609           @Override
1610           public ListenableFuture<Object> reload(Object key, Object oldValue) {
1611             return Futures.immediateFailedFuture(e);
1612           }
1613         };
1614 
1615     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
1616     Object key = new Object();
1617     CacheStats stats = cache.stats();
1618     assertEquals(0, stats.missCount());
1619     assertEquals(0, stats.loadSuccessCount());
1620     assertEquals(0, stats.loadExceptionCount());
1621     assertEquals(0, stats.hitCount());
1622 
1623     assertSame(one, cache.getUnchecked(key));
1624     stats = cache.stats();
1625     assertEquals(1, stats.missCount());
1626     assertEquals(1, stats.loadSuccessCount());
1627     assertEquals(0, stats.loadExceptionCount());
1628     assertEquals(0, stats.hitCount());
1629 
1630     cache.refresh(key);
1631     checkLoggedCause(e);
1632     stats = cache.stats();
1633     assertEquals(1, stats.missCount());
1634     assertEquals(1, stats.loadSuccessCount());
1635     assertEquals(1, stats.loadExceptionCount());
1636     assertEquals(0, stats.hitCount());
1637 
1638     assertSame(one, cache.getUnchecked(key));
1639     stats = cache.stats();
1640     assertEquals(1, stats.missCount());
1641     assertEquals(1, stats.loadSuccessCount());
1642     assertEquals(1, stats.loadExceptionCount());
1643     assertEquals(1, stats.hitCount());
1644   }
1645 
testRefreshUncheckedException()1646   public void testRefreshUncheckedException() {
1647     final Object one = new Object();
1648     final Exception e = new RuntimeException();
1649     FakeTicker ticker = new FakeTicker();
1650     CacheLoader<Object, Object> loader =
1651         new CacheLoader<Object, Object>() {
1652           @Override
1653           public Object load(Object key) {
1654             return one;
1655           }
1656 
1657           @Override
1658           public ListenableFuture<Object> reload(Object key, Object oldValue) {
1659             return Futures.immediateFailedFuture(e);
1660           }
1661         };
1662 
1663     LoadingCache<Object, Object> cache =
1664         CacheBuilder.newBuilder()
1665             .recordStats()
1666             .ticker(ticker)
1667             .refreshAfterWrite(1, MILLISECONDS)
1668             .build(loader);
1669     Object key = new Object();
1670     CacheStats stats = cache.stats();
1671     assertEquals(0, stats.missCount());
1672     assertEquals(0, stats.loadSuccessCount());
1673     assertEquals(0, stats.loadExceptionCount());
1674     assertEquals(0, stats.hitCount());
1675 
1676     assertSame(one, cache.getUnchecked(key));
1677     stats = cache.stats();
1678     assertEquals(1, stats.missCount());
1679     assertEquals(1, stats.loadSuccessCount());
1680     assertEquals(0, stats.loadExceptionCount());
1681     assertEquals(0, stats.hitCount());
1682 
1683     ticker.advance(1, MILLISECONDS);
1684     assertSame(one, cache.getUnchecked(key));
1685     stats = cache.stats();
1686     assertEquals(1, stats.missCount());
1687     assertEquals(1, stats.loadSuccessCount());
1688     assertEquals(0, stats.loadExceptionCount());
1689     assertEquals(1, stats.hitCount());
1690 
1691     ticker.advance(1, MILLISECONDS);
1692     assertSame(one, cache.getUnchecked(key));
1693     // refreshed
1694     stats = cache.stats();
1695     assertEquals(1, stats.missCount());
1696     assertEquals(1, stats.loadSuccessCount());
1697     assertEquals(1, stats.loadExceptionCount());
1698     assertEquals(2, stats.hitCount());
1699 
1700     ticker.advance(1, MILLISECONDS);
1701     assertSame(one, cache.getUnchecked(key));
1702     stats = cache.stats();
1703     assertEquals(1, stats.missCount());
1704     assertEquals(1, stats.loadSuccessCount());
1705     assertEquals(2, stats.loadExceptionCount());
1706     assertEquals(3, stats.hitCount());
1707   }
1708 
testBulkLoadUncheckedException()1709   public void testBulkLoadUncheckedException() throws ExecutionException {
1710     Exception e = new RuntimeException();
1711     CacheLoader<Object, Object> loader = exceptionLoader(e);
1712     LoadingCache<Object, Object> cache =
1713         CacheBuilder.newBuilder().recordStats().build(bulkLoader(loader));
1714     CacheStats stats = cache.stats();
1715     assertEquals(0, stats.missCount());
1716     assertEquals(0, stats.loadSuccessCount());
1717     assertEquals(0, stats.loadExceptionCount());
1718     assertEquals(0, stats.hitCount());
1719 
1720     try {
1721       cache.getAll(asList(new Object()));
1722       fail();
1723     } catch (UncheckedExecutionException expected) {
1724       assertThat(expected).hasCauseThat().isSameInstanceAs(e);
1725     }
1726     stats = cache.stats();
1727     assertEquals(1, stats.missCount());
1728     assertEquals(0, stats.loadSuccessCount());
1729     assertEquals(1, stats.loadExceptionCount());
1730     assertEquals(0, stats.hitCount());
1731   }
1732 
testReloadAfterFailure()1733   public void testReloadAfterFailure() throws ExecutionException {
1734     final AtomicInteger count = new AtomicInteger();
1735     final Exception e = new IllegalStateException("exception to trigger failure on first load()");
1736     CacheLoader<Integer, String> failOnceFunction =
1737         new CacheLoader<Integer, String>() {
1738 
1739           @Override
1740           public String load(Integer key) throws Exception {
1741             if (count.getAndIncrement() == 0) {
1742               throw e;
1743             }
1744             return key.toString();
1745           }
1746         };
1747     CountingRemovalListener<Integer, String> removalListener = countingRemovalListener();
1748     LoadingCache<Integer, String> cache =
1749         CacheBuilder.newBuilder().removalListener(removalListener).build(failOnceFunction);
1750 
1751     try {
1752       cache.getUnchecked(1);
1753       fail();
1754     } catch (UncheckedExecutionException ue) {
1755       assertThat(ue).hasCauseThat().isSameInstanceAs(e);
1756     }
1757 
1758     assertEquals("1", cache.getUnchecked(1));
1759     assertEquals(0, removalListener.getCount());
1760 
1761     count.set(0);
1762     cache.refresh(2);
1763     checkLoggedCause(e);
1764 
1765     assertEquals("2", cache.getUnchecked(2));
1766     assertEquals(0, removalListener.getCount());
1767   }
1768 
testReloadAfterValueReclamation()1769   public void testReloadAfterValueReclamation() throws InterruptedException, ExecutionException {
1770     CountingLoader countingLoader = new CountingLoader();
1771     LoadingCache<Object, Object> cache =
1772         CacheBuilder.newBuilder().weakValues().build(countingLoader);
1773     ConcurrentMap<Object, Object> map = cache.asMap();
1774 
1775     int iterations = 10;
1776     WeakReference<Object> ref = new WeakReference<>(null);
1777     int expectedComputations = 0;
1778     for (int i = 0; i < iterations; i++) {
1779       // The entry should get garbage collected and recomputed.
1780       Object oldValue = ref.get();
1781       if (oldValue == null) {
1782         expectedComputations++;
1783       }
1784       ref = new WeakReference<>(cache.getUnchecked(1));
1785       oldValue = null;
1786       Thread.sleep(i);
1787       System.gc();
1788     }
1789     assertEquals(expectedComputations, countingLoader.getCount());
1790 
1791     for (int i = 0; i < iterations; i++) {
1792       // The entry should get garbage collected and recomputed.
1793       Object oldValue = ref.get();
1794       if (oldValue == null) {
1795         expectedComputations++;
1796       }
1797       cache.refresh(1);
1798       checkNothingLogged();
1799       ref = new WeakReference<>(map.get(1));
1800       oldValue = null;
1801       Thread.sleep(i);
1802       System.gc();
1803     }
1804     assertEquals(expectedComputations, countingLoader.getCount());
1805   }
1806 
testReloadAfterSimulatedValueReclamation()1807   public void testReloadAfterSimulatedValueReclamation() throws ExecutionException {
1808     CountingLoader countingLoader = new CountingLoader();
1809     LoadingCache<Object, Object> cache =
1810         CacheBuilder.newBuilder().concurrencyLevel(1).weakValues().build(countingLoader);
1811 
1812     Object key = new Object();
1813     assertNotNull(cache.getUnchecked(key));
1814 
1815     CacheTesting.simulateValueReclamation(cache, key);
1816 
1817     // this blocks if computation can't deal with partially-collected values
1818     assertNotNull(cache.getUnchecked(key));
1819     assertEquals(1, cache.size());
1820     assertEquals(2, countingLoader.getCount());
1821 
1822     CacheTesting.simulateValueReclamation(cache, key);
1823     cache.refresh(key);
1824     checkNothingLogged();
1825     assertEquals(1, cache.size());
1826     assertEquals(3, countingLoader.getCount());
1827   }
1828 
testReloadAfterSimulatedKeyReclamation()1829   public void testReloadAfterSimulatedKeyReclamation() throws ExecutionException {
1830     CountingLoader countingLoader = new CountingLoader();
1831     LoadingCache<Object, Object> cache =
1832         CacheBuilder.newBuilder().concurrencyLevel(1).weakKeys().build(countingLoader);
1833 
1834     Object key = new Object();
1835     assertNotNull(cache.getUnchecked(key));
1836     assertEquals(1, cache.size());
1837 
1838     CacheTesting.simulateKeyReclamation(cache, key);
1839 
1840     // this blocks if computation can't deal with partially-collected values
1841     assertNotNull(cache.getUnchecked(key));
1842     assertEquals(2, countingLoader.getCount());
1843 
1844     CacheTesting.simulateKeyReclamation(cache, key);
1845     cache.refresh(key);
1846     checkNothingLogged();
1847     assertEquals(3, countingLoader.getCount());
1848   }
1849 
1850   /**
1851    * Make sure LoadingCache correctly wraps ExecutionExceptions and UncheckedExecutionExceptions.
1852    */
testLoadingExceptionWithCause()1853   public void testLoadingExceptionWithCause() {
1854     final Exception cause = new Exception();
1855     final UncheckedExecutionException uee = new UncheckedExecutionException(cause);
1856     final ExecutionException ee = new ExecutionException(cause);
1857 
1858     LoadingCache<Object, Object> cacheUnchecked =
1859         CacheBuilder.newBuilder().build(exceptionLoader(uee));
1860     LoadingCache<Object, Object> cacheChecked =
1861         CacheBuilder.newBuilder().build(exceptionLoader(ee));
1862 
1863     try {
1864       cacheUnchecked.get(new Object());
1865       fail();
1866     } catch (ExecutionException e) {
1867       fail();
1868     } catch (UncheckedExecutionException caughtEe) {
1869       assertThat(caughtEe).hasCauseThat().isSameInstanceAs(uee);
1870     }
1871 
1872     try {
1873       cacheUnchecked.getUnchecked(new Object());
1874       fail();
1875     } catch (UncheckedExecutionException caughtUee) {
1876       assertThat(caughtUee).hasCauseThat().isSameInstanceAs(uee);
1877     }
1878 
1879     cacheUnchecked.refresh(new Object());
1880     checkLoggedCause(uee);
1881 
1882     try {
1883       cacheUnchecked.getAll(asList(new Object()));
1884       fail();
1885     } catch (ExecutionException e) {
1886       fail();
1887     } catch (UncheckedExecutionException caughtEe) {
1888       assertThat(caughtEe).hasCauseThat().isSameInstanceAs(uee);
1889     }
1890 
1891     try {
1892       cacheChecked.get(new Object());
1893       fail();
1894     } catch (ExecutionException caughtEe) {
1895       assertThat(caughtEe).hasCauseThat().isSameInstanceAs(ee);
1896     }
1897 
1898     try {
1899       cacheChecked.getUnchecked(new Object());
1900       fail();
1901     } catch (UncheckedExecutionException caughtUee) {
1902       assertThat(caughtUee).hasCauseThat().isSameInstanceAs(ee);
1903     }
1904 
1905     cacheChecked.refresh(new Object());
1906     checkLoggedCause(ee);
1907 
1908     try {
1909       cacheChecked.getAll(asList(new Object()));
1910       fail();
1911     } catch (ExecutionException caughtEe) {
1912       assertThat(caughtEe).hasCauseThat().isSameInstanceAs(ee);
1913     }
1914   }
1915 
testBulkLoadingExceptionWithCause()1916   public void testBulkLoadingExceptionWithCause() {
1917     final Exception cause = new Exception();
1918     final UncheckedExecutionException uee = new UncheckedExecutionException(cause);
1919     final ExecutionException ee = new ExecutionException(cause);
1920 
1921     LoadingCache<Object, Object> cacheUnchecked =
1922         CacheBuilder.newBuilder().build(bulkLoader(exceptionLoader(uee)));
1923     LoadingCache<Object, Object> cacheChecked =
1924         CacheBuilder.newBuilder().build(bulkLoader(exceptionLoader(ee)));
1925 
1926     try {
1927       cacheUnchecked.getAll(asList(new Object()));
1928       fail();
1929     } catch (ExecutionException e) {
1930       fail();
1931     } catch (UncheckedExecutionException caughtEe) {
1932       assertThat(caughtEe).hasCauseThat().isSameInstanceAs(uee);
1933     }
1934 
1935     try {
1936       cacheChecked.getAll(asList(new Object()));
1937       fail();
1938     } catch (ExecutionException caughtEe) {
1939       assertThat(caughtEe).hasCauseThat().isSameInstanceAs(ee);
1940     }
1941   }
1942 
testConcurrentLoading()1943   public void testConcurrentLoading() throws InterruptedException {
1944     testConcurrentLoading(CacheBuilder.newBuilder());
1945   }
1946 
testConcurrentLoading(CacheBuilder<Object, Object> builder)1947   private static void testConcurrentLoading(CacheBuilder<Object, Object> builder)
1948       throws InterruptedException {
1949     testConcurrentLoadingDefault(builder);
1950     testConcurrentLoadingNull(builder);
1951     testConcurrentLoadingUncheckedException(builder);
1952     testConcurrentLoadingCheckedException(builder);
1953   }
1954 
testConcurrentExpirationLoading()1955   public void testConcurrentExpirationLoading() throws InterruptedException {
1956     testConcurrentLoading(CacheBuilder.newBuilder().expireAfterWrite(10, TimeUnit.SECONDS));
1957   }
1958 
1959   /**
1960    * On a successful concurrent computation, only one thread does the work, but all the threads get
1961    * the same result.
1962    */
testConcurrentLoadingDefault(CacheBuilder<Object, Object> builder)1963   private static void testConcurrentLoadingDefault(CacheBuilder<Object, Object> builder)
1964       throws InterruptedException {
1965 
1966     int count = 10;
1967     final AtomicInteger callCount = new AtomicInteger();
1968     final CountDownLatch startSignal = new CountDownLatch(count + 1);
1969     final Object result = new Object();
1970 
1971     LoadingCache<String, Object> cache =
1972         builder.build(
1973             new CacheLoader<String, Object>() {
1974               @Override
1975               public Object load(String key) throws InterruptedException {
1976                 callCount.incrementAndGet();
1977                 startSignal.await();
1978                 return result;
1979               }
1980             });
1981 
1982     List<Object> resultArray = doConcurrentGet(cache, "bar", count, startSignal);
1983 
1984     assertEquals(1, callCount.get());
1985     for (int i = 0; i < count; i++) {
1986       assertSame("result(" + i + ") didn't match expected", result, resultArray.get(i));
1987     }
1988   }
1989 
1990   /**
1991    * On a concurrent computation that returns null, all threads should get an
1992    * InvalidCacheLoadException, with the loader only called once. The result should not be cached (a
1993    * later request should call the loader again).
1994    */
testConcurrentLoadingNull(CacheBuilder<Object, Object> builder)1995   private static void testConcurrentLoadingNull(CacheBuilder<Object, Object> builder)
1996       throws InterruptedException {
1997 
1998     int count = 10;
1999     final AtomicInteger callCount = new AtomicInteger();
2000     final CountDownLatch startSignal = new CountDownLatch(count + 1);
2001 
2002     LoadingCache<String, String> cache =
2003         builder.build(
2004             new CacheLoader<String, String>() {
2005               @Override
2006               public String load(String key) throws InterruptedException {
2007                 callCount.incrementAndGet();
2008                 startSignal.await();
2009                 return null;
2010               }
2011             });
2012 
2013     List<Object> result = doConcurrentGet(cache, "bar", count, startSignal);
2014 
2015     assertEquals(1, callCount.get());
2016     for (int i = 0; i < count; i++) {
2017       assertThat(result.get(i)).isInstanceOf(InvalidCacheLoadException.class);
2018     }
2019 
2020     // subsequent calls should call the loader again, not get the old exception
2021     try {
2022       cache.getUnchecked("bar");
2023       fail();
2024     } catch (InvalidCacheLoadException expected) {
2025     }
2026     assertEquals(2, callCount.get());
2027   }
2028 
2029   /**
2030    * On a concurrent computation that throws an unchecked exception, all threads should get the
2031    * (wrapped) exception, with the loader called only once. The result should not be cached (a later
2032    * request should call the loader again).
2033    */
testConcurrentLoadingUncheckedException(CacheBuilder<Object, Object> builder)2034   private static void testConcurrentLoadingUncheckedException(CacheBuilder<Object, Object> builder)
2035       throws InterruptedException {
2036 
2037     int count = 10;
2038     final AtomicInteger callCount = new AtomicInteger();
2039     final CountDownLatch startSignal = new CountDownLatch(count + 1);
2040     final RuntimeException e = new RuntimeException();
2041 
2042     LoadingCache<String, String> cache =
2043         builder.build(
2044             new CacheLoader<String, String>() {
2045               @Override
2046               public String load(String key) throws InterruptedException {
2047                 callCount.incrementAndGet();
2048                 startSignal.await();
2049                 throw e;
2050               }
2051             });
2052 
2053     List<Object> result = doConcurrentGet(cache, "bar", count, startSignal);
2054 
2055     assertEquals(1, callCount.get());
2056     for (int i = 0; i < count; i++) {
2057       // doConcurrentGet alternates between calling getUnchecked and calling get, but an unchecked
2058       // exception thrown by the loader is always wrapped as an UncheckedExecutionException.
2059       assertThat(result.get(i)).isInstanceOf(UncheckedExecutionException.class);
2060       assertThat(((UncheckedExecutionException) result.get(i))).hasCauseThat().isSameInstanceAs(e);
2061     }
2062 
2063     // subsequent calls should call the loader again, not get the old exception
2064     try {
2065       cache.getUnchecked("bar");
2066       fail();
2067     } catch (UncheckedExecutionException expected) {
2068     }
2069     assertEquals(2, callCount.get());
2070   }
2071 
2072   /**
2073    * On a concurrent computation that throws a checked exception, all threads should get the
2074    * (wrapped) exception, with the loader called only once. The result should not be cached (a later
2075    * request should call the loader again).
2076    */
testConcurrentLoadingCheckedException(CacheBuilder<Object, Object> builder)2077   private static void testConcurrentLoadingCheckedException(CacheBuilder<Object, Object> builder)
2078       throws InterruptedException {
2079 
2080     int count = 10;
2081     final AtomicInteger callCount = new AtomicInteger();
2082     final CountDownLatch startSignal = new CountDownLatch(count + 1);
2083     final IOException e = new IOException();
2084 
2085     LoadingCache<String, String> cache =
2086         builder.build(
2087             new CacheLoader<String, String>() {
2088               @Override
2089               public String load(String key) throws IOException, InterruptedException {
2090                 callCount.incrementAndGet();
2091                 startSignal.await();
2092                 throw e;
2093               }
2094             });
2095 
2096     List<Object> result = doConcurrentGet(cache, "bar", count, startSignal);
2097 
2098     assertEquals(1, callCount.get());
2099     for (int i = 0; i < count; i++) {
2100       // doConcurrentGet alternates between calling getUnchecked and calling get. If we call get(),
2101       // we should get an ExecutionException; if we call getUnchecked(), we should get an
2102       // UncheckedExecutionException.
2103       int mod = i % 3;
2104       if (mod == 0 || mod == 2) {
2105         assertThat(result.get(i)).isInstanceOf(ExecutionException.class);
2106         assertThat((ExecutionException) result.get(i)).hasCauseThat().isSameInstanceAs(e);
2107       } else {
2108         assertThat(result.get(i)).isInstanceOf(UncheckedExecutionException.class);
2109         assertThat((UncheckedExecutionException) result.get(i)).hasCauseThat().isSameInstanceAs(e);
2110       }
2111     }
2112 
2113     // subsequent calls should call the loader again, not get the old exception
2114     try {
2115       cache.getUnchecked("bar");
2116       fail();
2117     } catch (UncheckedExecutionException expected) {
2118     }
2119     assertEquals(2, callCount.get());
2120   }
2121 
2122   /**
2123    * Test-helper method that performs {@code nThreads} concurrent calls to {@code cache.get(key)} or
2124    * {@code cache.getUnchecked(key)}, and returns a List containing each of the results. The result
2125    * for any given call to {@code cache.get} or {@code cache.getUnchecked} is the value returned, or
2126    * the exception thrown.
2127    *
2128    * <p>As we iterate from {@code 0} to {@code nThreads}, threads with an even index will call
2129    * {@code getUnchecked}, and threads with an odd index will call {@code get}. If the cache throws
2130    * exceptions, this difference may be visible in the returned List.
2131    */
doConcurrentGet( final LoadingCache<K, ?> cache, final K key, int nThreads, final CountDownLatch gettersStartedSignal)2132   private static <K> List<Object> doConcurrentGet(
2133       final LoadingCache<K, ?> cache,
2134       final K key,
2135       int nThreads,
2136       final CountDownLatch gettersStartedSignal)
2137       throws InterruptedException {
2138 
2139     final AtomicReferenceArray<Object> result = new AtomicReferenceArray<>(nThreads);
2140     final CountDownLatch gettersComplete = new CountDownLatch(nThreads);
2141     for (int i = 0; i < nThreads; i++) {
2142       final int index = i;
2143       Thread thread =
2144           new Thread(
2145               new Runnable() {
2146                 @Override
2147                 public void run() {
2148                   gettersStartedSignal.countDown();
2149                   Object value = null;
2150                   try {
2151                     int mod = index % 3;
2152                     if (mod == 0) {
2153                       value = cache.get(key);
2154                     } else if (mod == 1) {
2155                       value = cache.getUnchecked(key);
2156                     } else {
2157                       cache.refresh(key);
2158                       value = cache.get(key);
2159                     }
2160                     result.set(index, value);
2161                   } catch (Throwable t) {
2162                     result.set(index, t);
2163                   }
2164                   gettersComplete.countDown();
2165                 }
2166               });
2167       thread.start();
2168       // we want to wait until each thread is WAITING - one thread waiting inside CacheLoader.load
2169       // (in startSignal.await()), and the others waiting for that thread's result.
2170       while (thread.isAlive() && thread.getState() != Thread.State.WAITING) {
2171         Thread.yield();
2172       }
2173     }
2174     gettersStartedSignal.countDown();
2175     gettersComplete.await();
2176 
2177     List<Object> resultList = Lists.newArrayListWithExpectedSize(nThreads);
2178     for (int i = 0; i < nThreads; i++) {
2179       resultList.add(result.get(i));
2180     }
2181     return resultList;
2182   }
2183 
testAsMapDuringLoading()2184   public void testAsMapDuringLoading() throws InterruptedException, ExecutionException {
2185     final CountDownLatch getStartedSignal = new CountDownLatch(2);
2186     final CountDownLatch letGetFinishSignal = new CountDownLatch(1);
2187     final CountDownLatch getFinishedSignal = new CountDownLatch(2);
2188     final String getKey = "get";
2189     final String refreshKey = "refresh";
2190     final String suffix = "Suffix";
2191 
2192     CacheLoader<String, String> computeFunction =
2193         new CacheLoader<String, String>() {
2194           @Override
2195           public String load(String key) throws InterruptedException {
2196             getStartedSignal.countDown();
2197             letGetFinishSignal.await();
2198             return key + suffix;
2199           }
2200         };
2201 
2202     final LoadingCache<String, String> cache = CacheBuilder.newBuilder().build(computeFunction);
2203     ConcurrentMap<String, String> map = cache.asMap();
2204     map.put(refreshKey, refreshKey);
2205     assertEquals(1, map.size());
2206     assertFalse(map.containsKey(getKey));
2207     assertSame(refreshKey, map.get(refreshKey));
2208 
2209     new Thread() {
2210       @Override
2211       public void run() {
2212         cache.getUnchecked(getKey);
2213         getFinishedSignal.countDown();
2214       }
2215     }.start();
2216     new Thread() {
2217       @Override
2218       public void run() {
2219         cache.refresh(refreshKey);
2220         getFinishedSignal.countDown();
2221       }
2222     }.start();
2223 
2224     getStartedSignal.await();
2225 
2226     // computation is in progress; asMap shouldn't have changed
2227     assertEquals(1, map.size());
2228     assertFalse(map.containsKey(getKey));
2229     assertSame(refreshKey, map.get(refreshKey));
2230 
2231     // let computation complete
2232     letGetFinishSignal.countDown();
2233     getFinishedSignal.await();
2234     checkNothingLogged();
2235 
2236     // asMap view should have been updated
2237     assertEquals(2, cache.size());
2238     assertEquals(getKey + suffix, map.get(getKey));
2239     assertEquals(refreshKey + suffix, map.get(refreshKey));
2240   }
2241 
testInvalidateDuringLoading()2242   public void testInvalidateDuringLoading() throws InterruptedException, ExecutionException {
2243     // computation starts; invalidate() is called on the key being computed, computation finishes
2244     final CountDownLatch computationStarted = new CountDownLatch(2);
2245     final CountDownLatch letGetFinishSignal = new CountDownLatch(1);
2246     final CountDownLatch getFinishedSignal = new CountDownLatch(2);
2247     final String getKey = "get";
2248     final String refreshKey = "refresh";
2249     final String suffix = "Suffix";
2250 
2251     CacheLoader<String, String> computeFunction =
2252         new CacheLoader<String, String>() {
2253           @Override
2254           public String load(String key) throws InterruptedException {
2255             computationStarted.countDown();
2256             letGetFinishSignal.await();
2257             return key + suffix;
2258           }
2259         };
2260 
2261     final LoadingCache<String, String> cache = CacheBuilder.newBuilder().build(computeFunction);
2262     ConcurrentMap<String, String> map = cache.asMap();
2263     map.put(refreshKey, refreshKey);
2264 
2265     new Thread() {
2266       @Override
2267       public void run() {
2268         cache.getUnchecked(getKey);
2269         getFinishedSignal.countDown();
2270       }
2271     }.start();
2272     new Thread() {
2273       @Override
2274       public void run() {
2275         cache.refresh(refreshKey);
2276         getFinishedSignal.countDown();
2277       }
2278     }.start();
2279 
2280     computationStarted.await();
2281     cache.invalidate(getKey);
2282     cache.invalidate(refreshKey);
2283     assertFalse(map.containsKey(getKey));
2284     assertFalse(map.containsKey(refreshKey));
2285 
2286     // let computation complete
2287     letGetFinishSignal.countDown();
2288     getFinishedSignal.await();
2289     checkNothingLogged();
2290 
2291     // results should be visible
2292     assertEquals(2, cache.size());
2293     assertEquals(getKey + suffix, map.get(getKey));
2294     assertEquals(refreshKey + suffix, map.get(refreshKey));
2295     assertEquals(2, cache.size());
2296   }
2297 
testInvalidateAndReloadDuringLoading()2298   public void testInvalidateAndReloadDuringLoading()
2299       throws InterruptedException, ExecutionException {
2300     // computation starts; clear() is called, computation finishes
2301     final CountDownLatch computationStarted = new CountDownLatch(2);
2302     final CountDownLatch letGetFinishSignal = new CountDownLatch(1);
2303     final CountDownLatch getFinishedSignal = new CountDownLatch(4);
2304     final String getKey = "get";
2305     final String refreshKey = "refresh";
2306     final String suffix = "Suffix";
2307 
2308     CacheLoader<String, String> computeFunction =
2309         new CacheLoader<String, String>() {
2310           @Override
2311           public String load(String key) throws InterruptedException {
2312             computationStarted.countDown();
2313             letGetFinishSignal.await();
2314             return key + suffix;
2315           }
2316         };
2317 
2318     final LoadingCache<String, String> cache = CacheBuilder.newBuilder().build(computeFunction);
2319     ConcurrentMap<String, String> map = cache.asMap();
2320     map.put(refreshKey, refreshKey);
2321 
2322     new Thread() {
2323       @Override
2324       public void run() {
2325         cache.getUnchecked(getKey);
2326         getFinishedSignal.countDown();
2327       }
2328     }.start();
2329     new Thread() {
2330       @Override
2331       public void run() {
2332         cache.refresh(refreshKey);
2333         getFinishedSignal.countDown();
2334       }
2335     }.start();
2336 
2337     computationStarted.await();
2338     cache.invalidate(getKey);
2339     cache.invalidate(refreshKey);
2340     assertFalse(map.containsKey(getKey));
2341     assertFalse(map.containsKey(refreshKey));
2342 
2343     // start new computations
2344     new Thread() {
2345       @Override
2346       public void run() {
2347         cache.getUnchecked(getKey);
2348         getFinishedSignal.countDown();
2349       }
2350     }.start();
2351     new Thread() {
2352       @Override
2353       public void run() {
2354         cache.refresh(refreshKey);
2355         getFinishedSignal.countDown();
2356       }
2357     }.start();
2358 
2359     // let computation complete
2360     letGetFinishSignal.countDown();
2361     getFinishedSignal.await();
2362     checkNothingLogged();
2363 
2364     // results should be visible
2365     assertEquals(2, cache.size());
2366     assertEquals(getKey + suffix, map.get(getKey));
2367     assertEquals(refreshKey + suffix, map.get(refreshKey));
2368   }
2369 
testExpandDuringLoading()2370   public void testExpandDuringLoading() throws InterruptedException {
2371     final int count = 3;
2372     final AtomicInteger callCount = new AtomicInteger();
2373     // tells the computing thread when to start computing
2374     final CountDownLatch computeSignal = new CountDownLatch(1);
2375     // tells the main thread when computation is pending
2376     final CountDownLatch secondSignal = new CountDownLatch(1);
2377     // tells the main thread when the second get has started
2378     final CountDownLatch thirdSignal = new CountDownLatch(1);
2379     // tells the main thread when the third get has started
2380     final CountDownLatch fourthSignal = new CountDownLatch(1);
2381     // tells the test when all gets have returned
2382     final CountDownLatch doneSignal = new CountDownLatch(count);
2383 
2384     CacheLoader<String, String> computeFunction =
2385         new CacheLoader<String, String>() {
2386           @Override
2387           public String load(String key) throws InterruptedException {
2388             callCount.incrementAndGet();
2389             secondSignal.countDown();
2390             computeSignal.await();
2391             return key + "foo";
2392           }
2393         };
2394 
2395     final LoadingCache<String, String> cache =
2396         CacheBuilder.newBuilder().weakKeys().build(computeFunction);
2397 
2398     final AtomicReferenceArray<String> result = new AtomicReferenceArray<>(count);
2399 
2400     final String key = "bar";
2401 
2402     // start computing thread
2403     new Thread() {
2404       @Override
2405       public void run() {
2406         result.set(0, cache.getUnchecked(key));
2407         doneSignal.countDown();
2408       }
2409     }.start();
2410 
2411     // wait for computation to start
2412     secondSignal.await();
2413 
2414     // start waiting thread
2415     new Thread() {
2416       @Override
2417       public void run() {
2418         thirdSignal.countDown();
2419         result.set(1, cache.getUnchecked(key));
2420         doneSignal.countDown();
2421       }
2422     }.start();
2423 
2424     // give the second get a chance to run; it is okay for this to be racy
2425     // as the end result should be the same either way
2426     thirdSignal.await();
2427     Thread.yield();
2428 
2429     // Expand!
2430     CacheTesting.forceExpandSegment(cache, key);
2431 
2432     // start another waiting thread
2433     new Thread() {
2434       @Override
2435       public void run() {
2436         fourthSignal.countDown();
2437         result.set(2, cache.getUnchecked(key));
2438         doneSignal.countDown();
2439       }
2440     }.start();
2441 
2442     // give the third get a chance to run; it is okay for this to be racy
2443     // as the end result should be the same either way
2444     fourthSignal.await();
2445     Thread.yield();
2446 
2447     // let computation finish
2448     computeSignal.countDown();
2449     doneSignal.await();
2450 
2451     assertTrue(callCount.get() == 1);
2452     assertEquals("barfoo", result.get(0));
2453     assertEquals("barfoo", result.get(1));
2454     assertEquals("barfoo", result.get(2));
2455     assertEquals("barfoo", cache.getUnchecked(key));
2456   }
2457 
2458   // Test ignored because it is extremely flaky in CI builds
2459 
2460   public void
ignoreTestExpandDuringRefresh()2461       ignoreTestExpandDuringRefresh()
2462       throws InterruptedException, ExecutionException {
2463     final AtomicInteger callCount = new AtomicInteger();
2464     // tells the computing thread when to start computing
2465     final CountDownLatch computeSignal = new CountDownLatch(1);
2466     // tells the main thread when computation is pending
2467     final CountDownLatch secondSignal = new CountDownLatch(1);
2468     // tells the main thread when the second get has started
2469     final CountDownLatch thirdSignal = new CountDownLatch(1);
2470     // tells the main thread when the third get has started
2471     final CountDownLatch fourthSignal = new CountDownLatch(1);
2472     // tells the test when all gets have returned
2473     final CountDownLatch doneSignal = new CountDownLatch(3);
2474     final String suffix = "Suffix";
2475 
2476     CacheLoader<String, String> computeFunction =
2477         new CacheLoader<String, String>() {
2478           @Override
2479           public String load(String key) throws InterruptedException {
2480             callCount.incrementAndGet();
2481             secondSignal.countDown();
2482             computeSignal.await();
2483             return key + suffix;
2484           }
2485         };
2486 
2487     final AtomicReferenceArray<String> result = new AtomicReferenceArray<>(2);
2488 
2489     final LoadingCache<String, String> cache = CacheBuilder.newBuilder().build(computeFunction);
2490     final String key = "bar";
2491     cache.asMap().put(key, key);
2492 
2493     // start computing thread
2494     new Thread() {
2495       @Override
2496       public void run() {
2497         cache.refresh(key);
2498         doneSignal.countDown();
2499       }
2500     }.start();
2501 
2502     // wait for computation to start
2503     secondSignal.await();
2504     checkNothingLogged();
2505 
2506     // start waiting thread
2507     new Thread() {
2508       @Override
2509       public void run() {
2510         thirdSignal.countDown();
2511         result.set(0, cache.getUnchecked(key));
2512         doneSignal.countDown();
2513       }
2514     }.start();
2515 
2516     // give the second get a chance to run; it is okay for this to be racy
2517     // as the end result should be the same either way
2518     thirdSignal.await();
2519     Thread.yield();
2520 
2521     // Expand!
2522     CacheTesting.forceExpandSegment(cache, key);
2523 
2524     // start another waiting thread
2525     new Thread() {
2526       @Override
2527       public void run() {
2528         fourthSignal.countDown();
2529         result.set(1, cache.getUnchecked(key));
2530         doneSignal.countDown();
2531       }
2532     }.start();
2533 
2534     // give the third get a chance to run; it is okay for this to be racy
2535     // as the end result should be the same either way
2536     fourthSignal.await();
2537     Thread.yield();
2538 
2539     // let computation finish
2540     computeSignal.countDown();
2541     doneSignal.await();
2542 
2543     assertTrue(callCount.get() == 1);
2544     assertEquals(key, result.get(0));
2545     assertEquals(key, result.get(1));
2546     assertEquals(key + suffix, cache.getUnchecked(key));
2547   }
2548 
throwing(final Exception exception)2549   static <T> Callable<T> throwing(final Exception exception) {
2550     return new Callable<T>() {
2551       @Override
2552       public T call() throws Exception {
2553         throw exception;
2554       }
2555     };
2556   }
2557 }
2558