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 static com.google.inject.Asserts.assertContains;
     20 import static com.google.inject.Asserts.assertNotSerializable;
     21 import static java.lang.annotation.RetentionPolicy.RUNTIME;
     22 
     23 
     24 import junit.framework.TestCase;
     25 
     26 import java.io.IOException;
     27 import java.lang.annotation.Retention;
     28 import java.util.concurrent.Callable;
     29 import java.util.concurrent.ExecutionException;
     30 import java.util.concurrent.ExecutorService;
     31 import java.util.concurrent.Executors;
     32 import java.util.concurrent.Future;
     33 import java.util.concurrent.atomic.AtomicReference;
     34 
     35 /**
     36  * @author crazybob (at) google.com (Bob Lee)
     37  */
     38 
     39 public class InjectorTest extends TestCase {
     40 
     41   @Retention(RUNTIME)
     42   @BindingAnnotation @interface Other {}
     43 
     44   @Retention(RUNTIME)
     45   @BindingAnnotation @interface S {}
     46 
     47   @Retention(RUNTIME)
     48   @BindingAnnotation @interface I {}
     49 
     50   public void testToStringDoesNotInfinitelyRecurse() {
     51     Injector injector = Guice.createInjector(Stage.TOOL);
     52     injector.toString();
     53     injector.getBinding(Injector.class).toString();
     54   }
     55 
     56   public void testProviderMethods() throws CreationException {
     57     final SampleSingleton singleton = new SampleSingleton();
     58     final SampleSingleton other = new SampleSingleton();
     59 
     60     Injector injector = Guice.createInjector(new AbstractModule() {
     61       protected void configure() {
     62         bind(SampleSingleton.class).toInstance(singleton);
     63         bind(SampleSingleton.class)
     64             .annotatedWith(Other.class)
     65             .toInstance(other);
     66       }
     67     });
     68 
     69     assertSame(singleton,
     70         injector.getInstance(Key.get(SampleSingleton.class)));
     71     assertSame(singleton, injector.getInstance(SampleSingleton.class));
     72 
     73     assertSame(other,
     74         injector.getInstance(Key.get(SampleSingleton.class, Other.class)));
     75   }
     76 
     77   static class SampleSingleton {}
     78 
     79   public void testInjection() throws CreationException {
     80     Injector injector = createFooInjector();
     81     Foo foo = injector.getInstance(Foo.class);
     82 
     83     assertEquals("test", foo.s);
     84     assertEquals("test", foo.bar.getTee().getS());
     85     assertSame(foo.bar, foo.copy);
     86     assertEquals(5, foo.i);
     87     assertEquals(5, foo.bar.getI());
     88 
     89     // Test circular dependency.
     90     assertSame(foo.bar, foo.bar.getTee().getBar());
     91   }
     92 
     93   private Injector createFooInjector() throws CreationException {
     94     return Guice.createInjector(new AbstractModule() {
     95       protected void configure() {
     96         bind(Bar.class).to(BarImpl.class);
     97         bind(Tee.class).to(TeeImpl.class);
     98         bindConstant().annotatedWith(S.class).to("test");
     99         bindConstant().annotatedWith(I.class).to(5);
    100       }
    101     });
    102   }
    103 
    104   public void testGetInstance() throws CreationException {
    105     Injector injector = createFooInjector();
    106 
    107     Bar bar = injector.getInstance(Key.get(Bar.class));
    108     assertEquals("test", bar.getTee().getS());
    109     assertEquals(5, bar.getI());
    110   }
    111 
    112   public void testIntAndIntegerAreInterchangeable()
    113       throws CreationException {
    114     Injector injector = Guice.createInjector(new AbstractModule() {
    115       protected void configure() {
    116         bindConstant().annotatedWith(I.class).to(5);
    117       }
    118     });
    119 
    120     IntegerWrapper iw = injector.getInstance(IntegerWrapper.class);
    121     assertEquals(5, (int) iw.i);
    122   }
    123 
    124   public void testInjectorApiIsNotSerializable() throws IOException {
    125     Injector injector = Guice.createInjector();
    126     assertNotSerializable(injector);
    127     assertNotSerializable(injector.getProvider(String.class));
    128     assertNotSerializable(injector.getBinding(String.class));
    129     for (Binding<?> binding : injector.getBindings().values()) {
    130       assertNotSerializable(binding);
    131     }
    132   }
    133 
    134   static class IntegerWrapper {
    135     @Inject @I Integer i;
    136   }
    137 
    138   static class Foo {
    139 
    140     @Inject Bar bar;
    141     @Inject Bar copy;
    142 
    143     @Inject @S String s;
    144 
    145     int i;
    146 
    147     @Inject
    148     void setI(@I int i) {
    149       this.i = i;
    150     }
    151   }
    152 
    153   interface Bar {
    154 
    155     Tee getTee();
    156     int getI();
    157   }
    158 
    159   @Singleton
    160   static class BarImpl implements Bar {
    161 
    162     @Inject @I int i;
    163 
    164     Tee tee;
    165 
    166     @Inject
    167     void initialize(Tee tee) {
    168       this.tee = tee;
    169     }
    170 
    171     public Tee getTee() {
    172       return tee;
    173     }
    174 
    175     public int getI() {
    176       return i;
    177     }
    178   }
    179 
    180   interface Tee {
    181 
    182     String getS();
    183     Bar getBar();
    184   }
    185 
    186   static class TeeImpl implements Tee {
    187 
    188     final String s;
    189     @Inject Bar bar;
    190 
    191     @Inject
    192     TeeImpl(@S String s) {
    193       this.s = s;
    194     }
    195 
    196     public String getS() {
    197       return s;
    198     }
    199 
    200     public Bar getBar() {
    201       return bar;
    202     }
    203   }
    204 
    205   public void testInjectStatics() throws CreationException {
    206     Guice.createInjector(new AbstractModule() {
    207       protected void configure() {
    208         bindConstant().annotatedWith(S.class).to("test");
    209         bindConstant().annotatedWith(I.class).to(5);
    210         requestStaticInjection(Static.class);
    211       }
    212     });
    213 
    214     assertEquals("test", Static.s);
    215     assertEquals(5, Static.i);
    216   }
    217 
    218   public void testInjectStaticInterface() {
    219     try {
    220       Guice.createInjector(new AbstractModule() {
    221         protected void configure() {
    222           requestStaticInjection(Interface.class);
    223         }
    224       });
    225       fail();
    226     } catch(CreationException ce) {
    227       assertEquals(1, ce.getErrorMessages().size());
    228       Asserts.assertContains(
    229           ce.getMessage(),
    230           "1) " + Interface.class.getName()
    231               + " is an interface, but interfaces have no static injection points.",
    232           "at " + InjectorTest.class.getName(),
    233           "configure");
    234     }
    235   }
    236 
    237   private static interface Interface {}
    238 
    239   static class Static {
    240 
    241     @Inject @I static int i;
    242 
    243     static String s;
    244 
    245     @Inject static void setS(@S String s) {
    246       Static.s = s;
    247     }
    248   }
    249 
    250   public void testPrivateInjection() throws CreationException {
    251     Injector injector = Guice.createInjector(new AbstractModule() {
    252       protected void configure() {
    253         bind(String.class).toInstance("foo");
    254         bind(int.class).toInstance(5);
    255       }
    256     });
    257 
    258     Private p = injector.getInstance(Private.class);
    259     assertEquals("foo", p.fromConstructor);
    260     assertEquals(5, p.fromMethod);
    261   }
    262 
    263   static class Private {
    264     String fromConstructor;
    265     int fromMethod;
    266 
    267     @Inject
    268     private Private(String fromConstructor) {
    269       this.fromConstructor = fromConstructor;
    270     }
    271 
    272     @Inject
    273     private void setInt(int i) {
    274       this.fromMethod = i;
    275     }
    276   }
    277 
    278   public void testProtectedInjection() throws CreationException {
    279     Injector injector = Guice.createInjector(new AbstractModule() {
    280       protected void configure() {
    281         bind(String.class).toInstance("foo");
    282         bind(int.class).toInstance(5);
    283       }
    284     });
    285 
    286     Protected p = injector.getInstance(Protected.class);
    287     assertEquals("foo", p.fromConstructor);
    288     assertEquals(5, p.fromMethod);
    289   }
    290 
    291   static class Protected {
    292     String fromConstructor;
    293     int fromMethod;
    294 
    295     @Inject
    296     protected Protected(String fromConstructor) {
    297       this.fromConstructor = fromConstructor;
    298     }
    299 
    300     @Inject
    301     protected void setInt(int i) {
    302       this.fromMethod = i;
    303     }
    304   }
    305 
    306   public void testInstanceInjectionHappensAfterFactoriesAreSetUp() {
    307     Guice.createInjector(new AbstractModule() {
    308       protected void configure() {
    309         bind(Object.class).toInstance(new Object() {
    310           @Inject Runnable r;
    311         });
    312 
    313         bind(Runnable.class).to(MyRunnable.class);
    314       }
    315     });
    316   }
    317 
    318   public void testSubtypeNotProvided() {
    319     try {
    320       Guice.createInjector().getInstance(Money.class);
    321       fail();
    322     } catch (ProvisionException expected) {
    323       assertContains(expected.getMessage(),
    324           Tree.class.getName() + " doesn't provide instances of " + Money.class.getName(),
    325           "while locating ", Tree.class.getName(),
    326           "while locating ", Money.class.getName());
    327     }
    328   }
    329 
    330   public void testNotASubtype() {
    331     try {
    332       Guice.createInjector().getInstance(PineTree.class);
    333       fail();
    334     } catch (ConfigurationException expected) {
    335       assertContains(expected.getMessage(),
    336           Tree.class.getName() + " doesn't extend " + PineTree.class.getName(),
    337           "while locating ", PineTree.class.getName());
    338     }
    339   }
    340 
    341   public void testRecursiveImplementationType() {
    342     try {
    343       Guice.createInjector().getInstance(SeaHorse.class);
    344       fail();
    345     } catch (ConfigurationException expected) {
    346       assertContains(expected.getMessage(),
    347           "@ImplementedBy points to the same class it annotates.",
    348           "while locating ", SeaHorse.class.getName());
    349     }
    350   }
    351 
    352   public void testRecursiveProviderType() {
    353     try {
    354       Guice.createInjector().getInstance(Chicken.class);
    355       fail();
    356     } catch (ConfigurationException expected) {
    357       assertContains(expected.getMessage(),
    358           "@ProvidedBy points to the same class it annotates",
    359           "while locating ", Chicken.class.getName());
    360     }
    361   }
    362 
    363   static class MyRunnable implements Runnable {
    364    public void run() {}
    365   }
    366 
    367   @ProvidedBy(Tree.class)
    368   static class Money {}
    369 
    370   static class Tree implements Provider<Object> {
    371     public Object get() {
    372       return "Money doesn't grow on trees";
    373     }
    374   }
    375 
    376   @ImplementedBy(Tree.class)
    377   static class PineTree extends Tree {}
    378 
    379   @ImplementedBy(SeaHorse.class)
    380   static class SeaHorse {}
    381 
    382   @ProvidedBy(Chicken.class)
    383   static class Chicken implements Provider<Chicken> {
    384     public Chicken get() {
    385       return this;
    386     }
    387   }
    388 
    389   public void testJitBindingFromAnotherThreadDuringInjection() {
    390     final ExecutorService executorService = Executors.newSingleThreadExecutor();
    391     final AtomicReference<JustInTime> got = new AtomicReference<JustInTime>();
    392 
    393     Guice.createInjector(new AbstractModule() {
    394       protected void configure() {
    395         requestInjection(new Object() {
    396           @Inject void initialize(final Injector injector)
    397               throws ExecutionException, InterruptedException {
    398             Future<JustInTime> future = executorService.submit(new Callable<JustInTime>() {
    399               public JustInTime call() throws Exception {
    400                 return injector.getInstance(JustInTime.class);
    401               }
    402             });
    403             got.set(future.get());
    404           }
    405         });
    406       }
    407     });
    408 
    409     assertNotNull(got.get());
    410   }
    411 
    412   static class JustInTime {}
    413 }
    414