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