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