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
15package com.google.common.cache;
16
17import static com.google.common.cache.CacheTesting.checkEmpty;
18import static com.google.common.cache.TestingCacheLoaders.identityLoader;
19import static java.util.Arrays.asList;
20import static java.util.concurrent.TimeUnit.SECONDS;
21
22import com.google.common.base.Function;
23import com.google.common.cache.CacheBuilderFactory.DurationSpec;
24import com.google.common.cache.LocalCache.Strength;
25import com.google.common.collect.ImmutableSet;
26import com.google.common.collect.Iterables;
27import com.google.common.collect.Maps;
28import com.google.common.testing.EqualsTester;
29
30import junit.framework.TestCase;
31
32import java.util.Collection;
33import java.util.Map.Entry;
34import java.util.Set;
35import java.util.concurrent.ExecutionException;
36
37/**
38 * {@link LoadingCache} tests that deal with empty caches.
39 *
40 * @author mike nonemacher
41 */
42
43public class EmptyCachesTest extends TestCase {
44
45  public void testEmpty() {
46    for (LoadingCache<Object, Object> cache : caches()) {
47      checkEmpty(cache);
48    }
49  }
50
51  public void testInvalidate_empty() {
52    for (LoadingCache<Object, Object> cache : caches()) {
53      cache.getUnchecked("a");
54      cache.getUnchecked("b");
55      cache.invalidate("a");
56      cache.invalidate("b");
57      cache.invalidate(0);
58      checkEmpty(cache);
59    }
60  }
61
62  public void testInvalidateAll_empty() {
63    for (LoadingCache<Object, Object> cache : caches()) {
64      cache.getUnchecked("a");
65      cache.getUnchecked("b");
66      cache.getUnchecked("c");
67      cache.invalidateAll();
68      checkEmpty(cache);
69    }
70  }
71
72  public void testEquals_null() {
73    for (LoadingCache<Object, Object> cache : caches()) {
74      assertFalse(cache.equals(null));
75    }
76  }
77
78  public void testEqualsAndHashCode_different() {
79    for (CacheBuilder<Object, Object> builder : cacheFactory().buildAllPermutations()) {
80      // all caches should be different: instance equality
81      new EqualsTester()
82          .addEqualityGroup(builder.build(identityLoader()))
83          .addEqualityGroup(builder.build(identityLoader()))
84          .addEqualityGroup(builder.build(identityLoader()))
85          .testEquals();
86    }
87  }
88
89  public void testGet_null() throws ExecutionException {
90    for (LoadingCache<Object, Object> cache : caches()) {
91      try {
92        cache.get(null);
93        fail("Expected NullPointerException");
94      } catch (NullPointerException e) {
95        // expected
96      }
97      checkEmpty(cache);
98    }
99  }
100
101  public void testGetUnchecked_null() {
102    for (LoadingCache<Object, Object> cache : caches()) {
103      try {
104        cache.getUnchecked(null);
105        fail("Expected NullPointerException");
106      } catch (NullPointerException e) {
107        // expected
108      }
109      checkEmpty(cache);
110    }
111  }
112
113  /* ---------------- Key Set -------------- */
114
115  public void testKeySet_nullToArray() {
116    for (LoadingCache<Object, Object> cache : caches()) {
117      Set<Object> keys = cache.asMap().keySet();
118      try {
119        keys.toArray(null);
120        fail();
121      } catch (NullPointerException e) {
122        // expected
123      }
124      checkEmpty(cache);
125    }
126  }
127
128  public void testKeySet_addNotSupported() {
129    for (LoadingCache<Object, Object> cache : caches()) {
130      try {
131        cache.asMap().keySet().add(1);
132        fail();
133      } catch (UnsupportedOperationException e) {
134        // expected
135      }
136
137      try {
138        cache.asMap().keySet().addAll(asList(1, 2));
139        fail();
140      } catch (UnsupportedOperationException e) {
141        // expected
142      }
143    }
144  }
145
146  public void testKeySet_clear() {
147    for (LoadingCache<Object, Object> cache : caches()) {
148      warmUp(cache, 0, 100);
149
150      Set<Object> keys = cache.asMap().keySet();
151      keys.clear();
152      checkEmpty(keys);
153      checkEmpty(cache);
154    }
155  }
156
157  public void testKeySet_empty_remove() {
158    for (LoadingCache<Object, Object> cache : caches()) {
159      Set<Object> keys = cache.asMap().keySet();
160      assertFalse(keys.remove(null));
161      assertFalse(keys.remove(6));
162      assertFalse(keys.remove(-6));
163      assertFalse(keys.removeAll(asList(null, 0, 15, 1500)));
164      assertFalse(keys.retainAll(asList(null, 0, 15, 1500)));
165      checkEmpty(keys);
166      checkEmpty(cache);
167    }
168  }
169
170  public void testKeySet_remove() {
171    for (LoadingCache<Object, Object> cache : caches()) {
172      cache.getUnchecked(1);
173      cache.getUnchecked(2);
174
175      Set<Object> keys = cache.asMap().keySet();
176      // We don't know whether these are still in the cache, so we can't assert on the return
177      // values of these removes, but the cache should be empty after the removes, regardless.
178      keys.remove(1);
179      keys.remove(2);
180      assertFalse(keys.remove(null));
181      assertFalse(keys.remove(6));
182      assertFalse(keys.remove(-6));
183      assertFalse(keys.removeAll(asList(null, 0, 15, 1500)));
184      assertFalse(keys.retainAll(asList(null, 0, 15, 1500)));
185      checkEmpty(keys);
186      checkEmpty(cache);
187    }
188  }
189
190  /* ---------------- Values -------------- */
191
192  public void testValues_nullToArray() {
193    for (LoadingCache<Object, Object> cache : caches()) {
194      Collection<Object> values = cache.asMap().values();
195      try {
196        values.toArray(null);
197        fail();
198      } catch (NullPointerException e) {
199        // expected
200      }
201      checkEmpty(cache);
202    }
203  }
204
205  public void testValues_addNotSupported() {
206    for (LoadingCache<Object, Object> cache : caches()) {
207      try {
208        cache.asMap().values().add(1);
209        fail();
210      } catch (UnsupportedOperationException e) {
211        // expected
212      }
213
214      try {
215        cache.asMap().values().addAll(asList(1, 2));
216        fail();
217      } catch (UnsupportedOperationException e) {
218        // expected
219      }
220    }
221  }
222
223  public void testValues_clear() {
224    for (LoadingCache<Object, Object> cache : caches()) {
225      warmUp(cache, 0, 100);
226
227      Collection<Object> values = cache.asMap().values();
228      values.clear();
229      checkEmpty(values);
230      checkEmpty(cache);
231    }
232  }
233
234  public void testValues_empty_remove() {
235    for (LoadingCache<Object, Object> cache : caches()) {
236      Collection<Object> values = cache.asMap().values();
237      assertFalse(values.remove(null));
238      assertFalse(values.remove(6));
239      assertFalse(values.remove(-6));
240      assertFalse(values.removeAll(asList(null, 0, 15, 1500)));
241      assertFalse(values.retainAll(asList(null, 0, 15, 1500)));
242      checkEmpty(values);
243      checkEmpty(cache);
244    }
245  }
246
247  public void testValues_remove() {
248    for (LoadingCache<Object, Object> cache : caches()) {
249      cache.getUnchecked(1);
250      cache.getUnchecked(2);
251
252      Collection<Object> values = cache.asMap().keySet();
253      // We don't know whether these are still in the cache, so we can't assert on the return
254      // values of these removes, but the cache should be empty after the removes, regardless.
255      values.remove(1);
256      values.remove(2);
257      assertFalse(values.remove(null));
258      assertFalse(values.remove(6));
259      assertFalse(values.remove(-6));
260      assertFalse(values.removeAll(asList(null, 0, 15, 1500)));
261      assertFalse(values.retainAll(asList(null, 0, 15, 1500)));
262      checkEmpty(values);
263      checkEmpty(cache);
264    }
265  }
266
267  /* ---------------- Entry Set -------------- */
268
269  public void testEntrySet_nullToArray() {
270    for (LoadingCache<Object, Object> cache : caches()) {
271      Set<Entry<Object, Object>> entries = cache.asMap().entrySet();
272      try {
273        entries.toArray(null);
274        fail();
275      } catch (NullPointerException e) {
276        // expected
277      }
278      checkEmpty(cache);
279    }
280  }
281
282  public void testEntrySet_addNotSupported() {
283    for (LoadingCache<Object, Object> cache : caches()) {
284      try {
285        cache.asMap().entrySet().add(entryOf(1, 1));
286        fail();
287      } catch (UnsupportedOperationException e) {
288        // expected
289      }
290
291      try {
292        cache.asMap().values().addAll(asList(entryOf(1, 1), entryOf(2, 2)));
293        fail();
294      } catch (UnsupportedOperationException e) {
295        // expected
296      }
297    }
298  }
299
300  public void testEntrySet_clear() {
301    for (LoadingCache<Object, Object> cache : caches()) {
302      warmUp(cache, 0, 100);
303
304      Set<Entry<Object, Object>> entrySet = cache.asMap().entrySet();
305      entrySet.clear();
306      checkEmpty(entrySet);
307      checkEmpty(cache);
308    }
309  }
310
311  public void testEntrySet_empty_remove() {
312    for (LoadingCache<Object, Object> cache : caches()) {
313      Set<Entry<Object, Object>> entrySet = cache.asMap().entrySet();
314      assertFalse(entrySet.remove(null));
315      assertFalse(entrySet.remove(entryOf(6, 6)));
316      assertFalse(entrySet.remove(entryOf(-6, -6)));
317      assertFalse(entrySet.removeAll(asList(null, entryOf(0, 0), entryOf(15, 15))));
318      assertFalse(entrySet.retainAll(asList(null, entryOf(0, 0), entryOf(15, 15))));
319      checkEmpty(entrySet);
320      checkEmpty(cache);
321    }
322  }
323
324  public void testEntrySet_remove() {
325    for (LoadingCache<Object, Object> cache : caches()) {
326      cache.getUnchecked(1);
327      cache.getUnchecked(2);
328
329      Set<Entry<Object, Object>> entrySet = cache.asMap().entrySet();
330      // We don't know whether these are still in the cache, so we can't assert on the return
331      // values of these removes, but the cache should be empty after the removes, regardless.
332      entrySet.remove(entryOf(1, 1));
333      entrySet.remove(entryOf(2, 2));
334      assertFalse(entrySet.remove(null));
335      assertFalse(entrySet.remove(entryOf(1, 1)));
336      assertFalse(entrySet.remove(entryOf(6, 6)));
337      assertFalse(entrySet.removeAll(asList(null, entryOf(1, 1), entryOf(15, 15))));
338      assertFalse(entrySet.retainAll(asList(null, entryOf(1, 1), entryOf(15, 15))));
339      checkEmpty(entrySet);
340      checkEmpty(cache);
341    }
342  }
343
344  /* ---------------- Local utilities -------------- */
345
346  /**
347   * Most of the tests in this class run against every one of these caches.
348   */
349  private Iterable<LoadingCache<Object, Object>> caches() {
350    // lots of different ways to configure a LoadingCache
351    CacheBuilderFactory factory = cacheFactory();
352    return Iterables.transform(factory.buildAllPermutations(),
353        new Function<CacheBuilder<Object, Object>, LoadingCache<Object, Object>>() {
354          @Override public LoadingCache<Object, Object> apply(
355              CacheBuilder<Object, Object> builder) {
356            return builder.build(identityLoader());
357          }
358        });
359  }
360
361  private CacheBuilderFactory cacheFactory() {
362    return new CacheBuilderFactory()
363        .withKeyStrengths(ImmutableSet.of(Strength.STRONG, Strength.WEAK))
364        .withValueStrengths(ImmutableSet.copyOf(Strength.values()))
365        .withConcurrencyLevels(ImmutableSet.of(1, 4, 16, 64))
366        .withMaximumSizes(ImmutableSet.of(0, 1, 10, 100, 1000))
367        .withInitialCapacities(ImmutableSet.of(0, 1, 10, 100, 1000))
368        .withExpireAfterWrites(ImmutableSet.of(
369            DurationSpec.of(0, SECONDS),
370            DurationSpec.of(1, SECONDS),
371            DurationSpec.of(24 * 60 * 60 * 1, SECONDS)))
372        .withExpireAfterAccesses(ImmutableSet.of(
373            DurationSpec.of(0, SECONDS),
374            DurationSpec.of(1, SECONDS),
375            DurationSpec.of(24 * 60 * 60 * 1, SECONDS)))
376        .withRefreshes(ImmutableSet.of(
377            DurationSpec.of(1, SECONDS),
378            DurationSpec.of(24 * 60 * 60 * 1, SECONDS)));
379  }
380
381  private void warmUp(LoadingCache<Object, Object> cache, int minimum, int maximum) {
382    for (int i = minimum; i < maximum; i++) {
383      cache.getUnchecked(i);
384    }
385  }
386
387  private Entry<Object, Object> entryOf(Object key, Object value) {
388    return Maps.immutableEntry(key, value);
389  }
390}
391