Home | History | Annotate | Download | only in inject
      1 /**
      2  * Copyright (C) 2006 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.inject;
     18 
     19 import com.google.common.collect.Iterables;
     20 import com.google.inject.name.Named;
     21 import com.google.inject.name.Names;
     22 import com.google.inject.spi.Message;
     23 
     24 import junit.framework.TestCase;
     25 
     26 import java.util.List;
     27 
     28 /**
     29  * @author crazybob (at) google.com (Bob Lee)
     30  */
     31 public class ImplicitBindingTest extends TestCase {
     32 
     33   public void testCircularDependency() throws CreationException {
     34     Injector injector = Guice.createInjector();
     35     Foo foo = injector.getInstance(Foo.class);
     36     assertSame(foo, foo.bar.foo);
     37   }
     38 
     39   static class Foo {
     40     @Inject Bar bar;
     41   }
     42 
     43   static class Bar {
     44     final Foo foo;
     45     @Inject
     46     public Bar(Foo foo) {
     47       this.foo = foo;
     48     }
     49   }
     50 
     51   public void testDefaultImplementation() {
     52     Injector injector = Guice.createInjector();
     53     I i = injector.getInstance(I.class);
     54     i.go();
     55   }
     56 
     57   @ImplementedBy(IImpl.class)
     58   interface I {
     59     void go();
     60   }
     61 
     62   static class IImpl implements I {
     63     public void go() {}
     64   }
     65 
     66   static class AlternateImpl implements I {
     67     public void go() {}
     68   }
     69 
     70   public void testDefaultProvider() {
     71     Injector injector = Guice.createInjector();
     72     Provided provided = injector.getInstance(Provided.class);
     73     provided.go();
     74   }
     75 
     76   public void testBindingOverridesImplementedBy() {
     77     Injector injector = Guice.createInjector(new AbstractModule() {
     78       @Override
     79       protected void configure() {
     80         bind(I.class).to(AlternateImpl.class);
     81       }
     82     });
     83     assertEquals(AlternateImpl.class, injector.getInstance(I.class).getClass());
     84   }
     85 
     86   @ProvidedBy(ProvidedProvider.class)
     87   interface Provided {
     88     void go();
     89   }
     90 
     91   public void testNoImplicitBindingIsCreatedForAnnotatedKeys() {
     92     try {
     93       Guice.createInjector().getInstance(Key.get(I.class, Names.named("i")));
     94       fail();
     95     } catch (ConfigurationException expected) {
     96       Asserts.assertContains(expected.getMessage(),
     97           "1) No implementation for " + I.class.getName(),
     98           "annotated with @" + Named.class.getName() + "(value=i) was bound.",
     99           "while locating " + I.class.getName(),
    100           " annotated with @" + Named.class.getName() + "(value=i)");
    101     }
    102   }
    103 
    104   static class ProvidedProvider implements Provider<Provided> {
    105     public Provided get() {
    106       return new Provided() {
    107         public void go() {}
    108       };
    109     }
    110   }
    111 
    112   /**
    113    * When we're building the binding for A, we temporarily insert that binding to support circular
    114    * dependencies. And so we can successfully create a binding for B. But later, when the binding
    115    * for A ultimately fails, we need to clean up the dependent binding for B.
    116    *
    117    * The test loops through linked bindings & bindings with constructor & member injections,
    118    * to make sure that all are cleaned up and traversed.  It also makes sure we don't touch
    119    * explicit bindings.
    120    */
    121   public void testCircularJitBindingsLeaveNoResidue() {
    122     Injector injector = Guice.createInjector(new AbstractModule() {
    123       @Override
    124       protected void configure() {
    125         bind(Valid.class);
    126         bind(Valid2.class);
    127       }
    128     });
    129 
    130     // Capture good bindings.
    131     Binding v1 = injector.getBinding(Valid.class);
    132     Binding v2 = injector.getBinding(Valid2.class);
    133     Binding jv1 = injector.getBinding(JitValid.class);
    134     Binding jv2 = injector.getBinding(JitValid2.class);
    135 
    136     // Then validate that a whole series of invalid bindings are erased.
    137     assertFailure(injector, Invalid.class);
    138     assertFailure(injector, InvalidLinked.class);
    139     assertFailure(injector, InvalidLinkedImpl.class);
    140     assertFailure(injector, InvalidLinked2.class);
    141     assertFailure(injector, InvalidLinked2Impl.class);
    142     assertFailure(injector, InvalidProvidedBy.class);
    143     assertFailure(injector, InvalidProvidedByProvider.class);
    144     assertFailure(injector, InvalidProvidedBy2.class);
    145     assertFailure(injector, InvalidProvidedBy2Provider.class);
    146     assertFailure(injector, Invalid2.class);
    147 
    148     // Validate we didn't do anything to the valid explicit bindings.
    149     assertSame(v1, injector.getBinding(Valid.class));
    150     assertSame(v2, injector.getBinding(Valid2.class));
    151 
    152     // Validate that we didn't erase the valid JIT bindings
    153     assertSame(jv1, injector.getBinding(JitValid.class));
    154     assertSame(jv2, injector.getBinding(JitValid2.class));
    155   }
    156 
    157   @SuppressWarnings("unchecked")
    158   private void assertFailure(Injector injector, Class clazz) {
    159     try {
    160       injector.getBinding(clazz);
    161       fail("Shouldn't have been able to get binding of: " + clazz);
    162     } catch(ConfigurationException expected) {
    163       Message msg = Iterables.getOnlyElement(expected.getErrorMessages());
    164       assertEquals("No implementation for " + InvalidInterface.class.getName() + " was bound.",
    165           msg.getMessage());
    166       List<Object> sources = msg.getSources();
    167       // Assert that the first item in the sources if the key for the class we're looking up,
    168       // ensuring that each lookup is "new".
    169       assertEquals(Key.get(clazz).toString(), sources.get(0).toString());
    170       // Assert that the last item in each lookup contains the InvalidInterface class
    171       Asserts.assertContains(sources.get(sources.size()-1).toString(),
    172           Key.get(InvalidInterface.class).toString());
    173     }
    174   }
    175 
    176   static class Invalid {
    177     @Inject Valid a;
    178     @Inject JitValid b;
    179     @Inject InvalidProvidedBy c;
    180     @Inject Invalid(InvalidLinked a) {}
    181     @Inject void foo(InvalidInterface a) {}
    182 
    183   }
    184 
    185   @ImplementedBy(InvalidLinkedImpl.class)
    186   static interface InvalidLinked {}
    187   static class InvalidLinkedImpl implements InvalidLinked {
    188     @Inject InvalidLinked2 a;
    189   }
    190 
    191   @ImplementedBy(InvalidLinked2Impl.class)
    192   static interface InvalidLinked2 {}
    193   static class InvalidLinked2Impl implements InvalidLinked2 {
    194     @Inject InvalidLinked2Impl(Invalid2 a) {}
    195   }
    196 
    197   @ProvidedBy(InvalidProvidedByProvider.class)
    198   static interface InvalidProvidedBy {}
    199   static class InvalidProvidedByProvider implements Provider<InvalidProvidedBy> {
    200     @Inject InvalidProvidedBy2 a;
    201     public InvalidProvidedBy get() {
    202       return null;
    203     }
    204   }
    205 
    206   @ProvidedBy(InvalidProvidedBy2Provider.class)
    207   static interface InvalidProvidedBy2 {}
    208   static class InvalidProvidedBy2Provider implements Provider<InvalidProvidedBy2> {
    209     @Inject Invalid2 a;
    210     public InvalidProvidedBy2 get() {
    211       return null;
    212     }
    213   }
    214 
    215   static class Invalid2 {
    216     @Inject Invalid a;
    217   }
    218 
    219   interface InvalidInterface {}
    220 
    221   static class Valid { @Inject Valid2 a; }
    222   static class Valid2 {}
    223 
    224   static class JitValid { @Inject JitValid2 a; }
    225   static class JitValid2 {}
    226 
    227   /**
    228    * Regression test for https://github.com/google/guice/issues/319
    229    *
    230    * The bug is that a class that asks for a provider for itself during injection time,
    231    * where any one of the other types required to fulfill the object creation was bound
    232    * in a child constructor, explodes when the injected Provider is called.
    233    *
    234    * It works just fine when the other types are bound in a main injector.
    235    */
    236   public void testInstancesRequestingProvidersForThemselvesWithChildInjectors() {
    237     final Module testModule = new AbstractModule() {
    238       @Override
    239       protected void configure() {
    240         bind(String.class)
    241           .toProvider(TestStringProvider.class);
    242       }
    243     };
    244 
    245     // Verify it works when the type is setup in the parent.
    246     Injector parentSetupRootInjector = Guice.createInjector(testModule);
    247     Injector parentSetupChildInjector = parentSetupRootInjector.createChildInjector();
    248     assertEquals(TestStringProvider.TEST_VALUE,
    249         parentSetupChildInjector.getInstance(
    250             RequiresProviderForSelfWithOtherType.class).getValue());
    251 
    252     // Verify it works when the type is setup in the child, not the parent.
    253     // If it still occurs, the bug will explode here.
    254     Injector childSetupRootInjector = Guice.createInjector();
    255     Injector childSetupChildInjector = childSetupRootInjector.createChildInjector(testModule);
    256     assertEquals(TestStringProvider.TEST_VALUE,
    257         childSetupChildInjector.getInstance(
    258             RequiresProviderForSelfWithOtherType.class).getValue());
    259   }
    260 
    261   static class TestStringProvider implements Provider<String> {
    262     static final String TEST_VALUE = "This is to verify it all works";
    263 
    264     public String get() {
    265       return TEST_VALUE;
    266     }
    267   }
    268 
    269   static class RequiresProviderForSelfWithOtherType {
    270     private final Provider<RequiresProviderForSelfWithOtherType> selfProvider;
    271     private final String providedStringValue;
    272 
    273     @Inject
    274     RequiresProviderForSelfWithOtherType(
    275         String providedStringValue,
    276         Provider<RequiresProviderForSelfWithOtherType> selfProvider
    277         ) {
    278       this.providedStringValue = providedStringValue;
    279       this.selfProvider = selfProvider;
    280     }
    281 
    282     public String getValue() {
    283       // Attempt to get another instance of ourself. This pattern
    284       // is possible for recursive processing.
    285       selfProvider.get();
    286 
    287       return providedStringValue;
    288     }
    289   }
    290 
    291   /**
    292    * Ensure that when we cleanup failed JIT bindings, we don't break.
    293    * The test here requires a sequence of JIT bindings:
    294    *   A-> B
    295    *   B -> C, A
    296    *   C -> A, D
    297    *   D not JITable
    298    * The problem was that C cleaned up A's binding and then handed control back to B,
    299    * which tried to continue processing A.. but A was removed from the jitBindings Map,
    300    * so it attempts to create a new JIT binding for A, but we haven't yet finished
    301    * constructing the first JIT binding for A, so we get a recursive
    302    * computation exception from ComputingConcurrentHashMap.
    303    *
    304    * We also throw in a valid JIT binding, E, to guarantee that if
    305    * something fails in this flow, it can be recreated later if it's
    306    * not from a failed sequence.
    307    */
    308   public void testRecursiveJitBindingsCleanupCorrectly() throws Exception {
    309     Injector injector = Guice.createInjector();
    310     try {
    311       injector.getInstance(A.class);
    312       fail("Expected failure");
    313     } catch(ConfigurationException expected) {
    314       Message msg = Iterables.getOnlyElement(expected.getErrorMessages());
    315       Asserts.assertContains(msg.getMessage(),
    316           "Could not find a suitable constructor in " + D.class.getName());
    317     }
    318     // Assert that we've removed all the bindings.
    319     assertNull(injector.getExistingBinding(Key.get(A.class)));
    320     assertNull(injector.getExistingBinding(Key.get(B.class)));
    321     assertNull(injector.getExistingBinding(Key.get(C.class)));
    322     assertNull(injector.getExistingBinding(Key.get(D.class)));
    323 
    324     // Confirm that we didn't prevent 'E' from working.
    325     assertNotNull(injector.getBinding(Key.get(E.class)));
    326   }
    327 
    328   static class A {
    329     @Inject public A(B b) {}
    330   }
    331 
    332   static class B {
    333     @Inject public B(C c, A a) {}
    334   }
    335 
    336   static class C {
    337     @Inject public C(A a, D d, E e) {}
    338   }
    339 
    340   static class D {
    341     public D(int i) {}
    342   }
    343 
    344   // Valid JITable binding
    345   static class E { }
    346 
    347   public void testProvidedByNonEmptyEnum() {
    348     NonEmptyEnum cardSuit = Guice.createInjector().getInstance(NonEmptyEnum.class);
    349 
    350     assertEquals(NonEmptyEnum.HEARTS, cardSuit);
    351   }
    352 
    353   public void testProvidedByEmptyEnum() {
    354     EmptyEnum emptyEnumValue = Guice.createInjector().getInstance(EmptyEnum.class);
    355     assertNull(emptyEnumValue);
    356   }
    357 
    358   @ProvidedBy(NonEmptyEnumProvider.class)
    359   enum NonEmptyEnum { HEARTS, DIAMONDS, CLUBS, SPADES }
    360 
    361   static final class NonEmptyEnumProvider implements Provider<NonEmptyEnum> {
    362     @Override
    363     public NonEmptyEnum get() {
    364       return NonEmptyEnum.HEARTS;
    365     }
    366   }
    367 
    368   @ProvidedBy(EmptyEnumProvider.class)
    369   enum EmptyEnum {}
    370 
    371   static final class EmptyEnumProvider implements Provider<EmptyEnum> {
    372     @Override
    373     public EmptyEnum get() {
    374       return null;
    375     }
    376   }
    377 
    378   // An enum cannot be implemented by anything, so it should not be possible to have a successful
    379   // binding when the enum is annotated with @ImplementedBy.
    380   public void testImplementedByEnum() {
    381     Injector injector = Guice.createInjector();
    382     try {
    383       injector.getInstance(EnumWithImplementedBy.class);
    384       fail("Expected failure");
    385     } catch(ConfigurationException expected) {
    386       Message msg = Iterables.getOnlyElement(expected.getErrorMessages());
    387       Asserts.assertContains(msg.getMessage(),
    388           "No implementation for " + EnumWithImplementedBy.class.getName() + " was bound.");
    389     }
    390   }
    391 
    392   @ImplementedBy(EnumWithImplementedByEnum.class)
    393   enum EnumWithImplementedBy {}
    394   private static class EnumWithImplementedByEnum {}
    395 }
    396