crazyboblee | 0789b19 | 2007-02-13 02:43:28 +0000 | [diff] [blame] | 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 | |
sberlin | d9c913a | 2011-06-26 21:02:54 +0000 | [diff] [blame] | 19 | import com.google.common.collect.Iterables; |
limpbizkit | 896cdae | 2008-08-11 17:45:04 +0000 | [diff] [blame] | 20 | import com.google.inject.name.Named; |
| 21 | import com.google.inject.name.Names; |
guice.mirrorbot@gmail.com | efa4e9f | 2011-09-13 17:41:32 +0000 | [diff] [blame] | 22 | import com.google.inject.spi.Message; |
sberlin | b7a02b0 | 2011-07-08 00:34:16 +0000 | [diff] [blame] | 23 | |
crazyboblee | 0789b19 | 2007-02-13 02:43:28 +0000 | [diff] [blame] | 24 | import junit.framework.TestCase; |
| 25 | |
sberlin | b7a02b0 | 2011-07-08 00:34:16 +0000 | [diff] [blame] | 26 | import java.util.List; |
| 27 | |
crazyboblee | 0789b19 | 2007-02-13 02:43:28 +0000 | [diff] [blame] | 28 | /** |
| 29 | * @author crazybob@google.com (Bob Lee) |
| 30 | */ |
| 31 | public class ImplicitBindingTest extends TestCase { |
| 32 | |
crazyboblee | 5746d5d | 2007-02-18 21:52:24 +0000 | [diff] [blame] | 33 | public void testCircularDependency() throws CreationException { |
kevinb9n | b950ad9 | 2007-03-13 02:16:16 +0000 | [diff] [blame] | 34 | Injector injector = Guice.createInjector(); |
kevinb9n | 27f8a58 | 2007-02-28 22:54:06 +0000 | [diff] [blame] | 35 | Foo foo = injector.getInstance(Foo.class); |
crazyboblee | 0789b19 | 2007-02-13 02:43:28 +0000 | [diff] [blame] | 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 |
| 46 | public Bar(Foo foo) { |
| 47 | this.foo = foo; |
| 48 | } |
| 49 | } |
crazyboblee | 3854582 | 2007-03-01 23:33:48 +0000 | [diff] [blame] | 50 | |
| 51 | public void testDefaultImplementation() { |
kevinb9n | b950ad9 | 2007-03-13 02:16:16 +0000 | [diff] [blame] | 52 | Injector injector = Guice.createInjector(); |
crazyboblee | 3854582 | 2007-03-01 23:33:48 +0000 | [diff] [blame] | 53 | 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 | |
limpbizkit | a665a83 | 2008-03-19 21:57:54 +0000 | [diff] [blame] | 66 | static class AlternateImpl implements I { |
| 67 | public void go() {} |
| 68 | } |
| 69 | |
crazyboblee | 3854582 | 2007-03-01 23:33:48 +0000 | [diff] [blame] | 70 | public void testDefaultProvider() { |
kevinb9n | b950ad9 | 2007-03-13 02:16:16 +0000 | [diff] [blame] | 71 | Injector injector = Guice.createInjector(); |
crazyboblee | 3854582 | 2007-03-01 23:33:48 +0000 | [diff] [blame] | 72 | Provided provided = injector.getInstance(Provided.class); |
| 73 | provided.go(); |
| 74 | } |
| 75 | |
limpbizkit | a665a83 | 2008-03-19 21:57:54 +0000 | [diff] [blame] | 76 | public void testBindingOverridesImplementedBy() { |
| 77 | Injector injector = Guice.createInjector(new AbstractModule() { |
guice.mirrorbot@gmail.com | efa4e9f | 2011-09-13 17:41:32 +0000 | [diff] [blame] | 78 | @Override |
limpbizkit | a665a83 | 2008-03-19 21:57:54 +0000 | [diff] [blame] | 79 | protected void configure() { |
| 80 | bind(I.class).to(AlternateImpl.class); |
| 81 | } |
| 82 | }); |
| 83 | assertEquals(AlternateImpl.class, injector.getInstance(I.class).getClass()); |
| 84 | } |
| 85 | |
crazyboblee | 3854582 | 2007-03-01 23:33:48 +0000 | [diff] [blame] | 86 | @ProvidedBy(ProvidedProvider.class) |
| 87 | interface Provided { |
| 88 | void go(); |
| 89 | } |
| 90 | |
limpbizkit | 896cdae | 2008-08-11 17:45:04 +0000 | [diff] [blame] | 91 | public void testNoImplicitBindingIsCreatedForAnnotatedKeys() { |
| 92 | try { |
| 93 | Guice.createInjector().getInstance(Key.get(I.class, Names.named("i"))); |
| 94 | fail(); |
limpbizkit | 490833f | 2008-11-02 00:12:39 +0000 | [diff] [blame] | 95 | } catch (ConfigurationException expected) { |
limpbizkit | 896cdae | 2008-08-11 17:45:04 +0000 | [diff] [blame] | 96 | Asserts.assertContains(expected.getMessage(), |
limpbizkit | a6e0e78 | 2008-09-03 06:19:56 +0000 | [diff] [blame] | 97 | "1) No implementation for " + I.class.getName(), |
| 98 | "annotated with @" + Named.class.getName() + "(value=i) was bound.", |
limpbizkit | 72d11dd | 2008-11-02 07:59:13 +0000 | [diff] [blame] | 99 | "while locating " + I.class.getName(), |
limpbizkit | 0689806 | 2008-11-02 05:14:55 +0000 | [diff] [blame] | 100 | " annotated with @" + Named.class.getName() + "(value=i)"); |
limpbizkit | 896cdae | 2008-08-11 17:45:04 +0000 | [diff] [blame] | 101 | } |
limpbizkit | 896cdae | 2008-08-11 17:45:04 +0000 | [diff] [blame] | 102 | } |
| 103 | |
crazyboblee | 3854582 | 2007-03-01 23:33:48 +0000 | [diff] [blame] | 104 | static class ProvidedProvider implements Provider<Provided> { |
| 105 | public Provided get() { |
| 106 | return new Provided() { |
| 107 | public void go() {} |
| 108 | }; |
| 109 | } |
| 110 | } |
| 111 | |
limpbizkit | 72fb152 | 2009-06-12 05:31:32 +0000 | [diff] [blame] | 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. |
sberlin | 7df9cf3 | 2010-05-25 12:25:46 +0000 | [diff] [blame] | 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. |
limpbizkit | 72fb152 | 2009-06-12 05:31:32 +0000 | [diff] [blame] | 120 | */ |
| 121 | public void testCircularJitBindingsLeaveNoResidue() { |
sberlin | 7df9cf3 | 2010-05-25 12:25:46 +0000 | [diff] [blame] | 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); |
limpbizkit | 72fb152 | 2009-06-12 05:31:32 +0000 | [diff] [blame] | 135 | |
sberlin | 7df9cf3 | 2010-05-25 12:25:46 +0000 | [diff] [blame] | 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); |
sberlin | 47e69ef | 2010-07-03 15:35:50 +0000 | [diff] [blame] | 142 | assertFailure(injector, InvalidProvidedBy.class); |
| 143 | assertFailure(injector, InvalidProvidedByProvider.class); |
| 144 | assertFailure(injector, InvalidProvidedBy2.class); |
| 145 | assertFailure(injector, InvalidProvidedBy2Provider.class); |
sberlin | 7df9cf3 | 2010-05-25 12:25:46 +0000 | [diff] [blame] | 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") |
| 158 | private void assertFailure(Injector injector, Class clazz) { |
limpbizkit | 72fb152 | 2009-06-12 05:31:32 +0000 | [diff] [blame] | 159 | try { |
sberlin | 7df9cf3 | 2010-05-25 12:25:46 +0000 | [diff] [blame] | 160 | injector.getBinding(clazz); |
| 161 | fail("Shouldn't have been able to get binding of: " + clazz); |
| 162 | } catch(ConfigurationException expected) { |
guice.mirrorbot@gmail.com | efa4e9f | 2011-09-13 17:41:32 +0000 | [diff] [blame] | 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(); |
sberlin | 7df9cf3 | 2010-05-25 12:25:46 +0000 | [diff] [blame] | 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()); |
limpbizkit | 72fb152 | 2009-06-12 05:31:32 +0000 | [diff] [blame] | 173 | } |
| 174 | } |
| 175 | |
sberlin | 7df9cf3 | 2010-05-25 12:25:46 +0000 | [diff] [blame] | 176 | static class Invalid { |
| 177 | @Inject Valid a; |
| 178 | @Inject JitValid b; |
sberlin | 47e69ef | 2010-07-03 15:35:50 +0000 | [diff] [blame] | 179 | @Inject InvalidProvidedBy c; |
sberlin | 7df9cf3 | 2010-05-25 12:25:46 +0000 | [diff] [blame] | 180 | @Inject Invalid(InvalidLinked a) {} |
| 181 | @Inject void foo(InvalidInterface a) {} |
| 182 | |
limpbizkit | 72fb152 | 2009-06-12 05:31:32 +0000 | [diff] [blame] | 183 | } |
| 184 | |
sberlin | 7df9cf3 | 2010-05-25 12:25:46 +0000 | [diff] [blame] | 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 { |
| 194 | @Inject InvalidLinked2Impl(Invalid2 a) {} |
| 195 | } |
| 196 | |
sberlin | 47e69ef | 2010-07-03 15:35:50 +0000 | [diff] [blame] | 197 | @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 | |
sberlin | 7df9cf3 | 2010-05-25 12:25:46 +0000 | [diff] [blame] | 215 | static class Invalid2 { |
| 216 | @Inject Invalid a; |
limpbizkit | 72fb152 | 2009-06-12 05:31:32 +0000 | [diff] [blame] | 217 | } |
| 218 | |
sberlin | 7df9cf3 | 2010-05-25 12:25:46 +0000 | [diff] [blame] | 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 | /** |
Sam Berlin | 0558b32 | 2014-07-10 16:33:32 -0400 | [diff] [blame^] | 228 | * Regression test for https://github.com/google/guice/issues/319 |
sberlin | 7df9cf3 | 2010-05-25 12:25:46 +0000 | [diff] [blame] | 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 | */ |
| 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.com | efa4e9f | 2011-09-13 17:41:32 +0000 | [diff] [blame] | 289 | } |
sberlin | 7df9cf3 | 2010-05-25 12:25:46 +0000 | [diff] [blame] | 290 | |
guice.mirrorbot@gmail.com | efa4e9f | 2011-09-13 17:41:32 +0000 | [diff] [blame] | 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 | */ |
| 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 | |
crazyboblee | 0789b19 | 2007-02-13 02:43:28 +0000 | [diff] [blame] | 347 | } |