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 190 testKeySet_populated()191 public void testKeySet_populated() { 192 for (LoadingCache<Object, Object> cache : caches()) { 193 Set<Object> keys = cache.asMap().keySet(); 194 List<Entry<Object, Object>> warmed = warmUp(cache); 195 196 Set<Object> expected = Maps.newHashMap(cache.asMap()).keySet(); 197 assertThat(keys).containsExactlyElementsIn(expected); 198 assertThat(keys.toArray()).asList().containsExactlyElementsIn(expected); 199 assertThat(keys.toArray(new Object[0])).asList().containsExactlyElementsIn(expected); 200 201 new EqualsTester() 202 .addEqualityGroup(cache.asMap().keySet(), keys) 203 .addEqualityGroup(ImmutableSet.of()) 204 .testEquals(); 205 assertEquals(WARMUP_SIZE, keys.size()); 206 for (int i = WARMUP_MIN; i < WARMUP_MAX; i++) { 207 Object key = warmed.get(i - WARMUP_MIN).getKey(); 208 assertTrue(keys.contains(key)); 209 assertTrue(keys.remove(key)); 210 assertFalse(keys.remove(key)); 211 assertFalse(keys.contains(key)); 212 } 213 checkEmpty(keys); 214 checkEmpty(cache); 215 } 216 } 217 testValues_populated()218 public void testValues_populated() { 219 for (LoadingCache<Object, Object> cache : caches()) { 220 Collection<Object> values = cache.asMap().values(); 221 List<Entry<Object, Object>> warmed = warmUp(cache); 222 223 Collection<Object> expected = Maps.newHashMap(cache.asMap()).values(); 224 assertThat(values).containsExactlyElementsIn(expected); 225 assertThat(values.toArray()).asList().containsExactlyElementsIn(expected); 226 assertThat(values.toArray(new Object[0])).asList().containsExactlyElementsIn(expected); 227 228 assertEquals(WARMUP_SIZE, values.size()); 229 for (int i = WARMUP_MIN; i < WARMUP_MAX; i++) { 230 Object value = warmed.get(i - WARMUP_MIN).getValue(); 231 assertTrue(values.contains(value)); 232 assertTrue(values.remove(value)); 233 assertFalse(values.remove(value)); 234 assertFalse(values.contains(value)); 235 } 236 checkEmpty(values); 237 checkEmpty(cache); 238 } 239 } 240 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(expected); 249 assertThat(entries.toArray()).asList().containsExactlyElementsIn(expected); 250 assertThat(entries.toArray(new Object[0])).asList().containsExactlyElementsIn(expected); 251 252 new EqualsTester() 253 .addEqualityGroup(cache.asMap().entrySet(), entries) 254 .addEqualityGroup(ImmutableSet.of()) 255 .testEquals(); 256 assertEquals(WARMUP_SIZE, entries.size()); 257 for (int i = WARMUP_MIN; i < WARMUP_MAX; i++) { 258 Entry<Object, Object> newEntry = warmed.get(i - WARMUP_MIN); 259 assertTrue(entries.contains(newEntry)); 260 assertTrue(entries.remove(newEntry)); 261 assertFalse(entries.remove(newEntry)); 262 assertFalse(entries.contains(newEntry)); 263 } 264 checkEmpty(entries); 265 checkEmpty(cache); 266 } 267 } 268 testWriteThroughEntry()269 public void testWriteThroughEntry() { 270 for (LoadingCache<Object, Object> cache : caches()) { 271 cache.getUnchecked(1); 272 Entry<Object, Object> entry = Iterables.getOnlyElement(cache.asMap().entrySet()); 273 274 cache.invalidate(1); 275 assertEquals(0, cache.size()); 276 277 entry.setValue(3); 278 assertEquals(1, cache.size()); 279 assertEquals(3, cache.getIfPresent(1)); 280 checkValidState(cache); 281 282 try { 283 entry.setValue(null); 284 fail(); 285 } catch (NullPointerException expected) { 286 } 287 checkValidState(cache); 288 } 289 } 290 291 /* ---------------- Local utilities -------------- */ 292 293 /** Most of the tests in this class run against every one of these caches. */ caches()294 private Iterable<LoadingCache<Object, Object>> caches() { 295 // lots of different ways to configure a LoadingCache 296 CacheBuilderFactory factory = cacheFactory(); 297 return Iterables.transform( 298 factory.buildAllPermutations(), 299 new Function<CacheBuilder<Object, Object>, LoadingCache<Object, Object>>() { 300 @Override 301 public LoadingCache<Object, Object> apply(CacheBuilder<Object, Object> builder) { 302 return builder.recordStats().build(identityLoader()); 303 } 304 }); 305 } 306 307 private CacheBuilderFactory cacheFactory() { 308 // This is trickier than expected. We plan to put 15 values in each of these (WARMUP_MIN to 309 // WARMUP_MAX), but the tests assume no values get evicted. Even with a maximumSize of 100, one 310 // of the values gets evicted. With weak keys, we use identity equality, which means using 311 // System.identityHashCode, which means the assignment of keys to segments is nondeterministic, 312 // so more than (maximumSize / #segments) keys could get assigned to the same segment, which 313 // would cause one to be evicted. 314 return new CacheBuilderFactory() 315 .withKeyStrengths(ImmutableSet.of(Strength.STRONG, Strength.WEAK)) 316 .withValueStrengths(ImmutableSet.copyOf(Strength.values())) 317 .withConcurrencyLevels(ImmutableSet.of(1, 4, 16, 64)) 318 .withMaximumSizes(ImmutableSet.of(400, 1000)) 319 .withInitialCapacities(ImmutableSet.of(0, 1, 10, 100, 1000)) 320 .withExpireAfterWrites( 321 ImmutableSet.of( 322 // DurationSpec.of(500, MILLISECONDS), 323 DurationSpec.of(1, SECONDS), DurationSpec.of(1, DAYS))) 324 .withExpireAfterAccesses( 325 ImmutableSet.of( 326 // DurationSpec.of(500, MILLISECONDS), 327 DurationSpec.of(1, SECONDS), DurationSpec.of(1, DAYS))) 328 .withRefreshes( 329 ImmutableSet.of( 330 // DurationSpec.of(500, MILLISECONDS), 331 DurationSpec.of(1, SECONDS), DurationSpec.of(1, DAYS))); 332 } 333 334 private List<Entry<Object, Object>> warmUp(LoadingCache<Object, Object> cache) { 335 return warmUp(cache, WARMUP_MIN, WARMUP_MAX); 336 } 337 338 /** 339 * Returns the entries that were added to the map, so they won't fall out of a map with weak or 340 * soft references until the caller drops the reference to the returned entries. 341 */ 342 private List<Entry<Object, Object>> warmUp( 343 LoadingCache<Object, Object> cache, int minimum, int maximum) { 344 345 List<Entry<Object, Object>> entries = Lists.newArrayList(); 346 for (int i = minimum; i < maximum; i++) { 347 Object key = i; 348 Object value = cache.getUnchecked(key); 349 entries.add(entryOf(key, value)); 350 } 351 return entries; 352 } 353 354 private Entry<Object, Object> entryOf(Object key, Object value) { 355 return Maps.immutableEntry(key, value); 356 } 357 358 private void assertMapSize(Map<?, ?> map, int size) { 359 assertEquals(size, map.size()); 360 if (size > 0) { 361 assertFalse(map.isEmpty()); 362 } else { 363 assertTrue(map.isEmpty()); 364 } 365 assertCollectionSize(map.keySet(), size); 366 assertCollectionSize(map.entrySet(), size); 367 assertCollectionSize(map.values(), size); 368 } 369 370 private void assertCollectionSize(Collection<?> collection, int size) { 371 assertEquals(size, collection.size()); 372 if (size > 0) { 373 assertFalse(collection.isEmpty()); 374 } else { 375 assertTrue(collection.isEmpty()); 376 } 377 assertEquals(size, Iterables.size(collection)); 378 assertEquals(size, Iterators.size(collection.iterator())); 379 } 380 } 381