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
18package com.google.inject;
19
20import static com.google.inject.Asserts.assertContains;
21
22import com.google.inject.name.Named;
23import com.google.inject.name.Names;
24
25import 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 */
35public 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
84  public void testEverythingInjectorGetInstance() {
85    Guice.createInjector(everythingModule)
86        .getInstance(HasOptionalInjections.class)
87        .assertEverythingInjected();
88  }
89
90  public void testPartialInjectorGetInstance() {
91    Guice.createInjector(partialModule)
92        .getInstance(HasOptionalInjections.class)
93        .assertNothingInjected();
94  }
95
96  public void testNothingInjectorGetInstance() {
97    Guice.createInjector()
98        .getInstance(HasOptionalInjections.class)
99        .assertNothingInjected();
100  }
101
102  public void testEverythingInjectorInjectMembers() {
103    HasOptionalInjections instance = new HasOptionalInjections();
104    Guice.createInjector(everythingModule).injectMembers(instance);
105    instance.assertEverythingInjected();
106  }
107
108  public void testPartialInjectorInjectMembers() {
109    HasOptionalInjections instance = new HasOptionalInjections();
110    Guice.createInjector(partialModule).injectMembers(instance);
111    instance.assertNothingInjected();
112  }
113
114  public void testNothingInjectorInjectMembers() {
115    HasOptionalInjections instance = new HasOptionalInjections();
116    Guice.createInjector().injectMembers(instance);
117    instance.assertNothingInjected();
118  }
119
120  public void testEverythingInjectorToInstance() {
121    Guice.createInjector(everythingModule, toInstanceModule)
122        .getInstance(HasOptionalInjections.class)
123        .assertEverythingInjected();
124  }
125
126  public void testPartialInjectorToInstance() {
127    Guice.createInjector(partialModule, toInstanceModule)
128        .getInstance(HasOptionalInjections.class)
129        .assertNothingInjected();
130  }
131
132  public void testNothingInjectorToInstance() {
133    Guice.createInjector(toInstanceModule)
134        .getInstance(HasOptionalInjections.class)
135        .assertNothingInjected();
136  }
137
138  public void testEverythingInjectorToProviderInstance() {
139    Guice.createInjector(everythingModule, toProviderInstanceModule)
140        .getInstance(HasOptionalInjections.class)
141        .assertEverythingInjected();
142  }
143
144  public void testPartialInjectorToProviderInstance() {
145    Guice.createInjector(partialModule, toProviderInstanceModule)
146        .getInstance(HasOptionalInjections.class)
147        .assertNothingInjected();
148  }
149
150  public void testNothingInjectorToProviderInstance() {
151    Guice.createInjector(toProviderInstanceModule)
152        .getInstance(HasOptionalInjections.class)
153        .assertNothingInjected();
154  }
155
156  public void testEverythingInjectorToProvider() {
157    Guice.createInjector(everythingModule, toProviderModule)
158        .getInstance(HasOptionalInjections.class)
159        .assertEverythingInjected();
160  }
161
162  public void testPartialInjectorToProvider() {
163    Guice.createInjector(partialModule, toProviderModule)
164        .getInstance(HasOptionalInjections.class)
165        .assertNothingInjected();
166  }
167
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
185    @Inject(optional=true) void methodInjectZeroArguments() {
186      invoked0 = true;
187    }
188
189    @Inject(optional=true) void methodInjectOneArgument(B b) {
190      this.b = b;
191      invoked1 = true;
192    }
193
194    @Inject(optional=true) void methodInjectTwoArguments(C c, D d) {
195      this.c = c;
196      this.d = d;
197      invoked2 = true;
198    }
199
200    @Inject(optional=true) void methodInjectAnnotated(@Named("e") E e) {
201      this.e = e;
202      invokedAnnotated = true;
203    }
204
205    @Inject(optional=true) void methodInjectProvider(Provider<G> gProvider) {
206      this.gProvider = gProvider;
207      invokeProvider = true;
208    }
209
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
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> {
242    public HasOptionalInjections get() {
243      return this;
244    }
245  }
246
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)
262    HasOptionalConstructor() {}
263  }
264
265  @Inject(optional=true) static A staticInjectA;
266
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   */
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