1 /* 2 * Copyright (C) 2011 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 15 package com.google.common.cache; 16 17 import static com.google.common.cache.LocalCache.Strength.STRONG; 18 import static com.google.common.collect.Maps.immutableEntry; 19 import static com.google.common.truth.Truth.assertThat; 20 21 import com.google.common.base.Function; 22 import com.google.common.cache.LocalCache.Strength; 23 import com.google.common.cache.TestingRemovalListeners.CountingRemovalListener; 24 import com.google.common.collect.ImmutableSet; 25 import com.google.common.collect.Iterables; 26 import java.lang.ref.WeakReference; 27 import junit.framework.TestCase; 28 29 /** 30 * Tests of basic {@link LoadingCache} operations with all possible combinations of key & value 31 * strengths. 32 * 33 * @author mike nonemacher 34 */ 35 public class CacheReferencesTest extends TestCase { 36 37 private static final CacheLoader<Key, String> KEY_TO_STRING_LOADER = 38 new CacheLoader<Key, String>() { 39 @Override 40 public String load(Key key) { 41 return key.toString(); 42 } 43 }; 44 factoryWithAllKeyStrengths()45 private CacheBuilderFactory factoryWithAllKeyStrengths() { 46 return new CacheBuilderFactory() 47 .withKeyStrengths(ImmutableSet.of(STRONG, Strength.WEAK)) 48 .withValueStrengths(ImmutableSet.of(STRONG, Strength.WEAK, Strength.SOFT)); 49 } 50 caches()51 private Iterable<LoadingCache<Key, String>> caches() { 52 CacheBuilderFactory factory = factoryWithAllKeyStrengths(); 53 return Iterables.transform( 54 factory.buildAllPermutations(), 55 new Function<CacheBuilder<Object, Object>, LoadingCache<Key, String>>() { 56 @Override 57 public LoadingCache<Key, String> apply(CacheBuilder<Object, Object> builder) { 58 return builder.build(KEY_TO_STRING_LOADER); 59 } 60 }); 61 } 62 63 public void testContainsKeyAndValue() { 64 for (LoadingCache<Key, String> cache : caches()) { 65 // maintain strong refs so these won't be collected, regardless of cache's key/value strength 66 Key key = new Key(1); 67 String value = key.toString(); 68 assertSame(value, cache.getUnchecked(key)); 69 assertTrue(cache.asMap().containsKey(key)); 70 assertTrue(cache.asMap().containsValue(value)); 71 assertEquals(1, cache.size()); 72 } 73 } 74 75 public void testClear() { 76 for (LoadingCache<Key, String> cache : caches()) { 77 Key key = new Key(1); 78 String value = key.toString(); 79 assertSame(value, cache.getUnchecked(key)); 80 assertFalse(cache.asMap().isEmpty()); 81 cache.invalidateAll(); 82 assertEquals(0, cache.size()); 83 assertTrue(cache.asMap().isEmpty()); 84 assertFalse(cache.asMap().containsKey(key)); 85 assertFalse(cache.asMap().containsValue(value)); 86 } 87 } 88 89 public void testKeySetEntrySetValues() { 90 for (LoadingCache<Key, String> cache : caches()) { 91 Key key1 = new Key(1); 92 String value1 = key1.toString(); 93 Key key2 = new Key(2); 94 String value2 = key2.toString(); 95 assertSame(value1, cache.getUnchecked(key1)); 96 assertSame(value2, cache.getUnchecked(key2)); 97 assertEquals(ImmutableSet.of(key1, key2), cache.asMap().keySet()); 98 assertThat(cache.asMap().values()).containsExactly(value1, value2); 99 assertEquals( 100 ImmutableSet.of(immutableEntry(key1, value1), immutableEntry(key2, value2)), 101 cache.asMap().entrySet()); 102 } 103 } 104 105 public void testInvalidate() { 106 for (LoadingCache<Key, String> cache : caches()) { 107 Key key1 = new Key(1); 108 String value1 = key1.toString(); 109 Key key2 = new Key(2); 110 String value2 = key2.toString(); 111 assertSame(value1, cache.getUnchecked(key1)); 112 assertSame(value2, cache.getUnchecked(key2)); 113 cache.invalidate(key1); 114 assertFalse(cache.asMap().containsKey(key1)); 115 assertTrue(cache.asMap().containsKey(key2)); 116 assertEquals(1, cache.size()); 117 assertEquals(ImmutableSet.of(key2), cache.asMap().keySet()); 118 assertThat(cache.asMap().values()).contains(value2); 119 assertEquals(ImmutableSet.of(immutableEntry(key2, value2)), cache.asMap().entrySet()); 120 } 121 } 122 123 // fails in Maven with 64-bit JDK: http://code.google.com/p/guava-libraries/issues/detail?id=1568 124 125 private void assertCleanup( 126 LoadingCache<Integer, String> cache, 127 CountingRemovalListener<Integer, String> removalListener) { 128 129 // initialSize will most likely be 2, but it's possible for the GC to have already run, so we'll 130 // observe a size of 1 131 long initialSize = cache.size(); 132 assertTrue(initialSize == 1 || initialSize == 2); 133 134 // wait up to 5s 135 byte[] filler = new byte[1024]; 136 for (int i = 0; i < 500; i++) { 137 System.gc(); 138 139 CacheTesting.drainReferenceQueues(cache); 140 if (cache.size() == 1) { 141 break; 142 } 143 try { 144 Thread.sleep(10); 145 } catch (InterruptedException e) { 146 /* ignore */ 147 } 148 try { 149 // Fill up heap so soft references get cleared. 150 filler = new byte[Math.max(filler.length, filler.length * 2)]; 151 } catch (OutOfMemoryError e) { 152 } 153 } 154 155 CacheTesting.processPendingNotifications(cache); 156 assertEquals(1, cache.size()); 157 assertEquals(1, removalListener.getCount()); 158 } 159 160 // A simple type whose .toString() will return the same value each time, but without maintaining 161 // a strong reference to that value. 162 static class Key { 163 private final int value; 164 private WeakReference<String> toString; 165 166 Key(int value) { 167 this.value = value; 168 } 169 170 @Override 171 public synchronized String toString() { 172 String s; 173 if (toString != null) { 174 s = toString.get(); 175 if (s != null) { 176 return s; 177 } 178 } 179 s = Integer.toString(value); 180 toString = new WeakReference<>(s); 181 return s; 182 } 183 } 184 } 185