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