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
17package com.google.common.cache;
18
19import com.google.common.annotations.GwtCompatible;
20import com.google.common.collect.ImmutableList;
21import com.google.common.collect.ImmutableMap;
22import com.google.common.collect.ImmutableSet;
23import com.google.common.collect.Sets;
24import com.google.common.testing.FakeTicker;
25
26import junit.framework.TestCase;
27
28import java.util.Iterator;
29import java.util.Map;
30import java.util.Map.Entry;
31import java.util.Set;
32import java.util.concurrent.Callable;
33import java.util.concurrent.ConcurrentMap;
34import java.util.concurrent.ExecutionException;
35import 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
44public class CacheBuilderGwtTest extends TestCase {
45
46  private FakeTicker fakeTicker;
47
48  @Override
49  protected void setUp() throws Exception {
50    super.setUp();
51
52    fakeTicker = new FakeTicker();
53  }
54
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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