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