PageRenderTime 5274ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/core/test/com/google/inject/ImplicitBindingTest.java

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