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