blob: 601d632d5a8ff3be62ad7cfd002afcf32eaea1ad [file] [log] [blame]
crazyboblee0789b192007-02-13 02:43:28 +00001/**
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
17package com.google.inject;
18
sberlind9c913a2011-06-26 21:02:54 +000019import com.google.common.collect.Iterables;
limpbizkit896cdae2008-08-11 17:45:04 +000020import com.google.inject.name.Named;
21import com.google.inject.name.Names;
guice.mirrorbot@gmail.comefa4e9f2011-09-13 17:41:32 +000022import com.google.inject.spi.Message;
sberlinb7a02b02011-07-08 00:34:16 +000023
crazyboblee0789b192007-02-13 02:43:28 +000024import junit.framework.TestCase;
25
sberlinb7a02b02011-07-08 00:34:16 +000026import java.util.List;
27
crazyboblee0789b192007-02-13 02:43:28 +000028/**
29 * @author crazybob@google.com (Bob Lee)
30 */
31public class ImplicitBindingTest extends TestCase {
32
crazyboblee5746d5d2007-02-18 21:52:24 +000033 public void testCircularDependency() throws CreationException {
kevinb9nb950ad92007-03-13 02:16:16 +000034 Injector injector = Guice.createInjector();
kevinb9n27f8a582007-02-28 22:54:06 +000035 Foo foo = injector.getInstance(Foo.class);
crazyboblee0789b192007-02-13 02:43:28 +000036 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
46 public Bar(Foo foo) {
47 this.foo = foo;
48 }
49 }
crazyboblee38545822007-03-01 23:33:48 +000050
51 public void testDefaultImplementation() {
kevinb9nb950ad92007-03-13 02:16:16 +000052 Injector injector = Guice.createInjector();
crazyboblee38545822007-03-01 23:33:48 +000053 I i = injector.getInstance(I.class);
54 i.go();
55 }
56
57 @ImplementedBy(IImpl.class)
58 interface I {
59 void go();
60 }
61
62 static class IImpl implements I {
63 public void go() {}
64 }
65
limpbizkita665a832008-03-19 21:57:54 +000066 static class AlternateImpl implements I {
67 public void go() {}
68 }
69
crazyboblee38545822007-03-01 23:33:48 +000070 public void testDefaultProvider() {
kevinb9nb950ad92007-03-13 02:16:16 +000071 Injector injector = Guice.createInjector();
crazyboblee38545822007-03-01 23:33:48 +000072 Provided provided = injector.getInstance(Provided.class);
73 provided.go();
74 }
75
limpbizkita665a832008-03-19 21:57:54 +000076 public void testBindingOverridesImplementedBy() {
77 Injector injector = Guice.createInjector(new AbstractModule() {
guice.mirrorbot@gmail.comefa4e9f2011-09-13 17:41:32 +000078 @Override
limpbizkita665a832008-03-19 21:57:54 +000079 protected void configure() {
80 bind(I.class).to(AlternateImpl.class);
81 }
82 });
83 assertEquals(AlternateImpl.class, injector.getInstance(I.class).getClass());
84 }
85
crazyboblee38545822007-03-01 23:33:48 +000086 @ProvidedBy(ProvidedProvider.class)
87 interface Provided {
88 void go();
89 }
90
limpbizkit896cdae2008-08-11 17:45:04 +000091 public void testNoImplicitBindingIsCreatedForAnnotatedKeys() {
92 try {
93 Guice.createInjector().getInstance(Key.get(I.class, Names.named("i")));
94 fail();
limpbizkit490833f2008-11-02 00:12:39 +000095 } catch (ConfigurationException expected) {
limpbizkit896cdae2008-08-11 17:45:04 +000096 Asserts.assertContains(expected.getMessage(),
limpbizkita6e0e782008-09-03 06:19:56 +000097 "1) No implementation for " + I.class.getName(),
98 "annotated with @" + Named.class.getName() + "(value=i) was bound.",
limpbizkit72d11dd2008-11-02 07:59:13 +000099 "while locating " + I.class.getName(),
limpbizkit06898062008-11-02 05:14:55 +0000100 " annotated with @" + Named.class.getName() + "(value=i)");
limpbizkit896cdae2008-08-11 17:45:04 +0000101 }
limpbizkit896cdae2008-08-11 17:45:04 +0000102 }
103
crazyboblee38545822007-03-01 23:33:48 +0000104 static class ProvidedProvider implements Provider<Provided> {
105 public Provided get() {
106 return new Provided() {
107 public void go() {}
108 };
109 }
110 }
111
limpbizkit72fb1522009-06-12 05:31:32 +0000112 /**
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.
sberlin7df9cf32010-05-25 12:25:46 +0000116 *
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.
limpbizkit72fb1522009-06-12 05:31:32 +0000120 */
121 public void testCircularJitBindingsLeaveNoResidue() {
sberlin7df9cf32010-05-25 12:25:46 +0000122 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);
limpbizkit72fb1522009-06-12 05:31:32 +0000135
sberlin7df9cf32010-05-25 12:25:46 +0000136 // 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);
sberlin47e69ef2010-07-03 15:35:50 +0000142 assertFailure(injector, InvalidProvidedBy.class);
143 assertFailure(injector, InvalidProvidedByProvider.class);
144 assertFailure(injector, InvalidProvidedBy2.class);
145 assertFailure(injector, InvalidProvidedBy2Provider.class);
sberlin7df9cf32010-05-25 12:25:46 +0000146 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")
158 private void assertFailure(Injector injector, Class clazz) {
limpbizkit72fb1522009-06-12 05:31:32 +0000159 try {
sberlin7df9cf32010-05-25 12:25:46 +0000160 injector.getBinding(clazz);
161 fail("Shouldn't have been able to get binding of: " + clazz);
162 } catch(ConfigurationException expected) {
guice.mirrorbot@gmail.comefa4e9f2011-09-13 17:41:32 +0000163 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();
sberlin7df9cf32010-05-25 12:25:46 +0000167 // 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());
limpbizkit72fb1522009-06-12 05:31:32 +0000173 }
174 }
175
sberlin7df9cf32010-05-25 12:25:46 +0000176 static class Invalid {
177 @Inject Valid a;
178 @Inject JitValid b;
sberlin47e69ef2010-07-03 15:35:50 +0000179 @Inject InvalidProvidedBy c;
sberlin7df9cf32010-05-25 12:25:46 +0000180 @Inject Invalid(InvalidLinked a) {}
181 @Inject void foo(InvalidInterface a) {}
182
limpbizkit72fb1522009-06-12 05:31:32 +0000183 }
184
sberlin7df9cf32010-05-25 12:25:46 +0000185 @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 {
194 @Inject InvalidLinked2Impl(Invalid2 a) {}
195 }
196
sberlin47e69ef2010-07-03 15:35:50 +0000197 @ProvidedBy(InvalidProvidedByProvider.class)
198 static interface InvalidProvidedBy {}
199 static class InvalidProvidedByProvider implements Provider<InvalidProvidedBy> {
200 @Inject InvalidProvidedBy2 a;
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;
210 public InvalidProvidedBy2 get() {
211 return null;
212 }
213 }
214
sberlin7df9cf32010-05-25 12:25:46 +0000215 static class Invalid2 {
216 @Inject Invalid a;
limpbizkit72fb1522009-06-12 05:31:32 +0000217 }
218
sberlin7df9cf32010-05-25 12:25:46 +0000219 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 /**
Sam Berlin0558b322014-07-10 16:33:32 -0400228 * Regression test for https://github.com/google/guice/issues/319
sberlin7df9cf32010-05-25 12:25:46 +0000229 *
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 */
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
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
274 RequiresProviderForSelfWithOtherType(
275 String providedStringValue,
276 Provider<RequiresProviderForSelfWithOtherType> selfProvider
277 ) {
278 this.providedStringValue = providedStringValue;
279 this.selfProvider = selfProvider;
280 }
281
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 }
guice.mirrorbot@gmail.comefa4e9f2011-09-13 17:41:32 +0000289 }
sberlin7df9cf32010-05-25 12:25:46 +0000290
guice.mirrorbot@gmail.comefa4e9f2011-09-13 17:41:32 +0000291 /**
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 */
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 {
329 @Inject public A(B b) {}
330 }
331
332 static class B {
333 @Inject public B(C c, A a) {}
334 }
335
336 static class C {
337 @Inject public C(A a, D d, E e) {}
338 }
339
340 static class D {
341 public D(int i) {}
342 }
343
344 // Valid JITable binding
345 static class E { }
346
crazyboblee0789b192007-02-13 02:43:28 +0000347}