| /** |
| * Copyright (C) 2006 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; |
| |
| import com.google.common.collect.Iterables; |
| import com.google.inject.name.Named; |
| import com.google.inject.name.Names; |
| import com.google.inject.spi.Message; |
| |
| import junit.framework.TestCase; |
| |
| import java.util.List; |
| |
| /** |
| * @author crazybob@google.com (Bob Lee) |
| */ |
| public class ImplicitBindingTest extends TestCase { |
| |
| public void testCircularDependency() throws CreationException { |
| Injector injector = Guice.createInjector(); |
| Foo foo = injector.getInstance(Foo.class); |
| assertSame(foo, foo.bar.foo); |
| } |
| |
| static class Foo { |
| @Inject Bar bar; |
| } |
| |
| static class Bar { |
| final Foo foo; |
| @Inject |
| public Bar(Foo foo) { |
| this.foo = foo; |
| } |
| } |
| |
| public void testDefaultImplementation() { |
| Injector injector = Guice.createInjector(); |
| I i = injector.getInstance(I.class); |
| i.go(); |
| } |
| |
| @ImplementedBy(IImpl.class) |
| interface I { |
| void go(); |
| } |
| |
| static class IImpl implements I { |
| public void go() {} |
| } |
| |
| static class AlternateImpl implements I { |
| public void go() {} |
| } |
| |
| public void testDefaultProvider() { |
| Injector injector = Guice.createInjector(); |
| Provided provided = injector.getInstance(Provided.class); |
| provided.go(); |
| } |
| |
| public void testBindingOverridesImplementedBy() { |
| Injector injector = Guice.createInjector(new AbstractModule() { |
| @Override |
| protected void configure() { |
| bind(I.class).to(AlternateImpl.class); |
| } |
| }); |
| assertEquals(AlternateImpl.class, injector.getInstance(I.class).getClass()); |
| } |
| |
| @ProvidedBy(ProvidedProvider.class) |
| interface Provided { |
| void go(); |
| } |
| |
| public void testNoImplicitBindingIsCreatedForAnnotatedKeys() { |
| try { |
| Guice.createInjector().getInstance(Key.get(I.class, Names.named("i"))); |
| fail(); |
| } catch (ConfigurationException expected) { |
| Asserts.assertContains(expected.getMessage(), |
| "1) No implementation for " + I.class.getName(), |
| "annotated with @" + Named.class.getName() + "(value=i) was bound.", |
| "while locating " + I.class.getName(), |
| " annotated with @" + Named.class.getName() + "(value=i)"); |
| } |
| } |
| |
| static class ProvidedProvider implements Provider<Provided> { |
| public Provided get() { |
| return new Provided() { |
| public void go() {} |
| }; |
| } |
| } |
| |
| /** |
| * When we're building the binding for A, we temporarily insert that binding to support circular |
| * dependencies. And so we can successfully create a binding for B. But later, when the binding |
| * for A ultimately fails, we need to clean up the dependent binding for B. |
| * |
| * The test loops through linked bindings & bindings with constructor & member injections, |
| * to make sure that all are cleaned up and traversed. It also makes sure we don't touch |
| * explicit bindings. |
| */ |
| public void testCircularJitBindingsLeaveNoResidue() { |
| Injector injector = Guice.createInjector(new AbstractModule() { |
| @Override |
| protected void configure() { |
| bind(Valid.class); |
| bind(Valid2.class); |
| } |
| }); |
| |
| // Capture good bindings. |
| Binding v1 = injector.getBinding(Valid.class); |
| Binding v2 = injector.getBinding(Valid2.class); |
| Binding jv1 = injector.getBinding(JitValid.class); |
| Binding jv2 = injector.getBinding(JitValid2.class); |
| |
| // Then validate that a whole series of invalid bindings are erased. |
| assertFailure(injector, Invalid.class); |
| assertFailure(injector, InvalidLinked.class); |
| assertFailure(injector, InvalidLinkedImpl.class); |
| assertFailure(injector, InvalidLinked2.class); |
| assertFailure(injector, InvalidLinked2Impl.class); |
| assertFailure(injector, InvalidProvidedBy.class); |
| assertFailure(injector, InvalidProvidedByProvider.class); |
| assertFailure(injector, InvalidProvidedBy2.class); |
| assertFailure(injector, InvalidProvidedBy2Provider.class); |
| assertFailure(injector, Invalid2.class); |
| |
| // Validate we didn't do anything to the valid explicit bindings. |
| assertSame(v1, injector.getBinding(Valid.class)); |
| assertSame(v2, injector.getBinding(Valid2.class)); |
| |
| // Validate that we didn't erase the valid JIT bindings |
| assertSame(jv1, injector.getBinding(JitValid.class)); |
| assertSame(jv2, injector.getBinding(JitValid2.class)); |
| } |
| |
| @SuppressWarnings("unchecked") |
| private void assertFailure(Injector injector, Class clazz) { |
| try { |
| injector.getBinding(clazz); |
| fail("Shouldn't have been able to get binding of: " + clazz); |
| } catch(ConfigurationException expected) { |
| Message msg = Iterables.getOnlyElement(expected.getErrorMessages()); |
| assertEquals("No implementation for " + InvalidInterface.class.getName() + " was bound.", |
| msg.getMessage()); |
| List<Object> sources = msg.getSources(); |
| // Assert that the first item in the sources if the key for the class we're looking up, |
| // ensuring that each lookup is "new". |
| assertEquals(Key.get(clazz).toString(), sources.get(0).toString()); |
| // Assert that the last item in each lookup contains the InvalidInterface class |
| Asserts.assertContains(sources.get(sources.size()-1).toString(), |
| Key.get(InvalidInterface.class).toString()); |
| } |
| } |
| |
| static class Invalid { |
| @Inject Valid a; |
| @Inject JitValid b; |
| @Inject InvalidProvidedBy c; |
| @Inject Invalid(InvalidLinked a) {} |
| @Inject void foo(InvalidInterface a) {} |
| |
| } |
| |
| @ImplementedBy(InvalidLinkedImpl.class) |
| static interface InvalidLinked {} |
| static class InvalidLinkedImpl implements InvalidLinked { |
| @Inject InvalidLinked2 a; |
| } |
| |
| @ImplementedBy(InvalidLinked2Impl.class) |
| static interface InvalidLinked2 {} |
| static class InvalidLinked2Impl implements InvalidLinked2 { |
| @Inject InvalidLinked2Impl(Invalid2 a) {} |
| } |
| |
| @ProvidedBy(InvalidProvidedByProvider.class) |
| static interface InvalidProvidedBy {} |
| static class InvalidProvidedByProvider implements Provider<InvalidProvidedBy> { |
| @Inject InvalidProvidedBy2 a; |
| public InvalidProvidedBy get() { |
| return null; |
| } |
| } |
| |
| @ProvidedBy(InvalidProvidedBy2Provider.class) |
| static interface InvalidProvidedBy2 {} |
| static class InvalidProvidedBy2Provider implements Provider<InvalidProvidedBy2> { |
| @Inject Invalid2 a; |
| public InvalidProvidedBy2 get() { |
| return null; |
| } |
| } |
| |
| static class Invalid2 { |
| @Inject Invalid a; |
| } |
| |
| interface InvalidInterface {} |
| |
| static class Valid { @Inject Valid2 a; } |
| static class Valid2 {} |
| |
| static class JitValid { @Inject JitValid2 a; } |
| static class JitValid2 {} |
| |
| /** |
| * Regression test for https://github.com/google/guice/issues/319 |
| * |
| * The bug is that a class that asks for a provider for itself during injection time, |
| * where any one of the other types required to fulfill the object creation was bound |
| * in a child constructor, explodes when the injected Provider is called. |
| * |
| * It works just fine when the other types are bound in a main injector. |
| */ |
| public void testInstancesRequestingProvidersForThemselvesWithChildInjectors() { |
| final Module testModule = new AbstractModule() { |
| @Override |
| protected void configure() { |
| bind(String.class) |
| .toProvider(TestStringProvider.class); |
| } |
| }; |
| |
| // Verify it works when the type is setup in the parent. |
| Injector parentSetupRootInjector = Guice.createInjector(testModule); |
| Injector parentSetupChildInjector = parentSetupRootInjector.createChildInjector(); |
| assertEquals(TestStringProvider.TEST_VALUE, |
| parentSetupChildInjector.getInstance( |
| RequiresProviderForSelfWithOtherType.class).getValue()); |
| |
| // Verify it works when the type is setup in the child, not the parent. |
| // If it still occurs, the bug will explode here. |
| Injector childSetupRootInjector = Guice.createInjector(); |
| Injector childSetupChildInjector = childSetupRootInjector.createChildInjector(testModule); |
| assertEquals(TestStringProvider.TEST_VALUE, |
| childSetupChildInjector.getInstance( |
| RequiresProviderForSelfWithOtherType.class).getValue()); |
| } |
| |
| static class TestStringProvider implements Provider<String> { |
| static final String TEST_VALUE = "This is to verify it all works"; |
| |
| public String get() { |
| return TEST_VALUE; |
| } |
| } |
| |
| static class RequiresProviderForSelfWithOtherType { |
| private final Provider<RequiresProviderForSelfWithOtherType> selfProvider; |
| private final String providedStringValue; |
| |
| @Inject |
| RequiresProviderForSelfWithOtherType( |
| String providedStringValue, |
| Provider<RequiresProviderForSelfWithOtherType> selfProvider |
| ) { |
| this.providedStringValue = providedStringValue; |
| this.selfProvider = selfProvider; |
| } |
| |
| public String getValue() { |
| // Attempt to get another instance of ourself. This pattern |
| // is possible for recursive processing. |
| selfProvider.get(); |
| |
| return providedStringValue; |
| } |
| } |
| |
| /** |
| * Ensure that when we cleanup failed JIT bindings, we don't break. |
| * The test here requires a sequence of JIT bindings: |
| * A-> B |
| * B -> C, A |
| * C -> A, D |
| * D not JITable |
| * The problem was that C cleaned up A's binding and then handed control back to B, |
| * which tried to continue processing A.. but A was removed from the jitBindings Map, |
| * so it attempts to create a new JIT binding for A, but we haven't yet finished |
| * constructing the first JIT binding for A, so we get a recursive |
| * computation exception from ComputingConcurrentHashMap. |
| * |
| * We also throw in a valid JIT binding, E, to guarantee that if |
| * something fails in this flow, it can be recreated later if it's |
| * not from a failed sequence. |
| */ |
| public void testRecursiveJitBindingsCleanupCorrectly() throws Exception { |
| Injector injector = Guice.createInjector(); |
| try { |
| injector.getInstance(A.class); |
| fail("Expected failure"); |
| } catch(ConfigurationException expected) { |
| Message msg = Iterables.getOnlyElement(expected.getErrorMessages()); |
| Asserts.assertContains(msg.getMessage(), |
| "Could not find a suitable constructor in " + D.class.getName()); |
| } |
| // Assert that we've removed all the bindings. |
| assertNull(injector.getExistingBinding(Key.get(A.class))); |
| assertNull(injector.getExistingBinding(Key.get(B.class))); |
| assertNull(injector.getExistingBinding(Key.get(C.class))); |
| assertNull(injector.getExistingBinding(Key.get(D.class))); |
| |
| // Confirm that we didn't prevent 'E' from working. |
| assertNotNull(injector.getBinding(Key.get(E.class))); |
| } |
| |
| static class A { |
| @Inject public A(B b) {} |
| } |
| |
| static class B { |
| @Inject public B(C c, A a) {} |
| } |
| |
| static class C { |
| @Inject public C(A a, D d, E e) {} |
| } |
| |
| static class D { |
| public D(int i) {} |
| } |
| |
| // Valid JITable binding |
| static class E { } |
| |
| public void testProvidedByNonEmptyEnum() { |
| NonEmptyEnum cardSuit = Guice.createInjector().getInstance(NonEmptyEnum.class); |
| |
| assertEquals(NonEmptyEnum.HEARTS, cardSuit); |
| } |
| |
| public void testProvidedByEmptyEnum() { |
| EmptyEnum emptyEnumValue = Guice.createInjector().getInstance(EmptyEnum.class); |
| assertNull(emptyEnumValue); |
| } |
| |
| @ProvidedBy(NonEmptyEnumProvider.class) |
| enum NonEmptyEnum { HEARTS, DIAMONDS, CLUBS, SPADES } |
| |
| static final class NonEmptyEnumProvider implements Provider<NonEmptyEnum> { |
| @Override |
| public NonEmptyEnum get() { |
| return NonEmptyEnum.HEARTS; |
| } |
| } |
| |
| @ProvidedBy(EmptyEnumProvider.class) |
| enum EmptyEnum {} |
| |
| static final class EmptyEnumProvider implements Provider<EmptyEnum> { |
| @Override |
| public EmptyEnum get() { |
| return null; |
| } |
| } |
| |
| // An enum cannot be implemented by anything, so it should not be possible to have a successful |
| // binding when the enum is annotated with @ImplementedBy. |
| public void testImplementedByEnum() { |
| Injector injector = Guice.createInjector(); |
| try { |
| injector.getInstance(EnumWithImplementedBy.class); |
| fail("Expected failure"); |
| } catch(ConfigurationException expected) { |
| Message msg = Iterables.getOnlyElement(expected.getErrorMessages()); |
| Asserts.assertContains(msg.getMessage(), |
| "No implementation for " + EnumWithImplementedBy.class.getName() + " was bound."); |
| } |
| } |
| |
| @ImplementedBy(EnumWithImplementedByEnum.class) |
| enum EnumWithImplementedBy {} |
| private static class EnumWithImplementedByEnum {} |
| } |