PageRenderTime 618ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/guice-3.0-rc2-src/core/test/com/google/inject/DuplicateBindingsTest.java

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