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