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