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