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