• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.google.common.collect.Iterables;
20 import com.google.inject.name.Named;
21 import com.google.inject.name.Names;
22 import com.google.inject.spi.Message;
23 
24 import junit.framework.TestCase;
25 
26 import java.util.List;
27 
28 /**
29  * @author crazybob@google.com (Bob Lee)
30  */
31 public class ImplicitBindingTest extends TestCase {
32 
testCircularDependency()33   public void testCircularDependency() throws CreationException {
34     Injector injector = Guice.createInjector();
35     Foo foo = injector.getInstance(Foo.class);
36     assertSame(foo, foo.bar.foo);
37   }
38 
39   static class Foo {
40     @Inject Bar bar;
41   }
42 
43   static class Bar {
44     final Foo foo;
45     @Inject
Bar(Foo foo)46     public Bar(Foo foo) {
47       this.foo = foo;
48     }
49   }
50 
testDefaultImplementation()51   public void testDefaultImplementation() {
52     Injector injector = Guice.createInjector();
53     I i = injector.getInstance(I.class);
54     i.go();
55   }
56 
57   @ImplementedBy(IImpl.class)
58   interface I {
go()59     void go();
60   }
61 
62   static class IImpl implements I {
go()63     public void go() {}
64   }
65 
66   static class AlternateImpl implements I {
go()67     public void go() {}
68   }
69 
testDefaultProvider()70   public void testDefaultProvider() {
71     Injector injector = Guice.createInjector();
72     Provided provided = injector.getInstance(Provided.class);
73     provided.go();
74   }
75 
testBindingOverridesImplementedBy()76   public void testBindingOverridesImplementedBy() {
77     Injector injector = Guice.createInjector(new AbstractModule() {
78       @Override
79       protected void configure() {
80         bind(I.class).to(AlternateImpl.class);
81       }
82     });
83     assertEquals(AlternateImpl.class, injector.getInstance(I.class).getClass());
84   }
85 
86   @ProvidedBy(ProvidedProvider.class)
87   interface Provided {
go()88     void go();
89   }
90 
testNoImplicitBindingIsCreatedForAnnotatedKeys()91   public void testNoImplicitBindingIsCreatedForAnnotatedKeys() {
92     try {
93       Guice.createInjector().getInstance(Key.get(I.class, Names.named("i")));
94       fail();
95     } catch (ConfigurationException expected) {
96       Asserts.assertContains(expected.getMessage(),
97           "1) No implementation for " + I.class.getName(),
98           "annotated with @" + Named.class.getName() + "(value=i) was bound.",
99           "while locating " + I.class.getName(),
100           " annotated with @" + Named.class.getName() + "(value=i)");
101     }
102   }
103 
104   static class ProvidedProvider implements Provider<Provided> {
get()105     public Provided get() {
106       return new Provided() {
107         public void go() {}
108       };
109     }
110   }
111 
112   /**
113    * When we're building the binding for A, we temporarily insert that binding to support circular
114    * dependencies. And so we can successfully create a binding for B. But later, when the binding
115    * for A ultimately fails, we need to clean up the dependent binding for B.
116    *
117    * The test loops through linked bindings & bindings with constructor & member injections,
118    * to make sure that all are cleaned up and traversed.  It also makes sure we don't touch
119    * explicit bindings.
120    */
testCircularJitBindingsLeaveNoResidue()121   public void testCircularJitBindingsLeaveNoResidue() {
122     Injector injector = Guice.createInjector(new AbstractModule() {
123       @Override
124       protected void configure() {
125         bind(Valid.class);
126         bind(Valid2.class);
127       }
128     });
129 
130     // Capture good bindings.
131     Binding v1 = injector.getBinding(Valid.class);
132     Binding v2 = injector.getBinding(Valid2.class);
133     Binding jv1 = injector.getBinding(JitValid.class);
134     Binding jv2 = injector.getBinding(JitValid2.class);
135 
136     // Then validate that a whole series of invalid bindings are erased.
137     assertFailure(injector, Invalid.class);
138     assertFailure(injector, InvalidLinked.class);
139     assertFailure(injector, InvalidLinkedImpl.class);
140     assertFailure(injector, InvalidLinked2.class);
141     assertFailure(injector, InvalidLinked2Impl.class);
142     assertFailure(injector, InvalidProvidedBy.class);
143     assertFailure(injector, InvalidProvidedByProvider.class);
144     assertFailure(injector, InvalidProvidedBy2.class);
145     assertFailure(injector, InvalidProvidedBy2Provider.class);
146     assertFailure(injector, Invalid2.class);
147 
148     // Validate we didn't do anything to the valid explicit bindings.
149     assertSame(v1, injector.getBinding(Valid.class));
150     assertSame(v2, injector.getBinding(Valid2.class));
151 
152     // Validate that we didn't erase the valid JIT bindings
153     assertSame(jv1, injector.getBinding(JitValid.class));
154     assertSame(jv2, injector.getBinding(JitValid2.class));
155   }
156 
157   @SuppressWarnings("unchecked")
assertFailure(Injector injector, Class clazz)158   private void assertFailure(Injector injector, Class clazz) {
159     try {
160       injector.getBinding(clazz);
161       fail("Shouldn't have been able to get binding of: " + clazz);
162     } catch(ConfigurationException expected) {
163       Message msg = Iterables.getOnlyElement(expected.getErrorMessages());
164       assertEquals("No implementation for " + InvalidInterface.class.getName() + " was bound.",
165           msg.getMessage());
166       List<Object> sources = msg.getSources();
167       // Assert that the first item in the sources if the key for the class we're looking up,
168       // ensuring that each lookup is "new".
169       assertEquals(Key.get(clazz).toString(), sources.get(0).toString());
170       // Assert that the last item in each lookup contains the InvalidInterface class
171       Asserts.assertContains(sources.get(sources.size()-1).toString(),
172           Key.get(InvalidInterface.class).toString());
173     }
174   }
175 
176   static class Invalid {
177     @Inject Valid a;
178     @Inject JitValid b;
179     @Inject InvalidProvidedBy c;
Invalid(InvalidLinked a)180     @Inject Invalid(InvalidLinked a) {}
foo(InvalidInterface a)181     @Inject void foo(InvalidInterface a) {}
182 
183   }
184 
185   @ImplementedBy(InvalidLinkedImpl.class)
186   static interface InvalidLinked {}
187   static class InvalidLinkedImpl implements InvalidLinked {
188     @Inject InvalidLinked2 a;
189   }
190 
191   @ImplementedBy(InvalidLinked2Impl.class)
192   static interface InvalidLinked2 {}
193   static class InvalidLinked2Impl implements InvalidLinked2 {
InvalidLinked2Impl(Invalid2 a)194     @Inject InvalidLinked2Impl(Invalid2 a) {}
195   }
196 
197   @ProvidedBy(InvalidProvidedByProvider.class)
198   static interface InvalidProvidedBy {}
199   static class InvalidProvidedByProvider implements Provider<InvalidProvidedBy> {
200     @Inject InvalidProvidedBy2 a;
get()201     public InvalidProvidedBy get() {
202       return null;
203     }
204   }
205 
206   @ProvidedBy(InvalidProvidedBy2Provider.class)
207   static interface InvalidProvidedBy2 {}
208   static class InvalidProvidedBy2Provider implements Provider<InvalidProvidedBy2> {
209     @Inject Invalid2 a;
get()210     public InvalidProvidedBy2 get() {
211       return null;
212     }
213   }
214 
215   static class Invalid2 {
216     @Inject Invalid a;
217   }
218 
219   interface InvalidInterface {}
220 
221   static class Valid { @Inject Valid2 a; }
222   static class Valid2 {}
223 
224   static class JitValid { @Inject JitValid2 a; }
225   static class JitValid2 {}
226 
227   /**
228    * Regression test for https://github.com/google/guice/issues/319
229    *
230    * The bug is that a class that asks for a provider for itself during injection time,
231    * where any one of the other types required to fulfill the object creation was bound
232    * in a child constructor, explodes when the injected Provider is called.
233    *
234    * It works just fine when the other types are bound in a main injector.
235    */
testInstancesRequestingProvidersForThemselvesWithChildInjectors()236   public void testInstancesRequestingProvidersForThemselvesWithChildInjectors() {
237     final Module testModule = new AbstractModule() {
238       @Override
239       protected void configure() {
240         bind(String.class)
241           .toProvider(TestStringProvider.class);
242       }
243     };
244 
245     // Verify it works when the type is setup in the parent.
246     Injector parentSetupRootInjector = Guice.createInjector(testModule);
247     Injector parentSetupChildInjector = parentSetupRootInjector.createChildInjector();
248     assertEquals(TestStringProvider.TEST_VALUE,
249         parentSetupChildInjector.getInstance(
250             RequiresProviderForSelfWithOtherType.class).getValue());
251 
252     // Verify it works when the type is setup in the child, not the parent.
253     // If it still occurs, the bug will explode here.
254     Injector childSetupRootInjector = Guice.createInjector();
255     Injector childSetupChildInjector = childSetupRootInjector.createChildInjector(testModule);
256     assertEquals(TestStringProvider.TEST_VALUE,
257         childSetupChildInjector.getInstance(
258             RequiresProviderForSelfWithOtherType.class).getValue());
259   }
260 
261   static class TestStringProvider implements Provider<String> {
262     static final String TEST_VALUE = "This is to verify it all works";
263 
get()264     public String get() {
265       return TEST_VALUE;
266     }
267   }
268 
269   static class RequiresProviderForSelfWithOtherType {
270     private final Provider<RequiresProviderForSelfWithOtherType> selfProvider;
271     private final String providedStringValue;
272 
273     @Inject
RequiresProviderForSelfWithOtherType( String providedStringValue, Provider<RequiresProviderForSelfWithOtherType> selfProvider )274     RequiresProviderForSelfWithOtherType(
275         String providedStringValue,
276         Provider<RequiresProviderForSelfWithOtherType> selfProvider
277         ) {
278       this.providedStringValue = providedStringValue;
279       this.selfProvider = selfProvider;
280     }
281 
getValue()282     public String getValue() {
283       // Attempt to get another instance of ourself. This pattern
284       // is possible for recursive processing.
285       selfProvider.get();
286 
287       return providedStringValue;
288     }
289   }
290 
291   /**
292    * Ensure that when we cleanup failed JIT bindings, we don't break.
293    * The test here requires a sequence of JIT bindings:
294    *   A-> B
295    *   B -> C, A
296    *   C -> A, D
297    *   D not JITable
298    * The problem was that C cleaned up A's binding and then handed control back to B,
299    * which tried to continue processing A.. but A was removed from the jitBindings Map,
300    * so it attempts to create a new JIT binding for A, but we haven't yet finished
301    * constructing the first JIT binding for A, so we get a recursive
302    * computation exception from ComputingConcurrentHashMap.
303    *
304    * We also throw in a valid JIT binding, E, to guarantee that if
305    * something fails in this flow, it can be recreated later if it's
306    * not from a failed sequence.
307    */
testRecursiveJitBindingsCleanupCorrectly()308   public void testRecursiveJitBindingsCleanupCorrectly() throws Exception {
309     Injector injector = Guice.createInjector();
310     try {
311       injector.getInstance(A.class);
312       fail("Expected failure");
313     } catch(ConfigurationException expected) {
314       Message msg = Iterables.getOnlyElement(expected.getErrorMessages());
315       Asserts.assertContains(msg.getMessage(),
316           "Could not find a suitable constructor in " + D.class.getName());
317     }
318     // Assert that we've removed all the bindings.
319     assertNull(injector.getExistingBinding(Key.get(A.class)));
320     assertNull(injector.getExistingBinding(Key.get(B.class)));
321     assertNull(injector.getExistingBinding(Key.get(C.class)));
322     assertNull(injector.getExistingBinding(Key.get(D.class)));
323 
324     // Confirm that we didn't prevent 'E' from working.
325     assertNotNull(injector.getBinding(Key.get(E.class)));
326   }
327 
328   static class A {
A(B b)329     @Inject public A(B b) {}
330   }
331 
332   static class B {
B(C c, A a)333     @Inject public B(C c, A a) {}
334   }
335 
336   static class C {
C(A a, D d, E e)337     @Inject public C(A a, D d, E e) {}
338   }
339 
340   static class D {
D(int i)341     public D(int i) {}
342   }
343 
344   // Valid JITable binding
345   static class E { }
346 
testProvidedByNonEmptyEnum()347   public void testProvidedByNonEmptyEnum() {
348     NonEmptyEnum cardSuit = Guice.createInjector().getInstance(NonEmptyEnum.class);
349 
350     assertEquals(NonEmptyEnum.HEARTS, cardSuit);
351   }
352 
testProvidedByEmptyEnum()353   public void testProvidedByEmptyEnum() {
354     EmptyEnum emptyEnumValue = Guice.createInjector().getInstance(EmptyEnum.class);
355     assertNull(emptyEnumValue);
356   }
357 
358   @ProvidedBy(NonEmptyEnumProvider.class)
359   enum NonEmptyEnum { HEARTS, DIAMONDS, CLUBS, SPADES }
360 
361   static final class NonEmptyEnumProvider implements Provider<NonEmptyEnum> {
362     @Override
get()363     public NonEmptyEnum get() {
364       return NonEmptyEnum.HEARTS;
365     }
366   }
367 
368   @ProvidedBy(EmptyEnumProvider.class)
369   enum EmptyEnum {}
370 
371   static final class EmptyEnumProvider implements Provider<EmptyEnum> {
372     @Override
get()373     public EmptyEnum get() {
374       return null;
375     }
376   }
377 
378   // An enum cannot be implemented by anything, so it should not be possible to have a successful
379   // binding when the enum is annotated with @ImplementedBy.
testImplementedByEnum()380   public void testImplementedByEnum() {
381     Injector injector = Guice.createInjector();
382     try {
383       injector.getInstance(EnumWithImplementedBy.class);
384       fail("Expected failure");
385     } catch(ConfigurationException expected) {
386       Message msg = Iterables.getOnlyElement(expected.getErrorMessages());
387       Asserts.assertContains(msg.getMessage(),
388           "No implementation for " + EnumWithImplementedBy.class.getName() + " was bound.");
389     }
390   }
391 
392   @ImplementedBy(EnumWithImplementedByEnum.class)
393   enum EnumWithImplementedBy {}
394   private static class EnumWithImplementedByEnum {}
395 }
396