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