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