• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.inject.internal;
18 
19 import static com.google.inject.Asserts.awaitClear;
20 import static com.google.inject.Asserts.awaitFullGc;
21 import static com.google.inject.internal.WeakKeySetUtils.assertBlacklisted;
22 import static com.google.inject.internal.WeakKeySetUtils.assertInSet;
23 import static com.google.inject.internal.WeakKeySetUtils.assertNotBlacklisted;
24 import static com.google.inject.internal.WeakKeySetUtils.assertNotInSet;
25 import static com.google.inject.internal.WeakKeySetUtils.assertSourceNotInSet;
26 
27 import com.google.common.collect.ImmutableList;
28 import com.google.common.collect.ImmutableMap;
29 import com.google.common.collect.ImmutableSet;
30 import com.google.inject.AbstractModule;
31 import com.google.inject.Binding;
32 import com.google.inject.Guice;
33 import com.google.inject.Injector;
34 import com.google.inject.Key;
35 import com.google.inject.Scope;
36 import com.google.inject.TypeLiteral;
37 import com.google.inject.spi.ModuleAnnotatedMethodScannerBinding;
38 import com.google.inject.spi.ProvisionListenerBinding;
39 import com.google.inject.spi.ScopeBinding;
40 import com.google.inject.spi.TypeConverterBinding;
41 import com.google.inject.spi.TypeListenerBinding;
42 import java.lang.annotation.Annotation;
43 import java.lang.ref.WeakReference;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.Set;
47 import junit.framework.TestCase;
48 
49 /**
50  * Tests for {@link WeakKeySet}.
51  *
52  * <p>Multibinding specific tests can be found in MultibinderTest and MapBinderTest.
53  *
54  * @author dweis@google.com (Daniel Weis)
55  */
56 public class WeakKeySetTest extends TestCase {
57 
58   private WeakKeySet set;
59 
60   @Override
setUp()61   protected void setUp() throws Exception {
62     set = new WeakKeySet(new Object());
63   }
64 
testEviction()65   public void testEviction() {
66     TestState state = new TestState();
67     Key<Integer> key = Key.get(Integer.class);
68     Object source = new Object();
69 
70     WeakReference<Key<Integer>> weakKeyRef = new WeakReference<>(key);
71 
72     set.add(key, state, source);
73     assertInSet(set, key, 1, source);
74 
75     state = null;
76 
77     awaitFullGc();
78 
79     assertNotInSet(set, Key.get(Integer.class));
80 
81     // Ensure there are no hanging references.
82     key = null;
83     awaitClear(weakKeyRef);
84   }
85 
testEviction_nullSource()86   public void testEviction_nullSource() {
87     TestState state = new TestState();
88     Key<Integer> key = Key.get(Integer.class);
89     Object source = null;
90 
91     WeakReference<Key<Integer>> weakKeyRef = new WeakReference<>(key);
92 
93     set.add(key, state, source);
94     assertInSet(set, key, 1, source);
95 
96     state = null;
97 
98     awaitFullGc();
99 
100     assertNotInSet(set, Key.get(Integer.class));
101 
102     // Ensure there are no hanging references.
103     key = null;
104     awaitClear(weakKeyRef);
105   }
106 
testEviction_keyOverlap_2x()107   public void testEviction_keyOverlap_2x() {
108     TestState state1 = new TestState();
109     TestState state2 = new TestState();
110     Key<Integer> key1 = Key.get(Integer.class);
111     Key<Integer> key2 = Key.get(Integer.class);
112     Object source1 = new Object();
113     Object source2 = new Object();
114 
115     set.add(key1, state1, source1);
116     assertInSet(set, key1, 1, source1);
117 
118     set.add(key2, state2, source2);
119     assertInSet(set, key2, 2, source1, source2);
120 
121     WeakReference<Key<Integer>> weakKey1Ref = new WeakReference<>(key1);
122     WeakReference<Key<Integer>> weakKey2Ref = new WeakReference<>(key2);
123     WeakReference<Object> weakSource1Ref = new WeakReference<>(source1);
124     WeakReference<Object> weakSource2Ref = new WeakReference<>(source2);
125 
126     Key<Integer> key = key1 = key2 = Key.get(Integer.class);
127     state1 = null;
128 
129     awaitFullGc();
130 
131     assertSourceNotInSet(set, key, source1);
132     assertInSet(set, key, 1, source2);
133 
134     source1 = source2 = null;
135 
136     awaitClear(weakSource1Ref);
137     // Key1 will be referenced as the key in the sources backingSet and won't be
138     // GC'd.
139 
140     // Should not be GC'd until state2 goes away.
141     assertNotNull(weakSource2Ref.get());
142 
143     state2 = null;
144 
145     awaitFullGc();
146 
147     assertNotInSet(set, key);
148 
149     awaitClear(weakKey2Ref);
150     awaitClear(weakSource2Ref);
151     // Now that the backing set is emptied, key1 is released.
152     awaitClear(weakKey1Ref);
153   }
154 
testNoEviction_keyOverlap_2x()155   public void testNoEviction_keyOverlap_2x() {
156     TestState state1 = new TestState();
157     TestState state2 = new TestState();
158     Key<Integer> key1 = Key.get(Integer.class);
159     Key<Integer> key2 = Key.get(Integer.class);
160     Object source1 = new Object();
161     Object source2 = new Object();
162 
163     set.add(key1, state1, source1);
164     assertInSet(set, key1, 1, source1);
165 
166     set.add(key2, state2, source2);
167     assertInSet(set, key2, 2, source1, source2);
168 
169     WeakReference<Key<Integer>> weakKey1Ref = new WeakReference<>(key1);
170     WeakReference<Key<Integer>> weakKey2Ref = new WeakReference<>(key2);
171 
172     Key<Integer> key = key1 = key2 = Key.get(Integer.class);
173 
174     awaitFullGc();
175     assertInSet(set, key, 2, source1, source2);
176 
177     // Ensure the keys don't get GC'd when states are still referenced. key1 will be present in the
178     // as the map key but key2 could be GC'd if the implementation does something wrong.
179     assertNotNull(weakKey1Ref.get());
180     assertNotNull(weakKey2Ref.get());
181   }
182 
testEviction_keyAndSourceOverlap_null()183   public void testEviction_keyAndSourceOverlap_null() {
184     TestState state1 = new TestState();
185     TestState state2 = new TestState();
186     Key<Integer> key1 = Key.get(Integer.class);
187     Key<Integer> key2 = Key.get(Integer.class);
188     Object source = null;
189 
190     set.add(key1, state1, source);
191     assertInSet(set, key1, 1, source);
192 
193     set.add(key2, state2, source);
194     // Same source so still only one value.
195     assertInSet(set, key2, 1, source);
196     assertInSet(set, key1, 1, source);
197 
198     WeakReference<Key<Integer>> weakKey1Ref = new WeakReference<>(key1);
199     WeakReference<Key<Integer>> weakKey2Ref = new WeakReference<>(key2);
200     WeakReference<Object> weakSourceRef = new WeakReference<>(source);
201 
202     Key<Integer> key = key1 = key2 = Key.get(Integer.class);
203     state1 = null;
204 
205     awaitFullGc();
206     // Should still have a single source.
207     assertInSet(set, key, 1, source);
208 
209     source = null;
210 
211     awaitClear(weakSourceRef);
212     // Key1 will be referenced as the key in the sources backingSet and won't be
213     // GC'd.
214 
215     state2 = null;
216 
217     awaitFullGc();
218     assertNotInSet(set, key);
219 
220     awaitClear(weakKey2Ref);
221     awaitClear(weakSourceRef);
222     // Now that the backing set is emptied, key1 is released.
223     awaitClear(weakKey1Ref);
224   }
225 
testEviction_keyAndSourceOverlap_nonNull()226   public void testEviction_keyAndSourceOverlap_nonNull() {
227     TestState state1 = new TestState();
228     TestState state2 = new TestState();
229     Key<Integer> key1 = Key.get(Integer.class);
230     Key<Integer> key2 = Key.get(Integer.class);
231     Object source = new Object();
232 
233     set.add(key1, state1, source);
234     assertInSet(set, key1, 1, source);
235 
236     set.add(key2, state2, source);
237     // Same source so still only one value.
238     assertInSet(set, key2, 1, source);
239 
240     WeakReference<Key<Integer>> weakKey1Ref = new WeakReference<>(key1);
241     WeakReference<Key<Integer>> weakKey2Ref = new WeakReference<>(key2);
242     WeakReference<Object> weakSourceRef = new WeakReference<>(source);
243 
244     Key<Integer> key = key1 = key2 = Key.get(Integer.class);
245     state1 = null;
246 
247     awaitFullGc();
248 
249     // Same source so still only one value.
250     assertInSet(set, key, 1, source);
251     assertInSet(set, key1, 1, source);
252 
253     source = null;
254 
255     awaitFullGc();
256     assertNotNull(weakSourceRef.get());
257     // Key1 will be referenced as the key in the sources backingSet and won't be
258     // GC'd.
259 
260     state2 = null;
261 
262     awaitFullGc();
263 
264     assertNotInSet(set, key);
265 
266     awaitClear(weakKey2Ref);
267     awaitClear(weakSourceRef);
268     // Now that the backing set is emptied, key1 is released.
269     awaitClear(weakKey1Ref);
270   }
271 
testEviction_keyOverlap_3x()272   public void testEviction_keyOverlap_3x() {
273     TestState state1 = new TestState();
274     TestState state2 = new TestState();
275     TestState state3 = new TestState();
276     Key<Integer> key1 = Key.get(Integer.class);
277     Key<Integer> key2 = Key.get(Integer.class);
278     Key<Integer> key3 = Key.get(Integer.class);
279     Object source1 = new Object();
280     Object source2 = new Object();
281     Object source3 = new Object();
282 
283     set.add(key1, state1, source1);
284     assertInSet(set, key1, 1, source1);
285 
286     set.add(key2, state2, source2);
287     assertInSet(set, key1, 2, source1, source2);
288 
289     set.add(key3, state3, source3);
290     assertInSet(set, key1, 3, source1, source2, source3);
291 
292     WeakReference<Key<Integer>> weakKey1Ref = new WeakReference<>(key1);
293     WeakReference<Key<Integer>> weakKey2Ref = new WeakReference<>(key2);
294     WeakReference<Key<Integer>> weakKey3Ref = new WeakReference<>(key3);
295     WeakReference<Object> weakSource1Ref = new WeakReference<>(source1);
296     WeakReference<Object> weakSource2Ref = new WeakReference<>(source2);
297     WeakReference<Object> weakSource3Ref = new WeakReference<>(source3);
298 
299     Key<Integer> key = key1 = key2 = key3 = Key.get(Integer.class);
300     state1 = null;
301 
302     awaitFullGc();
303     assertSourceNotInSet(set, key, source1);
304     assertInSet(set, key, 2, source2, source3);
305 
306     source1 = null;
307     // Key1 will be referenced as the key in the sources backingSet and won't be
308     // GC'd.
309     awaitClear(weakSource1Ref);
310 
311     state2 = null;
312     awaitFullGc();
313     assertSourceNotInSet(set, key, source2);
314     assertInSet(set, key, 1, source3);
315 
316     awaitClear(weakKey2Ref);
317 
318     source2 = null;
319     awaitClear(weakSource2Ref);
320     // Key1 will be referenced as the key in the sources backingSet and won't be
321     // GC'd.
322 
323     state3 = null;
324     awaitFullGc();
325     assertNotInSet(set, key);
326 
327     awaitClear(weakKey3Ref);
328     source3 = null;
329     awaitClear(weakSource3Ref);
330     // Now that the backing set is emptied, key1 is released.
331     awaitClear(weakKey1Ref);
332   }
333 
testWeakKeySet_integration()334   public void testWeakKeySet_integration() {
335     Injector parentInjector =
336         Guice.createInjector(
337             new AbstractModule() {
338               @Override
339               protected void configure() {
340                 bind(Integer.class).toInstance(4);
341               }
342             });
343     assertNotBlacklisted(parentInjector, Key.get(String.class));
344 
345     Injector childInjector =
346         parentInjector.createChildInjector(
347             new AbstractModule() {
348               @Override
349               protected void configure() {
350                 bind(String.class).toInstance("bar");
351               }
352             });
353     WeakReference<Injector> weakRef = new WeakReference<>(childInjector);
354     assertBlacklisted(parentInjector, Key.get(String.class));
355 
356     // Clear the ref, GC, and ensure that we are no longer blacklisting.
357     childInjector = null;
358     awaitClear(weakRef);
359     assertNotBlacklisted(parentInjector, Key.get(String.class));
360   }
361 
testWeakKeySet_integration_multipleChildren()362   public void testWeakKeySet_integration_multipleChildren() {
363     Injector parentInjector =
364         Guice.createInjector(
365             new AbstractModule() {
366               @Override
367               protected void configure() {
368                 bind(Integer.class).toInstance(4);
369               }
370             });
371     assertNotBlacklisted(parentInjector, Key.get(String.class));
372     assertNotBlacklisted(parentInjector, Key.get(Long.class));
373 
374     Injector childInjector1 =
375         parentInjector.createChildInjector(
376             new AbstractModule() {
377               @Override
378               protected void configure() {
379                 bind(String.class).toInstance("foo");
380               }
381             });
382     WeakReference<Injector> weakRef1 = new WeakReference<>(childInjector1);
383     assertBlacklisted(parentInjector, Key.get(String.class));
384     assertNotBlacklisted(parentInjector, Key.get(Long.class));
385 
386     Injector childInjector2 =
387         parentInjector.createChildInjector(
388             new AbstractModule() {
389               @Override
390               protected void configure() {
391                 bind(Long.class).toInstance(6L);
392               }
393             });
394     WeakReference<Injector> weakRef2 = new WeakReference<>(childInjector2);
395     assertBlacklisted(parentInjector, Key.get(String.class));
396     assertBlacklisted(parentInjector, Key.get(Long.class));
397 
398     // Clear ref1, GC, and ensure that we still blacklist.
399     childInjector1 = null;
400     awaitClear(weakRef1);
401     assertNotBlacklisted(parentInjector, Key.get(String.class));
402     assertBlacklisted(parentInjector, Key.get(Long.class));
403 
404     // Clear the ref, GC, and ensure that we are no longer blacklisting.
405     childInjector2 = null;
406     awaitClear(weakRef2);
407     assertNotBlacklisted(parentInjector, Key.get(String.class));
408     assertNotBlacklisted(parentInjector, Key.get(Long.class));
409   }
410 
testWeakKeySet_integration_multipleChildren_overlappingKeys()411   public void testWeakKeySet_integration_multipleChildren_overlappingKeys() {
412     Injector parentInjector =
413         Guice.createInjector(
414             new AbstractModule() {
415               @Override
416               protected void configure() {
417                 bind(Integer.class).toInstance(4);
418               }
419             });
420     assertNotBlacklisted(parentInjector, Key.get(String.class));
421 
422     Injector childInjector1 =
423         parentInjector.createChildInjector(
424             new AbstractModule() {
425               @Override
426               protected void configure() {
427                 bind(String.class).toInstance("foo");
428               }
429             });
430     WeakReference<Injector> weakRef1 = new WeakReference<>(childInjector1);
431     assertBlacklisted(parentInjector, Key.get(String.class));
432 
433     Injector childInjector2 =
434         parentInjector.createChildInjector(
435             new AbstractModule() {
436               @Override
437               protected void configure() {
438                 bind(String.class).toInstance("bar");
439               }
440             });
441     WeakReference<Injector> weakRef2 = new WeakReference<>(childInjector2);
442     assertBlacklisted(parentInjector, Key.get(String.class));
443 
444     // Clear ref1, GC, and ensure that we still blacklist.
445     childInjector1 = null;
446     awaitClear(weakRef1);
447     assertBlacklisted(parentInjector, Key.get(String.class));
448 
449     // Clear the ref, GC, and ensure that we are no longer blacklisting.
450     childInjector2 = null;
451     awaitClear(weakRef2);
452     assertNotBlacklisted(parentInjector, Key.get(String.class));
453   }
454 
455   private static class TestState implements State {
456     @Override
parent()457     public State parent() {
458       return new TestState();
459     }
460 
461     @Override
getExplicitBinding(Key<T> key)462     public <T> BindingImpl<T> getExplicitBinding(Key<T> key) {
463       return null;
464     }
465 
466     @Override
getExplicitBindingsThisLevel()467     public Map<Key<?>, Binding<?>> getExplicitBindingsThisLevel() {
468       throw new UnsupportedOperationException();
469     }
470 
471     @Override
putBinding(Key<?> key, BindingImpl<?> binding)472     public void putBinding(Key<?> key, BindingImpl<?> binding) {
473       throw new UnsupportedOperationException();
474     }
475 
476     @Override
getScopeBinding(Class<? extends Annotation> scopingAnnotation)477     public ScopeBinding getScopeBinding(Class<? extends Annotation> scopingAnnotation) {
478       return null;
479     }
480 
481     @Override
putScopeBinding(Class<? extends Annotation> annotationType, ScopeBinding scope)482     public void putScopeBinding(Class<? extends Annotation> annotationType, ScopeBinding scope) {
483       throw new UnsupportedOperationException();
484     }
485 
486     @Override
addConverter(TypeConverterBinding typeConverterBinding)487     public void addConverter(TypeConverterBinding typeConverterBinding) {
488       throw new UnsupportedOperationException();
489     }
490 
491     @Override
getConverter( String stringValue, TypeLiteral<?> type, Errors errors, Object source)492     public TypeConverterBinding getConverter(
493         String stringValue, TypeLiteral<?> type, Errors errors, Object source) {
494       throw new UnsupportedOperationException();
495     }
496 
497     @Override
getConvertersThisLevel()498     public Iterable<TypeConverterBinding> getConvertersThisLevel() {
499       return ImmutableSet.of();
500     }
501 
502     /*if[AOP]*/
503     @Override
addMethodAspect(MethodAspect methodAspect)504     public void addMethodAspect(MethodAspect methodAspect) {
505       throw new UnsupportedOperationException();
506     }
507 
508     @Override
getMethodAspects()509     public ImmutableList<MethodAspect> getMethodAspects() {
510       return ImmutableList.of();
511     }
512     /*end[AOP]*/
513 
514     @Override
addTypeListener(TypeListenerBinding typeListenerBinding)515     public void addTypeListener(TypeListenerBinding typeListenerBinding) {
516       throw new UnsupportedOperationException();
517     }
518 
519     @Override
getTypeListenerBindings()520     public List<TypeListenerBinding> getTypeListenerBindings() {
521       return ImmutableList.of();
522     }
523 
524     @Override
addProvisionListener(ProvisionListenerBinding provisionListenerBinding)525     public void addProvisionListener(ProvisionListenerBinding provisionListenerBinding) {
526       throw new UnsupportedOperationException();
527     }
528 
529     @Override
getProvisionListenerBindings()530     public List<ProvisionListenerBinding> getProvisionListenerBindings() {
531       return ImmutableList.of();
532     }
533 
534     @Override
addScanner(ModuleAnnotatedMethodScannerBinding scanner)535     public void addScanner(ModuleAnnotatedMethodScannerBinding scanner) {
536       throw new UnsupportedOperationException();
537     }
538 
539     @Override
getScannerBindings()540     public List<ModuleAnnotatedMethodScannerBinding> getScannerBindings() {
541       return ImmutableList.of();
542     }
543 
544     @Override
blacklist(Key<?> key, State state, Object source)545     public void blacklist(Key<?> key, State state, Object source) {}
546 
547     @Override
isBlacklisted(Key<?> key)548     public boolean isBlacklisted(Key<?> key) {
549       return true;
550     }
551 
552     @Override
getSourcesForBlacklistedKey(Key<?> key)553     public Set<Object> getSourcesForBlacklistedKey(Key<?> key) {
554       throw new UnsupportedOperationException();
555     }
556 
557     @Override
lock()558     public Object lock() {
559       throw new UnsupportedOperationException();
560     }
561 
singletonCreationLock()562     public Object singletonCreationLock() {
563       throw new UnsupportedOperationException();
564     }
565 
566     @Override
getScopes()567     public Map<Class<? extends Annotation>, Scope> getScopes() {
568       return ImmutableMap.of();
569     }
570   }
571 }
572