PageRenderTime 58ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/extensions/assistedinject/test/com/google/inject/assistedinject/FactoryModuleBuilderTest.java

https://gitlab.com/metamorphiccode/guice
Java | 540 lines | 458 code | 63 blank | 19 comment | 4 complexity | 23fc2c0fda33ed401ed50770770718df MD5 | raw file
  1. /**
  2. * Copyright (C) 2009 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.assistedinject;
  17. import static com.google.inject.Asserts.assertContains;
  18. import static com.google.inject.name.Names.named;
  19. import com.google.common.collect.ImmutableSet;
  20. import com.google.common.collect.Iterables;
  21. import com.google.inject.AbstractModule;
  22. import com.google.inject.Binding;
  23. import com.google.inject.CreationException;
  24. import com.google.inject.Guice;
  25. import com.google.inject.Inject;
  26. import com.google.inject.Injector;
  27. import com.google.inject.Key;
  28. import com.google.inject.Module;
  29. import com.google.inject.Provides;
  30. import com.google.inject.Singleton;
  31. import com.google.inject.Stage;
  32. import com.google.inject.TypeLiteral;
  33. import com.google.inject.name.Named;
  34. import com.google.inject.name.Names;
  35. import com.google.inject.spi.Dependency;
  36. import com.google.inject.spi.Element;
  37. import com.google.inject.spi.Elements;
  38. import com.google.inject.spi.HasDependencies;
  39. import com.google.inject.spi.Message;
  40. import junit.framework.TestCase;
  41. import java.util.Collection;
  42. import java.util.HashSet;
  43. import java.util.List;
  44. import java.util.Set;
  45. public class FactoryModuleBuilderTest extends TestCase {
  46. private enum Color { BLUE, GREEN, RED, GRAY, BLACK }
  47. public void testImplicitForwardingAssistedBindingFailsWithInterface() {
  48. try {
  49. Guice.createInjector(new AbstractModule() {
  50. @Override
  51. protected void configure() {
  52. bind(Car.class).to(Golf.class);
  53. install(new FactoryModuleBuilder().build(ColoredCarFactory.class));
  54. }
  55. });
  56. fail();
  57. } catch (CreationException ce) {
  58. assertContains(
  59. ce.getMessage(), "1) " + Car.class.getName() + " is an interface, not a concrete class.",
  60. "Unable to create AssistedInject factory.",
  61. "while locating " + Car.class.getName(),
  62. "at " + ColoredCarFactory.class.getName() + ".create(");
  63. assertEquals(1, ce.getErrorMessages().size());
  64. }
  65. }
  66. public void testImplicitForwardingAssistedBindingFailsWithAbstractClass() {
  67. try {
  68. Guice.createInjector(new AbstractModule() {
  69. @Override
  70. protected void configure() {
  71. bind(AbstractCar.class).to(ArtCar.class);
  72. install(new FactoryModuleBuilder().build(ColoredAbstractCarFactory.class));
  73. }
  74. });
  75. fail();
  76. } catch (CreationException ce) {
  77. assertContains(
  78. ce.getMessage(), "1) " + AbstractCar.class.getName() + " is abstract, not a concrete class.",
  79. "Unable to create AssistedInject factory.",
  80. "while locating " + AbstractCar.class.getName(),
  81. "at " + ColoredAbstractCarFactory.class.getName() + ".create(");
  82. assertEquals(1, ce.getErrorMessages().size());
  83. }
  84. }
  85. public void testImplicitForwardingAssistedBindingCreatesNewObjects() {
  86. final Mustang providedMustang = new Mustang(Color.BLUE);
  87. Injector injector = Guice.createInjector(new AbstractModule() {
  88. @Override protected void configure() {
  89. install(new FactoryModuleBuilder().build(MustangFactory.class));
  90. }
  91. @Provides Mustang provide() { return providedMustang; }
  92. });
  93. assertSame(providedMustang, injector.getInstance(Mustang.class));
  94. MustangFactory factory = injector.getInstance(MustangFactory.class);
  95. Mustang created = factory.create(Color.GREEN);
  96. assertNotSame(providedMustang, created);
  97. assertEquals(Color.BLUE, providedMustang.color);
  98. assertEquals(Color.GREEN, created.color);
  99. }
  100. public void testExplicitForwardingAssistedBindingFailsWithInterface() {
  101. try {
  102. Guice.createInjector(new AbstractModule() {
  103. @Override
  104. protected void configure() {
  105. bind(Volkswagen.class).to(Golf.class);
  106. install(new FactoryModuleBuilder()
  107. .implement(Car.class, Volkswagen.class)
  108. .build(ColoredCarFactory.class));
  109. }
  110. });
  111. fail();
  112. } catch (CreationException ce) {
  113. assertContains(
  114. ce.getMessage(), "1) " + Volkswagen.class.getName() + " is an interface, not a concrete class.",
  115. "Unable to create AssistedInject factory.",
  116. "while locating " + Volkswagen.class.getName(),
  117. "while locating " + Car.class.getName(),
  118. "at " + ColoredCarFactory.class.getName() + ".create(");
  119. assertEquals(1, ce.getErrorMessages().size());
  120. }
  121. }
  122. public void testExplicitForwardingAssistedBindingFailsWithAbstractClass() {
  123. try {
  124. Guice.createInjector(new AbstractModule() {
  125. @Override
  126. protected void configure() {
  127. bind(AbstractCar.class).to(ArtCar.class);
  128. install(new FactoryModuleBuilder()
  129. .implement(Car.class, AbstractCar.class)
  130. .build(ColoredCarFactory.class));
  131. }
  132. });
  133. fail();
  134. } catch (CreationException ce) {
  135. assertContains(
  136. ce.getMessage(), "1) " + AbstractCar.class.getName() + " is abstract, not a concrete class.",
  137. "Unable to create AssistedInject factory.",
  138. "while locating " + AbstractCar.class.getName(),
  139. "while locating " + Car.class.getName(),
  140. "at " + ColoredCarFactory.class.getName() + ".create(");
  141. assertEquals(1, ce.getErrorMessages().size());
  142. }
  143. }
  144. public void testExplicitForwardingAssistedBindingCreatesNewObjects() {
  145. final Mustang providedMustang = new Mustang(Color.BLUE);
  146. Injector injector = Guice.createInjector(new AbstractModule() {
  147. @Override protected void configure() {
  148. install(new FactoryModuleBuilder().implement(Car.class, Mustang.class).build(
  149. ColoredCarFactory.class));
  150. }
  151. @Provides Mustang provide() { return providedMustang; }
  152. });
  153. assertSame(providedMustang, injector.getInstance(Mustang.class));
  154. ColoredCarFactory factory = injector.getInstance(ColoredCarFactory.class);
  155. Mustang created = (Mustang)factory.create(Color.GREEN);
  156. assertNotSame(providedMustang, created);
  157. assertEquals(Color.BLUE, providedMustang.color);
  158. assertEquals(Color.GREEN, created.color);
  159. }
  160. public void testAnnotatedAndParentBoundReturnValue() {
  161. Injector injector = Guice.createInjector(new AbstractModule() {
  162. @Override protected void configure() {
  163. bind(Car.class).to(Golf.class);
  164. bind(Integer.class).toInstance(911);
  165. bind(Double.class).toInstance(5.0d);
  166. install(new FactoryModuleBuilder()
  167. .implement(Car.class, Names.named("german"), Beetle.class)
  168. .implement(Car.class, Names.named("american"), Mustang.class)
  169. .build(AnnotatedVersatileCarFactory.class));
  170. }
  171. });
  172. AnnotatedVersatileCarFactory factory = injector.getInstance(AnnotatedVersatileCarFactory.class);
  173. assertTrue(factory.getGermanCar(Color.BLACK) instanceof Beetle);
  174. assertTrue(injector.getInstance(Car.class) instanceof Golf);
  175. }
  176. public void testParentBoundReturnValue() {
  177. Injector injector = Guice.createInjector(new AbstractModule() {
  178. @Override protected void configure() {
  179. bind(Car.class).to(Golf.class);
  180. bind(Double.class).toInstance(5.0d);
  181. install(new FactoryModuleBuilder()
  182. .implement(Car.class, Mustang.class)
  183. .build(ColoredCarFactory.class));
  184. }
  185. });
  186. ColoredCarFactory factory = injector.getInstance(ColoredCarFactory.class);
  187. assertTrue(factory.create(Color.RED) instanceof Mustang);
  188. assertTrue(injector.getInstance(Car.class) instanceof Golf);
  189. }
  190. public void testConfigureAnnotatedReturnValue() {
  191. Injector injector = Guice.createInjector(new AbstractModule() {
  192. @Override protected void configure() {
  193. install(new FactoryModuleBuilder()
  194. .implement(Car.class, Names.named("german"), Beetle.class)
  195. .implement(Car.class, Names.named("american"), Mustang.class)
  196. .build(AnnotatedVersatileCarFactory.class));
  197. }
  198. });
  199. AnnotatedVersatileCarFactory factory = injector.getInstance(AnnotatedVersatileCarFactory.class);
  200. assertTrue(factory.getGermanCar(Color.GRAY) instanceof Beetle);
  201. assertTrue(factory.getAmericanCar(Color.BLACK) instanceof Mustang);
  202. }
  203. public void testNoBindingAssistedInject() {
  204. Injector injector = Guice.createInjector(new AbstractModule() {
  205. @Override
  206. protected void configure() {
  207. install(new FactoryModuleBuilder().build(MustangFactory.class));
  208. }
  209. });
  210. MustangFactory factory = injector.getInstance(MustangFactory.class);
  211. Mustang mustang = factory.create(Color.BLUE);
  212. assertEquals(Color.BLUE, mustang.color);
  213. }
  214. public void testBindingAssistedInject() {
  215. Injector injector = Guice.createInjector(new AbstractModule() {
  216. @Override
  217. protected void configure() {
  218. install(new FactoryModuleBuilder()
  219. .implement(Car.class, Mustang.class)
  220. .build(ColoredCarFactory.class));
  221. }
  222. });
  223. ColoredCarFactory factory = injector.getInstance(ColoredCarFactory.class);
  224. Mustang mustang = (Mustang) factory.create(Color.BLUE);
  225. assertEquals(Color.BLUE, mustang.color);
  226. }
  227. public void testDuplicateBindings() {
  228. Injector injector = Guice.createInjector(new AbstractModule() {
  229. @Override
  230. protected void configure() {
  231. install(new FactoryModuleBuilder()
  232. .implement(Car.class, Mustang.class)
  233. .build(ColoredCarFactory.class));
  234. install(new FactoryModuleBuilder()
  235. .implement(Car.class, Mustang.class)
  236. .build(ColoredCarFactory.class));
  237. }
  238. });
  239. ColoredCarFactory factory = injector.getInstance(ColoredCarFactory.class);
  240. Mustang mustang = (Mustang) factory.create(Color.BLUE);
  241. assertEquals(Color.BLUE, mustang.color);
  242. }
  243. public void testSimilarBindingsWithConflictingImplementations() {
  244. try {
  245. Injector injector = Guice.createInjector(new AbstractModule() {
  246. @Override
  247. protected void configure() {
  248. install(new FactoryModuleBuilder()
  249. .implement(Car.class, Mustang.class)
  250. .build(ColoredCarFactory.class));
  251. install(new FactoryModuleBuilder()
  252. .implement(Car.class, Golf.class)
  253. .build(ColoredCarFactory.class));
  254. }
  255. });
  256. injector.getInstance(ColoredCarFactory.class);
  257. fail();
  258. } catch (CreationException ce) {
  259. assertContains(ce.getMessage(),
  260. "A binding to " + ColoredCarFactory.class.getName() + " was already configured");
  261. assertEquals(1, ce.getErrorMessages().size());
  262. }
  263. }
  264. public void testMultipleReturnTypes() {
  265. Injector injector = Guice.createInjector(new AbstractModule() {
  266. @Override
  267. protected void configure() {
  268. bind(Double.class).toInstance(5.0d);
  269. install(new FactoryModuleBuilder().build(VersatileCarFactory.class));
  270. }
  271. });
  272. VersatileCarFactory factory = injector.getInstance(VersatileCarFactory.class);
  273. Mustang mustang = factory.getMustang(Color.RED);
  274. assertEquals(Color.RED, mustang.color);
  275. Beetle beetle = factory.getBeetle(Color.GREEN);
  276. assertEquals(Color.GREEN, beetle.color);
  277. }
  278. public void testParameterizedClassesWithNoImplements() {
  279. Injector injector = Guice.createInjector(new AbstractModule() {
  280. @Override
  281. protected void configure() {
  282. install(new FactoryModuleBuilder().build(new TypeLiteral<Foo.Factory<String>>() {}));
  283. }
  284. });
  285. Foo.Factory<String> factory = injector.getInstance(Key.get(new TypeLiteral<Foo.Factory<String>>() {}));
  286. @SuppressWarnings("unused")
  287. Foo<String> foo = factory.create(new Bar());
  288. }
  289. public void testGenericErrorMessageMakesSense() {
  290. try {
  291. Guice.createInjector(new AbstractModule() {
  292. @Override
  293. protected void configure() {
  294. install(new FactoryModuleBuilder().build(Key.get(Foo.Factory.class)));
  295. }
  296. });
  297. fail();
  298. } catch(CreationException ce ) {
  299. // Assert not only that it's the correct message, but also that it's the *only* message.
  300. Collection<Message> messages = ce.getErrorMessages();
  301. assertEquals(
  302. Foo.Factory.class.getName() + " cannot be used as a key; It is not fully specified.",
  303. Iterables.getOnlyElement(messages).getMessage());
  304. }
  305. }
  306. interface Car {}
  307. interface Volkswagen extends Car {}
  308. interface ColoredCarFactory {
  309. Car create(Color color);
  310. }
  311. interface MustangFactory {
  312. Mustang create(Color color);
  313. }
  314. interface VersatileCarFactory {
  315. Mustang getMustang(Color color);
  316. Beetle getBeetle(Color color);
  317. }
  318. interface AnnotatedVersatileCarFactory {
  319. @Named("german") Car getGermanCar(Color color);
  320. @Named("american") Car getAmericanCar(Color color);
  321. }
  322. public static class Golf implements Volkswagen {}
  323. public static class Mustang implements Car {
  324. private final Color color;
  325. @Inject
  326. public Mustang(@Assisted Color color) {
  327. this.color = color;
  328. }
  329. }
  330. public static class Beetle implements Car {
  331. private final Color color;
  332. @Inject
  333. public Beetle(@Assisted Color color) {
  334. this.color = color;
  335. }
  336. }
  337. public static class Foo<E> {
  338. static interface Factory<E> {
  339. Foo<E> create(Bar bar);
  340. }
  341. @SuppressWarnings("unused")
  342. @Inject Foo(@Assisted Bar bar, Baz<E> baz) {}
  343. }
  344. public static class Bar {}
  345. @SuppressWarnings("unused")
  346. public static class Baz<E> {}
  347. abstract static class AbstractCar implements Car {}
  348. interface ColoredAbstractCarFactory {
  349. AbstractCar create(Color color);
  350. }
  351. public static class ArtCar extends AbstractCar {}
  352. public void testFactoryBindingDependencies() {
  353. // validate dependencies work in all stages & as a raw element,
  354. // and that dependencies work for methods, fields, constructors,
  355. // and for @AssistedInject constructors too.
  356. Module module = new AbstractModule() {
  357. @Override
  358. protected void configure() {
  359. bind(Integer.class).toInstance(42);
  360. bind(Double.class).toInstance(4.2d);
  361. bind(Float.class).toInstance(4.2f);
  362. bind(String.class).annotatedWith(named("dog")).toInstance("dog");
  363. bind(String.class).annotatedWith(named("cat1")).toInstance("cat1");
  364. bind(String.class).annotatedWith(named("cat2")).toInstance("cat2");
  365. bind(String.class).annotatedWith(named("cat3")).toInstance("cat3");
  366. bind(String.class).annotatedWith(named("arbitrary")).toInstance("fail!");
  367. install(new FactoryModuleBuilder()
  368. .implement(Animal.class, Dog.class)
  369. .build(AnimalHouse.class));
  370. }
  371. };
  372. Set<Key<?>> expectedKeys = ImmutableSet.<Key<?>>of(
  373. Key.get(Integer.class),
  374. Key.get(Double.class),
  375. Key.get(Float.class),
  376. Key.get(String.class, named("dog")),
  377. Key.get(String.class, named("cat1")),
  378. Key.get(String.class, named("cat2")),
  379. Key.get(String.class, named("cat3"))
  380. );
  381. Injector injector = Guice.createInjector(module);
  382. validateDependencies(expectedKeys, injector.getBinding(AnimalHouse.class));
  383. injector = Guice.createInjector(Stage.TOOL, module);
  384. validateDependencies(expectedKeys, injector.getBinding(AnimalHouse.class));
  385. List<Element> elements = Elements.getElements(module);
  386. boolean found = false;
  387. for(Element element : elements) {
  388. if(element instanceof Binding) {
  389. Binding<?> binding = (Binding<?>) element;
  390. if(binding.getKey().equals(Key.get(AnimalHouse.class))) {
  391. found = true;
  392. validateDependencies(expectedKeys, binding);
  393. break;
  394. }
  395. }
  396. }
  397. assertTrue(found);
  398. }
  399. private void validateDependencies(Set<Key<?>> expectedKeys, Binding<?> binding) {
  400. Set<Dependency<?>> dependencies = ((HasDependencies)binding).getDependencies();
  401. Set<Key<?>> actualKeys = new HashSet<Key<?>>();
  402. for (Dependency<?> dependency : dependencies) {
  403. actualKeys.add(dependency.getKey());
  404. }
  405. assertEquals(expectedKeys, actualKeys);
  406. }
  407. interface AnimalHouse {
  408. Animal createAnimal(String name);
  409. Cat createCat(String name);
  410. Cat createCat(int age);
  411. }
  412. interface Animal {}
  413. @SuppressWarnings("unused")
  414. private static class Dog implements Animal {
  415. @Inject int a;
  416. @Inject Dog(@Assisted String a, double b) {}
  417. @Inject void register(@Named("dog") String a) {}
  418. }
  419. @SuppressWarnings("unused")
  420. private static class Cat implements Animal {
  421. @Inject float a;
  422. @AssistedInject Cat(@Assisted String a, @Named("cat1") String b) {}
  423. @AssistedInject Cat(@Assisted int a, @Named("cat2") String b) {}
  424. @AssistedInject Cat(@Assisted byte a, @Named("catfail") String b) {} // not a dependency!
  425. @Inject void register(@Named("cat3") String a) {}
  426. }
  427. public void testFactoryPublicAndReturnTypeNotPublic() {
  428. try {
  429. Guice.createInjector(new AbstractModule() {
  430. @Override
  431. protected void configure() {
  432. install(new FactoryModuleBuilder()
  433. .implement(Hidden.class, HiddenImpl.class)
  434. .build(NotHidden.class));
  435. }
  436. });
  437. } catch(CreationException ce) {
  438. assertEquals(NotHidden.class.getName() + " is public, but has a method that returns a non-public type: "
  439. + Hidden.class.getName() + ". Due to limitations with java.lang.reflect.Proxy, this is not allowed. "
  440. + "Please either make the factory non-public or the return type public.",
  441. Iterables.getOnlyElement(ce.getErrorMessages()).getMessage());
  442. }
  443. }
  444. interface Hidden {}
  445. public static class HiddenImpl implements Hidden {}
  446. public interface NotHidden {
  447. Hidden create();
  448. }
  449. public void testSingletonScopeOnAssistedClassIsIgnored() {
  450. try {
  451. Guice.createInjector(new AbstractModule() {
  452. @Override
  453. protected void configure() {
  454. install(new FactoryModuleBuilder().build(SingletonFactory.class));
  455. }
  456. });
  457. fail();
  458. } catch (CreationException ce) {
  459. assertEquals(1, ce.getErrorMessages().size());
  460. assertEquals("Found scope annotation [" + Singleton.class.getName() + "]"
  461. + " on implementation class [" + AssistedSingleton.class.getName() + "]"
  462. + " of AssistedInject factory [" + SingletonFactory.class.getName() + "]."
  463. + "\nThis is not allowed, please remove the scope annotation.",
  464. Iterables.getOnlyElement(ce.getErrorMessages()).getMessage());
  465. }
  466. }
  467. interface SingletonFactory {
  468. AssistedSingleton create(String string);
  469. }
  470. @SuppressWarnings("GuiceAssistedInjectScoping")
  471. @Singleton
  472. static class AssistedSingleton {
  473. @Inject
  474. public AssistedSingleton(@SuppressWarnings("unused") @Assisted String string) {
  475. }
  476. }
  477. }