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