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.CacheTesting.checkEmpty; 18 import static com.google.common.cache.CacheTesting.checkValidState; 19 import static com.google.common.cache.TestingCacheLoaders.identityLoader; 20 import static com.google.common.truth.Truth.assertThat; 21 import static java.util.concurrent.TimeUnit.DAYS; 22 import static java.util.concurrent.TimeUnit.SECONDS; 23 import static org.junit.Assert.assertThrows; 24 25 import com.google.common.base.Function; 26 import com.google.common.cache.CacheBuilderFactory.DurationSpec; 27 import com.google.common.cache.LocalCache.Strength; 28 import com.google.common.collect.ImmutableMap; 29 import com.google.common.collect.ImmutableSet; 30 import com.google.common.collect.Iterables; 31 import com.google.common.collect.Iterators; 32 import com.google.common.collect.Lists; 33 import com.google.common.collect.Maps; 34 import com.google.common.testing.EqualsTester; 35 import java.util.Collection; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.Map.Entry; 39 import java.util.Set; 40 import junit.framework.TestCase; 41 42 /** 43 * {@link LoadingCache} tests that deal with caches that actually contain some key-value mappings. 44 * 45 * @author mike nonemacher 46 */ 47 48 public class PopulatedCachesTest extends TestCase { 49 // we use integers as keys; make sure the range covers some values that ARE cached by 50 // Integer.valueOf(int), and some that are not cached. (127 is the highest cached value.) 51 static final int WARMUP_MIN = 120; 52 static final int WARMUP_MAX = 135; 53 static final int WARMUP_SIZE = WARMUP_MAX - WARMUP_MIN; 54 testSize_populated()55 public void testSize_populated() { 56 for (LoadingCache<Object, Object> cache : caches()) { 57 // don't let the entries get GCed 58 List<Entry<Object, Object>> unused = warmUp(cache); 59 assertEquals(WARMUP_SIZE, cache.size()); 60 assertMapSize(cache.asMap(), WARMUP_SIZE); 61 checkValidState(cache); 62 } 63 } 64 testContainsKey_found()65 public void testContainsKey_found() { 66 for (LoadingCache<Object, Object> cache : caches()) { 67 // don't let the entries get GCed 68 List<Entry<Object, Object>> warmed = warmUp(cache); 69 for (int i = WARMUP_MIN; i < WARMUP_MAX; i++) { 70 Entry<Object, Object> entry = warmed.get(i - WARMUP_MIN); 71 assertTrue(cache.asMap().containsKey(entry.getKey())); 72 assertTrue(cache.asMap().containsValue(entry.getValue())); 73 // this getUnchecked() call shouldn't be a cache miss; verified below 74 assertEquals(entry.getValue(), cache.getUnchecked(entry.getKey())); 75 } 76 assertEquals(WARMUP_SIZE, cache.stats().missCount()); 77 checkValidState(cache); 78 } 79 } 80 testPut_populated()81 public void testPut_populated() { 82 for (LoadingCache<Object, Object> cache : caches()) { 83 // don't let the entries get GCed 84 List<Entry<Object, Object>> warmed = warmUp(cache); 85 for (int i = WARMUP_MIN; i < WARMUP_MAX; i++) { 86 Entry<Object, Object> entry = warmed.get(i - WARMUP_MIN); 87 Object newValue = new Object(); 88 assertSame(entry.getValue(), cache.asMap().put(entry.getKey(), newValue)); 89 // don't let the new entry get GCed 90 warmed.add(entryOf(entry.getKey(), newValue)); 91 Object newKey = new Object(); 92 assertNull(cache.asMap().put(newKey, entry.getValue())); 93 // this getUnchecked() call shouldn't be a cache miss; verified below 94 assertEquals(newValue, cache.getUnchecked(entry.getKey())); 95 assertEquals(entry.getValue(), cache.getUnchecked(newKey)); 96 // don't let the new entry get GCed 97 warmed.add(entryOf(newKey, entry.getValue())); 98 } 99 assertEquals(WARMUP_SIZE, cache.stats().missCount()); 100 checkValidState(cache); 101 } 102 } 103 testPutIfAbsent_populated()104 public void testPutIfAbsent_populated() { 105 for (LoadingCache<Object, Object> cache : caches()) { 106 // don't let the entries get GCed 107 List<Entry<Object, Object>> warmed = warmUp(cache); 108 for (int i = WARMUP_MIN; i < WARMUP_MAX; i++) { 109 Entry<Object, Object> entry = warmed.get(i - WARMUP_MIN); 110 Object newValue = new Object(); 111 assertSame(entry.getValue(), cache.asMap().putIfAbsent(entry.getKey(), newValue)); 112 Object newKey = new Object(); 113 assertNull(cache.asMap().putIfAbsent(newKey, entry.getValue())); 114 // this getUnchecked() call shouldn't be a cache miss; verified below 115 assertEquals(entry.getValue(), cache.getUnchecked(entry.getKey())); 116 assertEquals(entry.getValue(), cache.getUnchecked(newKey)); 117 // don't let the new entry get GCed 118 warmed.add(entryOf(newKey, entry.getValue())); 119 } 120 assertEquals(WARMUP_SIZE, cache.stats().missCount()); 121 checkValidState(cache); 122 } 123 } 124 testPutAll_populated()125 public void testPutAll_populated() { 126 for (LoadingCache<Object, Object> cache : caches()) { 127 // don't let the entries get GCed 128 List<Entry<Object, Object>> unused = warmUp(cache); 129 Object newKey = new Object(); 130 Object newValue = new Object(); 131 cache.asMap().putAll(ImmutableMap.of(newKey, newValue)); 132 // this getUnchecked() call shouldn't be a cache miss; verified below 133 assertEquals(newValue, cache.getUnchecked(newKey)); 134 assertEquals(WARMUP_SIZE, cache.stats().missCount()); 135 checkValidState(cache); 136 } 137 } 138 testReplace_populated()139 public void testReplace_populated() { 140 for (LoadingCache<Object, Object> cache : caches()) { 141 // don't let the entries get GCed 142 List<Entry<Object, Object>> warmed = warmUp(cache); 143 for (int i = WARMUP_MIN; i < WARMUP_MAX; i++) { 144 Entry<Object, Object> entry = warmed.get(i - WARMUP_MIN); 145 Object newValue = new Object(); 146 assertSame(entry.getValue(), cache.asMap().replace(entry.getKey(), newValue)); 147 assertTrue(cache.asMap().replace(entry.getKey(), newValue, entry.getValue())); 148 Object newKey = new Object(); 149 assertNull(cache.asMap().replace(newKey, entry.getValue())); 150 assertFalse(cache.asMap().replace(newKey, entry.getValue(), newValue)); 151 // this getUnchecked() call shouldn't be a cache miss; verified below 152 assertEquals(entry.getValue(), cache.getUnchecked(entry.getKey())); 153 assertFalse(cache.asMap().containsKey(newKey)); 154 } 155 assertEquals(WARMUP_SIZE, cache.stats().missCount()); 156 checkValidState(cache); 157 } 158 } 159 testRemove_byKey()160 public void testRemove_byKey() { 161 for (LoadingCache<Object, Object> cache : caches()) { 162 // don't let the entries get GCed 163 List<Entry<Object, Object>> warmed = warmUp(cache); 164 for (int i = WARMUP_MIN; i < WARMUP_MAX; i++) { 165 Entry<Object, Object> entry = warmed.get(i - WARMUP_MIN); 166 Object key = entry.getKey(); 167 assertEquals(entry.getValue(), cache.asMap().remove(key)); 168 assertNull(cache.asMap().remove(key)); 169 assertFalse(cache.asMap().containsKey(key)); 170 } 171 checkEmpty(cache); 172 } 173 } 174 testRemove_byKeyAndValue()175 public void testRemove_byKeyAndValue() { 176 for (LoadingCache<Object, Object> cache : caches()) { 177 // don't let the entries get GCed 178 List<Entry<Object, Object>> warmed = warmUp(cache); 179 for (int i = WARMUP_MIN; i < WARMUP_MAX; i++) { 180 Object key = warmed.get(i - WARMUP_MIN).getKey(); 181 Object value = warmed.get(i - WARMUP_MIN).getValue(); 182 assertFalse(cache.asMap().remove(key, -1)); 183 assertTrue(cache.asMap().remove(key, value)); 184 assertFalse(cache.asMap().remove(key, -1)); 185 assertFalse(cache.asMap().containsKey(key)); 186 } 187 checkEmpty(cache); 188 } 189 } 190 191 testKeySet_populated()192 public void testKeySet_populated() { 193 for (LoadingCache<Object, Object> cache : caches()) { 194 Set<Object> keys = cache.asMap().keySet(); 195 List<Entry<Object, Object>> warmed = warmUp(cache); 196 197 Set<Object> expected = Maps.newHashMap(cache.asMap()).keySet(); 198 assertThat(keys).containsExactlyElementsIn(expected); 199 assertThat(keys.toArray()).asList().containsExactlyElementsIn(expected); 200 assertThat(keys.toArray(new Object[0])).asList().containsExactlyElementsIn(expected); 201 202 new EqualsTester() 203 .addEqualityGroup(cache.asMap().keySet(), keys) 204 .addEqualityGroup(ImmutableSet.of()) 205 .testEquals(); 206 assertEquals(WARMUP_SIZE, keys.size()); 207 for (int i = WARMUP_MIN; i < WARMUP_MAX; i++) { 208 Object key = warmed.get(i - WARMUP_MIN).getKey(); 209 assertTrue(keys.contains(key)); 210 assertTrue(keys.remove(key)); 211 assertFalse(keys.remove(key)); 212 assertFalse(keys.contains(key)); 213 } 214 checkEmpty(keys); 215 checkEmpty(cache); 216 } 217 } 218 testValues_populated()219 public void testValues_populated() { 220 for (LoadingCache<Object, Object> cache : caches()) { 221 Collection<Object> values = cache.asMap().values(); 222 List<Entry<Object, Object>> warmed = warmUp(cache); 223 224 Collection<Object> expected = Maps.newHashMap(cache.asMap()).values(); 225 assertThat(values).containsExactlyElementsIn(expected); 226 assertThat(values.toArray()).asList().containsExactlyElementsIn(expected); 227 assertThat(values.toArray(new Object[0])).asList().containsExactlyElementsIn(expected); 228 229 assertEquals(WARMUP_SIZE, values.size()); 230 for (int i = WARMUP_MIN; i < WARMUP_MAX; i++) { 231 Object value = warmed.get(i - WARMUP_MIN).getValue(); 232 assertTrue(values.contains(value)); 233 assertTrue(values.remove(value)); 234 assertFalse(values.remove(value)); 235 assertFalse(values.contains(value)); 236 } 237 checkEmpty(values); 238 checkEmpty(cache); 239 } 240 } 241 242 testEntrySet_populated()243 public void testEntrySet_populated() { 244 for (LoadingCache<Object, Object> cache : caches()) { 245 Set<Entry<Object, Object>> entries = cache.asMap().entrySet(); 246 List<Entry<Object, Object>> warmed = warmUp(cache, WARMUP_MIN, WARMUP_MAX); 247 248 Set<?> expected = Maps.newHashMap(cache.asMap()).entrySet(); 249 assertThat(entries).containsExactlyElementsIn(expected); 250 assertThat(entries.toArray()).asList().containsExactlyElementsIn(expected); 251 assertThat(entries.toArray(new Object[0])).asList().containsExactlyElementsIn(expected); 252 253 new EqualsTester() 254 .addEqualityGroup(cache.asMap().entrySet(), entries) 255 .addEqualityGroup(ImmutableSet.of()) 256 .testEquals(); 257 assertEquals(WARMUP_SIZE, entries.size()); 258 for (int i = WARMUP_MIN; i < WARMUP_MAX; i++) { 259 Entry<Object, Object> newEntry = warmed.get(i - WARMUP_MIN); 260 assertTrue(entries.contains(newEntry)); 261 assertTrue(entries.remove(newEntry)); 262 assertFalse(entries.remove(newEntry)); 263 assertFalse(entries.contains(newEntry)); 264 } 265 checkEmpty(entries); 266 checkEmpty(cache); 267 } 268 } 269 testWriteThroughEntry()270 public void testWriteThroughEntry() { 271 for (LoadingCache<Object, Object> cache : caches()) { 272 cache.getUnchecked(1); 273 Entry<Object, Object> entry = Iterables.getOnlyElement(cache.asMap().entrySet()); 274 275 cache.invalidate(1); 276 assertEquals(0, cache.size()); 277 278 entry.setValue(3); 279 assertEquals(1, cache.size()); 280 assertEquals(3, cache.getIfPresent(1)); 281 checkValidState(cache); 282 283 assertThrows(NullPointerException.class, () -> entry.setValue(null)); 284 checkValidState(cache); 285 } 286 } 287 288 /* ---------------- Local utilities -------------- */ 289 290 /** Most of the tests in this class run against every one of these caches. */ caches()291 private Iterable<LoadingCache<Object, Object>> caches() { 292 // lots of different ways to configure a LoadingCache 293 CacheBuilderFactory factory = cacheFactory(); 294 return Iterables.transform( 295 factory.buildAllPermutations(), 296 new Function<CacheBuilder<Object, Object>, LoadingCache<Object, Object>>() { 297 @Override 298 public LoadingCache<Object, Object> apply(CacheBuilder<Object, Object> builder) { 299 return builder.recordStats().build(identityLoader()); 300 } 301 }); 302 } 303 304 private CacheBuilderFactory cacheFactory() { 305 // This is trickier than expected. We plan to put 15 values in each of these (WARMUP_MIN to 306 // WARMUP_MAX), but the tests assume no values get evicted. Even with a maximumSize of 100, one 307 // of the values gets evicted. With weak keys, we use identity equality, which means using 308 // System.identityHashCode, which means the assignment of keys to segments is nondeterministic, 309 // so more than (maximumSize / #segments) keys could get assigned to the same segment, which 310 // would cause one to be evicted. 311 return new CacheBuilderFactory() 312 .withKeyStrengths(ImmutableSet.of(Strength.STRONG, Strength.WEAK)) 313 .withValueStrengths(ImmutableSet.copyOf(Strength.values())) 314 .withConcurrencyLevels(ImmutableSet.of(1, 4, 16, 64)) 315 .withMaximumSizes(ImmutableSet.of(400, 1000)) 316 .withInitialCapacities(ImmutableSet.of(0, 1, 10, 100, 1000)) 317 .withExpireAfterWrites( 318 ImmutableSet.of( 319 // DurationSpec.of(500, MILLISECONDS), 320 DurationSpec.of(1, SECONDS), DurationSpec.of(1, DAYS))) 321 .withExpireAfterAccesses( 322 ImmutableSet.of( 323 // DurationSpec.of(500, MILLISECONDS), 324 DurationSpec.of(1, SECONDS), DurationSpec.of(1, DAYS))) 325 .withRefreshes( 326 ImmutableSet.of( 327 // DurationSpec.of(500, MILLISECONDS), 328 DurationSpec.of(1, SECONDS), DurationSpec.of(1, DAYS))); 329 } 330 331 private List<Entry<Object, Object>> warmUp(LoadingCache<Object, Object> cache) { 332 return warmUp(cache, WARMUP_MIN, WARMUP_MAX); 333 } 334 335 /** 336 * Returns the entries that were added to the map, so they won't fall out of a map with weak or 337 * soft references until the caller drops the reference to the returned entries. 338 */ 339 private List<Entry<Object, Object>> warmUp( 340 LoadingCache<Object, Object> cache, int minimum, int maximum) { 341 342 List<Entry<Object, Object>> entries = Lists.newArrayList(); 343 for (int i = minimum; i < maximum; i++) { 344 Object key = i; 345 Object value = cache.getUnchecked(key); 346 entries.add(entryOf(key, value)); 347 } 348 return entries; 349 } 350 351 private Entry<Object, Object> entryOf(Object key, Object value) { 352 return Maps.immutableEntry(key, value); 353 } 354 355 private void assertMapSize(Map<?, ?> map, int size) { 356 assertEquals(size, map.size()); 357 if (size > 0) { 358 assertFalse(map.isEmpty()); 359 } else { 360 assertTrue(map.isEmpty()); 361 } 362 assertCollectionSize(map.keySet(), size); 363 assertCollectionSize(map.entrySet(), size); 364 assertCollectionSize(map.values(), size); 365 } 366 367 private void assertCollectionSize(Collection<?> collection, int size) { 368 assertEquals(size, collection.size()); 369 if (size > 0) { 370 assertFalse(collection.isEmpty()); 371 } else { 372 assertTrue(collection.isEmpty()); 373 } 374 assertEquals(size, Iterables.size(collection)); 375 assertEquals(size, Iterators.size(collection.iterator())); 376 } 377 } 378