11d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert/*
21d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Copyright (C) 2011 The Guava Authors
31d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
41d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
51d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * in compliance with the License. You may obtain a copy of the License at
61d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
71d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * http://www.apache.org/licenses/LICENSE-2.0
81d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
91d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Unless required by applicable law or agreed to in writing, software distributed under the License
101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * or implied. See the License for the specific language governing permissions and limitations under
121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * the License.
131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */
141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertpackage com.google.common.cache;
161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport static com.google.common.cache.TestingCacheLoaders.identityLoader;
181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport static com.google.common.cache.TestingRemovalListeners.countingRemovalListener;
191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport static java.util.Arrays.asList;
201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport static java.util.concurrent.TimeUnit.MILLISECONDS;
217dd252788645e940eada959bdde927426e2531c9Paul Duffinimport static org.truth0.Truth.ASSERT;
221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.cache.TestingCacheLoaders.IdentityLoader;
241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.cache.TestingRemovalListeners.CountingRemovalListener;
251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.collect.Iterators;
261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.testing.FakeTicker;
271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.util.concurrent.Callables;
281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
290888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport junit.framework.TestCase;
300888a09821a98ac0680fad765217302858e70fa4Paul Duffin
311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.List;
321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.Set;
331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.concurrent.ExecutionException;
341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.concurrent.atomic.AtomicInteger;
351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert/**
371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Tests relating to cache expiration: make sure entries expire at the right times, make sure
381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * expired entries don't show up, etc.
391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @author mike nonemacher
411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */
420888a09821a98ac0680fad765217302858e70fa4Paul Duffin@SuppressWarnings("deprecation") // tests of deprecated method
431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertpublic class CacheExpirationTest extends TestCase {
441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private static final long EXPIRING_TIME = 1000;
461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private static final int VALUE_PREFIX = 12345;
471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private static final String KEY_PREFIX = "key prefix:";
481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testExpiration_expireAfterWrite() {
501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    FakeTicker ticker = new FakeTicker();
511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    CountingRemovalListener<String, Integer> removalListener = countingRemovalListener();
521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    WatchedCreatorLoader loader = new WatchedCreatorLoader();
531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
540888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .expireAfterWrite(EXPIRING_TIME, MILLISECONDS)
550888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .removalListener(removalListener)
560888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .ticker(ticker)
570888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .build(loader);
581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    checkExpiration(cache, loader, ticker, removalListener);
591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testExpiration_expireAfterAccess() {
621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    FakeTicker ticker = new FakeTicker();
631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    CountingRemovalListener<String, Integer> removalListener = countingRemovalListener();
641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    WatchedCreatorLoader loader = new WatchedCreatorLoader();
651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
660888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .expireAfterAccess(EXPIRING_TIME, MILLISECONDS)
670888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .removalListener(removalListener)
680888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .ticker(ticker)
690888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .build(loader);
701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    checkExpiration(cache, loader, ticker, removalListener);
711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private void checkExpiration(LoadingCache<String, Integer> cache, WatchedCreatorLoader loader,
741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      FakeTicker ticker, CountingRemovalListener<String, Integer> removalListener) {
751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (int i = 0; i < 10; i++) {
771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals(Integer.valueOf(VALUE_PREFIX + i), cache.getUnchecked(KEY_PREFIX + i));
781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
801d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (int i = 0; i < 10; i++) {
811d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      loader.reset();
821d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals(Integer.valueOf(VALUE_PREFIX + i), cache.getUnchecked(KEY_PREFIX + i));
831d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertFalse("Creator should not have been called @#" + i, loader.wasCalled());
841d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
860888a09821a98ac0680fad765217302858e70fa4Paul Duffin    CacheTesting.expireEntries((LoadingCache<?, ?>) cache, EXPIRING_TIME, ticker);
871d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
881d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals("Map must be empty by now", 0, cache.size());
890888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertEquals("Eviction notifications must be received", 10,
900888a09821a98ac0680fad765217302858e70fa4Paul Duffin        removalListener.getCount());
911d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
920888a09821a98ac0680fad765217302858e70fa4Paul Duffin    CacheTesting.expireEntries((LoadingCache<?, ?>) cache, EXPIRING_TIME, ticker);
931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // ensure that no new notifications are sent
940888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertEquals("Eviction notifications must be received", 10,
950888a09821a98ac0680fad765217302858e70fa4Paul Duffin        removalListener.getCount());
961d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
971d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
981d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testExpiringGet_expireAfterWrite() {
991d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    FakeTicker ticker = new FakeTicker();
1001d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    CountingRemovalListener<String, Integer> removalListener = countingRemovalListener();
1011d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    WatchedCreatorLoader loader = new WatchedCreatorLoader();
1021d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
1030888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .expireAfterWrite(EXPIRING_TIME, MILLISECONDS)
1040888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .removalListener(removalListener)
1050888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .ticker(ticker)
1060888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .build(loader);
1071d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    runExpirationTest(cache, loader, ticker, removalListener);
1081d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1091d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testExpiringGet_expireAfterAccess() {
1111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    FakeTicker ticker = new FakeTicker();
1121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    CountingRemovalListener<String, Integer> removalListener = countingRemovalListener();
1131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    WatchedCreatorLoader loader = new WatchedCreatorLoader();
1141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
1150888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .expireAfterAccess(EXPIRING_TIME, MILLISECONDS)
1160888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .removalListener(removalListener)
1170888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .ticker(ticker)
1180888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .build(loader);
1191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    runExpirationTest(cache, loader, ticker, removalListener);
1201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private void runExpirationTest(LoadingCache<String, Integer> cache, WatchedCreatorLoader loader,
1231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      FakeTicker ticker, CountingRemovalListener<String, Integer> removalListener) {
1241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (int i = 0; i < 10; i++) {
1261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals(Integer.valueOf(VALUE_PREFIX + i), cache.getUnchecked(KEY_PREFIX + i));
1271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (int i = 0; i < 10; i++) {
1301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      loader.reset();
1311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals(Integer.valueOf(VALUE_PREFIX + i), cache.getUnchecked(KEY_PREFIX + i));
1321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertFalse("Loader should NOT have been called @#" + i, loader.wasCalled());
1331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // wait for entries to expire, but don't call expireEntries
1361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ticker.advance(EXPIRING_TIME * 10, MILLISECONDS);
1371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // add a single unexpired entry
1391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    cache.getUnchecked(KEY_PREFIX + 11);
1401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // collections views shouldn't expose expired entries
1421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(1, Iterators.size(cache.asMap().entrySet().iterator()));
1431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(1, Iterators.size(cache.asMap().keySet().iterator()));
1441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(1, Iterators.size(cache.asMap().values().iterator()));
1451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1460888a09821a98ac0680fad765217302858e70fa4Paul Duffin    CacheTesting.expireEntries((LoadingCache<?, ?>) cache, EXPIRING_TIME, ticker);
1471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (int i = 0; i < 11; i++) {
1491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertFalse(cache.asMap().containsKey(KEY_PREFIX + i));
1501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(11, removalListener.getCount());
1521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (int i = 0; i < 10; i++) {
1541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertFalse(cache.asMap().containsKey(KEY_PREFIX + i));
1551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      loader.reset();
1561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals(Integer.valueOf(VALUE_PREFIX + i), cache.getUnchecked(KEY_PREFIX + i));
1571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertTrue("Creator should have been called @#" + i, loader.wasCalled());
1581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // expire new values we just created
1610888a09821a98ac0680fad765217302858e70fa4Paul Duffin    CacheTesting.expireEntries((LoadingCache<?, ?>) cache, EXPIRING_TIME, ticker);
1620888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertEquals("Eviction notifications must be received", 21,
1630888a09821a98ac0680fad765217302858e70fa4Paul Duffin        removalListener.getCount());
1641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1650888a09821a98ac0680fad765217302858e70fa4Paul Duffin    CacheTesting.expireEntries((LoadingCache<?, ?>) cache, EXPIRING_TIME, ticker);
1661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // ensure that no new notifications are sent
1670888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertEquals("Eviction notifications must be received", 21,
1680888a09821a98ac0680fad765217302858e70fa4Paul Duffin        removalListener.getCount());
1691d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testRemovalListener_expireAfterWrite() {
1721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    FakeTicker ticker = new FakeTicker();
1731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    final AtomicInteger evictionCount = new AtomicInteger();
1741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    final AtomicInteger applyCount = new AtomicInteger();
1751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    final AtomicInteger totalSum = new AtomicInteger();
1761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1770888a09821a98ac0680fad765217302858e70fa4Paul Duffin    RemovalListener<Integer, AtomicInteger> removalListener =
1780888a09821a98ac0680fad765217302858e70fa4Paul Duffin        new RemovalListener<Integer, AtomicInteger>() {
1790888a09821a98ac0680fad765217302858e70fa4Paul Duffin          @Override
1800888a09821a98ac0680fad765217302858e70fa4Paul Duffin          public void onRemoval(RemovalNotification<Integer, AtomicInteger> notification) {
1810888a09821a98ac0680fad765217302858e70fa4Paul Duffin            if (notification.wasEvicted()) {
1820888a09821a98ac0680fad765217302858e70fa4Paul Duffin              evictionCount.incrementAndGet();
1830888a09821a98ac0680fad765217302858e70fa4Paul Duffin              totalSum.addAndGet(notification.getValue().get());
1840888a09821a98ac0680fad765217302858e70fa4Paul Duffin            }
1850888a09821a98ac0680fad765217302858e70fa4Paul Duffin          }
1860888a09821a98ac0680fad765217302858e70fa4Paul Duffin        };
1871d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1881d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    CacheLoader<Integer, AtomicInteger> loader = new CacheLoader<Integer, AtomicInteger>() {
1890888a09821a98ac0680fad765217302858e70fa4Paul Duffin      @Override public AtomicInteger load(Integer key) {
1901d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        applyCount.incrementAndGet();
1911d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        return new AtomicInteger();
1921d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      }
1931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    };
1941d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1951d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    LoadingCache<Integer, AtomicInteger> cache = CacheBuilder.newBuilder()
1960888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .removalListener(removalListener)
1970888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .expireAfterWrite(10, MILLISECONDS)
1980888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .ticker(ticker)
1991d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        .build(loader);
2001d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2011d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // Increment 100 times
2021d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (int i = 0; i < 100; ++i) {
2031d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      cache.getUnchecked(10).incrementAndGet();
2041d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      ticker.advance(1, MILLISECONDS);
2051d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
2061d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2071d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(evictionCount.get() + 1, applyCount.get());
2081d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    int remaining = cache.getUnchecked(10).get();
2091d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(100, totalSum.get() + remaining);
2101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
2111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testRemovalScheduler_expireAfterWrite() {
2131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    FakeTicker ticker = new FakeTicker();
2141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    CountingRemovalListener<String, Integer> removalListener = countingRemovalListener();
2151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    WatchedCreatorLoader loader = new WatchedCreatorLoader();
2161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
2170888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .expireAfterWrite(EXPIRING_TIME, MILLISECONDS)
2180888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .removalListener(removalListener)
2190888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .ticker(ticker)
2200888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .build(loader);
2211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    runRemovalScheduler(cache, removalListener, loader, ticker, KEY_PREFIX, EXPIRING_TIME);
2221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
2231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testRemovalScheduler_expireAfterAccess() {
2251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    FakeTicker ticker = new FakeTicker();
2261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    CountingRemovalListener<String, Integer> removalListener = countingRemovalListener();
2271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    WatchedCreatorLoader loader = new WatchedCreatorLoader();
2281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
2290888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .expireAfterAccess(EXPIRING_TIME, MILLISECONDS)
2300888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .removalListener(removalListener)
2310888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .ticker(ticker)
2320888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .build(loader);
2331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    runRemovalScheduler(cache, removalListener, loader, ticker, KEY_PREFIX, EXPIRING_TIME);
2341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
2351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testRemovalScheduler_expireAfterBoth() {
2371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    FakeTicker ticker = new FakeTicker();
2381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    CountingRemovalListener<String, Integer> removalListener = countingRemovalListener();
2391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    WatchedCreatorLoader loader = new WatchedCreatorLoader();
2401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
2411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        .expireAfterAccess(EXPIRING_TIME, MILLISECONDS)
2420888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .expireAfterWrite(EXPIRING_TIME, MILLISECONDS)
2430888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .removalListener(removalListener)
2440888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .ticker(ticker)
2450888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .build(loader);
2461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    runRemovalScheduler(cache, removalListener, loader, ticker, KEY_PREFIX, EXPIRING_TIME);
2471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
2481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testExpirationOrder_access() {
2501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // test lru within a single segment
2511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    FakeTicker ticker = new FakeTicker();
2521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    IdentityLoader<Integer> loader = identityLoader();
2530888a09821a98ac0680fad765217302858e70fa4Paul Duffin    LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder()
2540888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .concurrencyLevel(1)
2550888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .expireAfterAccess(11, MILLISECONDS)
2560888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .ticker(ticker)
2570888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .build(loader);
2581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (int i = 0; i < 10; i++) {
2591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      cache.getUnchecked(i);
2601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      ticker.advance(1, MILLISECONDS);
2611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
2621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    Set<Integer> keySet = cache.asMap().keySet();
2630888a09821a98ac0680fad765217302858e70fa4Paul Duffin    ASSERT.that(keySet).has().exactly(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
2641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // 0 expires
2661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ticker.advance(1, MILLISECONDS);
2670888a09821a98ac0680fad765217302858e70fa4Paul Duffin    ASSERT.that(keySet).has().exactly(1, 2, 3, 4, 5, 6, 7, 8, 9);
2681d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2691d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // reorder
2701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    getAll(cache, asList(0, 1, 2));
2711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    CacheTesting.drainRecencyQueues(cache);
2721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ticker.advance(2, MILLISECONDS);
2730888a09821a98ac0680fad765217302858e70fa4Paul Duffin    ASSERT.that(keySet).has().exactly(3, 4, 5, 6, 7, 8, 9, 0, 1, 2);
2741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // 3 expires
2761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ticker.advance(1, MILLISECONDS);
2770888a09821a98ac0680fad765217302858e70fa4Paul Duffin    ASSERT.that(keySet).has().exactly(4, 5, 6, 7, 8, 9, 0, 1, 2);
2781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // reorder
2801d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    getAll(cache, asList(5, 7, 9));
2811d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    CacheTesting.drainRecencyQueues(cache);
2820888a09821a98ac0680fad765217302858e70fa4Paul Duffin    ASSERT.that(keySet).has().exactly(4, 6, 8, 0, 1, 2, 5, 7, 9);
2831d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2841d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // 4 expires
2851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ticker.advance(1, MILLISECONDS);
2860888a09821a98ac0680fad765217302858e70fa4Paul Duffin    ASSERT.that(keySet).has().exactly(6, 8, 0, 1, 2, 5, 7, 9);
2871d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ticker.advance(1, MILLISECONDS);
2880888a09821a98ac0680fad765217302858e70fa4Paul Duffin    ASSERT.that(keySet).has().exactly(6, 8, 0, 1, 2, 5, 7, 9);
2891d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2901d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // 6 expires
2911d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ticker.advance(1, MILLISECONDS);
2920888a09821a98ac0680fad765217302858e70fa4Paul Duffin    ASSERT.that(keySet).has().exactly(8, 0, 1, 2, 5, 7, 9);
2931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ticker.advance(1, MILLISECONDS);
2940888a09821a98ac0680fad765217302858e70fa4Paul Duffin    ASSERT.that(keySet).has().exactly(8, 0, 1, 2, 5, 7, 9);
2951d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2961d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // 8 expires
2971d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ticker.advance(1, MILLISECONDS);
2980888a09821a98ac0680fad765217302858e70fa4Paul Duffin    ASSERT.that(keySet).has().exactly(0, 1, 2, 5, 7, 9);
2991d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
3001d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3011d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testExpirationOrder_write() throws ExecutionException {
3021d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // test lru within a single segment
3031d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    FakeTicker ticker = new FakeTicker();
3041d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    IdentityLoader<Integer> loader = identityLoader();
3050888a09821a98ac0680fad765217302858e70fa4Paul Duffin    LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder()
3060888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .concurrencyLevel(1)
3070888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .expireAfterWrite(11, MILLISECONDS)
3080888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .ticker(ticker)
3090888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .build(loader);
3101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (int i = 0; i < 10; i++) {
3111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      cache.getUnchecked(i);
3121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      ticker.advance(1, MILLISECONDS);
3131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
3141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    Set<Integer> keySet = cache.asMap().keySet();
3150888a09821a98ac0680fad765217302858e70fa4Paul Duffin    ASSERT.that(keySet).has().exactly(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
3161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // 0 expires
3181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ticker.advance(1, MILLISECONDS);
3190888a09821a98ac0680fad765217302858e70fa4Paul Duffin    ASSERT.that(keySet).has().exactly(1, 2, 3, 4, 5, 6, 7, 8, 9);
3201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // get doesn't stop 1 from expiring
3221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    getAll(cache, asList(0, 1, 2));
3231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    CacheTesting.drainRecencyQueues(cache);
3241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ticker.advance(1, MILLISECONDS);
3250888a09821a98ac0680fad765217302858e70fa4Paul Duffin    ASSERT.that(keySet).has().exactly(2, 3, 4, 5, 6, 7, 8, 9, 0);
3261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // get(K, Callable) doesn't stop 2 from expiring
3281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    cache.get(2, Callables.returning(-2));
3291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    CacheTesting.drainRecencyQueues(cache);
3301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ticker.advance(1, MILLISECONDS);
3310888a09821a98ac0680fad765217302858e70fa4Paul Duffin    ASSERT.that(keySet).has().exactly(3, 4, 5, 6, 7, 8, 9, 0);
3321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // asMap.put saves 3
3341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    cache.asMap().put(3, -3);
3351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ticker.advance(1, MILLISECONDS);
3360888a09821a98ac0680fad765217302858e70fa4Paul Duffin    ASSERT.that(keySet).has().exactly(4, 5, 6, 7, 8, 9, 0, 3);
3371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // asMap.replace saves 4
3391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    cache.asMap().replace(4, -4);
3401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ticker.advance(1, MILLISECONDS);
3410888a09821a98ac0680fad765217302858e70fa4Paul Duffin    ASSERT.that(keySet).has().exactly(5, 6, 7, 8, 9, 0, 3, 4);
3421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // 5 expires
3441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ticker.advance(1, MILLISECONDS);
3450888a09821a98ac0680fad765217302858e70fa4Paul Duffin    ASSERT.that(keySet).has().exactly(6, 7, 8, 9, 0, 3, 4);
3461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
3471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testExpirationOrder_writeAccess() throws ExecutionException {
3491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // test lru within a single segment
3501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    FakeTicker ticker = new FakeTicker();
3511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    IdentityLoader<Integer> loader = identityLoader();
3520888a09821a98ac0680fad765217302858e70fa4Paul Duffin    LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder()
3530888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .concurrencyLevel(1)
3540888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .expireAfterWrite(5, MILLISECONDS)
3550888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .expireAfterAccess(3, MILLISECONDS)
3560888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .ticker(ticker)
3571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        .build(loader);
3581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (int i = 0; i < 5; i++) {
3591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      cache.getUnchecked(i);
3601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
3611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ticker.advance(1, MILLISECONDS);
3621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (int i = 5; i < 10; i++) {
3631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      cache.getUnchecked(i);
3641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
3651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ticker.advance(1, MILLISECONDS);
3661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    Set<Integer> keySet = cache.asMap().keySet();
3680888a09821a98ac0680fad765217302858e70fa4Paul Duffin    ASSERT.that(keySet).has().exactly(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
3691d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // get saves 1, 3; 0, 2, 4 expire
3711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    getAll(cache, asList(1, 3));
3721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    CacheTesting.drainRecencyQueues(cache);
3731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ticker.advance(1, MILLISECONDS);
3740888a09821a98ac0680fad765217302858e70fa4Paul Duffin    ASSERT.that(keySet).has().exactly(5, 6, 7, 8, 9, 1, 3);
3751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // get saves 6, 8; 5, 7, 9 expire
3771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    getAll(cache, asList(6, 8));
3781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    CacheTesting.drainRecencyQueues(cache);
3791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ticker.advance(1, MILLISECONDS);
3800888a09821a98ac0680fad765217302858e70fa4Paul Duffin    ASSERT.that(keySet).has().exactly(1, 3, 6, 8);
3811d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3821d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // get fails to save 1, put saves 3
3831d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    cache.asMap().put(3, -3);
3841d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    getAll(cache, asList(1));
3851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    CacheTesting.drainRecencyQueues(cache);
3861d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ticker.advance(1, MILLISECONDS);
3870888a09821a98ac0680fad765217302858e70fa4Paul Duffin    ASSERT.that(keySet).has().exactly(6, 8, 3);
3881d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3891d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // get(K, Callable) fails to save 8, replace saves 6
3901d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    cache.asMap().replace(6, -6);
3911d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    cache.get(8, Callables.returning(-8));
3921d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    CacheTesting.drainRecencyQueues(cache);
3931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ticker.advance(1, MILLISECONDS);
3940888a09821a98ac0680fad765217302858e70fa4Paul Duffin    ASSERT.that(keySet).has().exactly(3, 6);
3951d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
3961d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3971d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private void runRemovalScheduler(LoadingCache<String, Integer> cache,
3980888a09821a98ac0680fad765217302858e70fa4Paul Duffin      CountingRemovalListener<String, Integer> removalListener,
3990888a09821a98ac0680fad765217302858e70fa4Paul Duffin      WatchedCreatorLoader loader,
4001d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      FakeTicker ticker, String keyPrefix, long ttl) {
4011d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4021d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    int shift1 = 10 + VALUE_PREFIX;
4031d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    loader.setValuePrefix(shift1);
4041d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // fill with initial data
4051d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (int i = 0; i < 10; i++) {
4061d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals(Integer.valueOf(i + shift1), cache.getUnchecked(keyPrefix + i));
4071d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
4081d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(10, CacheTesting.expirationQueueSize(cache));
4091d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(0, removalListener.getCount());
4101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // wait, so that entries have just 10 ms to live
4121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ticker.advance(ttl * 2 / 3, MILLISECONDS);
4131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(10, CacheTesting.expirationQueueSize(cache));
4151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(0, removalListener.getCount());
4161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    int shift2 = shift1 + 10;
4181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    loader.setValuePrefix(shift2);
4191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // fill with new data - has to live for 20 ms more
4201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (int i = 0; i < 10; i++) {
4211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      cache.invalidate(keyPrefix + i);
4220888a09821a98ac0680fad765217302858e70fa4Paul Duffin      assertEquals("key: " + keyPrefix + i,
4230888a09821a98ac0680fad765217302858e70fa4Paul Duffin          Integer.valueOf(i + shift2), cache.getUnchecked(keyPrefix + i));
4241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
4251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(10, CacheTesting.expirationQueueSize(cache));
4260888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertEquals(10, removalListener.getCount());  // these are the invalidated ones
4271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // old timeouts must expire after this wait
4291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ticker.advance(ttl * 2 / 3, MILLISECONDS);
4301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(10, CacheTesting.expirationQueueSize(cache));
4321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(10, removalListener.getCount());
4331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // check that new values are still there - they still have 10 ms to live
4351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (int i = 0; i < 10; i++) {
4361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      loader.reset();
4371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals(Integer.valueOf(i + shift2), cache.getUnchecked(keyPrefix + i));
4381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertFalse("Creator should NOT have been called @#" + i, loader.wasCalled());
4391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
4401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(10, removalListener.getCount());
4411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
4421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private void getAll(LoadingCache<Integer, Integer> cache, List<Integer> keys) {
4441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (int i : keys) {
4451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      cache.getUnchecked(i);
4461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
4471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
4481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private static class WatchedCreatorLoader extends CacheLoader<String, Integer> {
4501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    boolean wasCalled = false; // must be set in load()
4511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    String keyPrefix = KEY_PREFIX;
4521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    int valuePrefix = VALUE_PREFIX;
4531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4540888a09821a98ac0680fad765217302858e70fa4Paul Duffin    public WatchedCreatorLoader() {
4550888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
4561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    public void reset() {
4581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      wasCalled = false;
4591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
4601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    public boolean wasCalled() {
4621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      return wasCalled;
4631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
4641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    public void setKeyPrefix(String keyPrefix) {
4661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      this.keyPrefix = keyPrefix;
4671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
4681d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4691d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    public void setValuePrefix(int valuePrefix) {
4701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      this.valuePrefix = valuePrefix;
4711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
4721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4730888a09821a98ac0680fad765217302858e70fa4Paul Duffin    @Override public Integer load(String key) {
4741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      wasCalled = true;
4751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      return valuePrefix + Integer.parseInt(key.substring(keyPrefix.length()));
4761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
4771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
4781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert}
479