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