PageRenderTime 623ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

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

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