/core/test/com/google/inject/internal/WeakKeySetTest.java
Java | 528 lines | 360 code | 116 blank | 52 comment | 0 complexity | 4fac5e5f308088a3050686c49254a716 MD5 | raw file
- /**
- * Copyright (C) 2014 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package com.google.inject.internal;
- import static com.google.inject.Asserts.awaitClear;
- import static com.google.inject.Asserts.awaitFullGc;
- import static com.google.inject.internal.WeakKeySetUtils.assertBlacklisted;
- import static com.google.inject.internal.WeakKeySetUtils.assertInSet;
- import static com.google.inject.internal.WeakKeySetUtils.assertNotBlacklisted;
- import static com.google.inject.internal.WeakKeySetUtils.assertNotInSet;
- import static com.google.inject.internal.WeakKeySetUtils.assertSourceNotInSet;
- import com.google.common.collect.ImmutableList;
- import com.google.common.collect.ImmutableMap;
- import com.google.common.collect.ImmutableSet;
- import com.google.inject.AbstractModule;
- import com.google.inject.Binding;
- import com.google.inject.Guice;
- import com.google.inject.Injector;
- import com.google.inject.Key;
- import com.google.inject.Scope;
- import com.google.inject.TypeLiteral;
- import com.google.inject.spi.ModuleAnnotatedMethodScannerBinding;
- import com.google.inject.spi.ProvisionListenerBinding;
- import com.google.inject.spi.ScopeBinding;
- import com.google.inject.spi.TypeConverterBinding;
- import com.google.inject.spi.TypeListenerBinding;
- import junit.framework.TestCase;
- import java.lang.annotation.Annotation;
- import java.lang.ref.WeakReference;
- import java.util.List;
- import java.util.Map;
- import java.util.Set;
- /**
- * Tests for {@link WeakKeySet}.
- * <p>
- * Multibinding specific tests can be found in MultibinderTest and MapBinderTest.
- *
- * @author dweis@google.com (Daniel Weis)
- */
- public class WeakKeySetTest extends TestCase {
- private WeakKeySet set;
- @Override
- protected void setUp() throws Exception {
- set = new WeakKeySet(new Object());
- }
- public void testEviction() {
- TestState state = new TestState();
- Key<Integer> key = Key.get(Integer.class);
- Object source = new Object();
-
- WeakReference<Key<Integer>> weakKeyRef = new WeakReference<Key<Integer>>(key);
- set.add(key, state, source);
- assertInSet(set, key, 1, source);
- state = null;
- awaitFullGc();
- assertNotInSet(set, Key.get(Integer.class));
- // Ensure there are no hanging references.
- key = null;
- awaitClear(weakKeyRef);
- }
-
- public void testEviction_nullSource() {
- TestState state = new TestState();
- Key<Integer> key = Key.get(Integer.class);
- Object source = null;
-
- WeakReference<Key<Integer>> weakKeyRef = new WeakReference<Key<Integer>>(key);
- set.add(key, state, source);
- assertInSet(set, key, 1, source);
- state = null;
- awaitFullGc();
- assertNotInSet(set, Key.get(Integer.class));
- // Ensure there are no hanging references.
- key = null;
- awaitClear(weakKeyRef);
- }
- public void testEviction_keyOverlap_2x() {
- TestState state1 = new TestState();
- TestState state2 = new TestState();
- Key<Integer> key1 = Key.get(Integer.class);
- Key<Integer> key2 = Key.get(Integer.class);
- Object source1 = new Object();
- Object source2 = new Object();
- set.add(key1, state1, source1);
- assertInSet(set, key1, 1, source1);
- set.add(key2, state2, source2);
- assertInSet(set, key2, 2, source1, source2);
- WeakReference<Key<Integer>> weakKey1Ref = new WeakReference<Key<Integer>>(key1);
- WeakReference<Key<Integer>> weakKey2Ref = new WeakReference<Key<Integer>>(key2);
- WeakReference<Object> weakSource1Ref = new WeakReference<Object>(source1);
- WeakReference<Object> weakSource2Ref = new WeakReference<Object>(source2);
- Key<Integer> key = key1 = key2 = Key.get(Integer.class);
- state1 = null;
- awaitFullGc();
- assertSourceNotInSet(set, key, source1);
- assertInSet(set, key, 1, source2);
- source1 = source2 = null;
-
- awaitClear(weakSource1Ref);
- // Key1 will be referenced as the key in the sources backingSet and won't be
- // GC'd.
-
- // Should not be GC'd until state2 goes away.
- assertNotNull(weakSource2Ref.get());
- state2 = null;
- awaitFullGc();
- assertNotInSet(set, key);
- awaitClear(weakKey2Ref);
- awaitClear(weakSource2Ref);
- // Now that the backing set is emptied, key1 is released.
- awaitClear(weakKey1Ref);
- }
-
- public void testNoEviction_keyOverlap_2x() {
- TestState state1 = new TestState();
- TestState state2 = new TestState();
- Key<Integer> key1 = Key.get(Integer.class);
- Key<Integer> key2 = Key.get(Integer.class);
- Object source1 = new Object();
- Object source2 = new Object();
- set.add(key1, state1, source1);
- assertInSet(set, key1, 1, source1);
- set.add(key2, state2, source2);
- assertInSet(set, key2, 2, source1, source2);
- WeakReference<Key<Integer>> weakKey1Ref = new WeakReference<Key<Integer>>(key1);
- WeakReference<Key<Integer>> weakKey2Ref = new WeakReference<Key<Integer>>(key2);
- Key<Integer> key = key1 = key2 = Key.get(Integer.class);
- awaitFullGc();
- assertInSet(set, key, 2, source1, source2);
- // Ensure the keys don't get GC'd when states are still referenced. key1 will be present in the
- // as the map key but key2 could be GC'd if the implementation does something wrong.
- assertNotNull(weakKey1Ref.get());
- assertNotNull(weakKey2Ref.get());
- }
- public void testEviction_keyAndSourceOverlap_null() {
- TestState state1 = new TestState();
- TestState state2 = new TestState();
- Key<Integer> key1 = Key.get(Integer.class);
- Key<Integer> key2 = Key.get(Integer.class);
- Object source = null;
- set.add(key1, state1, source);
- assertInSet(set, key1, 1, source);
- set.add(key2, state2, source);
- // Same source so still only one value.
- assertInSet(set, key2, 1, source);
- assertInSet(set, key1, 1, source);
- WeakReference<Key<Integer>> weakKey1Ref = new WeakReference<Key<Integer>>(key1);
- WeakReference<Key<Integer>> weakKey2Ref = new WeakReference<Key<Integer>>(key2);
- WeakReference<Object> weakSourceRef = new WeakReference<Object>(source);
- Key<Integer> key = key1 = key2 = Key.get(Integer.class);
- state1 = null;
- awaitFullGc();
- // Should still have a single source.
- assertInSet(set, key, 1, source);
- source = null;
- awaitClear(weakSourceRef);
- // Key1 will be referenced as the key in the sources backingSet and won't be
- // GC'd.
- state2 = null;
- awaitFullGc();
- assertNotInSet(set, key);
- awaitClear(weakKey2Ref);
- awaitClear(weakSourceRef);
- // Now that the backing set is emptied, key1 is released.
- awaitClear(weakKey1Ref);
- }
-
- public void testEviction_keyAndSourceOverlap_nonNull() {
- TestState state1 = new TestState();
- TestState state2 = new TestState();
- Key<Integer> key1 = Key.get(Integer.class);
- Key<Integer> key2 = Key.get(Integer.class);
- Object source = new Object();
- set.add(key1, state1, source);
- assertInSet(set, key1, 1, source);
- set.add(key2, state2, source);
- // Same source so still only one value.
- assertInSet(set, key2, 1, source);
- WeakReference<Key<Integer>> weakKey1Ref = new WeakReference<Key<Integer>>(key1);
- WeakReference<Key<Integer>> weakKey2Ref = new WeakReference<Key<Integer>>(key2);
- WeakReference<Object> weakSourceRef = new WeakReference<Object>(source);
- Key<Integer> key = key1 = key2 = Key.get(Integer.class);
- state1 = null;
- awaitFullGc();
- // Same source so still only one value.
- assertInSet(set, key, 1, source);
- assertInSet(set, key1, 1, source);
-
- source = null;
- awaitFullGc();
- assertNotNull(weakSourceRef.get());
- // Key1 will be referenced as the key in the sources backingSet and won't be
- // GC'd.
- state2 = null;
- awaitFullGc();
- assertNotInSet(set, key);
- awaitClear(weakKey2Ref);
- awaitClear(weakSourceRef);
- // Now that the backing set is emptied, key1 is released.
- awaitClear(weakKey1Ref);
- }
- public void testEviction_keyOverlap_3x() {
- TestState state1 = new TestState();
- TestState state2 = new TestState();
- TestState state3 = new TestState();
- Key<Integer> key1 = Key.get(Integer.class);
- Key<Integer> key2 = Key.get(Integer.class);
- Key<Integer> key3 = Key.get(Integer.class);
- Object source1 = new Object();
- Object source2 = new Object();
- Object source3 = new Object();
- set.add(key1, state1, source1);
- assertInSet(set, key1, 1, source1);
- set.add(key2, state2, source2);
- assertInSet(set, key1, 2, source1, source2);
- set.add(key3, state3, source3);
- assertInSet(set, key1, 3, source1, source2, source3);
- WeakReference<Key<Integer>> weakKey1Ref = new WeakReference<Key<Integer>>(key1);
- WeakReference<Key<Integer>> weakKey2Ref = new WeakReference<Key<Integer>>(key2);
- WeakReference<Key<Integer>> weakKey3Ref = new WeakReference<Key<Integer>>(key3);
- WeakReference<Object> weakSource1Ref = new WeakReference<Object>(source1);
- WeakReference<Object> weakSource2Ref = new WeakReference<Object>(source2);
- WeakReference<Object> weakSource3Ref = new WeakReference<Object>(source3);
- Key<Integer> key = key1 = key2 = key3 = Key.get(Integer.class);
- state1 = null;
- awaitFullGc();
- assertSourceNotInSet(set, key, source1);
- assertInSet(set, key, 2, source2, source3);
- source1 = null;
- // Key1 will be referenced as the key in the sources backingSet and won't be
- // GC'd.
- awaitClear(weakSource1Ref);
- state2 = null;
- awaitFullGc();
- assertSourceNotInSet(set, key, source2);
- assertInSet(set, key, 1, source3);
- awaitClear(weakKey2Ref);
-
- source2 = null;
- awaitClear(weakSource2Ref);
- // Key1 will be referenced as the key in the sources backingSet and won't be
- // GC'd.
- state3 = null;
- awaitFullGc();
- assertNotInSet(set, key);
- awaitClear(weakKey3Ref);
- source3 = null;
- awaitClear(weakSource3Ref);
- // Now that the backing set is emptied, key1 is released.
- awaitClear(weakKey1Ref);
- }
- public void testWeakKeySet_integration() {
- Injector parentInjector = Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- bind(Integer.class).toInstance(4);
- }
- });
- assertNotBlacklisted(parentInjector, Key.get(String.class));
- Injector childInjector = parentInjector.createChildInjector(new AbstractModule() {
- @Override protected void configure() {
- bind(String.class).toInstance("bar");
- }
- });
- WeakReference<Injector> weakRef = new WeakReference<Injector>(childInjector);
- assertBlacklisted(parentInjector, Key.get(String.class));
-
- // Clear the ref, GC, and ensure that we are no longer blacklisting.
- childInjector = null;
- awaitClear(weakRef);
- assertNotBlacklisted(parentInjector, Key.get(String.class));
- }
-
- public void testWeakKeySet_integration_multipleChildren() {
- Injector parentInjector = Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- bind(Integer.class).toInstance(4);
- }
- });
- assertNotBlacklisted(parentInjector, Key.get(String.class));
- assertNotBlacklisted(parentInjector, Key.get(Long.class));
- Injector childInjector1 = parentInjector.createChildInjector(new AbstractModule() {
- @Override protected void configure() {
- bind(String.class).toInstance("foo");
- }
- });
- WeakReference<Injector> weakRef1 = new WeakReference<Injector>(childInjector1);
- assertBlacklisted(parentInjector, Key.get(String.class));
- assertNotBlacklisted(parentInjector, Key.get(Long.class));
-
- Injector childInjector2 = parentInjector.createChildInjector(new AbstractModule() {
- @Override protected void configure() {
- bind(Long.class).toInstance(6L);
- }
- });
- WeakReference<Injector> weakRef2 = new WeakReference<Injector>(childInjector2);
- assertBlacklisted(parentInjector, Key.get(String.class));
- assertBlacklisted(parentInjector, Key.get(Long.class));
-
- // Clear ref1, GC, and ensure that we still blacklist.
- childInjector1 = null;
- awaitClear(weakRef1);
- assertNotBlacklisted(parentInjector, Key.get(String.class));
- assertBlacklisted(parentInjector, Key.get(Long.class));
- // Clear the ref, GC, and ensure that we are no longer blacklisting.
- childInjector2 = null;
- awaitClear(weakRef2);
- assertNotBlacklisted(parentInjector, Key.get(String.class));
- assertNotBlacklisted(parentInjector, Key.get(Long.class));
- }
-
- public void testWeakKeySet_integration_multipleChildren_overlappingKeys() {
- Injector parentInjector = Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- bind(Integer.class).toInstance(4);
- }
- });
- assertNotBlacklisted(parentInjector, Key.get(String.class));
- Injector childInjector1 = parentInjector.createChildInjector(new AbstractModule() {
- @Override protected void configure() {
- bind(String.class).toInstance("foo");
- }
- });
- WeakReference<Injector> weakRef1 = new WeakReference<Injector>(childInjector1);
- assertBlacklisted(parentInjector, Key.get(String.class));
-
- Injector childInjector2 = parentInjector.createChildInjector(new AbstractModule() {
- @Override protected void configure() {
- bind(String.class).toInstance("bar");
- }
- });
- WeakReference<Injector> weakRef2 = new WeakReference<Injector>(childInjector2);
- assertBlacklisted(parentInjector, Key.get(String.class));
-
- // Clear ref1, GC, and ensure that we still blacklist.
- childInjector1 = null;
- awaitClear(weakRef1);
- assertBlacklisted(parentInjector, Key.get(String.class));
- // Clear the ref, GC, and ensure that we are no longer blacklisting.
- childInjector2 = null;
- awaitClear(weakRef2);
- assertNotBlacklisted(parentInjector, Key.get(String.class));
- }
- private static class TestState implements State {
- public State parent() {
- return new TestState();
- }
- public <T> BindingImpl<T> getExplicitBinding(Key<T> key) {
- return null;
- }
- public Map<Key<?>, Binding<?>> getExplicitBindingsThisLevel() {
- throw new UnsupportedOperationException();
- }
- public void putBinding(Key<?> key, BindingImpl<?> binding) {
- throw new UnsupportedOperationException();
- }
- public ScopeBinding getScopeBinding(Class<? extends Annotation> scopingAnnotation) {
- return null;
- }
- public void putScopeBinding(Class<? extends Annotation> annotationType, ScopeBinding scope) {
- throw new UnsupportedOperationException();
- }
- public void addConverter(TypeConverterBinding typeConverterBinding) {
- throw new UnsupportedOperationException();
- }
- public TypeConverterBinding getConverter(String stringValue, TypeLiteral<?> type, Errors errors,
- Object source) {
- throw new UnsupportedOperationException();
- }
- public Iterable<TypeConverterBinding> getConvertersThisLevel() {
- return ImmutableSet.of();
- }
- /*if[AOP]*/
- public void addMethodAspect(MethodAspect methodAspect) {
- throw new UnsupportedOperationException();
- }
- public ImmutableList<MethodAspect> getMethodAspects() {
- return ImmutableList.of();
- }
- /*end[AOP]*/
- public void addTypeListener(TypeListenerBinding typeListenerBinding) {
- throw new UnsupportedOperationException();
- }
- public List<TypeListenerBinding> getTypeListenerBindings() {
- return ImmutableList.of();
- }
- public void addProvisionListener(ProvisionListenerBinding provisionListenerBinding) {
- throw new UnsupportedOperationException();
- }
- public List<ProvisionListenerBinding> getProvisionListenerBindings() {
- return ImmutableList.of();
- }
- public void addScanner(ModuleAnnotatedMethodScannerBinding scanner) {
- throw new UnsupportedOperationException();
- }
- public List<ModuleAnnotatedMethodScannerBinding> getScannerBindings() {
- return ImmutableList.of();
- }
- public void blacklist(Key<?> key, State state, Object source) {
- }
- public boolean isBlacklisted(Key<?> key) {
- return true;
- }
- public Set<Object> getSourcesForBlacklistedKey(Key<?> key) {
- throw new UnsupportedOperationException();
- }
- public Object lock() {
- throw new UnsupportedOperationException();
- }
- public Object singletonCreationLock() {
- throw new UnsupportedOperationException();
- }
- public Map<Class<? extends Annotation>, Scope> getScopes() {
- return ImmutableMap.of();
- }
- }
- }