/** * 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.VisitType.BOTH; import static com.google.inject.multibindings.SpiUtils.VisitType.MODULE; 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.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.Asserts; 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.internal.WeakKeySetUtils; 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 com.google.inject.util.Types; 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.lang.reflect.Type; import java.util.Arrays; import java.util.Collection; 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 { private static final Set> FRAMEWORK_KEYS = ImmutableSet.of( Key.get(java.util.logging.Logger.class), Key.get(Stage.class), Key.get(Injector.class) ); final TypeLiteral>> mapOfStringJavaxProvider = new TypeLiteral>>() {}; final TypeLiteral>> mapOfStringProvider = new TypeLiteral>>() {}; final TypeLiteral> mapOfString = new TypeLiteral>() {}; final TypeLiteral> mapOfIntString = new TypeLiteral>() {}; final TypeLiteral> mapOfInteger = new TypeLiteral>() {}; final TypeLiteral>> mapOfSetOfString = new TypeLiteral>>() {}; private final TypeLiteral stringType = TypeLiteral.get(String.class); private final TypeLiteral intType = TypeLiteral.get(Integer.class); private Type javaxProviderOf(Type type) { return Types.newParameterizedType(javax.inject.Provider.class, type); } private Type mapEntryOf(Type keyType, Type valueType) { return Types.newParameterizedTypeWithOwner(Map.class, Map.Entry.class, keyType, valueType); } private Type collectionOf(Type type) { return Types.newParameterizedType(Collection.class, type); } public void testAllBindings() { Module module = new AbstractModule() { @Override protected void configure() { MapBinder.newMapBinder(binder(), String.class, String.class).permitDuplicates(); } }; Injector injector = Guice.createInjector(module); Map, Binding> bindings = injector.getBindings(); ImmutableSet> expectedBindings = ImmutableSet.>builder() .add( // Map Key.get(Types.mapOf(String.class, String.class)), // Map> Key.get(Types.mapOf(String.class, Types.providerOf(String.class))), // Map> Key.get(Types.mapOf(String.class, javaxProviderOf(String.class))), // Map> Key.get(Types.mapOf(String.class, Types.setOf(String.class))), // Map> Key.get(Types.mapOf(String.class, Types.setOf(Types.providerOf(String.class)))), // Set>> Key.get(Types.setOf(mapEntryOf(String.class, Types.providerOf(String.class)))), // Collection>>> Key.get(collectionOf(Types.providerOf( mapEntryOf(String.class, Types.providerOf(String.class))))), // Collection>>> Key.get(collectionOf(javaxProviderOf( mapEntryOf(String.class, Types.providerOf(String.class))))), // @Named(...) Boolean Key.get(Boolean.class, named("Multibinder>> permits duplicates")) ) .addAll(FRAMEWORK_KEYS).build(); Set> missingBindings = Sets.difference(expectedBindings, bindings.keySet()); Set> extraBindings = Sets.difference(bindings.keySet(), expectedBindings); assertTrue("There should be no missing bindings. Missing: " + missingBindings, missingBindings.isEmpty()); assertTrue("There should be no extra bindings. Extra: " + extraBindings, extraBindings.isEmpty()); } public void testMapBinderAggregatesMultipleModules() { Module abc = new AbstractModule() { @Override protected void configure() { MapBinder 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 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 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 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> key = Key.get(mapOfString, Names.named("abc")); Map 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 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> key = Key.get(mapOfString, Abc.class); Map 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 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 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> abcKey = Key.get(mapOfString, named("abc")); Map abc = injector.getInstance(abcKey); Key> deKey = Key.get(mapOfString, named("de")); Map 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 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 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> abcKey = Key.get(mapOfString, Abc.class); Map abc = injector.getInstance(abcKey); Key> deKey = Key.get(mapOfString, De.class); Map 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 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 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() { int nextValue = 1; @Override 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 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 mapbinder = MapBinder.newMapBinder(binder(), String.class, Object.class); mapbinder.addBinding("a").to(String.class); } } class Module2 extends AbstractModule { @Override protected void configure() { MapBinder 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 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 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 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 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 multibinder = MapBinder.newMapBinder( binder(), String.class, ValueType.class); multibinder.addBinding("b").toInstance(new ValueType(1, 3)); } }; Injector injector = Guice.createInjector(m1, m2); Map map = injector.getInstance(new Key>() {}); assertEquals(2, map.get("a").dataPart); assertEquals(3, map.get("b").dataPart); } public void testMapBinderMultimap() { AbstractModule ab1c = new AbstractModule() { @Override protected void configure() { MapBinder 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 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 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 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 mapBinder = MapBinder.newMapBinder( binder(), String.class, String.class); mapBinder.addBinding("a").toInstance("A"); mapBinder.permitDuplicates(); } }); Map> 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() { Module m = new AbstractModule() { @Override protected void configure() { MapBinder.newMapBinder(binder(), String.class, String.class) .addBinding("null").toProvider(Providers.of(null)); } }; Injector injector = Guice.createInjector(m); try { injector.getInstance(Key.get(mapOfString)); fail(); } catch(ProvisionException expected) { assertContains(expected.getMessage(), "1) Map injection failed due to null value for key \"null\", bound at: " + m.getClass().getName() + ".configure("); } } public void testMapBinderProviderIsScoped() { final Provider counter = new Provider() { int next = 1; @Override 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() { @Override protected void configure() { MapBinder 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> binding = injector.getBinding(new Key>() {}); HasDependencies withDependencies = (HasDependencies) binding; Key setKey = new Key>>>() {}; assertEquals(ImmutableSet.>of(Dependency.get(setKey)), withDependencies.getDependencies()); Set elements = Sets.newHashSet(); elements.addAll(recurseForDependencies(injector, withDependencies)); assertEquals(ImmutableSet.of("A", "B"), elements); } private Set recurseForDependencies(Injector injector, HasDependencies hasDependencies) { Set 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() { @Override protected void configure() { MapBinder 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> binding = injector.getBinding(new Key>() {}); HasDependencies withDependencies = (HasDependencies) binding; Key setKey = new Key>>>() {}; assertEquals(ImmutableSet.>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() { @Override protected void configure() { MapBinder mapBinder = MapBinder.newMapBinder(binder(), String.class, String.class); mapBinder.addBinding("leonardo").toInstance("blue"); mapBinder.addBinding("donatello").toInstance("purple"); install(new AbstractModule() { @Override protected void configure() { MapBinder.newMapBinder(binder(), String.class, String.class) .addBinding("michaelangelo").toInstance("orange"); } }); } }, new AbstractModule() { @Override protected void configure() { MapBinder.newMapBinder(binder(), String.class, String.class) .addBinding("raphael").toInstance("red"); } }); Map map = injector.getInstance(new Key>() {}); Iterator> 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() { @Override protected void configure() { MapBinder multibinder = MapBinder.newMapBinder(binder(), String.class, String.class); multibinder.addBinding("a").toInstance("A"); multibinder.addBinding("b").toInstance("B"); } }; Module cd = new AbstractModule() { @Override protected void configure() { MapBinder multibinder = MapBinder.newMapBinder(binder(), String.class, String.class); multibinder.addBinding("c").toInstance("C"); multibinder.addBinding("d").toInstance("D"); } }; Module ef = new AbstractModule() { @Override protected void configure() { MapBinder 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")); } public void testDeduplicateMapBindings() { Module module = new AbstractModule() { @Override protected void configure() { MapBinder mapbinder = MapBinder.newMapBinder(binder(), String.class, String.class); mapbinder.addBinding("a").toInstance("A"); mapbinder.addBinding("a").toInstance("A"); mapbinder.addBinding("b").toInstance("B"); mapbinder.addBinding("b").toInstance("B"); } }; Injector injector = Guice.createInjector(module); assertEquals(mapOf("a", "A", "b", "B"), injector.getInstance(Key.get(mapOfString))); assertMapVisitor(Key.get(mapOfString), stringType, stringType, setOf(module), BOTH, false, 0, instance("a", "A"), instance("b", "B")); } /** * With overrides, we should get the union of all map bindings. */ public void testModuleOverrideAndMapBindingsWithPermitDuplicates() { Module abc = new AbstractModule() { @Override protected void configure() { MapBinder 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() { @Override protected void configure() { MapBinder multibinder = MapBinder.newMapBinder(binder(), String.class, String.class); multibinder.addBinding("c").toInstance("C"); multibinder.addBinding("d").toInstance("D"); multibinder.permitDuplicates(); } }; Module ef = new AbstractModule() { @Override protected void configure() { MapBinder 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> injectedMap = new AtomicReference>(); final Object anObject = new Object() { @Inject void initialize(Map map) { injectedMap.set(map); } }; Module abc = new AbstractModule() { @Override protected void configure() { requestInjection(anObject); MapBinder 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>>> injectedMultimap = new AtomicReference>>>(); final Object anObject = new Object() { @Inject void initialize(Map>> multimap) { injectedMultimap.set(multimap); } }; Module abc = new AbstractModule() { @Override protected void configure() { requestInjection(anObject); MapBinder 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 map = Maps.transformValues(injectedMultimap.get(), new Function>, String>() { @Override public String apply(Set> 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 Map mapOf(Object... elements) { Map result = new HashMap(); for (int i = 0; i < elements.length; i += 2) { result.put((K)elements[i], (V)elements[i+1]); } return result; } @SuppressWarnings("unchecked") private Set 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 mb1 = MapBinder.newMapBinder(binder(), Integer.class, Integer.class, Marker.class); MapBinder 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> t = new TypeLiteral>() {}; Map s1 = injector.getInstance(Key.get(t, Marker.class)); Map 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 expected = new HashMap(); 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> map1 = injector.getBinding(Key.get(mapOfString)); map1.acceptTargetVisitor(collector); assertNotNull(collector.mapbinding); MapBinderBinding map1Binding = collector.mapbinding; Binding> map2 = injector.getBinding(Key.get(mapOfIntString)); map2.acceptTargetVisitor(collector); assertNotNull(collector.mapbinding); MapBinderBinding map2Binding = collector.mapbinding; List> bindings = injector.findBindingsByType(stringType); assertEquals("should have two elements: " + bindings, 2, bindings.size()); Binding a = bindings.get(0); Binding b = bindings.get(1); assertEquals("a", ((InstanceBinding) a).getInstance()); assertEquals("b", ((InstanceBinding) 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> mapKey = Key.get(new TypeLiteral>() {}); 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 binder = MapBinder.newMapBinder(binder(), String.class, String.class); binder.addBinding("bar").toInstance("foo"); } }); WeakReference weakRef = new WeakReference(childInjector); WeakKeySetUtils.assertBlacklisted(parentInjector, mapKey); // Clear the ref, GC, and ensure that we are no longer blacklisting. childInjector = null; Asserts.awaitClear(weakRef); WeakKeySetUtils.assertNotBlacklisted(parentInjector, mapKey); } }