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.LocalCache.Strength.STRONG;
181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport static com.google.common.collect.Maps.immutableEntry;
193ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffinimport static com.google.common.truth.Truth.assertThat;
201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.base.Function;
221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.cache.LocalCache.Strength;
231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.cache.TestingRemovalListeners.CountingRemovalListener;
241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.collect.ImmutableSet;
251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.collect.Iterables;
261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
277dd252788645e940eada959bdde927426e2531c9Paul Duffinimport junit.framework.TestCase;
281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
290888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport java.lang.ref.WeakReference;
300888a09821a98ac0680fad765217302858e70fa4Paul Duffin
311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert/**
321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Tests of basic {@link LoadingCache} operations with all possible combinations of key & value
331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * strengths.
341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @author mike nonemacher
361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */
371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertpublic class CacheReferencesTest extends TestCase {
381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private static final CacheLoader<Key,String> KEY_TO_STRING_LOADER =
401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      new CacheLoader<Key, String>() {
411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        @Override public String load(Key key) {
421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          return key.toString();
431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        }
441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      };
451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private CacheBuilderFactory factoryWithAllKeyStrengths() {
471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    return new CacheBuilderFactory()
481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        .withKeyStrengths(ImmutableSet.of(STRONG, Strength.WEAK))
491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        .withValueStrengths(ImmutableSet.of(STRONG, Strength.WEAK, Strength.SOFT));
501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private Iterable<LoadingCache<Key, String>> caches() {
531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    CacheBuilderFactory factory = factoryWithAllKeyStrengths();
541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    return Iterables.transform(factory.buildAllPermutations(),
551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        new Function<CacheBuilder<Object, Object>, LoadingCache<Key, String>>() {
561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          @Override public LoadingCache<Key, String> apply(CacheBuilder<Object, Object> builder) {
571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert            return builder.build(KEY_TO_STRING_LOADER);
581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          }
591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        });
601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testContainsKeyAndValue() {
631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (LoadingCache<Key, String> cache : caches()) {
641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      // maintain strong refs so these won't be collected, regardless of cache's key/value strength
651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      Key key = new Key(1);
661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      String value = key.toString();
671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertSame(value, cache.getUnchecked(key));
681d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertTrue(cache.asMap().containsKey(key));
691d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertTrue(cache.asMap().containsValue(value));
701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals(1, cache.size());
711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testClear() {
751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (LoadingCache<Key, String> cache : caches()) {
761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      Key key = new Key(1);
771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      String value = key.toString();
781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertSame(value, cache.getUnchecked(key));
791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertFalse(cache.asMap().isEmpty());
801d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      cache.invalidateAll();
811d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals(0, cache.size());
821d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertTrue(cache.asMap().isEmpty());
831d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertFalse(cache.asMap().containsKey(key));
841d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertFalse(cache.asMap().containsValue(value));
851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
861d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
871d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
881d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testKeySetEntrySetValues() {
891d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (LoadingCache<Key, String> cache : caches()) {
901d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      Key key1 = new Key(1);
911d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      String value1 = key1.toString();
921d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      Key key2 = new Key(2);
931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      String value2 = key2.toString();
941d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertSame(value1, cache.getUnchecked(key1));
951d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertSame(value2, cache.getUnchecked(key2));
961d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals(ImmutableSet.of(key1, key2), cache.asMap().keySet());
973ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffin      assertThat(cache.asMap().values()).has().exactly(value1, value2);
981d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals(ImmutableSet.of(immutableEntry(key1, value1), immutableEntry(key2, value2)),
991d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          cache.asMap().entrySet());
1001d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1011d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1021d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1031d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testInvalidate() {
1041d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (LoadingCache<Key, String> cache : caches()) {
1051d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      Key key1 = new Key(1);
1061d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      String value1 = key1.toString();
1071d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      Key key2 = new Key(2);
1081d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      String value2 = key2.toString();
1091d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertSame(value1, cache.getUnchecked(key1));
1101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertSame(value2, cache.getUnchecked(key2));
1111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      cache.invalidate(key1);
1121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertFalse(cache.asMap().containsKey(key1));
1131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertTrue(cache.asMap().containsKey(key2));
1141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals(1, cache.size());
1151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals(ImmutableSet.of(key2), cache.asMap().keySet());
1163ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffin      assertThat(cache.asMap().values()).has().item(value2);
1171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals(ImmutableSet.of(immutableEntry(key2, value2)), cache.asMap().entrySet());
1181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1210888a09821a98ac0680fad765217302858e70fa4Paul Duffin  // fails in Maven with 64-bit JDK: http://code.google.com/p/guava-libraries/issues/detail?id=1568
1221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private void assertCleanup(LoadingCache<Integer, String> cache,
1241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      CountingRemovalListener<Integer, String> removalListener) {
1251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // initialSize will most likely be 2, but it's possible for the GC to have already run, so we'll
1271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // observe a size of 1
1281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    long initialSize = cache.size();
1291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertTrue(initialSize == 1 || initialSize == 2);
1301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // wait up to 5s
1321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    byte[] filler = new byte[1024];
1331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (int i = 0; i < 500; i++) {
1341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      System.gc();
1351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      CacheTesting.drainReferenceQueues(cache);
1371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      if (cache.size() == 1) {
1381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        break;
1391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      }
1401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      try {
1411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        Thread.sleep(10);
1421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      } catch (InterruptedException e) { /* ignore */}
1431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      try {
1441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        // Fill up heap so soft references get cleared.
1451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        filler = new byte[Math.max(filler.length, filler.length * 2)];
1461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      } catch (OutOfMemoryError e) {}
1471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    CacheTesting.processPendingNotifications(cache);
1501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(1, cache.size());
1511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(1, removalListener.getCount());
1521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  // A simple type whose .toString() will return the same value each time, but without maintaining
1551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  // a strong reference to that value.
1561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  static class Key {
1571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    private final int value;
1581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    private WeakReference<String> toString;
1591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    Key(int value) {
1611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      this.value = value;
1621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override public synchronized String toString() {
1651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      String s;
1661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      if (toString != null) {
1671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        s = toString.get();
1681d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        if (s != null) {
1691d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          return s;
1701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        }
1711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      }
1721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      s = Integer.toString(value);
1731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      toString = new WeakReference<String>(s);
1741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      return s;
1751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert}
178