• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
15 package com.google.common.cache;
16 
17 import static com.google.common.cache.LocalCache.Strength.STRONG;
18 import static com.google.common.collect.Maps.immutableEntry;
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import com.google.common.base.Function;
22 import com.google.common.cache.LocalCache.Strength;
23 import com.google.common.cache.TestingRemovalListeners.CountingRemovalListener;
24 import com.google.common.collect.ImmutableSet;
25 import com.google.common.collect.Iterables;
26 
27 import junit.framework.TestCase;
28 
29 import java.lang.ref.WeakReference;
30 
31 /**
32  * Tests of basic {@link LoadingCache} operations with all possible combinations of key & value
33  * strengths.
34  *
35  * @author mike nonemacher
36  */
37 public class CacheReferencesTest extends TestCase {
38 
39   private static final CacheLoader<Key,String> KEY_TO_STRING_LOADER =
40       new CacheLoader<Key, String>() {
41         @Override public String load(Key key) {
42           return key.toString();
43         }
44       };
45 
factoryWithAllKeyStrengths()46   private CacheBuilderFactory factoryWithAllKeyStrengths() {
47     return new CacheBuilderFactory()
48         .withKeyStrengths(ImmutableSet.of(STRONG, Strength.WEAK))
49         .withValueStrengths(ImmutableSet.of(STRONG, Strength.WEAK, Strength.SOFT));
50   }
51 
caches()52   private Iterable<LoadingCache<Key, String>> caches() {
53     CacheBuilderFactory factory = factoryWithAllKeyStrengths();
54     return Iterables.transform(factory.buildAllPermutations(),
55         new Function<CacheBuilder<Object, Object>, LoadingCache<Key, String>>() {
56           @Override public LoadingCache<Key, String> apply(CacheBuilder<Object, Object> builder) {
57             return builder.build(KEY_TO_STRING_LOADER);
58           }
59         });
60   }
61 
62   public void testContainsKeyAndValue() {
63     for (LoadingCache<Key, String> cache : caches()) {
64       // maintain strong refs so these won't be collected, regardless of cache's key/value strength
65       Key key = new Key(1);
66       String value = key.toString();
67       assertSame(value, cache.getUnchecked(key));
68       assertTrue(cache.asMap().containsKey(key));
69       assertTrue(cache.asMap().containsValue(value));
70       assertEquals(1, cache.size());
71     }
72   }
73 
74   public void testClear() {
75     for (LoadingCache<Key, String> cache : caches()) {
76       Key key = new Key(1);
77       String value = key.toString();
78       assertSame(value, cache.getUnchecked(key));
79       assertFalse(cache.asMap().isEmpty());
80       cache.invalidateAll();
81       assertEquals(0, cache.size());
82       assertTrue(cache.asMap().isEmpty());
83       assertFalse(cache.asMap().containsKey(key));
84       assertFalse(cache.asMap().containsValue(value));
85     }
86   }
87 
88   public void testKeySetEntrySetValues() {
89     for (LoadingCache<Key, String> cache : caches()) {
90       Key key1 = new Key(1);
91       String value1 = key1.toString();
92       Key key2 = new Key(2);
93       String value2 = key2.toString();
94       assertSame(value1, cache.getUnchecked(key1));
95       assertSame(value2, cache.getUnchecked(key2));
96       assertEquals(ImmutableSet.of(key1, key2), cache.asMap().keySet());
97       assertThat(cache.asMap().values()).has().exactly(value1, value2);
98       assertEquals(ImmutableSet.of(immutableEntry(key1, value1), immutableEntry(key2, value2)),
99           cache.asMap().entrySet());
100     }
101   }
102 
103   public void testInvalidate() {
104     for (LoadingCache<Key, String> cache : caches()) {
105       Key key1 = new Key(1);
106       String value1 = key1.toString();
107       Key key2 = new Key(2);
108       String value2 = key2.toString();
109       assertSame(value1, cache.getUnchecked(key1));
110       assertSame(value2, cache.getUnchecked(key2));
111       cache.invalidate(key1);
112       assertFalse(cache.asMap().containsKey(key1));
113       assertTrue(cache.asMap().containsKey(key2));
114       assertEquals(1, cache.size());
115       assertEquals(ImmutableSet.of(key2), cache.asMap().keySet());
116       assertThat(cache.asMap().values()).has().item(value2);
117       assertEquals(ImmutableSet.of(immutableEntry(key2, value2)), cache.asMap().entrySet());
118     }
119   }
120 
121   // fails in Maven with 64-bit JDK: http://code.google.com/p/guava-libraries/issues/detail?id=1568
122 
123   private void assertCleanup(LoadingCache<Integer, String> cache,
124       CountingRemovalListener<Integer, String> removalListener) {
125 
126     // initialSize will most likely be 2, but it's possible for the GC to have already run, so we'll
127     // observe a size of 1
128     long initialSize = cache.size();
129     assertTrue(initialSize == 1 || initialSize == 2);
130 
131     // wait up to 5s
132     byte[] filler = new byte[1024];
133     for (int i = 0; i < 500; i++) {
134       System.gc();
135 
136       CacheTesting.drainReferenceQueues(cache);
137       if (cache.size() == 1) {
138         break;
139       }
140       try {
141         Thread.sleep(10);
142       } catch (InterruptedException e) { /* ignore */}
143       try {
144         // Fill up heap so soft references get cleared.
145         filler = new byte[Math.max(filler.length, filler.length * 2)];
146       } catch (OutOfMemoryError e) {}
147     }
148 
149     CacheTesting.processPendingNotifications(cache);
150     assertEquals(1, cache.size());
151     assertEquals(1, removalListener.getCount());
152   }
153 
154   // A simple type whose .toString() will return the same value each time, but without maintaining
155   // a strong reference to that value.
156   static class Key {
157     private final int value;
158     private WeakReference<String> toString;
159 
160     Key(int value) {
161       this.value = value;
162     }
163 
164     @Override public synchronized String toString() {
165       String s;
166       if (toString != null) {
167         s = toString.get();
168         if (s != null) {
169           return s;
170         }
171       }
172       s = Integer.toString(value);
173       toString = new WeakReference<String>(s);
174       return s;
175     }
176   }
177 }
178