PageRenderTime 4445ms CodeModel.GetById 33ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://github.com/peterlynch/spice
Java | 577 lines | 472 code | 83 blank | 22 comment | 4 complexity | 469055f3d674afe5c8ce435ed41acd2f MD5 | raw file
  1. /**
  2. * Copyright (C) 2006 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.ImmutableMap;
  19. import com.google.inject.internal.util.Maps;
  20. import com.google.inject.name.Named;
  21. import static com.google.inject.name.Names.named;
  22. import com.google.inject.spi.Element;
  23. import com.google.inject.spi.Elements;
  24. import com.google.inject.util.Providers;
  25. import java.io.IOException;
  26. import java.lang.annotation.ElementType;
  27. import java.lang.annotation.Retention;
  28. import static java.lang.annotation.RetentionPolicy.RUNTIME;
  29. import java.lang.annotation.Target;
  30. import java.util.ArrayList;
  31. import java.util.Arrays;
  32. import java.util.Iterator;
  33. import java.util.List;
  34. import java.util.Map;
  35. import junit.framework.TestCase;
  36. /**
  37. * @author crazybob@google.com (Bob Lee)
  38. */
  39. public class ScopesTest extends TestCase {
  40. private final AbstractModule singletonsModule = new AbstractModule() {
  41. protected void configure() {
  42. bind(BoundAsSingleton.class).in(Scopes.SINGLETON);
  43. bind(AnnotatedSingleton.class);
  44. bind(EagerSingleton.class).asEagerSingleton();
  45. bind(LinkedSingleton.class).to(RealLinkedSingleton.class);
  46. bind(DependsOnJustInTimeSingleton.class);
  47. bind(NotASingleton.class);
  48. bind(ImplementedBySingleton.class).in(Scopes.SINGLETON);
  49. bind(ProvidedBySingleton.class).in(Scopes.SINGLETON);
  50. }
  51. };
  52. @Override protected void setUp() throws Exception {
  53. AnnotatedSingleton.nextInstanceId = 0;
  54. BoundAsSingleton.nextInstanceId = 0;
  55. EagerSingleton.nextInstanceId = 0;
  56. RealLinkedSingleton.nextInstanceId = 0;
  57. JustInTimeSingleton.nextInstanceId = 0;
  58. NotASingleton.nextInstanceId = 0;
  59. Implementation.nextInstanceId = 0;
  60. ProvidedBySingleton.nextInstanceId = 0;
  61. ThrowingSingleton.nextInstanceId = 0;
  62. }
  63. public void testSingletons() {
  64. Injector injector = Guice.createInjector(singletonsModule);
  65. assertSame(
  66. injector.getInstance(BoundAsSingleton.class),
  67. injector.getInstance(BoundAsSingleton.class));
  68. assertSame(
  69. injector.getInstance(AnnotatedSingleton.class),
  70. injector.getInstance(AnnotatedSingleton.class));
  71. assertSame(
  72. injector.getInstance(EagerSingleton.class),
  73. injector.getInstance(EagerSingleton.class));
  74. assertSame(
  75. injector.getInstance(LinkedSingleton.class),
  76. injector.getInstance(LinkedSingleton.class));
  77. assertSame(
  78. injector.getInstance(JustInTimeSingleton.class),
  79. injector.getInstance(JustInTimeSingleton.class));
  80. assertNotSame(
  81. injector.getInstance(NotASingleton.class),
  82. injector.getInstance(NotASingleton.class));
  83. assertSame(
  84. injector.getInstance(ImplementedBySingleton.class),
  85. injector.getInstance(ImplementedBySingleton.class));
  86. assertSame(
  87. injector.getInstance(ProvidedBySingleton.class),
  88. injector.getInstance(ProvidedBySingleton.class));
  89. }
  90. public void testJustInTimeAnnotatedSingleton() {
  91. Injector injector = Guice.createInjector();
  92. assertSame(
  93. injector.getInstance(AnnotatedSingleton.class),
  94. injector.getInstance(AnnotatedSingleton.class));
  95. }
  96. public void testSingletonIsPerInjector() {
  97. assertNotSame(
  98. Guice.createInjector().getInstance(AnnotatedSingleton.class),
  99. Guice.createInjector().getInstance(AnnotatedSingleton.class));
  100. }
  101. public void testOverriddingAnnotation() {
  102. Injector injector = Guice.createInjector(new AbstractModule() {
  103. protected void configure() {
  104. bind(AnnotatedSingleton.class).in(Scopes.NO_SCOPE);
  105. }
  106. });
  107. assertNotSame(
  108. injector.getInstance(AnnotatedSingleton.class),
  109. injector.getInstance(AnnotatedSingleton.class));
  110. }
  111. public void testScopingAnnotationsOnAbstractTypeViaBind() {
  112. try {
  113. Guice.createInjector(new AbstractModule() {
  114. protected void configure() {
  115. bind(A.class).to(AImpl.class);
  116. }
  117. });
  118. fail();
  119. } catch (CreationException expected) {
  120. assertContains(expected.getMessage(),
  121. A.class.getName() + " is annotated with " + Singleton.class.getName(),
  122. "but scope annotations are not supported for abstract types.",
  123. "at " + A.class.getName() + ".class(ScopesTest.java:");
  124. }
  125. }
  126. @Singleton
  127. interface A {}
  128. static class AImpl implements A {}
  129. public void testScopingAnnotationsOnAbstractTypeViaImplementedBy() {
  130. try {
  131. Guice.createInjector().getInstance(D.class);
  132. fail();
  133. } catch (ConfigurationException expected) {
  134. assertContains(expected.getMessage(),
  135. D.class.getName() + " is annotated with " + Singleton.class.getName(),
  136. "but scope annotations are not supported for abstract types.",
  137. "at " + D.class.getName() + ".class(ScopesTest.java:");
  138. }
  139. }
  140. @Singleton @ImplementedBy(DImpl.class)
  141. interface D {}
  142. static class DImpl implements D {}
  143. public void testScopingAnnotationsOnAbstractTypeViaProvidedBy() {
  144. try {
  145. Guice.createInjector().getInstance(E.class);
  146. fail();
  147. } catch (ConfigurationException expected) {
  148. assertContains(expected.getMessage(),
  149. E.class.getName() + " is annotated with " + Singleton.class.getName(),
  150. "but scope annotations are not supported for abstract types.",
  151. "at " + E.class.getName() + ".class(ScopesTest.java:");
  152. }
  153. }
  154. @Singleton @ProvidedBy(EProvider.class)
  155. interface E {}
  156. static class EProvider implements Provider<E> {
  157. public E get() {
  158. return null;
  159. }
  160. }
  161. public void testScopeUsedButNotBound() {
  162. try {
  163. Guice.createInjector(new AbstractModule() {
  164. protected void configure() {
  165. bind(B.class).in(CustomScoped.class);
  166. bind(C.class);
  167. }
  168. });
  169. fail();
  170. } catch (CreationException expected) {
  171. assertContains(expected.getMessage(),
  172. "1) No scope is bound to " + CustomScoped.class.getName(),
  173. "at " + getClass().getName(), ".configure(ScopesTest.java:",
  174. "2) No scope is bound to " + CustomScoped.class.getName(),
  175. "at " + C.class.getName() + ".class");
  176. }
  177. }
  178. static class B {}
  179. @CustomScoped
  180. static class C {}
  181. public void testSingletonsInProductionStage() {
  182. Guice.createInjector(Stage.PRODUCTION, singletonsModule);
  183. assertEquals(1, AnnotatedSingleton.nextInstanceId);
  184. assertEquals(1, BoundAsSingleton.nextInstanceId);
  185. assertEquals(1, EagerSingleton.nextInstanceId);
  186. assertEquals(1, RealLinkedSingleton.nextInstanceId);
  187. assertEquals(1, JustInTimeSingleton.nextInstanceId);
  188. assertEquals(0, NotASingleton.nextInstanceId);
  189. }
  190. public void testSingletonsInDevelopmentStage() {
  191. Guice.createInjector(Stage.DEVELOPMENT, singletonsModule);
  192. assertEquals(0, AnnotatedSingleton.nextInstanceId);
  193. assertEquals(0, BoundAsSingleton.nextInstanceId);
  194. assertEquals(1, EagerSingleton.nextInstanceId);
  195. assertEquals(0, RealLinkedSingleton.nextInstanceId);
  196. assertEquals(0, JustInTimeSingleton.nextInstanceId);
  197. assertEquals(0, NotASingleton.nextInstanceId);
  198. }
  199. public void testSingletonScopeIsNotSerializable() throws IOException {
  200. Asserts.assertNotSerializable(Scopes.SINGLETON);
  201. }
  202. public void testNoScopeIsNotSerializable() throws IOException {
  203. Asserts.assertNotSerializable(Scopes.NO_SCOPE);
  204. }
  205. public void testUnscopedProviderWorksOutsideOfRequestedScope() {
  206. final RememberProviderScope scope = new RememberProviderScope();
  207. Injector injector = Guice.createInjector(new AbstractModule() {
  208. protected void configure() {
  209. bindScope(CustomScoped.class, scope);
  210. bind(List.class).to(ArrayList.class).in(CustomScoped.class);
  211. }
  212. });
  213. injector.getInstance(List.class);
  214. Provider<?> listProvider = scope.providers.get(Key.get(List.class));
  215. // this line fails with a NullPointerException because the Providers
  216. // passed to Scope.scope() don't work outside of the scope() method.
  217. assertTrue(listProvider.get() instanceof ArrayList);
  218. }
  219. public void testScopeAnnotationWithoutRuntimeRetention() {
  220. try {
  221. Guice.createInjector(new AbstractModule() {
  222. protected void configure() {
  223. bindScope(NotRuntimeRetainedScoped.class, Scopes.NO_SCOPE);
  224. }
  225. });
  226. fail();
  227. } catch (CreationException expected) {
  228. assertContains(expected.getMessage(),
  229. "1) Please annotate with @Retention(RUNTIME).",
  230. "at " + NotRuntimeRetainedScoped.class.getName() + ".class(ScopesTest.java:");
  231. }
  232. }
  233. public void testBindScopeToAnnotationWithoutScopeAnnotation() {
  234. try {
  235. Guice.createInjector(new AbstractModule() {
  236. protected void configure() {
  237. bindScope(Deprecated.class, Scopes.NO_SCOPE);
  238. }
  239. });
  240. fail();
  241. } catch (CreationException expected) {
  242. assertContains(expected.getMessage(),
  243. "1) Please annotate with @ScopeAnnotation.",
  244. "at " + Deprecated.class.getName() + ".class(");
  245. }
  246. }
  247. public void testBindScopeTooManyTimes() {
  248. try {
  249. Guice.createInjector(new AbstractModule() {
  250. protected void configure() {
  251. bindScope(CustomScoped.class, Scopes.NO_SCOPE);
  252. bindScope(CustomScoped.class, Scopes.SINGLETON);
  253. }
  254. });
  255. fail();
  256. } catch (CreationException expected) {
  257. assertContains(expected.getMessage(),
  258. "1) Scope Scopes.NO_SCOPE is already bound to " + CustomScoped.class.getName(),
  259. "Cannot bind Scopes.SINGLETON.",
  260. "at " + ScopesTest.class.getName(), ".configure(ScopesTest.java:");
  261. }
  262. }
  263. public void testDuplicateScopeAnnotations() {
  264. Injector injector = Guice.createInjector(new AbstractModule() {
  265. protected void configure() {
  266. bindScope(CustomScoped.class, Scopes.NO_SCOPE);
  267. }
  268. });
  269. try {
  270. injector.getInstance(SingletonAndCustomScoped.class);
  271. fail();
  272. } catch (ConfigurationException expected) {
  273. assertContains(expected.getMessage(),
  274. "1) More than one scope annotation was found: ",
  275. "while locating " + SingletonAndCustomScoped.class.getName());
  276. }
  277. }
  278. public void testNullScopedAsASingleton() {
  279. Provider<String> unscoped = new Provider<String>() {
  280. final Iterator<String> values = Arrays.asList(null, "A").iterator();
  281. public String get() {
  282. return values.next();
  283. }
  284. };
  285. Provider<String> scoped = Scopes.SINGLETON.scope(Key.get(String.class), unscoped);
  286. assertNull(scoped.get());
  287. assertNull(scoped.get());
  288. assertNull(scoped.get());
  289. }
  290. class RememberProviderScope implements Scope {
  291. final Map<Key<?>, Provider<?>> providers = Maps.newHashMap();
  292. public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
  293. providers.put(key, unscoped);
  294. return unscoped;
  295. }
  296. }
  297. public void testSingletonAnnotationOnParameterizedType() {
  298. Injector injector = Guice.createInjector();
  299. assertSame(injector.getInstance(new Key<Injected<String>>() {}),
  300. injector.getInstance(new Key<Injected<String>>() {}));
  301. assertSame(injector.getInstance(new Key<In<Integer>>() {}),
  302. injector.getInstance(new Key<In<Short>>() {}));
  303. }
  304. @ImplementedBy(Injected.class) public interface In<T> {}
  305. @Singleton public static class Injected<T> implements In<T> {}
  306. @Target({ ElementType.TYPE, ElementType.METHOD })
  307. @Retention(RUNTIME)
  308. @ScopeAnnotation
  309. public @interface CustomScoped {}
  310. @Target({ ElementType.TYPE, ElementType.METHOD })
  311. @ScopeAnnotation
  312. public @interface NotRuntimeRetainedScoped {}
  313. @Singleton
  314. static class AnnotatedSingleton {
  315. static int nextInstanceId;
  316. final int instanceId = nextInstanceId++;
  317. }
  318. static class BoundAsSingleton {
  319. static int nextInstanceId;
  320. final int instanceId = nextInstanceId++;
  321. }
  322. static class EagerSingleton {
  323. static int nextInstanceId;
  324. final int instanceId = nextInstanceId++;
  325. }
  326. interface LinkedSingleton {}
  327. @Singleton
  328. static class RealLinkedSingleton implements LinkedSingleton {
  329. static int nextInstanceId;
  330. final int instanceId = nextInstanceId++;
  331. }
  332. static class DependsOnJustInTimeSingleton {
  333. @Inject JustInTimeSingleton justInTimeSingleton;
  334. }
  335. @Singleton
  336. static class JustInTimeSingleton {
  337. static int nextInstanceId;
  338. final int instanceId = nextInstanceId++;
  339. }
  340. static class NotASingleton {
  341. static int nextInstanceId;
  342. final int instanceId = nextInstanceId++;
  343. }
  344. @Singleton @CustomScoped
  345. static class SingletonAndCustomScoped {}
  346. @ImplementedBy(Implementation.class)
  347. static interface ImplementedBySingleton {}
  348. @ProvidedBy(ImplementationProvider.class)
  349. static class ProvidedBySingleton {
  350. static int nextInstanceId;
  351. final int instanceId = nextInstanceId++;
  352. }
  353. static class Implementation implements ImplementedBySingleton {
  354. static int nextInstanceId;
  355. final int instanceId = nextInstanceId++;
  356. }
  357. static class ImplementationProvider implements Provider<ProvidedBySingleton> {
  358. public ProvidedBySingleton get() {
  359. return new ProvidedBySingleton();
  360. }
  361. }
  362. public void testScopeThatGetsAnUnrelatedObject() {
  363. Injector injector = Guice.createInjector(new AbstractModule() {
  364. protected void configure() {
  365. bind(B.class);
  366. bind(C.class);
  367. ProviderGetScope providerGetScope = new ProviderGetScope();
  368. requestInjection(providerGetScope);
  369. bindScope(CustomScoped.class, providerGetScope);
  370. }
  371. });
  372. injector.getInstance(C.class);
  373. }
  374. class ProviderGetScope implements Scope {
  375. @Inject Provider<B> bProvider;
  376. public <T> Provider<T> scope(Key<T> key, final Provider<T> unscoped) {
  377. return new Provider<T>() {
  378. public T get() {
  379. bProvider.get();
  380. return unscoped.get();
  381. }
  382. };
  383. }
  384. }
  385. public void testIsSingletonPositive() {
  386. final Key<String> a = Key.get(String.class, named("A"));
  387. final Key<String> b = Key.get(String.class, named("B"));
  388. final Key<String> c = Key.get(String.class, named("C"));
  389. final Key<String> d = Key.get(String.class, named("D"));
  390. final Key<String> e = Key.get(String.class, named("E"));
  391. final Key<String> f = Key.get(String.class, named("F"));
  392. final Key<String> g = Key.get(String.class, named("G"));
  393. final Key<Object> h = Key.get(Object.class, named("H"));
  394. Module singletonBindings = new AbstractModule() {
  395. protected void configure() {
  396. bind(a).to(b);
  397. bind(b).to(c);
  398. bind(c).toProvider(Providers.of("c")).in(Scopes.SINGLETON);
  399. bind(d).toInstance("d");
  400. bind(e).toProvider(Providers.of("e")).asEagerSingleton();
  401. bind(f).toProvider(Providers.of("f")).in(Singleton.class);
  402. bind(h).to(AnnotatedSingleton.class);
  403. }
  404. @Provides @Named("G") @Singleton String provideG() {
  405. return "g";
  406. }
  407. };
  408. @SuppressWarnings("unchecked") // we know the module contains only bindings
  409. List<Element> moduleBindings = Elements.getElements(singletonBindings);
  410. ImmutableMap<Key<?>, Binding<?>> map = indexBindings(moduleBindings);
  411. assertFalse(Scopes.isSingleton(map.get(a))); // linked bindings are not followed by modules
  412. assertFalse(Scopes.isSingleton(map.get(b)));
  413. assertTrue(Scopes.isSingleton(map.get(c)));
  414. assertTrue(Scopes.isSingleton(map.get(d)));
  415. assertTrue(Scopes.isSingleton(map.get(e)));
  416. assertTrue(Scopes.isSingleton(map.get(f)));
  417. assertTrue(Scopes.isSingleton(map.get(g)));
  418. assertFalse(Scopes.isSingleton(map.get(h))); // annotated classes are not followed by modules
  419. Injector injector = Guice.createInjector(singletonBindings);
  420. assertTrue(Scopes.isSingleton(injector.getBinding(a)));
  421. assertTrue(Scopes.isSingleton(injector.getBinding(b)));
  422. assertTrue(Scopes.isSingleton(injector.getBinding(c)));
  423. assertTrue(Scopes.isSingleton(injector.getBinding(d)));
  424. assertTrue(Scopes.isSingleton(injector.getBinding(e)));
  425. assertTrue(Scopes.isSingleton(injector.getBinding(f)));
  426. assertTrue(Scopes.isSingleton(injector.getBinding(g)));
  427. assertTrue(Scopes.isSingleton(injector.getBinding(h)));
  428. }
  429. public void testIsSingletonNegative() {
  430. final Key<String> a = Key.get(String.class, named("A"));
  431. final Key<String> b = Key.get(String.class, named("B"));
  432. final Key<String> c = Key.get(String.class, named("C"));
  433. final Key<String> d = Key.get(String.class, named("D"));
  434. final Key<String> e = Key.get(String.class, named("E"));
  435. Module singletonBindings = new AbstractModule() {
  436. protected void configure() {
  437. bind(a).to(b);
  438. bind(b).to(c);
  439. bind(c).toProvider(Providers.of("c")).in(Scopes.NO_SCOPE);
  440. bind(d).toProvider(Providers.of("d")).in(CustomScoped.class);
  441. bindScope(CustomScoped.class, Scopes.NO_SCOPE);
  442. }
  443. @Provides @Named("E") @CustomScoped String provideE() {
  444. return "e";
  445. }
  446. };
  447. @SuppressWarnings("unchecked") // we know the module contains only bindings
  448. List<Element> moduleBindings = Elements.getElements(singletonBindings);
  449. ImmutableMap<Key<?>, Binding<?>> map = indexBindings(moduleBindings);
  450. assertFalse(Scopes.isSingleton(map.get(a)));
  451. assertFalse(Scopes.isSingleton(map.get(b)));
  452. assertFalse(Scopes.isSingleton(map.get(c)));
  453. assertFalse(Scopes.isSingleton(map.get(d)));
  454. assertFalse(Scopes.isSingleton(map.get(e)));
  455. Injector injector = Guice.createInjector(singletonBindings);
  456. assertFalse(Scopes.isSingleton(injector.getBinding(a)));
  457. assertFalse(Scopes.isSingleton(injector.getBinding(b)));
  458. assertFalse(Scopes.isSingleton(injector.getBinding(c)));
  459. assertFalse(Scopes.isSingleton(injector.getBinding(d)));
  460. assertFalse(Scopes.isSingleton(injector.getBinding(e)));
  461. }
  462. ImmutableMap<Key<?>, Binding<?>> indexBindings(Iterable<Element> elements) {
  463. ImmutableMap.Builder<Key<?>, Binding<?>> builder = ImmutableMap.builder();
  464. for (Element element : elements) {
  465. if (element instanceof Binding) {
  466. Binding<?> binding = (Binding<?>) element;
  467. builder.put(binding.getKey(), binding);
  468. }
  469. }
  470. return builder.build();
  471. }
  472. @Singleton
  473. static class ThrowingSingleton {
  474. static int nextInstanceId;
  475. final int instanceId = nextInstanceId++;
  476. ThrowingSingleton() {
  477. if (instanceId == 0) {
  478. throw new RuntimeException();
  479. }
  480. }
  481. }
  482. public void testSingletonConstructorThrows() {
  483. Injector injector = Guice.createInjector();
  484. try {
  485. injector.getInstance(ThrowingSingleton.class);
  486. fail();
  487. } catch (ProvisionException expected) {
  488. }
  489. // this behaviour is unspecified. If we change Guice to re-throw the exception, this test
  490. // should be changed
  491. injector.getInstance(ThrowingSingleton.class);
  492. assertEquals(2, ThrowingSingleton.nextInstanceId);
  493. }
  494. }