PageRenderTime 50ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/spice-inject/guice-patches/vanilla.test/com/google/inject/BindingTest.java

https://github.com/peterlynch/spice
Java | 498 lines | 382 code | 85 blank | 31 comment | 2 complexity | 34e574b115f80df455bd5e0bf59b10fe MD5 | raw file
  1. /*
  2. * Copyright (C) 2007 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 com.google.inject.internal.util.ImmutableSet;
  19. import com.google.inject.internal.util.Sets;
  20. import com.google.inject.matcher.Matchers;
  21. import static com.google.inject.name.Names.named;
  22. import com.google.inject.spi.InjectionPoint;
  23. import com.google.inject.spi.TypeEncounter;
  24. import com.google.inject.spi.TypeListener;
  25. import java.lang.reflect.Constructor;
  26. import java.util.Collection;
  27. import java.util.List;
  28. import java.util.Map;
  29. import java.util.Set;
  30. import java.util.concurrent.atomic.AtomicInteger;
  31. import java.util.logging.Logger;
  32. import junit.framework.TestCase;
  33. import org.aopalliance.intercept.MethodInterceptor;
  34. import org.aopalliance.intercept.MethodInvocation;
  35. /**
  36. * @author crazybob@google.com (Bob Lee)
  37. */
  38. public class BindingTest extends TestCase {
  39. static class Dependent {
  40. @Inject A a;
  41. @Inject Dependent(A a, B b) {}
  42. @Inject void injectBob(Bob bob) {}
  43. }
  44. public void testExplicitCyclicDependency() {
  45. Guice.createInjector(new AbstractModule() {
  46. protected void configure() {
  47. bind(A.class);
  48. bind(B.class);
  49. }
  50. }).getInstance(A.class);
  51. }
  52. static class A { @Inject B b; }
  53. static class B { @Inject A a; }
  54. static class Bob {}
  55. static class MyModule extends AbstractModule {
  56. protected void configure() {
  57. // Linked.
  58. bind(Object.class).to(Runnable.class).in(Scopes.SINGLETON);
  59. // Instance.
  60. bind(Runnable.class).toInstance(new Runnable() {
  61. public void run() {}
  62. });
  63. // Provider instance.
  64. bind(Foo.class).toProvider(new Provider<Foo>() {
  65. public Foo get() {
  66. return new Foo();
  67. }
  68. }).in(Scopes.SINGLETON);
  69. // Provider.
  70. bind(Foo.class)
  71. .annotatedWith(named("provider"))
  72. .toProvider(FooProvider.class);
  73. // Class.
  74. bind(Bar.class).in(Scopes.SINGLETON);
  75. // Constant.
  76. bindConstant().annotatedWith(named("name")).to("Bob");
  77. }
  78. }
  79. static class Foo {}
  80. public static class FooProvider implements Provider<Foo> {
  81. public Foo get() {
  82. throw new UnsupportedOperationException();
  83. }
  84. }
  85. public static class Bar {}
  86. public void testBindToUnboundLinkedBinding() {
  87. try {
  88. Guice.createInjector(new AbstractModule() {
  89. protected void configure() {
  90. bind(Collection.class).to(List.class);
  91. }
  92. });
  93. fail();
  94. } catch (CreationException expected) {
  95. assertContains(expected.getMessage(), "No implementation for java.util.List was bound.");
  96. }
  97. }
  98. /**
  99. * This test ensures that the asEagerSingleton() scoping applies to the key,
  100. * not to what the key is linked to.
  101. */
  102. public void testScopeIsAppliedToKeyNotTarget() {
  103. Injector injector = Guice.createInjector(new AbstractModule() {
  104. protected void configure() {
  105. bind(Integer.class).toProvider(Counter.class).asEagerSingleton();
  106. bind(Number.class).toProvider(Counter.class).asEagerSingleton();
  107. }
  108. });
  109. assertNotSame(injector.getInstance(Integer.class), injector.getInstance(Number.class));
  110. }
  111. static class Counter implements Provider<Integer> {
  112. static AtomicInteger next = new AtomicInteger(1);
  113. public Integer get() {
  114. return next.getAndIncrement();
  115. }
  116. }
  117. public void testAnnotatedNoArgConstructor() {
  118. assertBindingSucceeds(PublicNoArgAnnotated.class);
  119. assertBindingSucceeds(ProtectedNoArgAnnotated.class);
  120. assertBindingSucceeds(PackagePrivateNoArgAnnotated.class);
  121. assertBindingSucceeds(PrivateNoArgAnnotated.class);
  122. }
  123. static class PublicNoArgAnnotated {
  124. @Inject public PublicNoArgAnnotated() { }
  125. }
  126. static class ProtectedNoArgAnnotated {
  127. @Inject protected ProtectedNoArgAnnotated() { }
  128. }
  129. static class PackagePrivateNoArgAnnotated {
  130. @Inject PackagePrivateNoArgAnnotated() { }
  131. }
  132. static class PrivateNoArgAnnotated {
  133. @Inject private PrivateNoArgAnnotated() { }
  134. }
  135. public void testUnannotatedNoArgConstructor() throws Exception{
  136. assertBindingSucceeds(PublicNoArg.class);
  137. assertBindingSucceeds(ProtectedNoArg.class);
  138. assertBindingSucceeds(PackagePrivateNoArg.class);
  139. assertBindingSucceeds(PrivateNoArgInPrivateClass.class);
  140. assertBindingFails(PrivateNoArg.class);
  141. }
  142. static class PublicNoArg {
  143. public PublicNoArg() { }
  144. }
  145. static class ProtectedNoArg {
  146. protected ProtectedNoArg() { }
  147. }
  148. static class PackagePrivateNoArg {
  149. PackagePrivateNoArg() { }
  150. }
  151. private static class PrivateNoArgInPrivateClass {
  152. PrivateNoArgInPrivateClass() { }
  153. }
  154. static class PrivateNoArg {
  155. private PrivateNoArg() { }
  156. }
  157. private void assertBindingSucceeds(final Class<?> clazz) {
  158. assertNotNull(Guice.createInjector().getInstance(clazz));
  159. }
  160. private void assertBindingFails(final Class<?> clazz) throws NoSuchMethodException {
  161. try {
  162. Guice.createInjector().getInstance(clazz);
  163. fail();
  164. } catch (ConfigurationException expected) {
  165. assertContains(expected.getMessage(),
  166. "Could not find a suitable constructor in " + PrivateNoArg.class.getName(),
  167. "at " + PrivateNoArg.class.getName() + ".class(BindingTest.java:");
  168. }
  169. }
  170. public void testTooManyConstructors() {
  171. try {
  172. Guice.createInjector().getInstance(TooManyConstructors.class);
  173. fail();
  174. } catch (ConfigurationException expected) {
  175. assertContains(expected.getMessage(),
  176. TooManyConstructors.class.getName() + " has more than one constructor annotated with "
  177. + "@Inject. Classes must have either one (and only one) constructor",
  178. "at " + TooManyConstructors.class.getName() + ".class(BindingTest.java:");
  179. }
  180. }
  181. static class TooManyConstructors {
  182. @Inject TooManyConstructors(Injector i) {}
  183. @Inject TooManyConstructors() {}
  184. }
  185. public void testToConstructorBinding() throws NoSuchMethodException {
  186. final Constructor<D> constructor = D.class.getConstructor(Stage.class);
  187. Injector injector = Guice.createInjector(new AbstractModule() {
  188. protected void configure() {
  189. bind(Object.class).toConstructor(constructor);
  190. }
  191. });
  192. D d = (D) injector.getInstance(Object.class);
  193. assertEquals(Stage.DEVELOPMENT, d.stage);
  194. }
  195. public void testToConstructorBindingsOnParameterizedTypes() throws NoSuchMethodException {
  196. final Constructor<C> constructor = C.class.getConstructor(Stage.class, Object.class);
  197. final Key<Object> s = new Key<Object>(named("s")) {};
  198. final Key<Object> i = new Key<Object>(named("i")) {};
  199. Injector injector = Guice.createInjector(new AbstractModule() {
  200. protected void configure() {
  201. bind(s).toConstructor(constructor, new TypeLiteral<C<Stage>>() {});
  202. bind(i).toConstructor(constructor, new TypeLiteral<C<Injector>>() {});
  203. }
  204. });
  205. C<Stage> one = (C<Stage>) injector.getInstance(s);
  206. assertEquals(Stage.DEVELOPMENT, one.stage);
  207. assertEquals(Stage.DEVELOPMENT, one.t);
  208. assertEquals(Stage.DEVELOPMENT, one.anotherT);
  209. C<Injector> two = (C<Injector>) injector.getInstance(i);
  210. assertEquals(Stage.DEVELOPMENT, two.stage);
  211. assertEquals(injector, two.t);
  212. assertEquals(injector, two.anotherT);
  213. }
  214. public void testToConstructorBindingsFailsOnRawTypes() throws NoSuchMethodException {
  215. final Constructor constructor = C.class.getConstructor(Stage.class, Object.class);
  216. try {
  217. Guice.createInjector(new AbstractModule() {
  218. protected void configure() {
  219. bind(Object.class).toConstructor(constructor);
  220. }
  221. });
  222. fail();
  223. } catch (CreationException expected) {
  224. assertContains(expected.getMessage(),
  225. "1) T cannot be used as a key; It is not fully specified.",
  226. "at " + C.class.getName() + ".<init>(BindingTest.java:",
  227. "2) T cannot be used as a key; It is not fully specified.",
  228. "at " + C.class.getName() + ".anotherT(BindingTest.java:");
  229. }
  230. }
  231. public void testToConstructorAndMethodInterceptors() throws NoSuchMethodException {
  232. final Constructor<D> constructor = D.class.getConstructor(Stage.class);
  233. final AtomicInteger count = new AtomicInteger();
  234. final MethodInterceptor countingInterceptor = new MethodInterceptor() {
  235. public Object invoke(MethodInvocation methodInvocation) throws Throwable {
  236. count.incrementAndGet();
  237. return methodInvocation.proceed();
  238. }
  239. };
  240. Injector injector = Guice.createInjector(new AbstractModule() {
  241. protected void configure() {
  242. bind(Object.class).toConstructor(constructor);
  243. bindInterceptor(Matchers.any(), Matchers.any(), countingInterceptor);
  244. }
  245. });
  246. D d = (D) injector.getInstance(Object.class);
  247. d.hashCode();
  248. d.hashCode();
  249. assertEquals(2, count.get());
  250. }
  251. public void testInaccessibleConstructor() throws NoSuchMethodException {
  252. final Constructor<E> constructor = E.class.getDeclaredConstructor(Stage.class);
  253. Injector injector = Guice.createInjector(new AbstractModule() {
  254. protected void configure() {
  255. bind(E.class).toConstructor(constructor);
  256. }
  257. });
  258. E e = injector.getInstance(E.class);
  259. assertEquals(Stage.DEVELOPMENT, e.stage);
  260. }
  261. public void testToConstructorAndScopes() throws NoSuchMethodException {
  262. final Constructor<F> constructor = F.class.getConstructor(Stage.class);
  263. final Key<Object> d = Key.get(Object.class, named("D")); // default scoping
  264. final Key<Object> s = Key.get(Object.class, named("S")); // singleton
  265. final Key<Object> n = Key.get(Object.class, named("N")); // "N" instances
  266. final Key<Object> r = Key.get(Object.class, named("R")); // a regular binding
  267. Injector injector = Guice.createInjector(new AbstractModule() {
  268. protected void configure() {
  269. bind(d).toConstructor(constructor);
  270. bind(s).toConstructor(constructor).in(Singleton.class);
  271. bind(n).toConstructor(constructor).in(Scopes.NO_SCOPE);
  272. bind(r).to(F.class);
  273. }
  274. });
  275. assertDistinct(injector, 1, d, d, d, d);
  276. assertDistinct(injector, 1, s, s, s, s);
  277. assertDistinct(injector, 4, n, n, n, n);
  278. assertDistinct(injector, 1, r, r, r, r);
  279. assertDistinct(injector, 4, d, d, r, r, s, s, n);
  280. }
  281. public void assertDistinct(Injector injector, int expectedCount, Key<?>... keys) {
  282. ImmutableSet.Builder<Object> builder = ImmutableSet.builder();
  283. for (Key<?> k : keys) {
  284. builder.add(injector.getInstance(k));
  285. }
  286. assertEquals(expectedCount, builder.build().size());
  287. }
  288. public void testToConstructorSpiData() throws NoSuchMethodException {
  289. final Set<TypeLiteral<?>> heardTypes = Sets.newHashSet();
  290. final Constructor<D> constructor = D.class.getConstructor(Stage.class);
  291. final TypeListener listener = new TypeListener() {
  292. public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
  293. if (!heardTypes.add(type)) {
  294. fail("Heard " + type + " multiple times!");
  295. }
  296. }
  297. };
  298. Guice.createInjector(new AbstractModule() {
  299. protected void configure() {
  300. bind(Object.class).toConstructor(constructor);
  301. bind(D.class).toConstructor(constructor);
  302. bindListener(Matchers.any(), listener);
  303. }
  304. });
  305. assertEquals(ImmutableSet.of(TypeLiteral.get(Stage.class), TypeLiteral.get(D.class)),
  306. heardTypes);
  307. }
  308. public void testInterfaceToImplementationConstructor() throws NoSuchMethodException {
  309. final Constructor<CFoo> constructor = CFoo.class.getDeclaredConstructor();
  310. Injector injector = Guice.createInjector(new AbstractModule() {
  311. protected void configure() {
  312. bind(IFoo.class).toConstructor(constructor);
  313. }
  314. });
  315. injector.getInstance(IFoo.class);
  316. }
  317. public static interface IFoo {}
  318. public static class CFoo implements IFoo {}
  319. public void testGetAllBindings() {
  320. Injector injector = Guice.createInjector(new AbstractModule() {
  321. protected void configure() {
  322. bind(D.class).toInstance(new D(Stage.PRODUCTION));
  323. bind(Object.class).to(D.class);
  324. getProvider(new Key<C<Stage>>() {});
  325. }
  326. });
  327. Map<Key<?>,Binding<?>> bindings = injector.getAllBindings();
  328. assertEquals(ImmutableSet.of(Key.get(Injector.class), Key.get(Stage.class), Key.get(D.class),
  329. Key.get(Logger.class), Key.get(Object.class), new Key<C<Stage>>() {}),
  330. bindings.keySet());
  331. // add a JIT binding
  332. injector.getInstance(F.class);
  333. Map<Key<?>,Binding<?>> bindings2 = injector.getAllBindings();
  334. assertEquals(ImmutableSet.of(Key.get(Injector.class), Key.get(Stage.class), Key.get(D.class),
  335. Key.get(Logger.class), Key.get(Object.class), new Key<C<Stage>>() {}, Key.get(F.class)),
  336. bindings2.keySet());
  337. // the original map shouldn't have changed
  338. assertEquals(ImmutableSet.of(Key.get(Injector.class), Key.get(Stage.class), Key.get(D.class),
  339. Key.get(Logger.class), Key.get(Object.class), new Key<C<Stage>>() {}),
  340. bindings.keySet());
  341. // check the bindings' values
  342. assertEquals(injector, bindings.get(Key.get(Injector.class)).getProvider().get());
  343. }
  344. public void testGetAllServletBindings() throws Exception {
  345. Injector injector = Guice.createInjector(new AbstractModule() {
  346. protected void configure() {
  347. bind(F.class); // an explicit binding that uses a JIT binding for a constructor
  348. }
  349. });
  350. injector.getAllBindings();
  351. }
  352. public static class C<T> {
  353. private Stage stage;
  354. private T t;
  355. @Inject T anotherT;
  356. public C(Stage stage, T t) {
  357. this.stage = stage;
  358. this.t = t;
  359. }
  360. @Inject C() {}
  361. }
  362. public static class D {
  363. Stage stage;
  364. public D(Stage stage) {
  365. this.stage = stage;
  366. }
  367. }
  368. private static class E {
  369. Stage stage;
  370. private E(Stage stage) {
  371. this.stage = stage;
  372. }
  373. }
  374. @Singleton
  375. public static class F {
  376. Stage stage;
  377. @Inject public F(Stage stage) {
  378. this.stage = stage;
  379. }
  380. }
  381. public void testTurkeyBaconProblemUsingToConstuctor() {
  382. Injector injector = Guice.createInjector(new AbstractModule() {
  383. @SuppressWarnings("unchecked")
  384. @Override
  385. public void configure() {
  386. bind(Bacon.class).to(UncookedBacon.class);
  387. bind(Bacon.class).annotatedWith(named("Turkey")).to(TurkeyBacon.class);
  388. bind(Bacon.class).annotatedWith(named("Cooked")).toConstructor(
  389. (Constructor)InjectionPoint.forConstructorOf(Bacon.class).getMember());
  390. }
  391. });
  392. Bacon bacon = injector.getInstance(Bacon.class);
  393. assertEquals(Food.PORK, bacon.getMaterial());
  394. assertFalse(bacon.isCooked());
  395. Bacon turkeyBacon = injector.getInstance(Key.get(Bacon.class, named("Turkey")));
  396. assertEquals(Food.TURKEY, turkeyBacon.getMaterial());
  397. assertTrue(turkeyBacon.isCooked());
  398. Bacon cookedBacon = injector.getInstance(Key.get(Bacon.class, named("Cooked")));
  399. assertEquals(Food.PORK, cookedBacon.getMaterial());
  400. assertTrue(cookedBacon.isCooked());
  401. }
  402. enum Food { TURKEY, PORK }
  403. private static class Bacon {
  404. public Food getMaterial() { return Food.PORK; }
  405. public boolean isCooked() { return true; }
  406. }
  407. private static class TurkeyBacon extends Bacon {
  408. public Food getMaterial() { return Food.TURKEY; }
  409. }
  410. private static class UncookedBacon extends Bacon {
  411. public boolean isCooked() { return false; }
  412. }
  413. }