/* * 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.assertContains; import static com.google.inject.internal.SpiUtils.assertOptionalVisitor; import static com.google.inject.internal.SpiUtils.instance; import static com.google.inject.internal.SpiUtils.linked; import static com.google.inject.internal.SpiUtils.providerInstance; import static com.google.inject.internal.SpiUtils.providerKey; import static com.google.inject.name.Names.named; import com.google.common.base.Optional; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; 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.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.Scopes; import com.google.inject.TypeLiteral; import com.google.inject.internal.SpiUtils.VisitType; import com.google.inject.multibindings.OptionalBinder; import com.google.inject.name.Named; import com.google.inject.name.Names; import com.google.inject.spi.Dependency; import com.google.inject.spi.Elements; 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 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.List; import java.util.Map.Entry; import java.util.Set; import junit.framework.TestCase; /** @author sameb@google.com (Sam Berlin) */ public class OptionalBinderTest extends TestCase { private static final boolean HAS_JAVA_OPTIONAL; private static final Class<?> JAVA_OPTIONAL_CLASS; private static final Method JAVA_OPTIONAL_OR_ELSE; static { Class<?> optional = null; Method orElse = null; try { optional = Class.forName("java.util.Optional"); orElse = optional.getDeclaredMethod("orElse", Object.class); } catch (ClassNotFoundException ignored) { } catch (NoSuchMethodException ignored) { } catch (SecurityException ignored) { } HAS_JAVA_OPTIONAL = optional != null; JAVA_OPTIONAL_CLASS = optional; JAVA_OPTIONAL_OR_ELSE = orElse; } final Key<String> stringKey = Key.get(String.class); final TypeLiteral<Optional<String>> optionalOfString = new TypeLiteral<Optional<String>>() {}; final TypeLiteral<?> javaOptionalOfString = HAS_JAVA_OPTIONAL ? RealOptionalBinder.javaOptionalOf(stringKey.getTypeLiteral()) : null; final TypeLiteral<Optional<Provider<String>>> optionalOfProviderString = new TypeLiteral<Optional<Provider<String>>>() {}; final TypeLiteral<?> javaOptionalOfProviderString = HAS_JAVA_OPTIONAL ? RealOptionalBinder.javaOptionalOfProvider(stringKey.getTypeLiteral()) : null; final TypeLiteral<Optional<javax.inject.Provider<String>>> optionalOfJavaxProviderString = new TypeLiteral<Optional<javax.inject.Provider<String>>>() {}; final TypeLiteral<?> javaOptionalOfJavaxProviderString = HAS_JAVA_OPTIONAL ? RealOptionalBinder.javaOptionalOfJavaxProvider(stringKey.getTypeLiteral()) : null; final Key<Integer> intKey = Key.get(Integer.class); final TypeLiteral<Optional<Integer>> optionalOfInteger = new TypeLiteral<Optional<Integer>>() {}; final TypeLiteral<?> javaOptionalOfInteger = HAS_JAVA_OPTIONAL ? RealOptionalBinder.javaOptionalOf(intKey.getTypeLiteral()) : null; final TypeLiteral<Optional<Provider<Integer>>> optionalOfProviderInteger = new TypeLiteral<Optional<Provider<Integer>>>() {}; final TypeLiteral<?> javaOptionalOfProviderInteger = HAS_JAVA_OPTIONAL ? RealOptionalBinder.javaOptionalOfProvider(intKey.getTypeLiteral()) : null; final TypeLiteral<Optional<javax.inject.Provider<Integer>>> optionalOfJavaxProviderInteger = new TypeLiteral<Optional<javax.inject.Provider<Integer>>>() {}; final TypeLiteral<?> javaOptionalOfJavaxProviderInteger = HAS_JAVA_OPTIONAL ? RealOptionalBinder.javaOptionalOfJavaxProvider(intKey.getTypeLiteral()) : null; final TypeLiteral<List<String>> listOfStrings = new TypeLiteral<List<String>>() {}; public void testTypeNotBoundByDefault() { Module module = new AbstractModule() { @Override protected void configure() { OptionalBinder.newOptionalBinder(binder(), String.class); requireBinding(new Key<Optional<String>>() {}); // the above specifies this. requireBinding(String.class); // but it doesn't specify this. binder().requireExplicitBindings(); // need to do this, otherwise String will JIT if (HAS_JAVA_OPTIONAL) { requireBinding(Key.get(javaOptionalOfString)); } } }; try { Guice.createInjector(module); fail(); } catch (CreationException ce) { assertContains( ce.getMessage(), "1) Explicit bindings are required and java.lang.String is not explicitly bound."); assertEquals(1, ce.getErrorMessages().size()); } } public void testOptionalIsAbsentByDefault() throws Exception { Module module = new AbstractModule() { @Override protected void configure() { OptionalBinder.newOptionalBinder(binder(), String.class); } }; Injector injector = Guice.createInjector(module); Optional<String> optional = injector.getInstance(Key.get(optionalOfString)); assertFalse(optional.isPresent()); Optional<Provider<String>> optionalP = injector.getInstance(Key.get(optionalOfProviderString)); assertFalse(optionalP.isPresent()); Optional<javax.inject.Provider<String>> optionalJxP = injector.getInstance(Key.get(optionalOfJavaxProviderString)); assertFalse(optionalJxP.isPresent()); assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 0, null, null, null); if (HAS_JAVA_OPTIONAL) { optional = toOptional(injector.getInstance(Key.get(javaOptionalOfString))); assertFalse(optional.isPresent()); optionalP = toOptional(injector.getInstance(Key.get(javaOptionalOfProviderString))); assertFalse(optionalP.isPresent()); optionalJxP = toOptional(injector.getInstance(Key.get(javaOptionalOfJavaxProviderString))); assertFalse(optionalJxP.isPresent()); } } public void testUsesUserBoundValue() throws Exception { Module module = new AbstractModule() { @Override protected void configure() { OptionalBinder.newOptionalBinder(binder(), String.class); } @Provides String provideString() { return "foo"; } }; Injector injector = Guice.createInjector(module); assertEquals("foo", injector.getInstance(String.class)); Optional<String> optional = injector.getInstance(Key.get(optionalOfString)); assertEquals("foo", optional.get()); Optional<Provider<String>> optionalP = injector.getInstance(Key.get(optionalOfProviderString)); assertEquals("foo", optionalP.get().get()); Optional<javax.inject.Provider<String>> optionalJxP = injector.getInstance(Key.get(optionalOfJavaxProviderString)); assertEquals("foo", optionalJxP.get().get()); assertOptionalVisitor( stringKey, setOf(module), VisitType.BOTH, 0, null, null, providerInstance("foo")); if (HAS_JAVA_OPTIONAL) { optional = toOptional(injector.getInstance(Key.get(javaOptionalOfString))); assertEquals("foo", optional.get()); optionalP = toOptional(injector.getInstance(Key.get(javaOptionalOfProviderString))); assertEquals("foo", optionalP.get().get()); optionalJxP = toOptional(injector.getInstance(Key.get(javaOptionalOfJavaxProviderString))); assertEquals("foo", optionalJxP.get().get()); } } public void testUsesUserBoundValueNullProvidersMakeAbsent() throws Exception { Module module = new AbstractModule() { @Override protected void configure() { OptionalBinder.newOptionalBinder(binder(), String.class); } @Provides String provideString() { return null; } }; Injector injector = Guice.createInjector(module); assertEquals(null, injector.getInstance(String.class)); Optional<String> optional = injector.getInstance(Key.get(optionalOfString)); assertFalse(optional.isPresent()); Optional<Provider<String>> optionalP = injector.getInstance(Key.get(optionalOfProviderString)); assertEquals(null, optionalP.get().get()); Optional<javax.inject.Provider<String>> optionalJxP = injector.getInstance(Key.get(optionalOfJavaxProviderString)); assertEquals(null, optionalJxP.get().get()); assertOptionalVisitor( stringKey, setOf(module), VisitType.BOTH, 0, null, null, providerInstance(null)); if (HAS_JAVA_OPTIONAL) { optional = toOptional(injector.getInstance(Key.get(javaOptionalOfString))); assertFalse(optional.isPresent()); optionalP = toOptional(injector.getInstance(Key.get(javaOptionalOfProviderString))); assertEquals(null, optionalP.get().get()); optionalJxP = toOptional(injector.getInstance(Key.get(javaOptionalOfJavaxProviderString))); assertEquals(null, optionalJxP.get().get()); } } private static class JitBinding { @Inject JitBinding() {} } private static class DependsOnJitBinding { @Inject DependsOnJitBinding(JitBinding jitBinding) {} } // A previous version of OptionalBinder would fail to find jit dependendencies that were created // by other bindings public void testOptionalBinderDependsOnJitBinding() { Module module = new AbstractModule() { @Override protected void configure() { OptionalBinder.newOptionalBinder(binder(), JitBinding.class); } }; // Everything should be absent since nothing triggered discovery of the jit binding Injector injector = Guice.createInjector(module); assertFalse(injector.getInstance(optionalKey(JitBinding.class)).isPresent()); assertNull(injector.getExistingBinding(Key.get(JitBinding.class))); // in this case, because jit bindings are allowed in this injector, the DependsOnJitBinding // binding will get initialized and create jit bindings for its dependency. The optionalbinder // should then pick it up module = new AbstractModule() { @Override protected void configure() { OptionalBinder.newOptionalBinder(binder(), JitBinding.class); bind(DependsOnJitBinding.class); } }; injector = Guice.createInjector(module); assertTrue(injector.getInstance(optionalKey(JitBinding.class)).isPresent()); assertNotNull(injector.getExistingBinding(Key.get(JitBinding.class))); // in this case, because the jit binding is discovered dynamically, the optionalbinder won't // find it. In prior implementations of OptionalBinder this would depend on the exact // sequencing of the installation of OptionalBinder vs. these injection points that trigger // dynamic injection. In the current implementation it will consistently not find it. module = new AbstractModule() { @Override protected void configure() { bind(Object.class) .toProvider( new Provider<Object>() { @Inject void setter(Injector injector) { injector.getInstance(JitBinding.class); } @Override public Object get() { return null; } }); OptionalBinder.newOptionalBinder(binder(), JitBinding.class); } }; injector = Guice.createInjector(module); assertFalse(injector.getInstance(optionalKey(JitBinding.class)).isPresent()); assertNotNull(injector.getExistingBinding(Key.get(JitBinding.class))); } public <T> Key<Optional<T>> optionalKey(Class<T> type) { return Key.get(RealOptionalBinder.optionalOf(TypeLiteral.get(type))); } public void testSetDefault() throws Exception { Module module = new AbstractModule() { @Override protected void configure() { OptionalBinder.newOptionalBinder(binder(), String.class).setDefault().toInstance("a"); } }; Injector injector = Guice.createInjector(module); assertEquals("a", injector.getInstance(String.class)); Optional<String> optional = injector.getInstance(Key.get(optionalOfString)); assertTrue(optional.isPresent()); assertEquals("a", optional.get()); Optional<Provider<String>> optionalP = injector.getInstance(Key.get(optionalOfProviderString)); assertTrue(optionalP.isPresent()); assertEquals("a", optionalP.get().get()); Optional<javax.inject.Provider<String>> optionalJxP = injector.getInstance(Key.get(optionalOfJavaxProviderString)); assertTrue(optionalJxP.isPresent()); assertEquals("a", optionalJxP.get().get()); assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 0, instance("a"), null, null); if (HAS_JAVA_OPTIONAL) { optional = toOptional(injector.getInstance(Key.get(javaOptionalOfString))); assertTrue(optional.isPresent()); assertEquals("a", optional.get()); optionalP = toOptional(injector.getInstance(Key.get(javaOptionalOfProviderString))); assertTrue(optionalP.isPresent()); assertEquals("a", optionalP.get().get()); optionalJxP = toOptional(injector.getInstance(Key.get(javaOptionalOfJavaxProviderString))); assertTrue(optionalJxP.isPresent()); assertEquals("a", optionalJxP.get().get()); } } public void testSetBinding() throws Exception { Module module = new AbstractModule() { @Override protected void configure() { OptionalBinder.newOptionalBinder(binder(), String.class).setBinding().toInstance("a"); } }; Injector injector = Guice.createInjector(module); assertEquals("a", injector.getInstance(String.class)); Optional<String> optional = injector.getInstance(Key.get(optionalOfString)); assertTrue(optional.isPresent()); assertEquals("a", optional.get()); Optional<Provider<String>> optionalP = injector.getInstance(Key.get(optionalOfProviderString)); assertTrue(optionalP.isPresent()); assertEquals("a", optionalP.get().get()); Optional<javax.inject.Provider<String>> optionalJxP = injector.getInstance(Key.get(optionalOfJavaxProviderString)); assertTrue(optionalJxP.isPresent()); assertEquals("a", optionalJxP.get().get()); assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 0, null, instance("a"), null); if (HAS_JAVA_OPTIONAL) { optional = toOptional(injector.getInstance(Key.get(javaOptionalOfString))); assertTrue(optional.isPresent()); assertEquals("a", optional.get()); optionalP = toOptional(injector.getInstance(Key.get(javaOptionalOfProviderString))); assertTrue(optionalP.isPresent()); assertEquals("a", optionalP.get().get()); optionalJxP = toOptional(injector.getInstance(Key.get(javaOptionalOfJavaxProviderString))); assertTrue(optionalJxP.isPresent()); assertEquals("a", optionalJxP.get().get()); } } public void testSetBindingOverridesDefault() throws Exception { Module module = new AbstractModule() { @Override protected void configure() { OptionalBinder<String> optionalBinder = OptionalBinder.newOptionalBinder(binder(), String.class); optionalBinder.setDefault().toInstance("a"); optionalBinder.setBinding().toInstance("b"); } }; Injector injector = Guice.createInjector(module); assertEquals("b", injector.getInstance(String.class)); Optional<String> optional = injector.getInstance(Key.get(optionalOfString)); assertTrue(optional.isPresent()); assertEquals("b", optional.get()); Optional<Provider<String>> optionalP = injector.getInstance(Key.get(optionalOfProviderString)); assertTrue(optionalP.isPresent()); assertEquals("b", optionalP.get().get()); Optional<javax.inject.Provider<String>> optionalJxP = injector.getInstance(Key.get(optionalOfJavaxProviderString)); assertTrue(optionalJxP.isPresent()); assertEquals("b", optionalJxP.get().get()); assertOptionalVisitor( stringKey, setOf(module), VisitType.BOTH, 0, instance("a"), instance("b"), null); if (HAS_JAVA_OPTIONAL) { optional = toOptional(injector.getInstance(Key.get(javaOptionalOfString))); assertTrue(optional.isPresent()); assertEquals("b", optional.get()); optionalP = toOptional(injector.getInstance(Key.get(javaOptionalOfProviderString))); assertTrue(optionalP.isPresent()); assertEquals("b", optionalP.get().get()); optionalJxP = toOptional(injector.getInstance(Key.get(javaOptionalOfJavaxProviderString))); assertTrue(optionalJxP.isPresent()); assertEquals("b", optionalJxP.get().get()); } } public void testSpreadAcrossModules() throws Exception { Module module1 = new AbstractModule() { @Override protected void configure() { OptionalBinder.newOptionalBinder(binder(), String.class); } }; Module module2 = new AbstractModule() { @Override protected void configure() { OptionalBinder.newOptionalBinder(binder(), String.class).setDefault().toInstance("a"); } }; Module module3 = new AbstractModule() { @Override protected void configure() { OptionalBinder.newOptionalBinder(binder(), String.class).setBinding().toInstance("b"); } }; Injector injector = Guice.createInjector(module1, module2, module3); assertEquals("b", injector.getInstance(String.class)); Optional<String> optional = injector.getInstance(Key.get(optionalOfString)); assertTrue(optional.isPresent()); assertEquals("b", optional.get()); Optional<Provider<String>> optionalP = injector.getInstance(Key.get(optionalOfProviderString)); assertTrue(optionalP.isPresent()); assertEquals("b", optionalP.get().get()); Optional<javax.inject.Provider<String>> optionalJxP = injector.getInstance(Key.get(optionalOfJavaxProviderString)); assertTrue(optionalJxP.isPresent()); assertEquals("b", optionalJxP.get().get()); assertOptionalVisitor( stringKey, setOf(module1, module2, module3), VisitType.BOTH, 0, instance("a"), instance("b"), null); if (HAS_JAVA_OPTIONAL) { optional = toOptional(injector.getInstance(Key.get(javaOptionalOfString))); assertTrue(optional.isPresent()); assertEquals("b", optional.get()); optionalP = toOptional(injector.getInstance(Key.get(javaOptionalOfProviderString))); assertTrue(optionalP.isPresent()); assertEquals("b", optionalP.get().get()); optionalJxP = toOptional(injector.getInstance(Key.get(javaOptionalOfJavaxProviderString))); assertTrue(optionalJxP.isPresent()); assertEquals("b", optionalJxP.get().get()); } } public void testExactSameBindingCollapses_defaults() throws Exception { Module module = new AbstractModule() { @Override protected void configure() { OptionalBinder.newOptionalBinder(binder(), String.class) .setDefault() .toInstance(new String("a")); // using new String to ensure .equals is checked. OptionalBinder.newOptionalBinder(binder(), String.class) .setDefault() .toInstance(new String("a")); } }; Injector injector = Guice.createInjector(module); assertEquals("a", injector.getInstance(String.class)); Optional<String> optional = injector.getInstance(Key.get(optionalOfString)); assertTrue(optional.isPresent()); assertEquals("a", optional.get()); Optional<Provider<String>> optionalP = injector.getInstance(Key.get(optionalOfProviderString)); assertTrue(optionalP.isPresent()); assertEquals("a", optionalP.get().get()); Optional<javax.inject.Provider<String>> optionalJxP = injector.getInstance(Key.get(optionalOfJavaxProviderString)); assertTrue(optionalJxP.isPresent()); assertEquals("a", optionalJxP.get().get()); assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 0, instance("a"), null, null); if (HAS_JAVA_OPTIONAL) { optional = toOptional(injector.getInstance(Key.get(javaOptionalOfString))); assertTrue(optional.isPresent()); assertEquals("a", optional.get()); optionalP = toOptional(injector.getInstance(Key.get(javaOptionalOfProviderString))); assertTrue(optionalP.isPresent()); assertEquals("a", optionalP.get().get()); optionalJxP = toOptional(injector.getInstance(Key.get(javaOptionalOfJavaxProviderString))); assertTrue(optionalJxP.isPresent()); assertEquals("a", optionalJxP.get().get()); } } public void testExactSameBindingCollapses_actual() throws Exception { Module module = new AbstractModule() { @Override protected void configure() { OptionalBinder.newOptionalBinder(binder(), String.class) .setBinding() .toInstance(new String("a")); // using new String to ensure .equals is checked. OptionalBinder.newOptionalBinder(binder(), String.class) .setBinding() .toInstance(new String("a")); } }; Injector injector = Guice.createInjector(module); assertEquals("a", injector.getInstance(String.class)); Optional<String> optional = injector.getInstance(Key.get(optionalOfString)); assertTrue(optional.isPresent()); assertEquals("a", optional.get()); Optional<Provider<String>> optionalP = injector.getInstance(Key.get(optionalOfProviderString)); assertTrue(optionalP.isPresent()); assertEquals("a", optionalP.get().get()); Optional<javax.inject.Provider<String>> optionalJxP = injector.getInstance(Key.get(optionalOfJavaxProviderString)); assertTrue(optionalJxP.isPresent()); assertEquals("a", optionalJxP.get().get()); assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 0, null, instance("a"), null); if (HAS_JAVA_OPTIONAL) { optional = toOptional(injector.getInstance(Key.get(javaOptionalOfString))); assertTrue(optional.isPresent()); assertEquals("a", optional.get()); optionalP = toOptional(injector.getInstance(Key.get(javaOptionalOfProviderString))); assertTrue(optionalP.isPresent()); assertEquals("a", optionalP.get().get()); optionalJxP = toOptional(injector.getInstance(Key.get(javaOptionalOfJavaxProviderString))); assertTrue(optionalJxP.isPresent()); assertEquals("a", optionalJxP.get().get()); } } public void testDifferentBindingsFail_defaults() { Module module = new AbstractModule() { @Override protected void configure() { OptionalBinder.newOptionalBinder(binder(), String.class).setDefault().toInstance("a"); OptionalBinder.newOptionalBinder(binder(), String.class).setDefault().toInstance("b"); } }; try { Guice.createInjector(module); fail(); } catch (CreationException ce) { assertEquals(ce.getMessage(), 1, ce.getErrorMessages().size()); assertContains( ce.getMessage(), "1) A binding to java.lang.String annotated with @" + RealOptionalBinder.Default.class.getName() + " was already configured at " + module.getClass().getName() + ".configure(", "at " + module.getClass().getName() + ".configure("); } } public void testDifferentBindingsFail_actual() { Module module = new AbstractModule() { @Override protected void configure() { OptionalBinder.newOptionalBinder(binder(), String.class).setBinding().toInstance("a"); OptionalBinder.newOptionalBinder(binder(), String.class).setBinding().toInstance("b"); } }; try { Guice.createInjector(module); fail(); } catch (CreationException ce) { assertEquals(ce.getMessage(), 1, ce.getErrorMessages().size()); assertContains( ce.getMessage(), "1) A binding to java.lang.String annotated with @" + RealOptionalBinder.Actual.class.getName() + " was already configured at " + module.getClass().getName() + ".configure(", "at " + module.getClass().getName() + ".configure("); } } public void testDifferentBindingsFail_both() { Module module = new AbstractModule() { @Override protected void configure() { OptionalBinder.newOptionalBinder(binder(), String.class).setDefault().toInstance("a"); OptionalBinder.newOptionalBinder(binder(), String.class).setDefault().toInstance("b"); OptionalBinder.newOptionalBinder(binder(), String.class).setBinding().toInstance("b"); OptionalBinder.newOptionalBinder(binder(), String.class).setBinding().toInstance("c"); } }; try { Guice.createInjector(module); fail(); } catch (CreationException ce) { assertEquals(ce.getMessage(), 2, ce.getErrorMessages().size()); assertContains( ce.getMessage(), "1) A binding to java.lang.String annotated with @" + RealOptionalBinder.Default.class.getName() + " was already configured at " + module.getClass().getName() + ".configure(", "at " + module.getClass().getName() + ".configure(", "2) A binding to java.lang.String annotated with @" + RealOptionalBinder.Actual.class.getName() + " was already configured at " + module.getClass().getName() + ".configure(", "at " + module.getClass().getName() + ".configure("); } } public void testQualifiedAggregatesTogether() throws Exception { Module module1 = new AbstractModule() { @Override protected void configure() { OptionalBinder.newOptionalBinder(binder(), Key.get(String.class, Names.named("foo"))); } }; Module module2 = new AbstractModule() { @Override protected void configure() { OptionalBinder.newOptionalBinder(binder(), Key.get(String.class, Names.named("foo"))) .setDefault() .toInstance("a"); } }; Module module3 = new AbstractModule() { @Override protected void configure() { OptionalBinder.newOptionalBinder(binder(), Key.get(String.class, Names.named("foo"))) .setBinding() .toInstance("b"); } }; Injector injector = Guice.createInjector(module1, module2, module3); assertEquals("b", injector.getInstance(Key.get(String.class, Names.named("foo")))); Optional<String> optional = injector.getInstance(Key.get(optionalOfString, Names.named("foo"))); assertTrue(optional.isPresent()); assertEquals("b", optional.get()); Optional<Provider<String>> optionalP = injector.getInstance(Key.get(optionalOfProviderString, Names.named("foo"))); assertTrue(optionalP.isPresent()); assertEquals("b", optionalP.get().get()); Optional<javax.inject.Provider<String>> optionalJxP = injector.getInstance(Key.get(optionalOfJavaxProviderString, Names.named("foo"))); assertTrue(optionalJxP.isPresent()); assertEquals("b", optionalJxP.get().get()); assertOptionalVisitor( Key.get(String.class, Names.named("foo")), setOf(module1, module2, module3), VisitType.BOTH, 0, instance("a"), instance("b"), null); if (HAS_JAVA_OPTIONAL) { optional = toOptional(injector.getInstance(Key.get(javaOptionalOfString, Names.named("foo")))); assertTrue(optional.isPresent()); assertEquals("b", optional.get()); optionalP = toOptional( injector.getInstance(Key.get(javaOptionalOfProviderString, Names.named("foo")))); assertTrue(optionalP.isPresent()); assertEquals("b", optionalP.get().get()); optionalJxP = toOptional( injector.getInstance(Key.get(javaOptionalOfJavaxProviderString, Names.named("foo")))); assertTrue(optionalJxP.isPresent()); assertEquals("b", optionalJxP.get().get()); } } public void testMultipleDifferentOptionals() { final Key<String> bKey = Key.get(String.class, named("b")); final Key<String> cKey = Key.get(String.class, named("c")); Module module = new AbstractModule() { @Override protected void configure() { OptionalBinder.newOptionalBinder(binder(), String.class).setDefault().toInstance("a"); OptionalBinder.newOptionalBinder(binder(), Integer.class).setDefault().toInstance(1); OptionalBinder.newOptionalBinder(binder(), bKey).setDefault().toInstance("b"); OptionalBinder.newOptionalBinder(binder(), cKey).setDefault().toInstance("c"); } }; Injector injector = Guice.createInjector(module); assertEquals("a", injector.getInstance(String.class)); assertEquals(1, injector.getInstance(Integer.class).intValue()); assertEquals("b", injector.getInstance(bKey)); assertEquals("c", injector.getInstance(cKey)); assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 3, instance("a"), null, null); assertOptionalVisitor(intKey, setOf(module), VisitType.BOTH, 3, instance(1), null, null); assertOptionalVisitor(bKey, setOf(module), VisitType.BOTH, 3, instance("b"), null, null); assertOptionalVisitor(cKey, setOf(module), VisitType.BOTH, 3, instance("c"), null, null); } public void testOptionalIsAppropriatelyLazy() throws Exception { Module module = new AbstractModule() { int nextValue = 1; @Override protected void configure() { OptionalBinder.newOptionalBinder(binder(), Integer.class) .setDefault() .to(Key.get(Integer.class, Names.named("foo"))); } @Provides @Named("foo") int provideInt() { return nextValue++; } }; Injector injector = Guice.createInjector(module); Optional<Provider<Integer>> optionalP = injector.getInstance(Key.get(optionalOfProviderInteger)); Optional<javax.inject.Provider<Integer>> optionalJxP = injector.getInstance(Key.get(optionalOfJavaxProviderInteger)); assertEquals(1, injector.getInstance(Integer.class).intValue()); assertEquals(2, injector.getInstance(Integer.class).intValue()); // Calling .get() on an Optional<Integer> multiple times will keep giving the same thing Optional<Integer> optional = injector.getInstance(Key.get(optionalOfInteger)); assertEquals(3, optional.get().intValue()); assertEquals(3, optional.get().intValue()); // But getting another Optional<Integer> will give a new one. assertEquals(4, injector.getInstance(Key.get(optionalOfInteger)).get().intValue()); // And the Optional<Provider> will return a provider that gives a new value each time. assertEquals(5, optionalP.get().get().intValue()); assertEquals(6, optionalP.get().get().intValue()); assertEquals(7, optionalJxP.get().get().intValue()); assertEquals(8, optionalJxP.get().get().intValue()); // and same rules with java.util.Optional if (HAS_JAVA_OPTIONAL) { optional = toOptional(injector.getInstance(Key.get(javaOptionalOfInteger))); assertEquals(9, optional.get().intValue()); assertEquals(9, optional.get().intValue()); optional = toOptional(injector.getInstance(Key.get(javaOptionalOfInteger))); assertEquals(10, optional.get().intValue()); optionalP = toOptional(injector.getInstance(Key.get(javaOptionalOfProviderInteger))); assertEquals(11, optionalP.get().get().intValue()); assertEquals(12, optionalP.get().get().intValue()); optionalJxP = toOptional(injector.getInstance(Key.get(javaOptionalOfJavaxProviderInteger))); assertEquals(13, optionalJxP.get().get().intValue()); assertEquals(14, optionalJxP.get().get().intValue()); } } public void testLinkedToNullProvidersMakeAbsentValuesAndPresentProviders_default() throws Exception { Module module = new AbstractModule() { @Override protected void configure() { OptionalBinder.newOptionalBinder(binder(), String.class) .setDefault() .toProvider(Providers.<String>of(null)); } }; Injector injector = Guice.createInjector(module); assertNull(injector.getInstance(String.class)); Optional<String> optional = injector.getInstance(Key.get(optionalOfString)); assertFalse(optional.isPresent()); Optional<Provider<String>> optionalP = injector.getInstance(Key.get(optionalOfProviderString)); assertTrue(optionalP.isPresent()); assertNull(optionalP.get().get()); Optional<javax.inject.Provider<String>> optionalJxP = injector.getInstance(Key.get(optionalOfJavaxProviderString)); assertTrue(optionalJxP.isPresent()); assertNull(optionalJxP.get().get()); assertOptionalVisitor( stringKey, setOf(module), VisitType.BOTH, 0, SpiUtils.<String>providerInstance(null), null, null); if (HAS_JAVA_OPTIONAL) { optional = toOptional(injector.getInstance(Key.get(javaOptionalOfString))); assertFalse(optional.isPresent()); optionalP = toOptional(injector.getInstance(Key.get(javaOptionalOfProviderString))); assertTrue(optionalP.isPresent()); assertNull(optionalP.get().get()); optionalJxP = toOptional(injector.getInstance(Key.get(javaOptionalOfJavaxProviderString))); assertTrue(optionalJxP.isPresent()); assertNull(optionalJxP.get().get()); } } public void testLinkedToNullProvidersMakeAbsentValuesAndPresentProviders_actual() throws Exception { Module module = new AbstractModule() { @Override protected void configure() { OptionalBinder.newOptionalBinder(binder(), String.class) .setBinding() .toProvider(Providers.<String>of(null)); } }; Injector injector = Guice.createInjector(module); assertNull(injector.getInstance(String.class)); Optional<String> optional = injector.getInstance(Key.get(optionalOfString)); assertFalse(optional.isPresent()); Optional<Provider<String>> optionalP = injector.getInstance(Key.get(optionalOfProviderString)); assertTrue(optionalP.isPresent()); assertNull(optionalP.get().get()); Optional<javax.inject.Provider<String>> optionalJxP = injector.getInstance(Key.get(optionalOfJavaxProviderString)); assertTrue(optionalJxP.isPresent()); assertNull(optionalJxP.get().get()); assertOptionalVisitor( stringKey, setOf(module), VisitType.BOTH, 0, null, SpiUtils.<String>providerInstance(null), null); if (HAS_JAVA_OPTIONAL) { optional = toOptional(injector.getInstance(Key.get(javaOptionalOfString))); assertFalse(optional.isPresent()); optionalP = toOptional(injector.getInstance(Key.get(javaOptionalOfProviderString))); assertTrue(optionalP.isPresent()); assertNull(optionalP.get().get()); optionalJxP = toOptional(injector.getInstance(Key.get(javaOptionalOfJavaxProviderString))); assertTrue(optionalJxP.isPresent()); assertNull(optionalJxP.get().get()); } } // TODO(sameb): Maybe change this? public void testLinkedToNullActualDoesntFallbackToDefault() throws Exception { Module module = new AbstractModule() { @Override protected void configure() { OptionalBinder.newOptionalBinder(binder(), String.class).setDefault().toInstance("a"); OptionalBinder.newOptionalBinder(binder(), String.class) .setBinding() .toProvider(Providers.<String>of(null)); } }; Injector injector = Guice.createInjector(module); assertNull(injector.getInstance(String.class)); Optional<String> optional = injector.getInstance(Key.get(optionalOfString)); assertFalse(optional.isPresent()); Optional<Provider<String>> optionalP = injector.getInstance(Key.get(optionalOfProviderString)); assertTrue(optionalP.isPresent()); assertNull(optionalP.get().get()); Optional<javax.inject.Provider<String>> optionalJxP = injector.getInstance(Key.get(optionalOfJavaxProviderString)); assertTrue(optionalJxP.isPresent()); assertNull(optionalP.get().get()); assertOptionalVisitor( stringKey, setOf(module), VisitType.BOTH, 0, instance("a"), SpiUtils.<String>providerInstance(null), null); if (HAS_JAVA_OPTIONAL) { optional = toOptional(injector.getInstance(Key.get(javaOptionalOfString))); assertFalse(optional.isPresent()); optionalP = toOptional(injector.getInstance(Key.get(javaOptionalOfProviderString))); assertTrue(optionalP.isPresent()); assertNull(optionalP.get().get()); optionalJxP = toOptional(injector.getInstance(Key.get(javaOptionalOfJavaxProviderString))); assertTrue(optionalJxP.isPresent()); assertNull(optionalJxP.get().get()); } } public void testSourceLinesInException() { try { Guice.createInjector( new AbstractModule() { @Override protected void configure() { OptionalBinder.newOptionalBinder(binder(), Integer.class).setDefault(); } }); fail(); } catch (CreationException expected) { assertContains( expected.getMessage(), "No implementation for java.lang.Integer", "at " + getClass().getName()); } } public void testDependencies_both() { Injector injector = Guice.createInjector( new AbstractModule() { @Override protected void configure() { OptionalBinder<String> optionalbinder = OptionalBinder.newOptionalBinder(binder(), String.class); optionalbinder.setDefault().toInstance("A"); optionalbinder.setBinding().to(Key.get(String.class, Names.named("b"))); bindConstant().annotatedWith(Names.named("b")).to("B"); } }); Binding<String> binding = injector.getBinding(Key.get(String.class)); HasDependencies withDependencies = (HasDependencies) binding; Set<String> elements = Sets.newHashSet(); elements.addAll(recurseForDependencies(injector, withDependencies)); assertEquals(ImmutableSet.of("B"), elements); } public void testDependencies_actual() { Injector injector = Guice.createInjector( new AbstractModule() { @Override protected void configure() { OptionalBinder<String> optionalbinder = OptionalBinder.newOptionalBinder(binder(), String.class); optionalbinder.setBinding().to(Key.get(String.class, Names.named("b"))); bindConstant().annotatedWith(Names.named("b")).to("B"); } }); Binding<String> binding = injector.getBinding(Key.get(String.class)); HasDependencies withDependencies = (HasDependencies) binding; Set<String> elements = Sets.newHashSet(); elements.addAll(recurseForDependencies(injector, withDependencies)); assertEquals(ImmutableSet.of("B"), elements); } public void testDependencies_default() { Injector injector = Guice.createInjector( new AbstractModule() { @Override protected void configure() { OptionalBinder<String> optionalbinder = OptionalBinder.newOptionalBinder(binder(), String.class); optionalbinder.setDefault().toInstance("A"); } }); Binding<String> binding = injector.getBinding(Key.get(String.class)); HasDependencies withDependencies = (HasDependencies) binding; Set<String> elements = Sets.newHashSet(); elements.addAll(recurseForDependencies(injector, withDependencies)); assertEquals(ImmutableSet.of("A"), elements); } @SuppressWarnings("rawtypes") 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; } /** Doubly-installed modules should not conflict, even when one is overridden. */ public void testModuleOverrideRepeatedInstalls_toInstance() { Module m = new AbstractModule() { @Override protected void configure() { OptionalBinder<String> b = OptionalBinder.newOptionalBinder(binder(), String.class); b.setDefault().toInstance("A"); b.setBinding().toInstance("B"); } }; assertEquals("B", Guice.createInjector(m, m).getInstance(Key.get(String.class))); Injector injector = Guice.createInjector(m, Modules.override(m).with(m)); assertEquals("B", injector.getInstance(Key.get(String.class))); assertOptionalVisitor( stringKey, setOf(m, Modules.override(m).with(m)), VisitType.BOTH, 0, instance("A"), instance("B"), null); } public void testModuleOverrideRepeatedInstalls_toKey() { final Key<String> aKey = Key.get(String.class, Names.named("A_string")); final Key<String> bKey = Key.get(String.class, Names.named("B_string")); Module m = new AbstractModule() { @Override protected void configure() { bind(aKey).toInstance("A"); bind(bKey).toInstance("B"); OptionalBinder<String> b = OptionalBinder.newOptionalBinder(binder(), String.class); b.setDefault().to(aKey); b.setBinding().to(bKey); } }; assertEquals("B", Guice.createInjector(m, m).getInstance(Key.get(String.class))); Injector injector = Guice.createInjector(m, Modules.override(m).with(m)); assertEquals("B", injector.getInstance(Key.get(String.class))); assertOptionalVisitor( stringKey, setOf(m, Modules.override(m).with(m)), VisitType.BOTH, 0, linked(aKey), linked(bKey), null); } public void testModuleOverrideRepeatedInstalls_toProviderInstance() { // Providers#of() does not redefine equals/hashCode, so use the same one both times. final Provider<String> aProvider = Providers.of("A"); final Provider<String> bProvider = Providers.of("B"); Module m = new AbstractModule() { @Override protected void configure() { OptionalBinder<String> b = OptionalBinder.newOptionalBinder(binder(), String.class); b.setDefault().toProvider(aProvider); b.setBinding().toProvider(bProvider); } }; assertEquals("B", Guice.createInjector(m, m).getInstance(Key.get(String.class))); Injector injector = Guice.createInjector(m, Modules.override(m).with(m)); assertEquals("B", injector.getInstance(Key.get(String.class))); assertOptionalVisitor( stringKey, setOf(m, Modules.override(m).with(m)), VisitType.BOTH, 0, providerInstance("A"), providerInstance("B"), null); } private static class AStringProvider implements Provider<String> { @Override public String get() { return "A"; } } private static class BStringProvider implements Provider<String> { @Override public String get() { return "B"; } } public void testModuleOverrideRepeatedInstalls_toProviderKey() { Module m = new AbstractModule() { @Override protected void configure() { OptionalBinder<String> b = OptionalBinder.newOptionalBinder(binder(), String.class); b.setDefault().toProvider(Key.get(AStringProvider.class)); b.setBinding().toProvider(Key.get(BStringProvider.class)); } }; assertEquals("B", Guice.createInjector(m, m).getInstance(Key.get(String.class))); Injector injector = Guice.createInjector(m, Modules.override(m).with(m)); assertEquals("B", injector.getInstance(Key.get(String.class))); assertOptionalVisitor( stringKey, setOf(m, Modules.override(m).with(m)), VisitType.BOTH, 0, providerKey(Key.get(AStringProvider.class)), providerKey(Key.get(BStringProvider.class)), null); } private static class StringGrabber { private final String string; @SuppressWarnings("unused") // Found by reflection public StringGrabber(@Named("A_string") String string) { this.string = string; } @SuppressWarnings("unused") // Found by reflection public StringGrabber(@Named("B_string") String string, int unused) { this.string = string; } @Override public int hashCode() { return string.hashCode(); } @Override public boolean equals(Object obj) { return (obj instanceof StringGrabber) && ((StringGrabber) obj).string.equals(string); } @Override public String toString() { return "StringGrabber(" + string + ")"; } } public void testModuleOverrideRepeatedInstalls_toConstructor() { Module m = new AbstractModule() { @Override protected void configure() { Key<String> aKey = Key.get(String.class, Names.named("A_string")); Key<String> bKey = Key.get(String.class, Names.named("B_string")); bind(aKey).toInstance("A"); bind(bKey).toInstance("B"); bind(Integer.class).toInstance(0); // used to disambiguate constructors OptionalBinder<StringGrabber> b = OptionalBinder.newOptionalBinder(binder(), StringGrabber.class); try { b.setDefault().toConstructor(StringGrabber.class.getConstructor(String.class)); b.setBinding() .toConstructor(StringGrabber.class.getConstructor(String.class, int.class)); } catch (NoSuchMethodException e) { fail("No such method: " + e.getMessage()); } } }; assertEquals("B", Guice.createInjector(m, m).getInstance(Key.get(StringGrabber.class)).string); Injector injector = Guice.createInjector(m, Modules.override(m).with(m)); assertEquals("B", injector.getInstance(Key.get(StringGrabber.class)).string); } /** * Unscoped bindings should not conflict, whether they were bound with no explicit scope, or * explicitly bound in {@link Scopes#NO_SCOPE}. */ public void testDuplicateUnscopedBindings() { Module m = new AbstractModule() { @Override protected void configure() { OptionalBinder<Integer> b = OptionalBinder.newOptionalBinder(binder(), Integer.class); b.setDefault().to(Key.get(Integer.class, named("foo"))); b.setDefault().to(Key.get(Integer.class, named("foo"))).in(Scopes.NO_SCOPE); b.setBinding().to(Key.get(Integer.class, named("foo"))); b.setBinding().to(Key.get(Integer.class, named("foo"))).in(Scopes.NO_SCOPE); } @Provides @Named("foo") int provideInt() { return 5; } }; assertEquals(5, Guice.createInjector(m).getInstance(Integer.class).intValue()); } /** Ensure key hash codes are fixed at injection time, not binding time. */ public void testKeyHashCodesFixedAtInjectionTime() { Module m = new AbstractModule() { @Override protected void configure() { OptionalBinder<List<String>> b = OptionalBinder.newOptionalBinder(binder(), listOfStrings); List<String> list = Lists.newArrayList(); b.setDefault().toInstance(list); b.setBinding().toInstance(list); list.add("A"); list.add("B"); } }; Injector injector = Guice.createInjector(m); for (Entry<Key<?>, Binding<?>> entry : injector.getAllBindings().entrySet()) { Key<?> bindingKey = entry.getKey(); Key<?> clonedKey; if (bindingKey.getAnnotation() != null) { clonedKey = Key.get(bindingKey.getTypeLiteral(), bindingKey.getAnnotation()); } else if (bindingKey.getAnnotationType() != null) { clonedKey = Key.get(bindingKey.getTypeLiteral(), bindingKey.getAnnotationType()); } else { clonedKey = Key.get(bindingKey.getTypeLiteral()); } assertEquals(bindingKey, clonedKey); assertEquals( "Incorrect hashcode for " + bindingKey + " -> " + entry.getValue(), bindingKey.hashCode(), clonedKey.hashCode()); } } /** Ensure bindings do not rehash their keys once returned from {@link Elements#getElements}. */ public void testBindingKeysFixedOnReturnFromGetElements() { final List<String> list = Lists.newArrayList(); Module m = new AbstractModule() { @Override protected void configure() { OptionalBinder<List<String>> b = OptionalBinder.newOptionalBinder(binder(), listOfStrings); b.setDefault().toInstance(list); list.add("A"); list.add("B"); } }; InstanceBinding<?> binding = Iterables.getOnlyElement(Iterables.filter(Elements.getElements(m), InstanceBinding.class)); Key<?> keyBefore = binding.getKey(); assertEquals(listOfStrings, keyBefore.getTypeLiteral()); list.add("C"); Key<?> keyAfter = binding.getKey(); assertSame(keyBefore, keyAfter); } @BindingAnnotation @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD}) private static @interface Marker {} @Marker public void testMatchingMarkerAnnotations() throws Exception { Method m = OptionalBinderTest.class.getDeclaredMethod("testMatchingMarkerAnnotations"); assertNotNull(m); final Annotation marker = m.getAnnotation(Marker.class); Injector injector = Guice.createInjector( new AbstractModule() { @Override public void configure() { OptionalBinder<Integer> mb1 = OptionalBinder.newOptionalBinder( binder(), Key.get(Integer.class, Marker.class)); OptionalBinder<Integer> mb2 = OptionalBinder.newOptionalBinder(binder(), Key.get(Integer.class, marker)); mb1.setDefault().toInstance(1); mb2.setBinding().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); } }); Integer i1 = injector.getInstance(Key.get(Integer.class, Marker.class)); Integer i2 = injector.getInstance(Key.get(Integer.class, marker)); // These must be identical, because the marker annotations collapsed to the same thing. assertSame(i1, i2); assertEquals(2, i2.intValue()); } // Tests for com.google.inject.internal.WeakKeySet not leaking memory. public void testWeakKeySet_integration() { Injector parentInjector = Guice.createInjector( new AbstractModule() { @Override protected void configure() { bind(String.class).toInstance("hi"); } }); WeakKeySetUtils.assertNotBlacklisted(parentInjector, Key.get(Integer.class)); Injector childInjector = parentInjector.createChildInjector( new AbstractModule() { @Override protected void configure() { OptionalBinder.newOptionalBinder(binder(), Integer.class) .setDefault() .toInstance(4); } }); WeakReference<Injector> weakRef = new WeakReference<Injector>(childInjector); WeakKeySetUtils.assertBlacklisted(parentInjector, Key.get(Integer.class)); // Clear the ref, GC, and ensure that we are no longer blacklisting. childInjector = null; Asserts.awaitClear(weakRef); WeakKeySetUtils.assertNotBlacklisted(parentInjector, Key.get(Integer.class)); } public void testCompareEqualsAgainstOtherAnnotation() { RealOptionalBinder.Actual impl1 = new RealOptionalBinder.ActualImpl("foo"); RealOptionalBinder.Actual other1 = Dummy.class.getAnnotation(RealOptionalBinder.Actual.class); assertEquals(impl1, other1); RealOptionalBinder.Default impl2 = new RealOptionalBinder.DefaultImpl("foo"); RealOptionalBinder.Default other2 = Dummy.class.getAnnotation(RealOptionalBinder.Default.class); assertEquals(impl2, other2); assertFalse(impl1.equals(impl2)); assertFalse(impl1.equals(other2)); assertFalse(impl2.equals(other1)); assertFalse(other1.equals(other2)); } @RealOptionalBinder.Actual("foo") @RealOptionalBinder.Default("foo") static class Dummy {} @SuppressWarnings("unchecked") private <V> Set<V> setOf(V... elements) { return ImmutableSet.copyOf(elements); } @SuppressWarnings("unchecked") private <T> Optional<T> toOptional(Object object) throws Exception { assertTrue( "not a java.util.Optional: " + object.getClass(), JAVA_OPTIONAL_CLASS.isInstance(object)); return Optional.fromNullable((T) JAVA_OPTIONAL_OR_ELSE.invoke(object, (Void) null)); } }