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 27 import junit.framework.TestCase; 28 29 import java.lang.ref.WeakReference; 30 31 /** 32 * Tests of basic {@link LoadingCache} operations with all possible combinations of key & value 33 * strengths. 34 * 35 * @author mike nonemacher 36 */ 37 public class CacheReferencesTest extends TestCase { 38 39 private static final CacheLoader<Key,String> KEY_TO_STRING_LOADER = 40 new CacheLoader<Key, String>() { 41 @Override public String load(Key key) { 42 return key.toString(); 43 } 44 }; 45 factoryWithAllKeyStrengths()46 private CacheBuilderFactory factoryWithAllKeyStrengths() { 47 return new CacheBuilderFactory() 48 .withKeyStrengths(ImmutableSet.of(STRONG, Strength.WEAK)) 49 .withValueStrengths(ImmutableSet.of(STRONG, Strength.WEAK, Strength.SOFT)); 50 } 51 caches()52 private Iterable<LoadingCache<Key, String>> caches() { 53 CacheBuilderFactory factory = factoryWithAllKeyStrengths(); 54 return Iterables.transform(factory.buildAllPermutations(), 55 new Function<CacheBuilder<Object, Object>, LoadingCache<Key, String>>() { 56 @Override public LoadingCache<Key, String> apply(CacheBuilder<Object, Object> builder) { 57 return builder.build(KEY_TO_STRING_LOADER); 58 } 59 }); 60 } 61 62 public void testContainsKeyAndValue() { 63 for (LoadingCache<Key, String> cache : caches()) { 64 // maintain strong refs so these won't be collected, regardless of cache's key/value strength 65 Key key = new Key(1); 66 String value = key.toString(); 67 assertSame(value, cache.getUnchecked(key)); 68 assertTrue(cache.asMap().containsKey(key)); 69 assertTrue(cache.asMap().containsValue(value)); 70 assertEquals(1, cache.size()); 71 } 72 } 73 74 public void testClear() { 75 for (LoadingCache<Key, String> cache : caches()) { 76 Key key = new Key(1); 77 String value = key.toString(); 78 assertSame(value, cache.getUnchecked(key)); 79 assertFalse(cache.asMap().isEmpty()); 80 cache.invalidateAll(); 81 assertEquals(0, cache.size()); 82 assertTrue(cache.asMap().isEmpty()); 83 assertFalse(cache.asMap().containsKey(key)); 84 assertFalse(cache.asMap().containsValue(value)); 85 } 86 } 87 88 public void testKeySetEntrySetValues() { 89 for (LoadingCache<Key, String> cache : caches()) { 90 Key key1 = new Key(1); 91 String value1 = key1.toString(); 92 Key key2 = new Key(2); 93 String value2 = key2.toString(); 94 assertSame(value1, cache.getUnchecked(key1)); 95 assertSame(value2, cache.getUnchecked(key2)); 96 assertEquals(ImmutableSet.of(key1, key2), cache.asMap().keySet()); 97 assertThat(cache.asMap().values()).has().exactly(value1, value2); 98 assertEquals(ImmutableSet.of(immutableEntry(key1, value1), immutableEntry(key2, value2)), 99 cache.asMap().entrySet()); 100 } 101 } 102 103 public void testInvalidate() { 104 for (LoadingCache<Key, String> cache : caches()) { 105 Key key1 = new Key(1); 106 String value1 = key1.toString(); 107 Key key2 = new Key(2); 108 String value2 = key2.toString(); 109 assertSame(value1, cache.getUnchecked(key1)); 110 assertSame(value2, cache.getUnchecked(key2)); 111 cache.invalidate(key1); 112 assertFalse(cache.asMap().containsKey(key1)); 113 assertTrue(cache.asMap().containsKey(key2)); 114 assertEquals(1, cache.size()); 115 assertEquals(ImmutableSet.of(key2), cache.asMap().keySet()); 116 assertThat(cache.asMap().values()).has().item(value2); 117 assertEquals(ImmutableSet.of(immutableEntry(key2, value2)), cache.asMap().entrySet()); 118 } 119 } 120 121 // fails in Maven with 64-bit JDK: http://code.google.com/p/guava-libraries/issues/detail?id=1568 122 123 private void assertCleanup(LoadingCache<Integer, String> cache, 124 CountingRemovalListener<Integer, String> removalListener) { 125 126 // initialSize will most likely be 2, but it's possible for the GC to have already run, so we'll 127 // observe a size of 1 128 long initialSize = cache.size(); 129 assertTrue(initialSize == 1 || initialSize == 2); 130 131 // wait up to 5s 132 byte[] filler = new byte[1024]; 133 for (int i = 0; i < 500; i++) { 134 System.gc(); 135 136 CacheTesting.drainReferenceQueues(cache); 137 if (cache.size() == 1) { 138 break; 139 } 140 try { 141 Thread.sleep(10); 142 } catch (InterruptedException e) { /* ignore */} 143 try { 144 // Fill up heap so soft references get cleared. 145 filler = new byte[Math.max(filler.length, filler.length * 2)]; 146 } catch (OutOfMemoryError e) {} 147 } 148 149 CacheTesting.processPendingNotifications(cache); 150 assertEquals(1, cache.size()); 151 assertEquals(1, removalListener.getCount()); 152 } 153 154 // A simple type whose .toString() will return the same value each time, but without maintaining 155 // a strong reference to that value. 156 static class Key { 157 private final int value; 158 private WeakReference<String> toString; 159 160 Key(int value) { 161 this.value = value; 162 } 163 164 @Override public synchronized String toString() { 165 String s; 166 if (toString != null) { 167 s = toString.get(); 168 if (s != null) { 169 return s; 170 } 171 } 172 s = Integer.toString(value); 173 toString = new WeakReference<String>(s); 174 return s; 175 } 176 } 177 } 178