1 /**
2  * Copyright (C) 2008 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 
18 package com.google.inject;
19 
20 import static com.google.inject.Asserts.assertContains;
21 
22 import com.google.inject.name.Named;
23 import com.google.inject.name.Names;
24 
25 import junit.framework.TestCase;
26 
27 /**
28  * This test verifies the ways things are injected (ie. getInstance(),
29  * injectMembers(), bind to instance, and bind to provider instance) for all
30  * states of optional bindings (fields, methods, multiple-argument methods,
31  * provider fields, provider methods, constructors).
32  *
33  * @author jessewilson@google.com (Jesse Wilson)
34  */
35 public class OptionalBindingTest extends TestCase {
36 
37   private static final A injectA = new A() {};
38   private static final B injectB = new B() {};
39   private static final C injectC = new C() {};
40   private static final D injectD = new D() {};
41   private static final E injectE = new E() {};
42   private static final F injectF = new F() {};
43   private static final G injectG = new G() {};
44 
45   private Module everythingModule = new AbstractModule() {
46     protected void configure() {
47       bind(A.class).toInstance(injectA);
48       bind(B.class).toInstance(injectB);
49       bind(C.class).toInstance(injectC);
50       bind(D.class).toInstance(injectD);
51       bind(E.class).annotatedWith(Names.named("e")).toInstance(injectE);
52       bind(F.class).toInstance(injectF);
53       bind(G.class).toInstance(injectG);
54     }
55   };
56 
57   private Module partialModule = new AbstractModule() {
58     protected void configure() {
59       bind(C.class).toInstance(new C() {});
60     }
61   };
62 
63   private Module toInstanceModule = new AbstractModule() {
64     protected void configure() {
65       bind(HasOptionalInjections.class)
66           .toInstance(new HasOptionalInjections());
67     }
68   };
69 
70   private Module toProviderInstanceModule = new AbstractModule() {
71     protected void configure() {
72       bind(HasOptionalInjections.class)
73           .toProvider(new HasOptionalInjectionsProvider());
74     }
75   };
76 
77   private Module toProviderModule = new AbstractModule() {
78     protected void configure() {
79       bind(HasOptionalInjections.class)
80           .toProvider(HasOptionalInjectionsProvider.class);
81     }
82   };
83 
testEverythingInjectorGetInstance()84   public void testEverythingInjectorGetInstance() {
85     Guice.createInjector(everythingModule)
86         .getInstance(HasOptionalInjections.class)
87         .assertEverythingInjected();
88   }
89 
testPartialInjectorGetInstance()90   public void testPartialInjectorGetInstance() {
91     Guice.createInjector(partialModule)
92         .getInstance(HasOptionalInjections.class)
93         .assertNothingInjected();
94   }
95 
testNothingInjectorGetInstance()96   public void testNothingInjectorGetInstance() {
97     Guice.createInjector()
98         .getInstance(HasOptionalInjections.class)
99         .assertNothingInjected();
100   }
101 
testEverythingInjectorInjectMembers()102   public void testEverythingInjectorInjectMembers() {
103     HasOptionalInjections instance = new HasOptionalInjections();
104     Guice.createInjector(everythingModule).injectMembers(instance);
105     instance.assertEverythingInjected();
106   }
107 
testPartialInjectorInjectMembers()108   public void testPartialInjectorInjectMembers() {
109     HasOptionalInjections instance = new HasOptionalInjections();
110     Guice.createInjector(partialModule).injectMembers(instance);
111     instance.assertNothingInjected();
112   }
113 
testNothingInjectorInjectMembers()114   public void testNothingInjectorInjectMembers() {
115     HasOptionalInjections instance = new HasOptionalInjections();
116     Guice.createInjector().injectMembers(instance);
117     instance.assertNothingInjected();
118   }
119 
testEverythingInjectorToInstance()120   public void testEverythingInjectorToInstance() {
121     Guice.createInjector(everythingModule, toInstanceModule)
122         .getInstance(HasOptionalInjections.class)
123         .assertEverythingInjected();
124   }
125 
testPartialInjectorToInstance()126   public void testPartialInjectorToInstance() {
127     Guice.createInjector(partialModule, toInstanceModule)
128         .getInstance(HasOptionalInjections.class)
129         .assertNothingInjected();
130   }
131 
testNothingInjectorToInstance()132   public void testNothingInjectorToInstance() {
133     Guice.createInjector(toInstanceModule)
134         .getInstance(HasOptionalInjections.class)
135         .assertNothingInjected();
136   }
137 
testEverythingInjectorToProviderInstance()138   public void testEverythingInjectorToProviderInstance() {
139     Guice.createInjector(everythingModule, toProviderInstanceModule)
140         .getInstance(HasOptionalInjections.class)
141         .assertEverythingInjected();
142   }
143 
testPartialInjectorToProviderInstance()144   public void testPartialInjectorToProviderInstance() {
145     Guice.createInjector(partialModule, toProviderInstanceModule)
146         .getInstance(HasOptionalInjections.class)
147         .assertNothingInjected();
148   }
149 
testNothingInjectorToProviderInstance()150   public void testNothingInjectorToProviderInstance() {
151     Guice.createInjector(toProviderInstanceModule)
152         .getInstance(HasOptionalInjections.class)
153         .assertNothingInjected();
154   }
155 
testEverythingInjectorToProvider()156   public void testEverythingInjectorToProvider() {
157     Guice.createInjector(everythingModule, toProviderModule)
158         .getInstance(HasOptionalInjections.class)
159         .assertEverythingInjected();
160   }
161 
testPartialInjectorToProvider()162   public void testPartialInjectorToProvider() {
163     Guice.createInjector(partialModule, toProviderModule)
164         .getInstance(HasOptionalInjections.class)
165         .assertNothingInjected();
166   }
167 
testNothingInjectorToProvider()168   public void testNothingInjectorToProvider() {
169     Guice.createInjector(toProviderModule)
170         .getInstance(HasOptionalInjections.class)
171         .assertNothingInjected();
172   }
173 
174   static class HasOptionalInjections {
175     A originalA = new A() {};
176     @Inject(optional=true) A a = originalA; // field injection
177     B b; // method injection with one argument
178     C c; // method injection with two arguments
179     D d; // method injection with two arguments
180     E e; // annotated injection
181     @Inject(optional=true) Provider<F> fProvider; // provider
182     Provider<G> gProvider; // method injection of provider
183     boolean invoked0, invoked1, invoked2, invokedAnnotated, invokeProvider;
184 
methodInjectZeroArguments()185     @Inject(optional=true) void methodInjectZeroArguments() {
186       invoked0 = true;
187     }
188 
methodInjectOneArgument(B b)189     @Inject(optional=true) void methodInjectOneArgument(B b) {
190       this.b = b;
191       invoked1 = true;
192     }
193 
methodInjectTwoArguments(C c, D d)194     @Inject(optional=true) void methodInjectTwoArguments(C c, D d) {
195       this.c = c;
196       this.d = d;
197       invoked2 = true;
198     }
199 
methodInjectAnnotated(@amed"e") E e)200     @Inject(optional=true) void methodInjectAnnotated(@Named("e") E e) {
201       this.e = e;
202       invokedAnnotated = true;
203     }
204 
methodInjectProvider(Provider<G> gProvider)205     @Inject(optional=true) void methodInjectProvider(Provider<G> gProvider) {
206       this.gProvider = gProvider;
207       invokeProvider = true;
208     }
209 
assertNothingInjected()210     void assertNothingInjected() {
211       assertSame(originalA, a);
212       assertNull(b);
213       assertNull(c);
214       assertNull(d);
215       assertNull(e);
216       assertNull(fProvider);
217       assertNull(gProvider);
218       assertTrue(invoked0);
219       assertFalse(invoked1);
220       assertFalse(invoked2);
221       assertFalse(invokedAnnotated);
222     }
223 
assertEverythingInjected()224     public void assertEverythingInjected() {
225       assertNotSame(injectA, originalA);
226       assertSame(injectA, a);
227       assertSame(injectB, b);
228       assertSame(injectC, c);
229       assertSame(injectD, d);
230       assertSame(injectE, e);
231       assertSame(injectF, fProvider.get());
232       assertSame(injectG, gProvider.get());
233       assertTrue(invoked0);
234       assertTrue(invoked1);
235       assertTrue(invoked2);
236       assertTrue(invokedAnnotated);
237     }
238   }
239 
240   static class HasOptionalInjectionsProvider
241       extends HasOptionalInjections implements Provider<HasOptionalInjections> {
get()242     public HasOptionalInjections get() {
243       return this;
244     }
245   }
246 
testOptionalConstructorBlowsUp()247   public void testOptionalConstructorBlowsUp() {
248     try {
249       Guice.createInjector().getInstance(HasOptionalConstructor.class);
250       fail();
251     } catch (ConfigurationException expected) {
252       assertContains(expected.getMessage(), "OptionalBindingTest$HasOptionalConstructor.<init>() "
253           + "is annotated @Inject(optional=true), but constructors cannot be optional.");
254     }
255   }
256 
257   static class HasOptionalConstructor {
258     // Suppress compiler errors by the error-prone checker InjectedConstructorAnnotations,
259     // which catches optional injected constructors.
260     @SuppressWarnings("InjectedConstructorAnnotations")
261     @Inject(optional=true)
HasOptionalConstructor()262     HasOptionalConstructor() {}
263   }
264 
265   @Inject(optional=true) static A staticInjectA;
266 
testStaticInjection()267   public void testStaticInjection() {
268     staticInjectA = injectA;
269     Guice.createInjector(new AbstractModule() {
270       protected void configure() {
271         requestStaticInjection(OptionalBindingTest.class);
272       }
273     });
274     assertSame(staticInjectA, injectA);
275   }
276 
277   /**
278    * Test for bug 107, where we weren't doing optional injection properly for
279    * indirect injections.
280    */
testIndirectOptionalInjection()281   public void testIndirectOptionalInjection() {
282     Indirect indirect = Guice.createInjector().getInstance(Indirect.class);
283     assertNotNull(indirect.hasOptionalInjections);
284     indirect.hasOptionalInjections.assertNothingInjected();
285   }
286 
287   static class Indirect {
288     @Inject HasOptionalInjections hasOptionalInjections;
289   }
290 
291   interface A {}
292   interface B {}
293   interface C {}
294   interface D {}
295   interface E {}
296   interface F {}
297   interface G {}
298 }
299