PageRenderTime 35ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

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

https://gitlab.com/metamorphiccode/guice
Java | 576 lines | 445 code | 83 blank | 48 comment | 23 complexity | 62621c91772c58b264f75716bd796a7b MD5 | raw file
  1. /**
  2. * Copyright (C) 2010 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 static com.google.inject.Asserts.*;
  18. import static com.google.inject.name.Names.named;
  19. import com.google.common.base.Objects;
  20. import com.google.common.collect.Lists;
  21. import com.google.inject.name.Named;
  22. import com.google.inject.spi.Element;
  23. import com.google.inject.spi.Elements;
  24. import com.google.inject.util.Providers;
  25. import junit.framework.TestCase;
  26. import java.lang.annotation.Annotation;
  27. import java.lang.reflect.Constructor;
  28. import java.util.Arrays;
  29. import java.util.Collection;
  30. import java.util.LinkedHashSet;
  31. import java.util.List;
  32. import java.util.logging.Logger;
  33. /**
  34. * A suite of tests for duplicate bindings.
  35. *
  36. * @author sameb@google.com (Sam Berlin)
  37. */
  38. public class DuplicateBindingsTest extends TestCase {
  39. private FooImpl foo = new FooImpl();
  40. private Provider<Foo> pFoo = Providers.<Foo>of(new FooImpl());
  41. private Class<? extends Provider<? extends Foo>> pclFoo = FooProvider.class;
  42. private Class<? extends Foo> clFoo = FooImpl.class;
  43. private Constructor<FooImpl> cFoo = FooImpl.cxtor();
  44. public void testDuplicateBindingsAreIgnored() {
  45. Injector injector = Guice.createInjector(
  46. new SimpleModule(foo, pFoo, pclFoo, clFoo, cFoo),
  47. new SimpleModule(foo, pFoo, pclFoo, clFoo, cFoo)
  48. );
  49. List<Key<?>> bindings = Lists.newArrayList(injector.getAllBindings().keySet());
  50. removeBasicBindings(bindings);
  51. // Ensure only one binding existed for each type.
  52. assertTrue(bindings.remove(Key.get(Foo.class, named("instance"))));
  53. assertTrue(bindings.remove(Key.get(Foo.class, named("pInstance"))));
  54. assertTrue(bindings.remove(Key.get(Foo.class, named("pKey"))));
  55. assertTrue(bindings.remove(Key.get(Foo.class, named("linkedKey"))));
  56. assertTrue(bindings.remove(Key.get(FooImpl.class)));
  57. assertTrue(bindings.remove(Key.get(Foo.class, named("constructor"))));
  58. assertTrue(bindings.remove(Key.get(FooProvider.class))); // JIT binding
  59. assertTrue(bindings.remove(Key.get(Foo.class, named("providerMethod"))));
  60. assertEquals(bindings.toString(), 0, bindings.size());
  61. }
  62. public void testElementsDeduplicate() {
  63. List<Element> elements = Elements.getElements(
  64. new SimpleModule(foo, pFoo, pclFoo, clFoo, cFoo),
  65. new SimpleModule(foo, pFoo, pclFoo, clFoo, cFoo)
  66. );
  67. assertEquals(14, elements.size());
  68. assertEquals(7, new LinkedHashSet<Element>(elements).size());
  69. }
  70. public void testProviderMethodsFailIfInstancesDiffer() {
  71. try {
  72. Guice.createInjector(new FailingProviderModule(), new FailingProviderModule());
  73. fail("should have failed");
  74. } catch(CreationException ce) {
  75. assertContains(ce.getMessage(),
  76. "A binding to " + Foo.class.getName() + " was already configured " +
  77. "at " + FailingProviderModule.class.getName(),
  78. "at " + FailingProviderModule.class.getName()
  79. );
  80. }
  81. }
  82. public void testSameScopeInstanceIgnored() {
  83. Guice.createInjector(
  84. new ScopedModule(Scopes.SINGLETON, foo, pFoo, pclFoo, clFoo, cFoo),
  85. new ScopedModule(Scopes.SINGLETON, foo, pFoo, pclFoo, clFoo, cFoo)
  86. );
  87. Guice.createInjector(
  88. new ScopedModule(Scopes.NO_SCOPE, foo, pFoo, pclFoo, clFoo, cFoo),
  89. new ScopedModule(Scopes.NO_SCOPE, foo, pFoo, pclFoo, clFoo, cFoo)
  90. );
  91. }
  92. public void testSameScopeAnnotationIgnored() {
  93. Guice.createInjector(
  94. new AnnotatedScopeModule(Singleton.class, foo, pFoo, pclFoo, clFoo, cFoo),
  95. new AnnotatedScopeModule(Singleton.class, foo, pFoo, pclFoo, clFoo, cFoo)
  96. );
  97. }
  98. public void testMixedAnnotationAndScopeForSingletonIgnored() {
  99. Guice.createInjector(
  100. new ScopedModule(Scopes.SINGLETON, foo, pFoo, pclFoo, clFoo, cFoo),
  101. new AnnotatedScopeModule(Singleton.class, foo, pFoo, pclFoo, clFoo, cFoo)
  102. );
  103. }
  104. public void testMixedScopeAndUnscopedIgnored() {
  105. Guice.createInjector(
  106. new SimpleModule(foo, pFoo, pclFoo, clFoo, cFoo),
  107. new ScopedModule(Scopes.NO_SCOPE, foo, pFoo, pclFoo, clFoo, cFoo)
  108. );
  109. }
  110. public void testMixedScopeFails() {
  111. try {
  112. Guice.createInjector(
  113. new SimpleModule(foo, pFoo, pclFoo, clFoo, cFoo),
  114. new ScopedModule(Scopes.SINGLETON, foo, pFoo, pclFoo, clFoo, cFoo)
  115. );
  116. fail("expected exception");
  117. } catch(CreationException ce) {
  118. String segment1 = "A binding to " + Foo.class.getName() + " annotated with "
  119. + named("pInstance") + " was already configured at " + SimpleModule.class.getName();
  120. String segment2 = "A binding to " + Foo.class.getName() + " annotated with " + named("pKey")
  121. + " was already configured at " + SimpleModule.class.getName();
  122. String segment3 = "A binding to " + Foo.class.getName() + " annotated with "
  123. + named("constructor") + " was already configured at " + SimpleModule.class.getName();
  124. String segment4 = "A binding to " + FooImpl.class.getName() + " was already configured at "
  125. + SimpleModule.class.getName();
  126. String atSegment = "at " + ScopedModule.class.getName();
  127. if (isIncludeStackTraceOff()) {
  128. assertContains(ce.getMessage(), segment1 , atSegment, segment2, atSegment, segment3,
  129. atSegment, segment4, atSegment);
  130. } else {
  131. assertContains(ce.getMessage(), segment1 , atSegment, segment2, atSegment, segment4,
  132. atSegment, segment3, atSegment);
  133. }
  134. }
  135. }
  136. @SuppressWarnings("unchecked")
  137. public void testMixedTargetsFails() {
  138. try {
  139. Guice.createInjector(
  140. new SimpleModule(foo, pFoo, pclFoo, clFoo, cFoo),
  141. new SimpleModule(new FooImpl(), Providers.<Foo>of(new FooImpl()),
  142. (Class)BarProvider.class, (Class)Bar.class, (Constructor)Bar.cxtor())
  143. );
  144. fail("expected exception");
  145. } catch(CreationException ce) {
  146. assertContains(ce.getMessage(),
  147. "A binding to " + Foo.class.getName() + " annotated with " + named("pInstance") + " was already configured at " + SimpleModule.class.getName(),
  148. "at " + SimpleModule.class.getName(),
  149. "A binding to " + Foo.class.getName() + " annotated with " + named("pKey") + " was already configured at " + SimpleModule.class.getName(),
  150. "at " + SimpleModule.class.getName(),
  151. "A binding to " + Foo.class.getName() + " annotated with " + named("linkedKey") + " was already configured at " + SimpleModule.class.getName(),
  152. "at " + SimpleModule.class.getName(),
  153. "A binding to " + Foo.class.getName() + " annotated with " + named("constructor") + " was already configured at " + SimpleModule.class.getName(),
  154. "at " + SimpleModule.class.getName());
  155. }
  156. }
  157. public void testExceptionInEqualsThrowsCreationException() {
  158. try {
  159. Guice.createInjector(new ThrowingModule(), new ThrowingModule());
  160. fail("expected exception");
  161. } catch(CreationException ce) {
  162. assertContains(ce.getMessage(),
  163. "A binding to " + Foo.class.getName() + " was already configured at " + ThrowingModule.class.getName(),
  164. "and an error was thrown while checking duplicate bindings. Error: java.lang.RuntimeException: Boo!",
  165. "at " + ThrowingModule.class.getName());
  166. }
  167. }
  168. public void testChildInjectorDuplicateParentFail() {
  169. Injector injector = Guice.createInjector(
  170. new SimpleModule(foo, pFoo, pclFoo, clFoo, cFoo)
  171. );
  172. try {
  173. injector.createChildInjector(
  174. new SimpleModule(foo, pFoo, pclFoo, clFoo, cFoo)
  175. );
  176. fail("expected exception");
  177. } catch(CreationException ce) {
  178. assertContains(ce.getMessage(),
  179. "A binding to " + Foo.class.getName() + " annotated with " + named("pInstance") + " was already configured at " + SimpleModule.class.getName(),
  180. "at " + SimpleModule.class.getName(),
  181. "A binding to " + Foo.class.getName() + " annotated with " + named("pKey") + " was already configured at " + SimpleModule.class.getName(),
  182. "at " + SimpleModule.class.getName(),
  183. "A binding to " + Foo.class.getName() + " annotated with " + named("linkedKey") + " was already configured at " + SimpleModule.class.getName(),
  184. "at " + SimpleModule.class.getName(),
  185. "A binding to " + Foo.class.getName() + " annotated with " + named("constructor") + " was already configured at " + SimpleModule.class.getName(),
  186. "at " + SimpleModule.class.getName(),
  187. "A binding to " + Foo.class.getName() + " annotated with " + named("providerMethod") + " was already configured at " + SimpleProviderModule.class.getName(),
  188. "at " + SimpleProviderModule.class.getName()
  189. );
  190. }
  191. }
  192. public void testDuplicatesSolelyInChildIgnored() {
  193. Injector injector = Guice.createInjector();
  194. injector.createChildInjector(
  195. new SimpleModule(foo, pFoo, pclFoo, clFoo, cFoo),
  196. new SimpleModule(foo, pFoo, pclFoo, clFoo, cFoo)
  197. );
  198. }
  199. public void testDifferentBindingTypesFail() {
  200. List<Element> elements = Elements.getElements(
  201. new FailedModule(foo, pFoo, pclFoo, clFoo, cFoo)
  202. );
  203. // Make sure every combination of the elements with another element fails.
  204. // This ensures that duplication checks the kind of binding also.
  205. for(Element e1 : elements) {
  206. for(Element e2: elements) {
  207. // if they're the same, this shouldn't fail.
  208. try {
  209. Guice.createInjector(Elements.getModule(Arrays.asList(e1, e2)));
  210. if(e1 != e2) {
  211. fail("must fail!");
  212. }
  213. } catch(CreationException expected) {
  214. if(e1 != e2) {
  215. assertContains(expected.getMessage(),
  216. "A binding to " + Foo.class.getName() + " was already configured at " + FailedModule.class.getName(),
  217. "at " + FailedModule.class.getName());
  218. } else {
  219. throw expected;
  220. }
  221. }
  222. }
  223. }
  224. }
  225. public void testJitBindingsAreCheckedAfterConversions() {
  226. Guice.createInjector(new AbstractModule() {
  227. @Override
  228. protected void configure() {
  229. bind(A.class);
  230. bind(A.class).to(RealA.class);
  231. }
  232. });
  233. }
  234. public void testEqualsNotCalledByDefaultOnInstance() {
  235. final HashEqualsTester a = new HashEqualsTester();
  236. a.throwOnEquals = true;
  237. Guice.createInjector(new AbstractModule() {
  238. @Override
  239. protected void configure() {
  240. bind(String.class);
  241. bind(HashEqualsTester.class).toInstance(a);
  242. }
  243. });
  244. }
  245. public void testEqualsNotCalledByDefaultOnProvider() {
  246. final HashEqualsTester a = new HashEqualsTester();
  247. a.throwOnEquals = true;
  248. Guice.createInjector(new AbstractModule() {
  249. @Override
  250. protected void configure() {
  251. bind(String.class);
  252. bind(Object.class).toProvider(a);
  253. }
  254. });
  255. }
  256. public void testHashcodeNeverCalledOnInstance() {
  257. final HashEqualsTester a = new HashEqualsTester();
  258. a.throwOnHashcode = true;
  259. a.equality = "test";
  260. final HashEqualsTester b = new HashEqualsTester();
  261. b.throwOnHashcode = true;
  262. b.equality = "test";
  263. Guice.createInjector(new AbstractModule() {
  264. @Override
  265. protected void configure() {
  266. bind(String.class);
  267. bind(HashEqualsTester.class).toInstance(a);
  268. bind(HashEqualsTester.class).toInstance(b);
  269. }
  270. });
  271. }
  272. public void testHashcodeNeverCalledOnProviderInstance() {
  273. final HashEqualsTester a = new HashEqualsTester();
  274. a.throwOnHashcode = true;
  275. a.equality = "test";
  276. final HashEqualsTester b = new HashEqualsTester();
  277. b.throwOnHashcode = true;
  278. b.equality = "test";
  279. Guice.createInjector(new AbstractModule() {
  280. @Override
  281. protected void configure() {
  282. bind(String.class);
  283. bind(Object.class).toProvider(a);
  284. bind(Object.class).toProvider(b);
  285. }
  286. });
  287. }
  288. private static class RealA extends A {}
  289. @ImplementedBy(RealA.class) private static class A {}
  290. private void removeBasicBindings(Collection<Key<?>> bindings) {
  291. bindings.remove(Key.get(Injector.class));
  292. bindings.remove(Key.get(Logger.class));
  293. bindings.remove(Key.get(Stage.class));
  294. }
  295. private static class ThrowingModule extends AbstractModule {
  296. @Override
  297. protected void configure() {
  298. bind(Foo.class).toInstance(new Foo() {
  299. @Override
  300. public boolean equals(Object obj) {
  301. throw new RuntimeException("Boo!");
  302. }
  303. });
  304. }
  305. }
  306. private static abstract class FooModule extends AbstractModule {
  307. protected final FooImpl foo;
  308. protected final Provider<Foo> pFoo;
  309. protected final Class<? extends Provider<? extends Foo>> pclFoo;
  310. protected final Class<? extends Foo> clFoo;
  311. protected final Constructor<FooImpl> cFoo;
  312. FooModule(FooImpl foo, Provider<Foo> pFoo, Class<? extends Provider<? extends Foo>> pclFoo,
  313. Class<? extends Foo> clFoo, Constructor<FooImpl> cFoo) {
  314. this.foo = foo;
  315. this.pFoo = pFoo;
  316. this.pclFoo = pclFoo;
  317. this.clFoo = clFoo;
  318. this.cFoo = cFoo;
  319. }
  320. }
  321. private static class FailedModule extends FooModule {
  322. FailedModule(FooImpl foo, Provider<Foo> pFoo, Class<? extends Provider<? extends Foo>> pclFoo,
  323. Class<? extends Foo> clFoo, Constructor<FooImpl> cFoo) {
  324. super(foo, pFoo, pclFoo, clFoo, cFoo);
  325. }
  326. protected void configure() {
  327. // InstanceBinding
  328. bind(Foo.class).toInstance(foo);
  329. // ProviderInstanceBinding
  330. bind(Foo.class).toProvider(pFoo);
  331. // ProviderKeyBinding
  332. bind(Foo.class).toProvider(pclFoo);
  333. // LinkedKeyBinding
  334. bind(Foo.class).to(clFoo);
  335. // ConstructorBinding
  336. bind(Foo.class).toConstructor(cFoo);
  337. }
  338. @Provides Foo foo() {
  339. return null;
  340. }
  341. }
  342. private static class FailingProviderModule extends AbstractModule {
  343. @Override protected void configure() {}
  344. @Provides Foo foo() {
  345. return null;
  346. }
  347. }
  348. private static class SimpleProviderModule extends AbstractModule {
  349. @Override protected void configure() {}
  350. @Provides @Named("providerMethod") Foo foo() {
  351. return null;
  352. }
  353. @Override
  354. public boolean equals(Object obj) {
  355. return obj.getClass() == getClass();
  356. }
  357. }
  358. private static class SimpleModule extends FooModule {
  359. SimpleModule(FooImpl foo, Provider<Foo> pFoo, Class<? extends Provider<? extends Foo>> pclFoo,
  360. Class<? extends Foo> clFoo, Constructor<FooImpl> cFoo) {
  361. super(foo, pFoo, pclFoo, clFoo, cFoo);
  362. }
  363. protected void configure() {
  364. // InstanceBinding
  365. bind(Foo.class).annotatedWith(named("instance")).toInstance(foo);
  366. // ProviderInstanceBinding
  367. bind(Foo.class).annotatedWith(named("pInstance")).toProvider(pFoo);
  368. // ProviderKeyBinding
  369. bind(Foo.class).annotatedWith(named("pKey")).toProvider(pclFoo);
  370. // LinkedKeyBinding
  371. bind(Foo.class).annotatedWith(named("linkedKey")).to(clFoo);
  372. // UntargettedBinding / ConstructorBinding
  373. bind(FooImpl.class);
  374. // ConstructorBinding
  375. bind(Foo.class).annotatedWith(named("constructor")).toConstructor(cFoo);
  376. // ProviderMethod
  377. // (reconstructed from an Element to ensure it doesn't get filtered out
  378. // by deduplicating Modules)
  379. install(Elements.getModule(Elements.getElements(new SimpleProviderModule())));
  380. }
  381. }
  382. private static class ScopedModule extends FooModule {
  383. private final Scope scope;
  384. ScopedModule(Scope scope, FooImpl foo, Provider<Foo> pFoo,
  385. Class<? extends Provider<? extends Foo>> pclFoo, Class<? extends Foo> clFoo,
  386. Constructor<FooImpl> cFoo) {
  387. super(foo, pFoo, pclFoo, clFoo, cFoo);
  388. this.scope = scope;
  389. }
  390. protected void configure() {
  391. // ProviderInstanceBinding
  392. bind(Foo.class).annotatedWith(named("pInstance")).toProvider(pFoo).in(scope);
  393. // ProviderKeyBinding
  394. bind(Foo.class).annotatedWith(named("pKey")).toProvider(pclFoo).in(scope);
  395. // LinkedKeyBinding
  396. bind(Foo.class).annotatedWith(named("linkedKey")).to(clFoo).in(scope);
  397. // UntargettedBinding / ConstructorBinding
  398. bind(FooImpl.class).in(scope);
  399. // ConstructorBinding
  400. bind(Foo.class).annotatedWith(named("constructor")).toConstructor(cFoo).in(scope);
  401. }
  402. }
  403. private static class AnnotatedScopeModule extends FooModule {
  404. private final Class<? extends Annotation> scope;
  405. AnnotatedScopeModule(Class<? extends Annotation> scope, FooImpl foo, Provider<Foo> pFoo,
  406. Class<? extends Provider<? extends Foo>> pclFoo, Class<? extends Foo> clFoo,
  407. Constructor<FooImpl> cFoo) {
  408. super(foo, pFoo, pclFoo, clFoo, cFoo);
  409. this.scope = scope;
  410. }
  411. protected void configure() {
  412. // ProviderInstanceBinding
  413. bind(Foo.class).annotatedWith(named("pInstance")).toProvider(pFoo).in(scope);
  414. // ProviderKeyBinding
  415. bind(Foo.class).annotatedWith(named("pKey")).toProvider(pclFoo).in(scope);
  416. // LinkedKeyBinding
  417. bind(Foo.class).annotatedWith(named("linkedKey")).to(clFoo).in(scope);
  418. // UntargettedBinding / ConstructorBinding
  419. bind(FooImpl.class).in(scope);
  420. // ConstructorBinding
  421. bind(Foo.class).annotatedWith(named("constructor")).toConstructor(cFoo).in(scope);
  422. }
  423. }
  424. private static interface Foo {}
  425. private static class FooImpl implements Foo {
  426. @Inject public FooImpl() {}
  427. private static Constructor<FooImpl> cxtor() {
  428. try {
  429. return FooImpl.class.getConstructor();
  430. } catch (SecurityException e) {
  431. throw new RuntimeException(e);
  432. } catch (NoSuchMethodException e) {
  433. throw new RuntimeException(e);
  434. }
  435. }
  436. }
  437. private static class FooProvider implements Provider<Foo> {
  438. public Foo get() {
  439. return new FooImpl();
  440. }
  441. }
  442. private static class Bar implements Foo {
  443. @Inject public Bar() {}
  444. private static Constructor<Bar> cxtor() {
  445. try {
  446. return Bar.class.getConstructor();
  447. } catch (SecurityException e) {
  448. throw new RuntimeException(e);
  449. } catch (NoSuchMethodException e) {
  450. throw new RuntimeException(e);
  451. }
  452. }
  453. }
  454. private static class BarProvider implements Provider<Foo> {
  455. public Foo get() {
  456. return new Bar();
  457. }
  458. }
  459. private static class HashEqualsTester implements Provider<Object> {
  460. private String equality;
  461. private boolean throwOnEquals;
  462. private boolean throwOnHashcode;
  463. @Override
  464. public boolean equals(Object obj) {
  465. if (throwOnEquals) {
  466. throw new RuntimeException();
  467. } else if (obj instanceof HashEqualsTester) {
  468. HashEqualsTester o = (HashEqualsTester)obj;
  469. if(o.throwOnEquals) {
  470. throw new RuntimeException();
  471. }
  472. if(equality == null && o.equality == null) {
  473. return this == o;
  474. } else {
  475. return Objects.equal(equality, o.equality);
  476. }
  477. } else {
  478. return false;
  479. }
  480. }
  481. @Override
  482. public int hashCode() {
  483. if(throwOnHashcode) {
  484. throw new RuntimeException();
  485. } else {
  486. return super.hashCode();
  487. }
  488. }
  489. public Object get() {
  490. return new Object();
  491. }
  492. }
  493. }