blob: 63a2da10269bca43d4465ed32c8ec6cd4b89facb [file] [log] [blame]
/**
* Copyright (C) 2008 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.multibindings;
import static com.google.inject.Asserts.asModuleChain;
import static com.google.inject.Asserts.assertContains;
import static com.google.inject.multibindings.SpiUtils.assertMapVisitor;
import static com.google.inject.multibindings.SpiUtils.instance;
import static com.google.inject.multibindings.SpiUtils.providerInstance;
import static com.google.inject.multibindings.SpiUtils.VisitType.BOTH;
import static com.google.inject.multibindings.SpiUtils.VisitType.MODULE;
import static com.google.inject.name.Names.named;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.inject.AbstractModule;
import com.google.inject.Binding;
import com.google.inject.BindingAnnotation;
import com.google.inject.ConfigurationException;
import com.google.inject.CreationException;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.Provides;
import com.google.inject.ProvisionException;
import com.google.inject.Stage;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Names;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.HasDependencies;
import com.google.inject.spi.InstanceBinding;
import com.google.inject.util.Modules;
import com.google.inject.util.Providers;
import junit.framework.TestCase;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
/**
* @author dpb@google.com (David P. Baker)
*/
public class MapBinderTest extends TestCase {
final TypeLiteral<Map<String, javax.inject.Provider<String>>> mapOfStringJavaxProvider =
new TypeLiteral<Map<String, javax.inject.Provider<String>>>() {};
final TypeLiteral<Map<String, Provider<String>>> mapOfStringProvider =
new TypeLiteral<Map<String, Provider<String>>>() {};
final TypeLiteral<Map<String, String>> mapOfString = new TypeLiteral<Map<String, String>>() {};
final TypeLiteral<Map<Integer, String>> mapOfIntString =
new TypeLiteral<Map<Integer, String>>() {};
final TypeLiteral<Map<String, Integer>> mapOfInteger = new TypeLiteral<Map<String, Integer>>() {};
final TypeLiteral<Map<String, Set<String>>> mapOfSetOfString =
new TypeLiteral<Map<String, Set<String>>>() {};
private final TypeLiteral<String> stringType = TypeLiteral.get(String.class);
private final TypeLiteral<Integer> intType = TypeLiteral.get(Integer.class);
public void testMapBinderAggregatesMultipleModules() {
Module abc = new AbstractModule() {
@Override protected void configure() {
MapBinder<String, String> multibinder = MapBinder.newMapBinder(
binder(), String.class, String.class);
multibinder.addBinding("a").toInstance("A");
multibinder.addBinding("b").toInstance("B");
multibinder.addBinding("c").toInstance("C");
}
};
Module de = new AbstractModule() {
@Override protected void configure() {
MapBinder<String, String> multibinder = MapBinder.newMapBinder(
binder(), String.class, String.class);
multibinder.addBinding("d").toInstance("D");
multibinder.addBinding("e").toInstance("E");
}
};
Injector injector = Guice.createInjector(abc, de);
Map<String, String> abcde = injector.getInstance(Key.get(mapOfString));
assertEquals(mapOf("a", "A", "b", "B", "c", "C", "d", "D", "e", "E"), abcde);
assertMapVisitor(Key.get(mapOfString), stringType, stringType, setOf(abc, de), BOTH, false, 0,
instance("a", "A"), instance("b", "B"), instance("c", "C"), instance("d", "D"), instance("e", "E"));
// just make sure these succeed
injector.getInstance(Key.get(mapOfStringProvider));
injector.getInstance(Key.get(mapOfStringJavaxProvider));
}
public void testMapBinderAggregationForAnnotationInstance() {
Module module = new AbstractModule() {
@Override protected void configure() {
MapBinder<String, String> multibinder = MapBinder.newMapBinder(
binder(), String.class, String.class, Names.named("abc"));
multibinder.addBinding("a").toInstance("A");
multibinder.addBinding("b").toInstance("B");
multibinder = MapBinder.newMapBinder(
binder(), String.class, String.class, Names.named("abc"));
multibinder.addBinding("c").toInstance("C");
}
};
Injector injector = Guice.createInjector(module);
Key<Map<String, String>> key = Key.get(mapOfString, Names.named("abc"));
Map<String, String> abc = injector.getInstance(key);
assertEquals(mapOf("a", "A", "b", "B", "c", "C"), abc);
assertMapVisitor(key, stringType, stringType, setOf(module), BOTH, false, 0,
instance("a", "A"), instance("b", "B"), instance("c", "C"));
// just make sure these succeed
injector.getInstance(Key.get(mapOfStringProvider, Names.named("abc")));
injector.getInstance(Key.get(mapOfStringJavaxProvider, Names.named("abc")));
}
public void testMapBinderAggregationForAnnotationType() {
Module module = new AbstractModule() {
@Override protected void configure() {
MapBinder<String, String> multibinder = MapBinder.newMapBinder(
binder(), String.class, String.class, Abc.class);
multibinder.addBinding("a").toInstance("A");
multibinder.addBinding("b").toInstance("B");
multibinder = MapBinder.newMapBinder(
binder(), String.class, String.class, Abc.class);
multibinder.addBinding("c").toInstance("C");
}
};
Injector injector = Guice.createInjector(module);
Key<Map<String, String>> key = Key.get(mapOfString, Abc.class);
Map<String, String> abc = injector.getInstance(key);
assertEquals(mapOf("a", "A", "b", "B", "c", "C"), abc);
assertMapVisitor(key, stringType, stringType, setOf(module), BOTH, false, 0,
instance("a", "A"), instance("b", "B"), instance("c", "C"));
// just make sure these succeed
injector.getInstance(Key.get(mapOfStringProvider, Abc.class));
injector.getInstance(Key.get(mapOfStringJavaxProvider, Abc.class));
}
public void testMapBinderWithMultipleAnnotationValueSets() {
Module module = new AbstractModule() {
@Override protected void configure() {
MapBinder<String, String> abcMapBinder = MapBinder.newMapBinder(
binder(), String.class, String.class, named("abc"));
abcMapBinder.addBinding("a").toInstance("A");
abcMapBinder.addBinding("b").toInstance("B");
abcMapBinder.addBinding("c").toInstance("C");
MapBinder<String, String> deMapBinder = MapBinder.newMapBinder(
binder(), String.class, String.class, named("de"));
deMapBinder.addBinding("d").toInstance("D");
deMapBinder.addBinding("e").toInstance("E");
}
};
Injector injector = Guice.createInjector(module);
Key<Map<String, String>> abcKey = Key.get(mapOfString, named("abc"));
Map<String, String> abc = injector.getInstance(abcKey);
Key<Map<String, String>> deKey = Key.get(mapOfString, named("de"));
Map<String, String> de = injector.getInstance(deKey);
assertEquals(mapOf("a", "A", "b", "B", "c", "C"), abc);
assertEquals(mapOf("d", "D", "e", "E"), de);
assertMapVisitor(abcKey, stringType, stringType, setOf(module), BOTH, false, 1,
instance("a", "A"), instance("b", "B"), instance("c", "C"));
assertMapVisitor(deKey, stringType, stringType, setOf(module), BOTH, false, 1,
instance("d", "D"), instance("e", "E"));
// just make sure these succeed
injector.getInstance(Key.get(mapOfStringProvider, named("abc")));
injector.getInstance(Key.get(mapOfStringJavaxProvider, named("abc")));
injector.getInstance(Key.get(mapOfStringProvider, named("de")));
injector.getInstance(Key.get(mapOfStringJavaxProvider, named("de")));
}
public void testMapBinderWithMultipleAnnotationTypeSets() {
Module module = new AbstractModule() {
@Override protected void configure() {
MapBinder<String, String> abcMapBinder = MapBinder.newMapBinder(
binder(), String.class, String.class, Abc.class);
abcMapBinder.addBinding("a").toInstance("A");
abcMapBinder.addBinding("b").toInstance("B");
abcMapBinder.addBinding("c").toInstance("C");
MapBinder<String, String> deMapBinder = MapBinder.newMapBinder(
binder(), String.class, String.class, De.class);
deMapBinder.addBinding("d").toInstance("D");
deMapBinder.addBinding("e").toInstance("E");
}
};
Injector injector = Guice.createInjector(module);
Key<Map<String, String>> abcKey = Key.get(mapOfString, Abc.class);
Map<String, String> abc = injector.getInstance(abcKey);
Key<Map<String, String>> deKey = Key.get(mapOfString, De.class);
Map<String, String> de = injector.getInstance(deKey);
assertEquals(mapOf("a", "A", "b", "B", "c", "C"), abc);
assertEquals(mapOf("d", "D", "e", "E"), de);
assertMapVisitor(abcKey, stringType, stringType, setOf(module), BOTH, false, 1,
instance("a", "A"), instance("b", "B"), instance("c", "C"));
assertMapVisitor(deKey, stringType, stringType, setOf(module), BOTH, false, 1,
instance("d", "D"), instance("e", "E"));
// just make sure these succeed
injector.getInstance(Key.get(mapOfStringProvider, Abc.class));
injector.getInstance(Key.get(mapOfStringJavaxProvider, Abc.class));
injector.getInstance(Key.get(mapOfStringProvider, De.class));
injector.getInstance(Key.get(mapOfStringJavaxProvider, De.class));
}
public void testMapBinderWithMultipleTypes() {
Module module = new AbstractModule() {
@Override protected void configure() {
MapBinder.newMapBinder(binder(), String.class, String.class)
.addBinding("a").toInstance("A");
MapBinder.newMapBinder(binder(), String.class, Integer.class)
.addBinding("1").toInstance(1);
}
};
Injector injector = Guice.createInjector(module);
assertEquals(mapOf("a", "A"), injector.getInstance(Key.get(mapOfString)));
assertEquals(mapOf("1", 1), injector.getInstance(Key.get(mapOfInteger)));
assertMapVisitor(Key.get(mapOfString), stringType, stringType, setOf(module), BOTH, false, 1,
instance("a", "A"));
assertMapVisitor(Key.get(mapOfInteger), stringType, intType, setOf(module), BOTH, false, 1,
instance("1", 1));
}
public void testMapBinderWithEmptyMap() {
Module module = new AbstractModule() {
@Override protected void configure() {
MapBinder.newMapBinder(binder(), String.class, String.class);
}
};
Injector injector = Guice.createInjector(module);
Map<String, String> map = injector.getInstance(Key.get(mapOfString));
assertEquals(Collections.emptyMap(), map);
assertMapVisitor(Key.get(mapOfString), stringType, stringType, setOf(module), BOTH, false, 0);
}
public void testMapBinderMapIsUnmodifiable() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
MapBinder.newMapBinder(binder(), String.class, String.class)
.addBinding("a").toInstance("A");
}
});
Map<String, String> map = injector.getInstance(Key.get(mapOfString));
try {
map.clear();
fail();
} catch(UnsupportedOperationException expected) {
}
}
public void testMapBinderMapIsLazy() {
Module module = new AbstractModule() {
@Override protected void configure() {
MapBinder.newMapBinder(binder(), String.class, Integer.class)
.addBinding("num").toProvider(new Provider<Integer>() {
int nextValue = 1;
public Integer get() {
return nextValue++;
}
});
}
};
Injector injector = Guice.createInjector(module);
assertEquals(mapOf("num", 1), injector.getInstance(Key.get(mapOfInteger)));
assertEquals(mapOf("num", 2), injector.getInstance(Key.get(mapOfInteger)));
assertEquals(mapOf("num", 3), injector.getInstance(Key.get(mapOfInteger)));
assertMapVisitor(Key.get(mapOfInteger), stringType, intType, setOf(module), BOTH, false, 0,
providerInstance("num", 1));
}
public void testMapBinderMapForbidsDuplicateKeys() {
Module module = new AbstractModule() {
@Override protected void configure() {
MapBinder<String, String> multibinder = MapBinder.newMapBinder(
binder(), String.class, String.class);
multibinder.addBinding("a").toInstance("A");
multibinder.addBinding("a").toInstance("B");
}
};
try {
Guice.createInjector(module);
fail();
} catch(CreationException expected) {
assertContains(expected.getMessage(),
"Map injection failed due to duplicated key \"a\"");
}
assertMapVisitor(Key.get(mapOfString), stringType, stringType, setOf(module), MODULE, false, 0,
instance("a", "A"), instance("a", "B"));
}
public void testExhaustiveDuplicateErrorMessage() throws Exception {
class Module1 extends AbstractModule {
@Override protected void configure() {
MapBinder<String, Object> mapbinder =
MapBinder.newMapBinder(binder(), String.class, Object.class);
mapbinder.addBinding("a").to(String.class);
}
}
class Module2 extends AbstractModule {
@Override protected void configure() {
MapBinder<String, Object> mapbinder =
MapBinder.newMapBinder(binder(), String.class, Object.class);
mapbinder.addBinding("a").to(Integer.class);
mapbinder.addBinding("b").to(String.class);
}
}
class Module3 extends AbstractModule {
@Override protected void configure() {
MapBinder<String, Object> mapbinder =
MapBinder.newMapBinder(binder(), String.class, Object.class);
mapbinder.addBinding("b").to(Integer.class);
}
}
class Main extends AbstractModule {
@Override protected void configure() {
MapBinder.newMapBinder(binder(), String.class, Object.class);
install(new Module1());
install(new Module2());
install(new Module3());
}
@Provides String provideString() { return "foo"; }
@Provides Integer provideInt() { return 42; }
}
try {
Guice.createInjector(new Main());
fail();
} catch(CreationException ce) {
assertContains(ce.getMessage(),
"Map injection failed due to duplicated key \"a\", from bindings:",
asModuleChain(Main.class, Module1.class),
asModuleChain(Main.class, Module2.class),
"and key: \"b\", from bindings:",
asModuleChain(Main.class, Module2.class),
asModuleChain(Main.class, Module3.class),
"at " + Main.class.getName() + ".configure(",
asModuleChain(Main.class, MapBinder.RealMapBinder.class));
assertEquals(1, ce.getErrorMessages().size());
}
}
public void testMapBinderMapPermitDuplicateElements() {
Module ab = new AbstractModule() {
@Override protected void configure() {
MapBinder<String, String> multibinder = MapBinder.newMapBinder(
binder(), String.class, String.class);
multibinder.addBinding("a").toInstance("A");
multibinder.addBinding("b").toInstance("B");
multibinder.permitDuplicates();
}
};
Module bc = new AbstractModule() {
@Override protected void configure() {
MapBinder<String, String> multibinder = MapBinder.newMapBinder(
binder(), String.class, String.class);
multibinder.addBinding("b").toInstance("B");
multibinder.addBinding("c").toInstance("C");
multibinder.permitDuplicates();
}
};
Injector injector = Guice.createInjector(ab, bc);
assertEquals(mapOf("a", "A", "b", "B", "c", "C"), injector.getInstance(Key.get(mapOfString)));
assertMapVisitor(Key.get(mapOfString), stringType, stringType, setOf(ab, bc), BOTH, true, 0,
instance("a", "A"), instance("b", "B"), instance("c", "C"));
}
public void testMapBinderMapDoesNotDedupeDuplicateValues() {
class ValueType {
int keyPart;
int dataPart;
private ValueType(int keyPart, int dataPart) {
this.keyPart = keyPart;
this.dataPart = dataPart;
}
@Override
public boolean equals(Object obj) {
return (obj instanceof ValueType) && (keyPart == ((ValueType) obj).keyPart);
}
@Override
public int hashCode() {
return keyPart;
}
}
Module m1 = new AbstractModule() {
@Override protected void configure() {
MapBinder<String, ValueType> multibinder = MapBinder.newMapBinder(
binder(), String.class, ValueType.class);
multibinder.addBinding("a").toInstance(new ValueType(1, 2));
}
};
Module m2 = new AbstractModule() {
@Override protected void configure() {
MapBinder<String, ValueType> multibinder = MapBinder.newMapBinder(
binder(), String.class, ValueType.class);
multibinder.addBinding("b").toInstance(new ValueType(1, 3));
}
};
Injector injector = Guice.createInjector(m1, m2);
Map<String, ValueType> map = injector.getInstance(new Key<Map<String, ValueType>>() {});
assertEquals(2, map.get("a").dataPart);
assertEquals(3, map.get("b").dataPart);
}
public void testMapBinderMultimap() {
AbstractModule ab1c = new AbstractModule() {
@Override protected void configure() {
MapBinder<String, String> multibinder = MapBinder.newMapBinder(
binder(), String.class, String.class);
multibinder.addBinding("a").toInstance("A");
multibinder.addBinding("b").toInstance("B1");
multibinder.addBinding("c").toInstance("C");
}
};
AbstractModule b2c = new AbstractModule() {
@Override protected void configure() {
MapBinder<String, String> multibinder = MapBinder.newMapBinder(
binder(), String.class, String.class);
multibinder.addBinding("b").toInstance("B2");
multibinder.addBinding("c").toInstance("C");
multibinder.permitDuplicates();
}
};
Injector injector = Guice.createInjector(ab1c, b2c);
assertEquals(mapOf("a", setOf("A"), "b", setOf("B1", "B2"), "c", setOf("C")),
injector.getInstance(Key.get(mapOfSetOfString)));
assertMapVisitor(Key.get(mapOfString), stringType, stringType, setOf(ab1c, b2c), BOTH, true, 0,
instance("a", "A"), instance("b", "B1"), instance("b", "B2"), instance("c", "C"));
}
public void testMapBinderMultimapWithAnotation() {
AbstractModule ab1 = new AbstractModule() {
@Override protected void configure() {
MapBinder<String, String> multibinder = MapBinder.newMapBinder(
binder(), String.class, String.class, Abc.class);
multibinder.addBinding("a").toInstance("A");
multibinder.addBinding("b").toInstance("B1");
}
};
AbstractModule b2c = new AbstractModule() {
@Override protected void configure() {
MapBinder<String, String> multibinder = MapBinder.newMapBinder(
binder(), String.class, String.class, Abc.class);
multibinder.addBinding("b").toInstance("B2");
multibinder.addBinding("c").toInstance("C");
multibinder.permitDuplicates();
}
};
Injector injector = Guice.createInjector(ab1, b2c);
assertEquals(mapOf("a", setOf("A"), "b", setOf("B1", "B2"), "c", setOf("C")),
injector.getInstance(Key.get(mapOfSetOfString, Abc.class)));
try {
injector.getInstance(Key.get(mapOfSetOfString));
fail();
} catch (ConfigurationException expected) {}
assertMapVisitor(Key.get(mapOfString, Abc.class), stringType, stringType, setOf(ab1, b2c), BOTH, true, 0,
instance("a", "A"), instance("b", "B1"), instance("b", "B2"), instance("c", "C"));
}
public void testMapBinderMultimapIsUnmodifiable() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
MapBinder<String, String> mapBinder = MapBinder.newMapBinder(
binder(), String.class, String.class);
mapBinder.addBinding("a").toInstance("A");
mapBinder.permitDuplicates();
}
});
Map<String, Set<String>> map = injector.getInstance(Key.get(mapOfSetOfString));
try {
map.clear();
fail();
} catch(UnsupportedOperationException expected) {
}
try {
map.get("a").clear();
fail();
} catch(UnsupportedOperationException expected) {
}
}
public void testMapBinderMapForbidsNullKeys() {
try {
Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
MapBinder.newMapBinder(binder(), String.class, String.class).addBinding(null);
}
});
fail();
} catch (CreationException expected) {}
}
public void testMapBinderMapForbidsNullValues() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
MapBinder.newMapBinder(binder(), String.class, String.class)
.addBinding("null").toProvider(Providers.<String>of(null));
}
});
try {
injector.getInstance(Key.get(mapOfString));
fail();
} catch(ProvisionException expected) {
assertContains(expected.getMessage(),
"1) Map injection failed due to null value for key \"null\"");
}
}
public void testMapBinderProviderIsScoped() {
final Provider<Integer> counter = new Provider<Integer>() {
int next = 1;
public Integer get() {
return next++;
}
};
Injector injector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
MapBinder.newMapBinder(binder(), String.class, Integer.class)
.addBinding("one").toProvider(counter).asEagerSingleton();
}
});
assertEquals(1, (int) injector.getInstance(Key.get(mapOfInteger)).get("one"));
assertEquals(1, (int) injector.getInstance(Key.get(mapOfInteger)).get("one"));
}
public void testSourceLinesInMapBindings() {
try {
Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
MapBinder.newMapBinder(binder(), String.class, Integer.class)
.addBinding("one");
}
});
fail();
} catch (CreationException expected) {
assertContains(expected.getMessage(),
"1) No implementation for java.lang.Integer",
"at " + getClass().getName());
}
}
/** We just want to make sure that mapbinder's binding depends on the underlying multibinder. */
public void testMultibinderDependencies() {
Injector injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
MapBinder<Integer, String> mapBinder
= MapBinder.newMapBinder(binder(), Integer.class, String.class);
mapBinder.addBinding(1).toInstance("A");
mapBinder.addBinding(2).to(Key.get(String.class, Names.named("b")));
bindConstant().annotatedWith(Names.named("b")).to("B");
}
});
Binding<Map<Integer, String>> binding = injector.getBinding(new Key<Map<Integer, String>>() {});
HasDependencies withDependencies = (HasDependencies) binding;
Key<?> setKey = new Key<Set<Map.Entry<Integer, Provider<String>>>>() {};
assertEquals(ImmutableSet.<Dependency<?>>of(Dependency.get(setKey)),
withDependencies.getDependencies());
Set<String> elements = Sets.newHashSet();
elements.addAll(recurseForDependencies(injector, withDependencies));
assertEquals(ImmutableSet.of("A", "B"), elements);
}
private Set<String> recurseForDependencies(Injector injector, HasDependencies hasDependencies) {
Set<String> elements = Sets.newHashSet();
for (Dependency<?> dependency : hasDependencies.getDependencies()) {
Binding<?> binding = injector.getBinding(dependency.getKey());
HasDependencies deps = (HasDependencies) binding;
if (binding instanceof InstanceBinding) {
elements.add((String) ((InstanceBinding) binding).getInstance());
} else {
elements.addAll(recurseForDependencies(injector, deps));
}
}
return elements;
}
/** We just want to make sure that mapbinder's binding depends on the underlying multibinder. */
public void testMultibinderDependenciesInToolStage() {
Injector injector = Guice.createInjector(Stage.TOOL, new AbstractModule() {
protected void configure() {
MapBinder<Integer, String> mapBinder
= MapBinder.newMapBinder(binder(), Integer.class, String.class);
mapBinder.addBinding(1).toInstance("A");
mapBinder.addBinding(2).to(Key.get(String.class, Names.named("b")));
bindConstant().annotatedWith(Names.named("b")).to("B");
}});
Binding<Map<Integer, String>> binding = injector.getBinding(new Key<Map<Integer, String>>() {});
HasDependencies withDependencies = (HasDependencies) binding;
Key<?> setKey = new Key<Set<Map.Entry<Integer, Provider<String>>>>() {};
assertEquals(ImmutableSet.<Dependency<?>>of(Dependency.get(setKey)),
withDependencies.getDependencies());
}
/**
* Our implementation maintains order, but doesn't guarantee it in the API spec.
* TODO: specify the iteration order?
*/
public void testBindOrderEqualsIterationOrder() {
Injector injector = Guice.createInjector(
new AbstractModule() {
protected void configure() {
MapBinder<String, String> mapBinder
= MapBinder.newMapBinder(binder(), String.class, String.class);
mapBinder.addBinding("leonardo").toInstance("blue");
mapBinder.addBinding("donatello").toInstance("purple");
install(new AbstractModule() {
protected void configure() {
MapBinder.newMapBinder(binder(), String.class, String.class)
.addBinding("michaelangelo").toInstance("orange");
}
});
}
},
new AbstractModule() {
protected void configure() {
MapBinder.newMapBinder(binder(), String.class, String.class)
.addBinding("raphael").toInstance("red");
}
});
Map<String, String> map = injector.getInstance(new Key<Map<String, String>>() {});
Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
assertEquals(Maps.immutableEntry("leonardo", "blue"), iterator.next());
assertEquals(Maps.immutableEntry("donatello", "purple"), iterator.next());
assertEquals(Maps.immutableEntry("michaelangelo", "orange"), iterator.next());
assertEquals(Maps.immutableEntry("raphael", "red"), iterator.next());
}
/**
* With overrides, we should get the union of all map bindings.
*/
public void testModuleOverrideAndMapBindings() {
Module ab = new AbstractModule() {
protected void configure() {
MapBinder<String, String> multibinder = MapBinder.newMapBinder(binder(), String.class, String.class);
multibinder.addBinding("a").toInstance("A");
multibinder.addBinding("b").toInstance("B");
}
};
Module cd = new AbstractModule() {
protected void configure() {
MapBinder<String, String> multibinder = MapBinder.newMapBinder(binder(), String.class, String.class);
multibinder.addBinding("c").toInstance("C");
multibinder.addBinding("d").toInstance("D");
}
};
Module ef = new AbstractModule() {
protected void configure() {
MapBinder<String, String> multibinder = MapBinder.newMapBinder(binder(), String.class, String.class);
multibinder.addBinding("e").toInstance("E");
multibinder.addBinding("f").toInstance("F");
}
};
Module abcd = Modules.override(ab).with(cd);
Injector injector = Guice.createInjector(abcd, ef);
assertEquals(mapOf("a", "A", "b", "B", "c", "C", "d", "D", "e", "E", "f", "F"),
injector.getInstance(Key.get(mapOfString)));
assertMapVisitor(Key.get(mapOfString), stringType, stringType, setOf(abcd, ef), BOTH, false, 0,
instance("a", "A"), instance("b", "B"), instance("c", "C"), instance("d", "D"), instance(
"e", "E"), instance("f", "F"));
}
/**
* With overrides, we should get the union of all map bindings.
*/
public void testModuleOverrideAndMapBindingsWithPermitDuplicates() {
Module abc = new AbstractModule() {
protected void configure() {
MapBinder<String, String> multibinder = MapBinder.newMapBinder(binder(), String.class, String.class);
multibinder.addBinding("a").toInstance("A");
multibinder.addBinding("b").toInstance("B");
multibinder.addBinding("c").toInstance("C");
multibinder.permitDuplicates();
}
};
Module cd = new AbstractModule() {
protected void configure() {
MapBinder<String, String> multibinder = MapBinder.newMapBinder(binder(), String.class, String.class);
multibinder.addBinding("c").toInstance("C");
multibinder.addBinding("d").toInstance("D");
multibinder.permitDuplicates();
}
};
Module ef = new AbstractModule() {
protected void configure() {
MapBinder<String, String> multibinder = MapBinder.newMapBinder(binder(), String.class, String.class);
multibinder.addBinding("e").toInstance("E");
multibinder.addBinding("f").toInstance("F");
multibinder.permitDuplicates();
}
};
Module abcd = Modules.override(abc).with(cd);
Injector injector = Guice.createInjector(abcd, ef);
assertEquals(mapOf("a", "A", "b", "B", "c", "C", "d", "D", "e", "E", "f", "F"),
injector.getInstance(Key.get(mapOfString)));
assertMapVisitor(Key.get(mapOfString), stringType, stringType, setOf(abcd, ef), BOTH, true, 0,
instance("a", "A"), instance("b", "B"), instance("c", "C"), instance(
"d", "D"), instance("e", "E"), instance("f", "F"));
}
/** Ensure there are no initialization race conditions in basic map injection. */
public void testBasicMapDependencyInjection() {
final AtomicReference<Map<String, String>> injectedMap =
new AtomicReference<Map<String, String>>();
final Object anObject = new Object() {
@Inject void initialize(Map<String, String> map) {
injectedMap.set(map);
}
};
Module abc = new AbstractModule() {
protected void configure() {
requestInjection(anObject);
MapBinder<String, String> multibinder =
MapBinder.newMapBinder(binder(), String.class, String.class);
multibinder.addBinding("a").toInstance("A");
multibinder.addBinding("b").toInstance("B");
multibinder.addBinding("c").toInstance("C");
}
};
Guice.createInjector(abc);
assertEquals(mapOf("a", "A", "b", "B", "c", "C"), injectedMap.get());
}
/** Ensure there are no initialization race conditions in provider multimap injection. */
public void testProviderMultimapDependencyInjection() {
final AtomicReference<Map<String, Set<Provider<String>>>> injectedMultimap =
new AtomicReference<Map<String, Set<Provider<String>>>>();
final Object anObject = new Object() {
@Inject void initialize(Map<String, Set<Provider<String>>> multimap) {
injectedMultimap.set(multimap);
}
};
Module abc = new AbstractModule() {
protected void configure() {
requestInjection(anObject);
MapBinder<String, String> multibinder =
MapBinder.newMapBinder(binder(), String.class, String.class);
multibinder.permitDuplicates();
multibinder.addBinding("a").toInstance("A");
multibinder.addBinding("b").toInstance("B");
multibinder.addBinding("c").toInstance("C");
}
};
Guice.createInjector(abc);
Map<String, String> map = Maps.transformValues(injectedMultimap.get(),
new Function<Set<Provider<String>>, String>() {
public String apply(Set<Provider<String>> stringProvidersSet) {
return Iterables.getOnlyElement(stringProvidersSet).get();
}
});
assertEquals(mapOf("a", "A", "b", "B", "c", "C"), map);
}
@Retention(RUNTIME) @BindingAnnotation
@interface Abc {}
@Retention(RUNTIME) @BindingAnnotation
@interface De {}
@SuppressWarnings("unchecked")
private <K, V> Map<K, V> mapOf(Object... elements) {
Map<K, V> result = new HashMap<K, V>();
for (int i = 0; i < elements.length; i += 2) {
result.put((K)elements[i], (V)elements[i+1]);
}
return result;
}
@SuppressWarnings("unchecked")
private <V> Set<V> setOf(V... elements) {
return new HashSet(Arrays.asList(elements));
}
@BindingAnnotation
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
private static @interface Marker {}
@Marker
public void testMapBinderMatching() throws Exception {
Method m = MapBinderTest.class.getDeclaredMethod("testMapBinderMatching");
assertNotNull(m);
final Annotation marker = m.getAnnotation(Marker.class);
Injector injector = Guice.createInjector(new AbstractModule() {
@Override public void configure() {
MapBinder<Integer, Integer> mb1 =
MapBinder.newMapBinder(binder(), Integer.class, Integer.class, Marker.class);
MapBinder<Integer, Integer> mb2 =
MapBinder.newMapBinder(binder(), Integer.class, Integer.class, marker);
mb1.addBinding(1).toInstance(1);
mb2.addBinding(2).toInstance(2);
// This assures us that the two binders are equivalent, so we expect the instance added to
// each to have been added to one set.
assertEquals(mb1, mb2);
}
});
TypeLiteral<Map<Integer, Integer>> t = new TypeLiteral<Map<Integer, Integer>>() {};
Map<Integer, Integer> s1 = injector.getInstance(Key.get(t, Marker.class));
Map<Integer, Integer> s2 = injector.getInstance(Key.get(t, marker));
// This assures us that the two sets are in fact equal. They may not be same set (as in Java
// object identical), but we shouldn't expect that, since probably Guice creates the set each
// time in case the elements are dependent on scope.
assertEquals(s1, s2);
// This ensures that MultiBinder is internally using the correct set name --
// making sure that instances of marker annotations have the same set name as
// MarkerAnnotation.class.
Map<Integer, Integer> expected = new HashMap<Integer, Integer>();
expected.put(1, 1);
expected.put(2, 2);
assertEquals(expected, s1);
}
public void testTwoMapBindersAreDistinct() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
MapBinder.newMapBinder(binder(), String.class, String.class)
.addBinding("A").toInstance("a");
MapBinder.newMapBinder(binder(), Integer.class, String.class)
.addBinding(1).toInstance("b");
}
});
Collector collector = new Collector();
Binding<Map<String, String>> map1 = injector.getBinding(Key.get(mapOfString));
map1.acceptTargetVisitor(collector);
assertNotNull(collector.mapbinding);
MapBinderBinding<?> map1Binding = collector.mapbinding;
Binding<Map<Integer, String>> map2 = injector.getBinding(Key.get(mapOfIntString));
map2.acceptTargetVisitor(collector);
assertNotNull(collector.mapbinding);
MapBinderBinding<?> map2Binding = collector.mapbinding;
List<Binding<String>> bindings = injector.findBindingsByType(stringType);
assertEquals("should have two elements: " + bindings, 2, bindings.size());
Binding<String> a = bindings.get(0);
Binding<String> b = bindings.get(1);
assertEquals("a", ((InstanceBinding<String>) a).getInstance());
assertEquals("b", ((InstanceBinding<String>) b).getInstance());
// Make sure the correct elements belong to their own sets.
assertTrue(map1Binding.containsElement(a));
assertFalse(map1Binding.containsElement(b));
assertFalse(map2Binding.containsElement(a));
assertTrue(map2Binding.containsElement(b));
}
// Tests for com.google.inject.internal.WeakKeySet not leaking memory.
public void testWeakKeySet_integration_mapbinder() {
Key<Map<String, String>> mapKey = Key.get(new TypeLiteral<Map<String, String>>() {});
Injector parentInjector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
bind(String.class).toInstance("hi");
}
});
WeakKeySetUtils.assertNotBlacklisted(parentInjector, mapKey);
Injector childInjector = parentInjector.createChildInjector(new AbstractModule() {
@Override protected void configure() {
MapBinder<String, String> binder =
MapBinder.newMapBinder(binder(), String.class, String.class);
binder.addBinding("bar").toInstance("foo");
}
});
WeakReference<Injector> weakRef = new WeakReference<Injector>(childInjector);
WeakKeySetUtils.assertBlacklisted(parentInjector, mapKey);
// Clear the ref, GC, and ensure that we are no longer blacklisting.
childInjector = null;
WeakKeySetUtils.awaitClear(weakRef);
WeakKeySetUtils.assertNotBlacklisted(parentInjector, mapKey);
}
}