/* * Copyright (C) 2012 The Guava Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.common.cache; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import com.google.common.testing.FakeTicker; import junit.framework.TestCase; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; /** * Test suite for {@link CacheBuilder}. * TODO(cpovirk): merge into CacheBuilderTest? * * @author Jon Donovan */ @GwtCompatible public class CacheBuilderGwtTest extends TestCase { private FakeTicker fakeTicker; @Override protected void setUp() throws Exception { super.setUp(); fakeTicker = new FakeTicker(); } public void testLoader() throws ExecutionException { final Cache<Integer, Integer> cache = CacheBuilder.newBuilder() .build(); Callable<Integer> loader = new Callable<Integer>() { private int i = 0; @Override public Integer call() throws Exception { return ++i; } }; cache.put(0, 10); assertEquals(Integer.valueOf(10), cache.get(0, loader)); assertEquals(Integer.valueOf(1), cache.get(20, loader)); assertEquals(Integer.valueOf(2), cache.get(34, loader)); cache.invalidate(0); assertEquals(Integer.valueOf(3), cache.get(0, loader)); cache.put(0, 10); cache.invalidateAll(); assertEquals(Integer.valueOf(4), cache.get(0, loader)); } public void testSizeConstraint() { final Cache<Integer, Integer> cache = CacheBuilder.newBuilder() .maximumSize(4) .build(); cache.put(1, 10); cache.put(2, 20); cache.put(3, 30); cache.put(4, 40); cache.put(5, 50); assertEquals(null, cache.getIfPresent(10)); // Order required to remove dependence on acces order / write order constraint. assertEquals(Integer.valueOf(20), cache.getIfPresent(2)); assertEquals(Integer.valueOf(30), cache.getIfPresent(3)); assertEquals(Integer.valueOf(40), cache.getIfPresent(4)); assertEquals(Integer.valueOf(50), cache.getIfPresent(5)); cache.put(1, 10); assertEquals(Integer.valueOf(10), cache.getIfPresent(1)); assertEquals(Integer.valueOf(30), cache.getIfPresent(3)); assertEquals(Integer.valueOf(40), cache.getIfPresent(4)); assertEquals(Integer.valueOf(50), cache.getIfPresent(5)); assertEquals(null, cache.getIfPresent(2)); } public void testLoadingCache() throws ExecutionException { CacheLoader<Integer, Integer> loader = new CacheLoader<Integer, Integer>() { int i = 0; @Override public Integer load(Integer key) throws Exception { return i++; } }; LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder() .build(loader); cache.put(10, 20); Map<Integer, Integer> map = cache.getAll(ImmutableList.of(10, 20, 30, 54, 443, 1)); assertEquals(Integer.valueOf(20), map.get(10)); assertEquals(Integer.valueOf(0), map.get(20)); assertEquals(Integer.valueOf(1), map.get(30)); assertEquals(Integer.valueOf(2), map.get(54)); assertEquals(Integer.valueOf(3), map.get(443)); assertEquals(Integer.valueOf(4), map.get(1)); assertEquals(Integer.valueOf(5), cache.get(6)); assertEquals(Integer.valueOf(6), cache.apply(7)); } public void testExpireAfterAccess() { final Cache<Integer, Integer> cache = CacheBuilder.newBuilder() .expireAfterAccess(1000, TimeUnit.MILLISECONDS) .ticker(fakeTicker) .build(); cache.put(0, 10); cache.put(2, 30); fakeTicker.advance(999, TimeUnit.MILLISECONDS); assertEquals(Integer.valueOf(30), cache.getIfPresent(2)); fakeTicker.advance(1, TimeUnit.MILLISECONDS); assertEquals(Integer.valueOf(30), cache.getIfPresent(2)); fakeTicker.advance(1000, TimeUnit.MILLISECONDS); assertEquals(null, cache.getIfPresent(0)); } public void testExpireAfterWrite() { final Cache<Integer, Integer> cache = CacheBuilder.newBuilder() .expireAfterWrite(1000, TimeUnit.MILLISECONDS) .ticker(fakeTicker) .build(); cache.put(10, 100); cache.put(20, 200); cache.put(4, 2); fakeTicker.advance(999, TimeUnit.MILLISECONDS); assertEquals(Integer.valueOf(100), cache.getIfPresent(10)); assertEquals(Integer.valueOf(200), cache.getIfPresent(20)); assertEquals(Integer.valueOf(2), cache.getIfPresent(4)); fakeTicker.advance(2, TimeUnit.MILLISECONDS); assertEquals(null, cache.getIfPresent(10)); assertEquals(null, cache.getIfPresent(20)); assertEquals(null, cache.getIfPresent(4)); cache.put(10, 20); assertEquals(Integer.valueOf(20), cache.getIfPresent(10)); fakeTicker.advance(1000, TimeUnit.MILLISECONDS); assertEquals(null, cache.getIfPresent(10)); } public void testExpireAfterWriteAndAccess() { final Cache<Integer, Integer> cache = CacheBuilder.newBuilder() .expireAfterWrite(1000, TimeUnit.MILLISECONDS) .expireAfterAccess(500, TimeUnit.MILLISECONDS) .ticker(fakeTicker) .build(); cache.put(10, 100); cache.put(20, 200); cache.put(4, 2); fakeTicker.advance(499, TimeUnit.MILLISECONDS); assertEquals(Integer.valueOf(100), cache.getIfPresent(10)); assertEquals(Integer.valueOf(200), cache.getIfPresent(20)); fakeTicker.advance(2, TimeUnit.MILLISECONDS); assertEquals(Integer.valueOf(100), cache.getIfPresent(10)); assertEquals(Integer.valueOf(200), cache.getIfPresent(20)); assertEquals(null, cache.getIfPresent(4)); fakeTicker.advance(499, TimeUnit.MILLISECONDS); assertEquals(null, cache.getIfPresent(10)); assertEquals(null, cache.getIfPresent(20)); cache.put(10, 20); assertEquals(Integer.valueOf(20), cache.getIfPresent(10)); fakeTicker.advance(500, TimeUnit.MILLISECONDS); assertEquals(null, cache.getIfPresent(10)); } public void testMapMethods() { Cache<Integer, Integer> cache = CacheBuilder.newBuilder() .build(); ConcurrentMap<Integer, Integer> asMap = cache.asMap(); cache.put(10, 100); cache.put(2, 52); asMap.replace(2, 79); asMap.replace(3, 60); assertEquals(null, cache.getIfPresent(3)); assertEquals(null, asMap.get(3)); assertEquals(Integer.valueOf(79), cache.getIfPresent(2)); assertEquals(Integer.valueOf(79), asMap.get(2)); asMap.replace(10, 100, 50); asMap.replace(2, 52, 99); assertEquals(Integer.valueOf(50), cache.getIfPresent(10)); assertEquals(Integer.valueOf(50), asMap.get(10)); assertEquals(Integer.valueOf(79), cache.getIfPresent(2)); assertEquals(Integer.valueOf(79), asMap.get(2)); asMap.remove(10, 100); asMap.remove(2, 79); assertEquals(Integer.valueOf(50), cache.getIfPresent(10)); assertEquals(Integer.valueOf(50), asMap.get(10)); assertEquals(null, cache.getIfPresent(2)); assertEquals(null, asMap.get(2)); asMap.putIfAbsent(2, 20); asMap.putIfAbsent(10, 20); assertEquals(Integer.valueOf(20), cache.getIfPresent(2)); assertEquals(Integer.valueOf(20), asMap.get(2)); assertEquals(Integer.valueOf(50), cache.getIfPresent(10)); assertEquals(Integer.valueOf(50), asMap.get(10)); } public void testRemovalListener() { final int[] stats = new int[4]; RemovalListener<Integer, Integer> countingListener = new RemovalListener<Integer, Integer>() { @Override public void onRemoval(RemovalNotification<Integer, Integer> notification) { switch (notification.getCause()) { case EXPIRED: stats[0]++; break; case EXPLICIT: stats[1]++; break; case REPLACED: stats[2]++; break; case SIZE: stats[3]++; break; default: throw new IllegalStateException("No collected exceptions in GWT CacheBuilder."); } } }; Cache<Integer, Integer> cache = CacheBuilder.newBuilder() .expireAfterWrite(1000, TimeUnit.MILLISECONDS) .removalListener(countingListener) .ticker(fakeTicker) .maximumSize(2) .build(); // Add more than two elements to increment size removals. cache.put(3, 20); cache.put(6, 2); cache.put(98, 45); cache.put(56, 76); cache.put(23, 84); // Replace the two present elements. cache.put(23, 20); cache.put(56, 49); cache.put(23, 2); cache.put(56, 4); // Expire the two present elements. fakeTicker.advance(1001, TimeUnit.MILLISECONDS); cache.getIfPresent(23); cache.getIfPresent(56); // Add two elements and invalidate them. cache.put(1, 4); cache.put(2, 8); cache.invalidateAll(); assertEquals(2, stats[0]); assertEquals(2, stats[1]); assertEquals(4, stats[2]); assertEquals(3, stats[3]); } public void testPutAll() { Cache<Integer, Integer> cache = CacheBuilder.newBuilder() .build(); cache.putAll(ImmutableMap.of(10, 20, 30, 50, 60, 90)); assertEquals(Integer.valueOf(20), cache.getIfPresent(10)); assertEquals(Integer.valueOf(50), cache.getIfPresent(30)); assertEquals(Integer.valueOf(90), cache.getIfPresent(60)); cache.asMap().putAll(ImmutableMap.of(10, 50, 30, 20, 60, 70, 5, 5)); assertEquals(Integer.valueOf(50), cache.getIfPresent(10)); assertEquals(Integer.valueOf(20), cache.getIfPresent(30)); assertEquals(Integer.valueOf(70), cache.getIfPresent(60)); assertEquals(Integer.valueOf(5), cache.getIfPresent(5)); } public void testInvalidate() { Cache<Integer, Integer> cache = CacheBuilder.newBuilder() .build(); cache.put(654, 2675); cache.put(2456, 56); cache.put(2, 15); cache.invalidate(654); assertFalse(cache.asMap().containsKey(654)); assertTrue(cache.asMap().containsKey(2456)); assertTrue(cache.asMap().containsKey(2)); } public void testInvalidateAll() { Cache<Integer, Integer> cache = CacheBuilder.newBuilder() .build(); cache.put(654, 2675); cache.put(2456, 56); cache.put(2, 15); cache.invalidateAll(); assertFalse(cache.asMap().containsKey(654)); assertFalse(cache.asMap().containsKey(2456)); assertFalse(cache.asMap().containsKey(2)); cache.put(654, 2675); cache.put(2456, 56); cache.put(2, 15); cache.put(1, 3); cache.invalidateAll(ImmutableSet.of(1, 2)); assertFalse(cache.asMap().containsKey(1)); assertFalse(cache.asMap().containsKey(2)); assertTrue(cache.asMap().containsKey(654)); assertTrue(cache.asMap().containsKey(2456)); } public void testAsMap_containsValue() { Cache<Integer, Integer> cache = CacheBuilder.newBuilder() .expireAfterWrite(20000, TimeUnit.MILLISECONDS) .ticker(fakeTicker) .build(); cache.put(654, 2675); fakeTicker.advance(10000, TimeUnit.MILLISECONDS); cache.put(2456, 56); cache.put(2, 15); fakeTicker.advance(10001, TimeUnit.MILLISECONDS); assertTrue(cache.asMap().containsValue(15)); assertTrue(cache.asMap().containsValue(56)); assertFalse(cache.asMap().containsValue(2675)); } public void testAsMap_containsKey() { Cache<Integer, Integer> cache = CacheBuilder.newBuilder() .expireAfterWrite(20000, TimeUnit.MILLISECONDS) .ticker(fakeTicker) .build(); cache.put(654, 2675); fakeTicker.advance(10000, TimeUnit.MILLISECONDS); cache.put(2456, 56); cache.put(2, 15); fakeTicker.advance(10001, TimeUnit.MILLISECONDS); assertTrue(cache.asMap().containsKey(2)); assertTrue(cache.asMap().containsKey(2456)); assertFalse(cache.asMap().containsKey(654)); } public void testAsMapValues_contains() { Cache<Integer, Integer> cache = CacheBuilder.newBuilder() .expireAfterWrite(1000, TimeUnit.MILLISECONDS) .ticker(fakeTicker) .build(); cache.put(10, 20); fakeTicker.advance(500, TimeUnit.MILLISECONDS); cache.put(20, 22); cache.put(5, 10); fakeTicker.advance(501, TimeUnit.MILLISECONDS); assertTrue(cache.asMap().values().contains(22)); assertTrue(cache.asMap().values().contains(10)); assertFalse(cache.asMap().values().contains(20)); } public void testAsMapKeySet() { Cache<Integer, Integer> cache = CacheBuilder.newBuilder() .expireAfterWrite(1000, TimeUnit.MILLISECONDS) .ticker(fakeTicker) .build(); cache.put(10, 20); fakeTicker.advance(500, TimeUnit.MILLISECONDS); cache.put(20, 22); cache.put(5, 10); fakeTicker.advance(501, TimeUnit.MILLISECONDS); Set<Integer> foundKeys = Sets.newHashSet(); for (Integer current : cache.asMap().keySet()) { foundKeys.add(current); } assertEquals(ImmutableSet.of(20, 5), foundKeys); } public void testAsMapKeySet_contains() { Cache<Integer, Integer> cache = CacheBuilder.newBuilder() .expireAfterWrite(1000, TimeUnit.MILLISECONDS) .ticker(fakeTicker) .build(); cache.put(10, 20); fakeTicker.advance(500, TimeUnit.MILLISECONDS); cache.put(20, 22); cache.put(5, 10); fakeTicker.advance(501, TimeUnit.MILLISECONDS); assertTrue(cache.asMap().keySet().contains(20)); assertTrue(cache.asMap().keySet().contains(5)); assertFalse(cache.asMap().keySet().contains(10)); } public void testAsMapEntrySet() { Cache<Integer, Integer> cache = CacheBuilder.newBuilder() .expireAfterWrite(1000, TimeUnit.MILLISECONDS) .ticker(fakeTicker) .build(); cache.put(10, 20); fakeTicker.advance(500, TimeUnit.MILLISECONDS); cache.put(20, 22); cache.put(5, 10); fakeTicker.advance(501, TimeUnit.MILLISECONDS); int sum = 0; for (Entry<Integer, Integer> current : cache.asMap().entrySet()) { sum += current.getKey() + current.getValue(); } assertEquals(57, sum); } public void testAsMapValues_iteratorRemove() { Cache<Integer, Integer> cache = CacheBuilder.newBuilder() .expireAfterWrite(1000, TimeUnit.MILLISECONDS) .ticker(fakeTicker) .build(); cache.put(10, 20); Iterator<Integer> iterator = cache.asMap().values().iterator(); iterator.next(); iterator.remove(); assertEquals(0, cache.size()); } }