1 /*
2  * Copyright (C) 2012 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.cache;
18 
19 import com.google.common.annotations.GwtCompatible;
20 import com.google.common.collect.ImmutableList;
21 import com.google.common.collect.ImmutableMap;
22 import com.google.common.collect.ImmutableSet;
23 import com.google.common.collect.Sets;
24 import com.google.common.testing.FakeTicker;
25 
26 import junit.framework.TestCase;
27 
28 import java.util.Iterator;
29 import java.util.Map;
30 import java.util.Map.Entry;
31 import java.util.Set;
32 import java.util.concurrent.Callable;
33 import java.util.concurrent.ConcurrentMap;
34 import java.util.concurrent.ExecutionException;
35 import java.util.concurrent.TimeUnit;
36 
37 /**
38  * Test suite for {@link CacheBuilder}.
39  * TODO(cpovirk): merge into CacheBuilderTest?
40  *
41  * @author Jon Donovan
42  */
43 @GwtCompatible
44 public class CacheBuilderGwtTest extends TestCase {
45 
46   private FakeTicker fakeTicker;
47 
48   @Override
setUp()49   protected void setUp() throws Exception {
50     super.setUp();
51 
52     fakeTicker = new FakeTicker();
53   }
54 
testLoader()55   public void testLoader() throws ExecutionException {
56 
57     final Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
58         .build();
59 
60     Callable<Integer> loader = new Callable<Integer>() {
61       private int i = 0;
62 
63       @Override
64       public Integer call() throws Exception {
65         return ++i;
66       }
67     };
68 
69     cache.put(0, 10);
70 
71     assertEquals(Integer.valueOf(10), cache.get(0, loader));
72     assertEquals(Integer.valueOf(1), cache.get(20, loader));
73     assertEquals(Integer.valueOf(2), cache.get(34, loader));
74 
75     cache.invalidate(0);
76     assertEquals(Integer.valueOf(3), cache.get(0, loader));
77 
78     cache.put(0, 10);
79     cache.invalidateAll();
80     assertEquals(Integer.valueOf(4), cache.get(0, loader));
81   }
82 
testSizeConstraint()83   public void testSizeConstraint() {
84     final Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
85         .maximumSize(4)
86         .build();
87 
88     cache.put(1, 10);
89     cache.put(2, 20);
90     cache.put(3, 30);
91     cache.put(4, 40);
92     cache.put(5, 50);
93 
94     assertEquals(null, cache.getIfPresent(10));
95     // Order required to remove dependence on acces order / write order constraint.
96     assertEquals(Integer.valueOf(20), cache.getIfPresent(2));
97     assertEquals(Integer.valueOf(30), cache.getIfPresent(3));
98     assertEquals(Integer.valueOf(40), cache.getIfPresent(4));
99     assertEquals(Integer.valueOf(50), cache.getIfPresent(5));
100 
101     cache.put(1, 10);
102     assertEquals(Integer.valueOf(10), cache.getIfPresent(1));
103     assertEquals(Integer.valueOf(30), cache.getIfPresent(3));
104     assertEquals(Integer.valueOf(40), cache.getIfPresent(4));
105     assertEquals(Integer.valueOf(50), cache.getIfPresent(5));
106     assertEquals(null, cache.getIfPresent(2));
107   }
108 
testLoadingCache()109   public void testLoadingCache() throws ExecutionException {
110     CacheLoader<Integer, Integer> loader = new CacheLoader<Integer, Integer>() {
111       int i = 0;
112       @Override
113       public Integer load(Integer key) throws Exception {
114         return i++;
115       }
116 
117     };
118 
119     LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder()
120         .build(loader);
121 
122     cache.put(10, 20);
123 
124     Map<Integer, Integer> map = cache.getAll(ImmutableList.of(10, 20, 30, 54, 443, 1));
125 
126     assertEquals(Integer.valueOf(20), map.get(10));
127     assertEquals(Integer.valueOf(0), map.get(20));
128     assertEquals(Integer.valueOf(1), map.get(30));
129     assertEquals(Integer.valueOf(2), map.get(54));
130     assertEquals(Integer.valueOf(3), map.get(443));
131     assertEquals(Integer.valueOf(4), map.get(1));
132     assertEquals(Integer.valueOf(5), cache.get(6));
133     assertEquals(Integer.valueOf(6), cache.apply(7));
134   }
135 
testExpireAfterAccess()136   public void testExpireAfterAccess() {
137     final Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
138         .expireAfterAccess(1000, TimeUnit.MILLISECONDS)
139         .ticker(fakeTicker)
140         .build();
141 
142     cache.put(0, 10);
143     cache.put(2, 30);
144 
145     fakeTicker.advance(999, TimeUnit.MILLISECONDS);
146     assertEquals(Integer.valueOf(30), cache.getIfPresent(2));
147     fakeTicker.advance(1, TimeUnit.MILLISECONDS);
148     assertEquals(Integer.valueOf(30), cache.getIfPresent(2));
149     fakeTicker.advance(1000, TimeUnit.MILLISECONDS);
150     assertEquals(null, cache.getIfPresent(0));
151   }
152 
testExpireAfterWrite()153   public void testExpireAfterWrite() {
154     final Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
155         .expireAfterWrite(1000, TimeUnit.MILLISECONDS)
156         .ticker(fakeTicker)
157         .build();
158 
159     cache.put(10, 100);
160     cache.put(20, 200);
161     cache.put(4, 2);
162 
163     fakeTicker.advance(999, TimeUnit.MILLISECONDS);
164     assertEquals(Integer.valueOf(100), cache.getIfPresent(10));
165     assertEquals(Integer.valueOf(200), cache.getIfPresent(20));
166     assertEquals(Integer.valueOf(2), cache.getIfPresent(4));
167 
168     fakeTicker.advance(2, TimeUnit.MILLISECONDS);
169     assertEquals(null, cache.getIfPresent(10));
170     assertEquals(null, cache.getIfPresent(20));
171     assertEquals(null, cache.getIfPresent(4));
172 
173     cache.put(10, 20);
174     assertEquals(Integer.valueOf(20), cache.getIfPresent(10));
175 
176     fakeTicker.advance(1000, TimeUnit.MILLISECONDS);
177     assertEquals(null, cache.getIfPresent(10));
178   }
179 
testExpireAfterWriteAndAccess()180   public void testExpireAfterWriteAndAccess() {
181     final Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
182         .expireAfterWrite(1000, TimeUnit.MILLISECONDS)
183         .expireAfterAccess(500, TimeUnit.MILLISECONDS)
184         .ticker(fakeTicker)
185         .build();
186 
187     cache.put(10, 100);
188     cache.put(20, 200);
189     cache.put(4, 2);
190 
191     fakeTicker.advance(499, TimeUnit.MILLISECONDS);
192     assertEquals(Integer.valueOf(100), cache.getIfPresent(10));
193     assertEquals(Integer.valueOf(200), cache.getIfPresent(20));
194 
195     fakeTicker.advance(2, TimeUnit.MILLISECONDS);
196     assertEquals(Integer.valueOf(100), cache.getIfPresent(10));
197     assertEquals(Integer.valueOf(200), cache.getIfPresent(20));
198     assertEquals(null, cache.getIfPresent(4));
199 
200     fakeTicker.advance(499, TimeUnit.MILLISECONDS);
201     assertEquals(null, cache.getIfPresent(10));
202     assertEquals(null, cache.getIfPresent(20));
203 
204     cache.put(10, 20);
205     assertEquals(Integer.valueOf(20), cache.getIfPresent(10));
206 
207     fakeTicker.advance(500, TimeUnit.MILLISECONDS);
208     assertEquals(null, cache.getIfPresent(10));
209   }
210 
testMapMethods()211   public void testMapMethods() {
212     Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
213         .build();
214 
215     ConcurrentMap<Integer, Integer> asMap = cache.asMap();
216 
217     cache.put(10, 100);
218     cache.put(2, 52);
219 
220     asMap.replace(2, 79);
221     asMap.replace(3, 60);
222 
223     assertEquals(null, cache.getIfPresent(3));
224     assertEquals(null, asMap.get(3));
225 
226     assertEquals(Integer.valueOf(79), cache.getIfPresent(2));
227     assertEquals(Integer.valueOf(79), asMap.get(2));
228 
229     asMap.replace(10, 100, 50);
230     asMap.replace(2, 52, 99);
231 
232     assertEquals(Integer.valueOf(50), cache.getIfPresent(10));
233     assertEquals(Integer.valueOf(50), asMap.get(10));
234     assertEquals(Integer.valueOf(79), cache.getIfPresent(2));
235     assertEquals(Integer.valueOf(79), asMap.get(2));
236 
237     asMap.remove(10, 100);
238     asMap.remove(2, 79);
239 
240     assertEquals(Integer.valueOf(50), cache.getIfPresent(10));
241     assertEquals(Integer.valueOf(50), asMap.get(10));
242     assertEquals(null, cache.getIfPresent(2));
243     assertEquals(null, asMap.get(2));
244 
245     asMap.putIfAbsent(2, 20);
246     asMap.putIfAbsent(10, 20);
247 
248     assertEquals(Integer.valueOf(20), cache.getIfPresent(2));
249     assertEquals(Integer.valueOf(20), asMap.get(2));
250     assertEquals(Integer.valueOf(50), cache.getIfPresent(10));
251     assertEquals(Integer.valueOf(50), asMap.get(10));
252   }
253 
testRemovalListener()254   public void testRemovalListener() {
255     final int[] stats = new int[4];
256 
257     RemovalListener<Integer, Integer> countingListener = new RemovalListener<Integer, Integer>() {
258       @Override
259       public void onRemoval(RemovalNotification<Integer, Integer> notification) {
260         switch (notification.getCause()) {
261           case EXPIRED:
262             stats[0]++;
263             break;
264           case EXPLICIT:
265             stats[1]++;
266             break;
267           case REPLACED:
268             stats[2]++;
269             break;
270           case SIZE:
271             stats[3]++;
272             break;
273           default:
274             throw new IllegalStateException("No collected exceptions in GWT CacheBuilder.");
275         }
276       }
277     };
278 
279     Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
280         .expireAfterWrite(1000, TimeUnit.MILLISECONDS)
281         .removalListener(countingListener)
282         .ticker(fakeTicker)
283         .maximumSize(2)
284         .build();
285 
286     // Add more than two elements to increment size removals.
287     cache.put(3, 20);
288     cache.put(6, 2);
289     cache.put(98, 45);
290     cache.put(56, 76);
291     cache.put(23, 84);
292 
293     // Replace the two present elements.
294     cache.put(23, 20);
295     cache.put(56, 49);
296     cache.put(23, 2);
297     cache.put(56, 4);
298 
299     // Expire the two present elements.
300     fakeTicker.advance(1001, TimeUnit.MILLISECONDS);
301 
302     cache.getIfPresent(23);
303     cache.getIfPresent(56);
304 
305     // Add two elements and invalidate them.
306     cache.put(1, 4);
307     cache.put(2, 8);
308 
309     cache.invalidateAll();
310 
311     assertEquals(2, stats[0]);
312     assertEquals(2, stats[1]);
313     assertEquals(4, stats[2]);
314     assertEquals(3, stats[3]);
315   }
316 
testPutAll()317   public void testPutAll() {
318     Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
319         .build();
320 
321     cache.putAll(ImmutableMap.of(10, 20, 30, 50, 60, 90));
322 
323     assertEquals(Integer.valueOf(20), cache.getIfPresent(10));
324     assertEquals(Integer.valueOf(50), cache.getIfPresent(30));
325     assertEquals(Integer.valueOf(90), cache.getIfPresent(60));
326 
327     cache.asMap().putAll(ImmutableMap.of(10, 50, 30, 20, 60, 70, 5, 5));
328 
329     assertEquals(Integer.valueOf(50), cache.getIfPresent(10));
330     assertEquals(Integer.valueOf(20), cache.getIfPresent(30));
331     assertEquals(Integer.valueOf(70), cache.getIfPresent(60));
332     assertEquals(Integer.valueOf(5), cache.getIfPresent(5));
333   }
334 
testInvalidate()335   public void testInvalidate() {
336     Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
337         .build();
338 
339     cache.put(654, 2675);
340     cache.put(2456, 56);
341     cache.put(2, 15);
342 
343     cache.invalidate(654);
344 
345     assertFalse(cache.asMap().containsKey(654));
346     assertTrue(cache.asMap().containsKey(2456));
347     assertTrue(cache.asMap().containsKey(2));
348   }
349 
testInvalidateAll()350   public void testInvalidateAll() {
351     Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
352         .build();
353 
354     cache.put(654, 2675);
355     cache.put(2456, 56);
356     cache.put(2, 15);
357 
358     cache.invalidateAll();
359     assertFalse(cache.asMap().containsKey(654));
360     assertFalse(cache.asMap().containsKey(2456));
361     assertFalse(cache.asMap().containsKey(2));
362 
363     cache.put(654, 2675);
364     cache.put(2456, 56);
365     cache.put(2, 15);
366     cache.put(1, 3);
367 
368     cache.invalidateAll(ImmutableSet.of(1, 2));
369 
370     assertFalse(cache.asMap().containsKey(1));
371     assertFalse(cache.asMap().containsKey(2));
372     assertTrue(cache.asMap().containsKey(654));
373     assertTrue(cache.asMap().containsKey(2456));
374   }
375 
testAsMap_containsValue()376   public void testAsMap_containsValue() {
377     Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
378         .expireAfterWrite(20000, TimeUnit.MILLISECONDS)
379         .ticker(fakeTicker)
380         .build();
381 
382     cache.put(654, 2675);
383     fakeTicker.advance(10000, TimeUnit.MILLISECONDS);
384     cache.put(2456, 56);
385     cache.put(2, 15);
386 
387     fakeTicker.advance(10001, TimeUnit.MILLISECONDS);
388 
389     assertTrue(cache.asMap().containsValue(15));
390     assertTrue(cache.asMap().containsValue(56));
391     assertFalse(cache.asMap().containsValue(2675));
392   }
393 
testAsMap_containsKey()394   public void testAsMap_containsKey() {
395     Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
396         .expireAfterWrite(20000, TimeUnit.MILLISECONDS)
397         .ticker(fakeTicker)
398         .build();
399 
400     cache.put(654, 2675);
401     fakeTicker.advance(10000, TimeUnit.MILLISECONDS);
402     cache.put(2456, 56);
403     cache.put(2, 15);
404 
405     fakeTicker.advance(10001, TimeUnit.MILLISECONDS);
406 
407     assertTrue(cache.asMap().containsKey(2));
408     assertTrue(cache.asMap().containsKey(2456));
409     assertFalse(cache.asMap().containsKey(654));
410   }
411 
testAsMapValues_contains()412   public void testAsMapValues_contains() {
413     Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
414         .expireAfterWrite(1000, TimeUnit.MILLISECONDS)
415         .ticker(fakeTicker)
416         .build();
417 
418     cache.put(10, 20);
419     fakeTicker.advance(500, TimeUnit.MILLISECONDS);
420     cache.put(20, 22);
421     cache.put(5, 10);
422 
423     fakeTicker.advance(501, TimeUnit.MILLISECONDS);
424 
425     assertTrue(cache.asMap().values().contains(22));
426     assertTrue(cache.asMap().values().contains(10));
427     assertFalse(cache.asMap().values().contains(20));
428   }
429 
testAsMapKeySet()430   public void testAsMapKeySet() {
431     Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
432         .expireAfterWrite(1000, TimeUnit.MILLISECONDS)
433         .ticker(fakeTicker)
434         .build();
435 
436     cache.put(10, 20);
437     fakeTicker.advance(500, TimeUnit.MILLISECONDS);
438     cache.put(20, 22);
439     cache.put(5, 10);
440 
441     fakeTicker.advance(501, TimeUnit.MILLISECONDS);
442 
443     Set<Integer> foundKeys = Sets.newHashSet();
444     for (Integer current : cache.asMap().keySet()) {
445       foundKeys.add(current);
446     }
447 
448     assertEquals(ImmutableSet.of(20, 5), foundKeys);
449   }
450 
451 
testAsMapKeySet_contains()452   public void testAsMapKeySet_contains() {
453     Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
454         .expireAfterWrite(1000, TimeUnit.MILLISECONDS)
455         .ticker(fakeTicker)
456         .build();
457 
458     cache.put(10, 20);
459     fakeTicker.advance(500, TimeUnit.MILLISECONDS);
460     cache.put(20, 22);
461     cache.put(5, 10);
462 
463     fakeTicker.advance(501, TimeUnit.MILLISECONDS);
464 
465     assertTrue(cache.asMap().keySet().contains(20));
466     assertTrue(cache.asMap().keySet().contains(5));
467     assertFalse(cache.asMap().keySet().contains(10));
468   }
469 
testAsMapEntrySet()470   public void testAsMapEntrySet() {
471     Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
472         .expireAfterWrite(1000, TimeUnit.MILLISECONDS)
473         .ticker(fakeTicker)
474         .build();
475 
476     cache.put(10, 20);
477     fakeTicker.advance(500, TimeUnit.MILLISECONDS);
478     cache.put(20, 22);
479     cache.put(5, 10);
480 
481     fakeTicker.advance(501, TimeUnit.MILLISECONDS);
482 
483     int sum = 0;
484     for (Entry<Integer, Integer> current : cache.asMap().entrySet()) {
485       sum += current.getKey() + current.getValue();
486     }
487     assertEquals(57, sum);
488   }
489 
testAsMapValues_iteratorRemove()490   public void testAsMapValues_iteratorRemove() {
491     Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
492         .expireAfterWrite(1000, TimeUnit.MILLISECONDS)
493         .ticker(fakeTicker)
494         .build();
495 
496     cache.put(10, 20);
497     Iterator<Integer> iterator = cache.asMap().values().iterator();
498     iterator.next();
499     iterator.remove();
500 
501     assertEquals(0, cache.size());
502   }
503 }
504