PageRenderTime 7878ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://gitlab.com/metamorphiccode/guice
Java | 564 lines | 448 code | 81 blank | 35 comment | 0 complexity | 573e065805151f80a71e749d05236bd0 MD5 | raw file
  1. /**
  2. * Copyright (C) 2008 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.asModuleChain;
  18. import static com.google.inject.Asserts.assertContains;
  19. import static com.google.inject.Asserts.getDeclaringSourcePart;
  20. import static com.google.inject.name.Names.named;
  21. import com.google.common.collect.ImmutableSet;
  22. import com.google.inject.name.Named;
  23. import com.google.inject.name.Names;
  24. import com.google.inject.spi.Dependency;
  25. import com.google.inject.spi.ExposedBinding;
  26. import com.google.inject.spi.PrivateElements;
  27. import com.google.inject.util.Types;
  28. import junit.framework.TestCase;
  29. import java.util.ArrayList;
  30. import java.util.Collection;
  31. import java.util.List;
  32. /**
  33. * @author jessewilson@google.com (Jesse Wilson)
  34. */
  35. public class PrivateModuleTest extends TestCase {
  36. public void testBasicUsage() {
  37. Injector injector = Guice.createInjector(new AbstractModule() {
  38. @Override protected void configure() {
  39. bind(String.class).annotatedWith(named("a")).toInstance("public");
  40. install(new PrivateModule() {
  41. @Override public void configure() {
  42. bind(String.class).annotatedWith(named("b")).toInstance("i");
  43. bind(AB.class).annotatedWith(named("one")).to(AB.class);
  44. expose(AB.class).annotatedWith(named("one"));
  45. }
  46. });
  47. install(new PrivateModule() {
  48. @Override public void configure() {
  49. bind(String.class).annotatedWith(named("b")).toInstance("ii");
  50. bind(AB.class).annotatedWith(named("two")).to(AB.class);
  51. expose(AB.class).annotatedWith(named("two"));
  52. }
  53. });
  54. }
  55. });
  56. AB ab1 = injector.getInstance(Key.get(AB.class, named("one")));
  57. assertEquals("public", ab1.a);
  58. assertEquals("i", ab1.b);
  59. AB ab2 = injector.getInstance(Key.get(AB.class, named("two")));
  60. assertEquals("public", ab2.a);
  61. assertEquals("ii", ab2.b);
  62. }
  63. public void testWithoutPrivateModules() {
  64. Injector injector = Guice.createInjector(new AbstractModule() {
  65. @Override protected void configure() {
  66. PrivateBinder bindA = binder().newPrivateBinder();
  67. bindA.bind(String.class).annotatedWith(named("a")).toInstance("i");
  68. bindA.expose(String.class).annotatedWith(named("a"));
  69. bindA.bind(String.class).annotatedWith(named("c")).toInstance("private to A");
  70. PrivateBinder bindB = binder().newPrivateBinder();
  71. bindB.bind(String.class).annotatedWith(named("b")).toInstance("ii");
  72. bindB.expose(String.class).annotatedWith(named("b"));
  73. bindB.bind(String.class).annotatedWith(named("c")).toInstance("private to B");
  74. }
  75. });
  76. assertEquals("i", injector.getInstance(Key.get(String.class, named("a"))));
  77. assertEquals("ii", injector.getInstance(Key.get(String.class, named("b"))));
  78. }
  79. public void testMisplacedExposedAnnotation() {
  80. try {
  81. Guice.createInjector(new AbstractModule() {
  82. @Override protected void configure() {}
  83. @Provides @Exposed
  84. String provideString() {
  85. return "i";
  86. }
  87. });
  88. fail();
  89. } catch (CreationException expected) {
  90. assertContains(expected.getMessage(), "Cannot expose java.lang.String on a standard binder. ",
  91. "Exposed bindings are only applicable to private binders.",
  92. " at " + PrivateModuleTest.class.getName(), "provideString(PrivateModuleTest.java:");
  93. }
  94. }
  95. public void testMisplacedExposeStatement() {
  96. try {
  97. Guice.createInjector(new AbstractModule() {
  98. @Override protected void configure() {
  99. ((PrivateBinder) binder()).expose(String.class).annotatedWith(named("a"));
  100. }
  101. });
  102. fail();
  103. } catch (CreationException expected) {
  104. assertContains(expected.getMessage(), "Cannot expose java.lang.String on a standard binder. ",
  105. "Exposed bindings are only applicable to private binders.",
  106. " at " + PrivateModuleTest.class.getName(), getDeclaringSourcePart(getClass()));
  107. }
  108. }
  109. public void testPrivateModulesAndProvidesMethods() {
  110. Injector injector = Guice.createInjector(new AbstractModule() {
  111. @Override protected void configure() {
  112. install(new PrivateModule() {
  113. @Override public void configure() {
  114. expose(String.class).annotatedWith(named("a"));
  115. }
  116. @Provides @Named("a") String providePublicA() {
  117. return "i";
  118. }
  119. @Provides @Named("b") String providePrivateB() {
  120. return "private";
  121. }
  122. });
  123. install(new PrivateModule() {
  124. @Override public void configure() {}
  125. @Provides @Named("c") String providePrivateC() {
  126. return "private";
  127. }
  128. @Provides @Exposed @Named("d") String providePublicD() {
  129. return "ii";
  130. }
  131. });
  132. }
  133. });
  134. assertEquals("i", injector.getInstance(Key.get(String.class, named("a"))));
  135. try {
  136. injector.getInstance(Key.get(String.class, named("b")));
  137. fail();
  138. } catch(ConfigurationException expected) {
  139. }
  140. try {
  141. injector.getInstance(Key.get(String.class, named("c")));
  142. fail();
  143. } catch(ConfigurationException expected) {
  144. }
  145. assertEquals("ii", injector.getInstance(Key.get(String.class, named("d"))));
  146. }
  147. public void testCannotBindAKeyExportedByASibling() {
  148. try {
  149. Guice.createInjector(new AbstractModule() {
  150. @Override protected void configure() {
  151. install(new PrivateModule() {
  152. @Override public void configure() {
  153. bind(String.class).toInstance("public");
  154. expose(String.class);
  155. }
  156. });
  157. install(new PrivateModule() {
  158. @Override public void configure() {
  159. bind(String.class).toInstance("private");
  160. }
  161. });
  162. }
  163. });
  164. fail();
  165. } catch (CreationException expected) {
  166. assertContains(expected.getMessage(),
  167. "A binding to java.lang.String was already configured at ",
  168. getClass().getName(), getDeclaringSourcePart(getClass()),
  169. " at " + getClass().getName(), getDeclaringSourcePart(getClass()));
  170. }
  171. }
  172. public void testExposeButNoBind() {
  173. try {
  174. Guice.createInjector(new AbstractModule() {
  175. @Override protected void configure() {
  176. bind(String.class).annotatedWith(named("a")).toInstance("a");
  177. bind(String.class).annotatedWith(named("b")).toInstance("b");
  178. install(new PrivateModule() {
  179. @Override public void configure() {
  180. expose(AB.class);
  181. }
  182. });
  183. }
  184. });
  185. fail("AB was exposed but not bound");
  186. } catch (CreationException expected) {
  187. assertContains(expected.getMessage(),
  188. "Could not expose() " + AB.class.getName() + ", it must be explicitly bound",
  189. getDeclaringSourcePart(getClass()));
  190. }
  191. }
  192. /**
  193. * Ensure that when we've got errors in different private modules, Guice presents all errors
  194. * in a unified message.
  195. */
  196. public void testMessagesFromPrivateModulesAreNicelyIntegrated() {
  197. try {
  198. Guice.createInjector(
  199. new PrivateModule() {
  200. @Override public void configure() {
  201. bind(C.class);
  202. }
  203. },
  204. new PrivateModule() {
  205. @Override public void configure() {
  206. bind(AB.class);
  207. }
  208. }
  209. );
  210. fail();
  211. } catch (CreationException expected) {
  212. assertContains(expected.getMessage(),
  213. "1) No implementation for " + C.class.getName() + " was bound.",
  214. "at " + getClass().getName(), getDeclaringSourcePart(getClass()),
  215. "2) No implementation for " + String.class.getName(), "Named(value=a) was bound.",
  216. "for field at " + AB.class.getName() + ".a(PrivateModuleTest.java:",
  217. "3) No implementation for " + String.class.getName(), "Named(value=b) was bound.",
  218. "for field at " + AB.class.getName() + ".b(PrivateModuleTest.java:",
  219. "3 errors");
  220. }
  221. }
  222. public void testNestedPrivateInjectors() {
  223. Injector injector = Guice.createInjector(new PrivateModule() {
  224. @Override public void configure() {
  225. expose(String.class);
  226. install(new PrivateModule() {
  227. @Override public void configure() {
  228. bind(String.class).toInstance("nested");
  229. expose(String.class);
  230. }
  231. });
  232. }
  233. });
  234. assertEquals("nested", injector.getInstance(String.class));
  235. }
  236. public void testInstallingRegularModulesFromPrivateModules() {
  237. Injector injector = Guice.createInjector(new PrivateModule() {
  238. @Override public void configure() {
  239. expose(String.class);
  240. install(new AbstractModule() {
  241. @Override protected void configure() {
  242. bind(String.class).toInstance("nested");
  243. }
  244. });
  245. }
  246. });
  247. assertEquals("nested", injector.getInstance(String.class));
  248. }
  249. public void testNestedPrivateModulesWithSomeKeysUnexposed() {
  250. Injector injector = Guice.createInjector(new PrivateModule() {
  251. @Override public void configure() {
  252. bind(String.class).annotatedWith(named("bound outer, exposed outer")).toInstance("boeo");
  253. expose(String.class).annotatedWith(named("bound outer, exposed outer"));
  254. bind(String.class).annotatedWith(named("bound outer, exposed none")).toInstance("boen");
  255. expose(String.class).annotatedWith(named("bound inner, exposed both"));
  256. install(new PrivateModule() {
  257. @Override public void configure() {
  258. bind(String.class).annotatedWith(named("bound inner, exposed both")).toInstance("bieb");
  259. expose(String.class).annotatedWith(named("bound inner, exposed both"));
  260. bind(String.class).annotatedWith(named("bound inner, exposed none")).toInstance("bien");
  261. }
  262. });
  263. }
  264. });
  265. assertEquals("boeo",
  266. injector.getInstance(Key.get(String.class, named("bound outer, exposed outer"))));
  267. assertEquals("bieb",
  268. injector.getInstance(Key.get(String.class, named("bound inner, exposed both"))));
  269. try {
  270. injector.getInstance(Key.get(String.class, named("bound outer, exposed none")));
  271. fail();
  272. } catch (ConfigurationException expected) {
  273. }
  274. try {
  275. injector.getInstance(Key.get(String.class, named("bound inner, exposed none")));
  276. fail();
  277. } catch (ConfigurationException expected) {
  278. }
  279. }
  280. public void testDependenciesBetweenPrivateAndPublic() {
  281. Injector injector = Guice.createInjector(
  282. new PrivateModule() {
  283. @Override protected void configure() {}
  284. @Provides @Exposed @Named("a") String provideA() {
  285. return "A";
  286. }
  287. @Provides @Exposed @Named("abc") String provideAbc(@Named("ab") String ab) {
  288. return ab + "C";
  289. }
  290. },
  291. new AbstractModule() {
  292. @Override protected void configure() {}
  293. @Provides @Named("ab") String provideAb(@Named("a") String a) {
  294. return a + "B";
  295. }
  296. @Provides @Named("abcd") String provideAbcd(@Named("abc") String abc) {
  297. return abc + "D";
  298. }
  299. }
  300. );
  301. assertEquals("ABCD", injector.getInstance(Key.get(String.class, named("abcd"))));
  302. }
  303. public void testDependenciesBetweenPrivateAndPublicWithPublicEagerSingleton() {
  304. Injector injector = Guice.createInjector(
  305. new PrivateModule() {
  306. @Override protected void configure() {}
  307. @Provides @Exposed @Named("a") String provideA() {
  308. return "A";
  309. }
  310. @Provides @Exposed @Named("abc") String provideAbc(@Named("ab") String ab) {
  311. return ab + "C";
  312. }
  313. },
  314. new AbstractModule() {
  315. @Override protected void configure() {
  316. bind(String.class).annotatedWith(named("abcde")).toProvider(new Provider<String>() {
  317. @Inject @Named("abcd") String abcd;
  318. public String get() {
  319. return abcd + "E";
  320. }
  321. }).asEagerSingleton();
  322. }
  323. @Provides @Named("ab") String provideAb(@Named("a") String a) {
  324. return a + "B";
  325. }
  326. @Provides @Named("abcd") String provideAbcd(@Named("abc") String abc) {
  327. return abc + "D";
  328. }
  329. }
  330. );
  331. assertEquals("ABCDE", injector.getInstance(Key.get(String.class, named("abcde"))));
  332. }
  333. public void testDependenciesBetweenPrivateAndPublicWithPrivateEagerSingleton() {
  334. Injector injector = Guice.createInjector(
  335. new AbstractModule() {
  336. @Override protected void configure() {}
  337. @Provides @Named("ab") String provideAb(@Named("a") String a) {
  338. return a + "B";
  339. }
  340. @Provides @Named("abcd") String provideAbcd(@Named("abc") String abc) {
  341. return abc + "D";
  342. }
  343. },
  344. new PrivateModule() {
  345. @Override protected void configure() {
  346. bind(String.class).annotatedWith(named("abcde")).toProvider(new Provider<String>() {
  347. @Inject @Named("abcd") String abcd;
  348. public String get() {
  349. return abcd + "E";
  350. }
  351. }).asEagerSingleton();
  352. expose(String.class).annotatedWith(named("abcde"));
  353. }
  354. @Provides @Exposed @Named("a") String provideA() {
  355. return "A";
  356. }
  357. @Provides @Exposed @Named("abc") String provideAbc(@Named("ab") String ab) {
  358. return ab + "C";
  359. }
  360. }
  361. );
  362. assertEquals("ABCDE", injector.getInstance(Key.get(String.class, named("abcde"))));
  363. }
  364. static class AB {
  365. @Inject @Named("a") String a;
  366. @Inject @Named("b") String b;
  367. }
  368. interface C {}
  369. public void testSpiAccess() {
  370. Injector injector = Guice.createInjector(new PrivateModule() {
  371. @Override public void configure() {
  372. bind(String.class).annotatedWith(named("a")).toInstance("private");
  373. bind(String.class).annotatedWith(named("b")).toInstance("exposed");
  374. expose(String.class).annotatedWith(named("b"));
  375. }
  376. });
  377. ExposedBinding<?> binding
  378. = (ExposedBinding<?>) injector.getBinding(Key.get(String.class, Names.named("b")));
  379. assertEquals(ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class))),
  380. binding.getDependencies());
  381. PrivateElements privateElements = binding.getPrivateElements();
  382. assertEquals(ImmutableSet.<Key<?>>of(Key.get(String.class, named("b"))),
  383. privateElements.getExposedKeys());
  384. assertContains(privateElements.getExposedSource(Key.get(String.class, named("b"))).toString(),
  385. PrivateModuleTest.class.getName(), getDeclaringSourcePart(getClass()));
  386. Injector privateInjector = privateElements.getInjector();
  387. assertEquals("private", privateInjector.getInstance(Key.get(String.class, Names.named("a"))));
  388. }
  389. public void testParentBindsSomethingInPrivate() {
  390. try {
  391. Guice.createInjector(new FailingModule());
  392. fail();
  393. } catch(CreationException expected) {
  394. assertEquals(1, expected.getErrorMessages().size());
  395. assertContains(expected.toString(),
  396. "Unable to create binding for java.util.List.",
  397. "It was already configured on one or more child injectors or private modules",
  398. "bound at " + FailingPrivateModule.class.getName() + ".configure(",
  399. asModuleChain(FailingModule.class, ManyPrivateModules.class, FailingPrivateModule.class),
  400. "bound at " + SecondFailingPrivateModule.class.getName() + ".configure(",
  401. asModuleChain(
  402. FailingModule.class, ManyPrivateModules.class, SecondFailingPrivateModule.class),
  403. "If it was in a PrivateModule, did you forget to expose the binding?",
  404. "at " + FailingModule.class.getName() + ".configure(");
  405. }
  406. }
  407. public void testParentBindingToPrivateLinkedJitBinding() {
  408. Injector injector = Guice.createInjector(new ManyPrivateModules());
  409. try {
  410. injector.getBinding(Key.get(Types.providerOf(List.class)));
  411. fail();
  412. } catch(ConfigurationException expected) {
  413. assertEquals(1, expected.getErrorMessages().size());
  414. assertContains(expected.toString(),
  415. "Unable to create binding for com.google.inject.Provider<java.util.List>.",
  416. "It was already configured on one or more child injectors or private modules",
  417. "bound at " + FailingPrivateModule.class.getName() + ".configure(",
  418. asModuleChain(ManyPrivateModules.class, FailingPrivateModule.class),
  419. "bound at " + SecondFailingPrivateModule.class.getName() + ".configure(",
  420. asModuleChain(ManyPrivateModules.class, SecondFailingPrivateModule.class),
  421. "If it was in a PrivateModule, did you forget to expose the binding?",
  422. "while locating com.google.inject.Provider<java.util.List>");
  423. }
  424. }
  425. public void testParentBindingToPrivateJitBinding() {
  426. Injector injector = Guice.createInjector(new ManyPrivateModules());
  427. try {
  428. injector.getBinding(PrivateFoo.class);
  429. fail();
  430. } catch(ConfigurationException expected) {
  431. assertEquals(1, expected.getErrorMessages().size());
  432. assertContains(expected.toString(),
  433. "Unable to create binding for " + PrivateFoo.class.getName(),
  434. "It was already configured on one or more child injectors or private modules",
  435. "(bound by a just-in-time binding)",
  436. "If it was in a PrivateModule, did you forget to expose the binding?",
  437. "while locating " + PrivateFoo.class.getName());
  438. }
  439. }
  440. private static class FailingModule extends AbstractModule {
  441. @Override protected void configure() {
  442. bind(Collection.class).to(List.class);
  443. install(new ManyPrivateModules());
  444. }
  445. }
  446. private static class ManyPrivateModules extends AbstractModule {
  447. @Override protected void configure() {
  448. // make sure duplicate sources are collapsed
  449. install(new FailingPrivateModule());
  450. install(new FailingPrivateModule());
  451. // but additional sources are listed
  452. install(new SecondFailingPrivateModule());
  453. }
  454. }
  455. private static class FailingPrivateModule extends PrivateModule {
  456. @Override protected void configure() {
  457. bind(List.class).toInstance(new ArrayList());
  458. // Add the Provider<List> binding, created just-in-time,
  459. // to make sure our linked JIT bindings have the correct source.
  460. getProvider(Key.get(Types.providerOf(List.class)));
  461. // Request a JIT binding for PrivateFoo, which can only
  462. // be created in the private module because it depends
  463. // on List.
  464. getProvider(PrivateFoo.class);
  465. }
  466. }
  467. /** A second class, so we can see another name in the source list. */
  468. private static class SecondFailingPrivateModule extends PrivateModule {
  469. @Override protected void configure() {
  470. bind(List.class).toInstance(new ArrayList());
  471. // Add the Provider<List> binding, created just-in-time,
  472. // to make sure our linked JIT bindings have the correct source.
  473. getProvider(Key.get(Types.providerOf(List.class)));
  474. // Request a JIT binding for PrivateFoo, which can only
  475. // be created in the private module because it depends
  476. // on List.
  477. getProvider(PrivateFoo.class);
  478. }
  479. }
  480. private static class PrivateFoo {
  481. @Inject List list;
  482. }
  483. }