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@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 
testToStringDoesNotInfinitelyRecurse()50   public void testToStringDoesNotInfinitelyRecurse() {
51     Injector injector = Guice.createInjector(Stage.TOOL);
52     injector.toString();
53     injector.getBinding(Injector.class).toString();
54   }
55 
testProviderMethods()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 
testInjection()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 
createFooInjector()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