blob: d95c797a09e1891ec70cec9935f1296add58c320 [file] [log] [blame]
crazybobleef33d23e2007-02-12 04:17:48 +00001/**
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
17package com.google.inject;
18
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -070019import static com.google.inject.Asserts.asModuleChain;
limpbizkit185d2a22008-06-16 07:22:47 +000020import static com.google.inject.Asserts.assertContains;
Christian Edward Gruberba5acdf2013-10-05 14:05:39 -070021import static com.google.inject.Asserts.getDeclaringSourcePart;
sberlinb7a02b02011-07-08 00:34:16 +000022import static com.google.inject.name.Names.named;
23import static java.lang.annotation.RetentionPolicy.RUNTIME;
24
sberlind9c913a2011-06-26 21:02:54 +000025import com.google.common.collect.ImmutableMap;
26import com.google.common.collect.Maps;
limpbizkit52aad3b2009-06-23 03:16:48 +000027import com.google.inject.name.Named;
limpbizkit52aad3b2009-06-23 03:16:48 +000028import com.google.inject.spi.Element;
29import com.google.inject.spi.Elements;
sberlin09ab5f92010-10-25 00:41:10 +000030import com.google.inject.spi.PrivateElements;
limpbizkit52aad3b2009-06-23 03:16:48 +000031import com.google.inject.util.Providers;
sberlinb7a02b02011-07-08 00:34:16 +000032
33import junit.framework.TestCase;
34
limpbizkitf530b252008-05-27 23:03:42 +000035import java.io.IOException;
limpbizkit9266b992008-06-04 01:57:08 +000036import java.lang.annotation.ElementType;
37import java.lang.annotation.Retention;
limpbizkit05a6c5e2008-06-05 00:29:11 +000038import java.lang.annotation.Target;
39import java.util.ArrayList;
limpbizkitb206d202008-12-02 06:16:53 +000040import java.util.Arrays;
41import java.util.Iterator;
limpbizkit9266b992008-06-04 01:57:08 +000042import java.util.List;
limpbizkit05a6c5e2008-06-05 00:29:11 +000043import java.util.Map;
limpbizkitf530b252008-05-27 23:03:42 +000044
crazybobleef33d23e2007-02-12 04:17:48 +000045/**
46 * @author crazybob@google.com (Bob Lee)
47 */
48public class ScopesTest extends TestCase {
49
limpbizkite81cf1b2008-06-02 16:41:22 +000050 private final AbstractModule singletonsModule = new AbstractModule() {
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -070051 @Override protected void configure() {
limpbizkite81cf1b2008-06-02 16:41:22 +000052 bind(BoundAsSingleton.class).in(Scopes.SINGLETON);
53 bind(AnnotatedSingleton.class);
54 bind(EagerSingleton.class).asEagerSingleton();
55 bind(LinkedSingleton.class).to(RealLinkedSingleton.class);
56 bind(DependsOnJustInTimeSingleton.class);
57 bind(NotASingleton.class);
limpbizkit27fc50d2008-08-08 07:43:39 +000058 bind(ImplementedBySingleton.class).in(Scopes.SINGLETON);
59 bind(ProvidedBySingleton.class).in(Scopes.SINGLETON);
limpbizkite81cf1b2008-06-02 16:41:22 +000060 }
61 };
62
limpbizkit349f6f22008-06-02 06:05:54 +000063 @Override protected void setUp() throws Exception {
64 AnnotatedSingleton.nextInstanceId = 0;
65 BoundAsSingleton.nextInstanceId = 0;
66 EagerSingleton.nextInstanceId = 0;
67 RealLinkedSingleton.nextInstanceId = 0;
limpbizkite81cf1b2008-06-02 16:41:22 +000068 JustInTimeSingleton.nextInstanceId = 0;
69 NotASingleton.nextInstanceId = 0;
limpbizkit27fc50d2008-08-08 07:43:39 +000070 Implementation.nextInstanceId = 0;
71 ProvidedBySingleton.nextInstanceId = 0;
limpbizkit1f1fca72009-07-23 17:46:52 +000072 ThrowingSingleton.nextInstanceId = 0;
crazybobleef33d23e2007-02-12 04:17:48 +000073 }
74
limpbizkit349f6f22008-06-02 06:05:54 +000075 public void testSingletons() {
limpbizkite81cf1b2008-06-02 16:41:22 +000076 Injector injector = Guice.createInjector(singletonsModule);
limpbizkitf530b252008-05-27 23:03:42 +000077
78 assertSame(
79 injector.getInstance(BoundAsSingleton.class),
80 injector.getInstance(BoundAsSingleton.class));
limpbizkit349f6f22008-06-02 06:05:54 +000081
82 assertSame(
83 injector.getInstance(AnnotatedSingleton.class),
84 injector.getInstance(AnnotatedSingleton.class));
85
86 assertSame(
87 injector.getInstance(EagerSingleton.class),
88 injector.getInstance(EagerSingleton.class));
89
90 assertSame(
91 injector.getInstance(LinkedSingleton.class),
92 injector.getInstance(LinkedSingleton.class));
limpbizkite81cf1b2008-06-02 16:41:22 +000093
limpbizkite81cf1b2008-06-02 16:41:22 +000094 assertSame(
95 injector.getInstance(JustInTimeSingleton.class),
96 injector.getInstance(JustInTimeSingleton.class));
97
98 assertNotSame(
99 injector.getInstance(NotASingleton.class),
100 injector.getInstance(NotASingleton.class));
limpbizkit27fc50d2008-08-08 07:43:39 +0000101
102 assertSame(
103 injector.getInstance(ImplementedBySingleton.class),
104 injector.getInstance(ImplementedBySingleton.class));
105
106 assertSame(
107 injector.getInstance(ProvidedBySingleton.class),
108 injector.getInstance(ProvidedBySingleton.class));
limpbizkitf530b252008-05-27 23:03:42 +0000109 }
110
111 public void testJustInTimeAnnotatedSingleton() {
112 Injector injector = Guice.createInjector();
113
114 assertSame(
115 injector.getInstance(AnnotatedSingleton.class),
116 injector.getInstance(AnnotatedSingleton.class));
117 }
118
119 public void testSingletonIsPerInjector() {
120 assertNotSame(
121 Guice.createInjector().getInstance(AnnotatedSingleton.class),
122 Guice.createInjector().getInstance(AnnotatedSingleton.class));
123 }
124
125 public void testOverriddingAnnotation() {
126 Injector injector = Guice.createInjector(new AbstractModule() {
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700127 @Override protected void configure() {
limpbizkitf530b252008-05-27 23:03:42 +0000128 bind(AnnotatedSingleton.class).in(Scopes.NO_SCOPE);
limpbizkit3d58d6b2008-03-08 16:11:47 +0000129 }
130 });
131
kevinb9nb6054822007-03-19 03:17:47 +0000132 assertNotSame(
limpbizkitf530b252008-05-27 23:03:42 +0000133 injector.getInstance(AnnotatedSingleton.class),
134 injector.getInstance(AnnotatedSingleton.class));
135 }
136
limpbizkit96d21642008-07-18 23:49:43 +0000137 public void testScopingAnnotationsOnAbstractTypeViaBind() {
limpbizkit185d2a22008-06-16 07:22:47 +0000138 try {
139 Guice.createInjector(new AbstractModule() {
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700140 @Override protected void configure() {
limpbizkit185d2a22008-06-16 07:22:47 +0000141 bind(A.class).to(AImpl.class);
142 }
143 });
144 fail();
145 } catch (CreationException expected) {
146 assertContains(expected.getMessage(),
limpbizkit185d2a22008-06-16 07:22:47 +0000147 A.class.getName() + " is annotated with " + Singleton.class.getName(),
limpbizkita6e0e782008-09-03 06:19:56 +0000148 "but scope annotations are not supported for abstract types.",
149 "at " + A.class.getName() + ".class(ScopesTest.java:");
limpbizkit185d2a22008-06-16 07:22:47 +0000150 }
151 }
152
153 @Singleton
154 interface A {}
155 static class AImpl implements A {}
limpbizkit96d21642008-07-18 23:49:43 +0000156
157 public void testScopingAnnotationsOnAbstractTypeViaImplementedBy() {
158 try {
159 Guice.createInjector().getInstance(D.class);
160 fail();
limpbizkit490833f2008-11-02 00:12:39 +0000161 } catch (ConfigurationException expected) {
limpbizkit96d21642008-07-18 23:49:43 +0000162 assertContains(expected.getMessage(),
limpbizkit96d21642008-07-18 23:49:43 +0000163 D.class.getName() + " is annotated with " + Singleton.class.getName(),
limpbizkita6e0e782008-09-03 06:19:56 +0000164 "but scope annotations are not supported for abstract types.",
165 "at " + D.class.getName() + ".class(ScopesTest.java:");
limpbizkit96d21642008-07-18 23:49:43 +0000166 }
167 }
168
169 @Singleton @ImplementedBy(DImpl.class)
170 interface D {}
171 static class DImpl implements D {}
172
173 public void testScopingAnnotationsOnAbstractTypeViaProvidedBy() {
174 try {
175 Guice.createInjector().getInstance(E.class);
176 fail();
limpbizkit490833f2008-11-02 00:12:39 +0000177 } catch (ConfigurationException expected) {
limpbizkit96d21642008-07-18 23:49:43 +0000178 assertContains(expected.getMessage(),
limpbizkit96d21642008-07-18 23:49:43 +0000179 E.class.getName() + " is annotated with " + Singleton.class.getName(),
limpbizkita6e0e782008-09-03 06:19:56 +0000180 "but scope annotations are not supported for abstract types.",
181 "at " + E.class.getName() + ".class(ScopesTest.java:");
limpbizkit96d21642008-07-18 23:49:43 +0000182 }
183 }
184
185 @Singleton @ProvidedBy(EProvider.class)
186 interface E {}
187 static class EProvider implements Provider<E> {
188 public E get() {
189 return null;
190 }
191 }
192
limpbizkit185d2a22008-06-16 07:22:47 +0000193 public void testScopeUsedButNotBound() {
194 try {
195 Guice.createInjector(new AbstractModule() {
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700196 @Override protected void configure() {
limpbizkit185d2a22008-06-16 07:22:47 +0000197 bind(B.class).in(CustomScoped.class);
198 bind(C.class);
199 }
200 });
201 fail();
202 } catch (CreationException expected) {
203 assertContains(expected.getMessage(),
limpbizkita6e0e782008-09-03 06:19:56 +0000204 "1) No scope is bound to " + CustomScoped.class.getName(),
Christian Edward Gruberba5acdf2013-10-05 14:05:39 -0700205 "at " + getClass().getName(), getDeclaringSourcePart(getClass()),
limpbizkita6e0e782008-09-03 06:19:56 +0000206 "2) No scope is bound to " + CustomScoped.class.getName(),
207 "at " + C.class.getName() + ".class");
limpbizkit185d2a22008-06-16 07:22:47 +0000208 }
209 }
210
211 static class B {}
212
213 @CustomScoped
214 static class C {}
215
limpbizkit349f6f22008-06-02 06:05:54 +0000216 public void testSingletonsInProductionStage() {
limpbizkite81cf1b2008-06-02 16:41:22 +0000217 Guice.createInjector(Stage.PRODUCTION, singletonsModule);
limpbizkitf530b252008-05-27 23:03:42 +0000218
limpbizkit349f6f22008-06-02 06:05:54 +0000219 assertEquals(1, AnnotatedSingleton.nextInstanceId);
220 assertEquals(1, BoundAsSingleton.nextInstanceId);
221 assertEquals(1, EagerSingleton.nextInstanceId);
222 assertEquals(1, RealLinkedSingleton.nextInstanceId);
limpbizkite81cf1b2008-06-02 16:41:22 +0000223 assertEquals(1, JustInTimeSingleton.nextInstanceId);
224 assertEquals(0, NotASingleton.nextInstanceId);
limpbizkitf530b252008-05-27 23:03:42 +0000225 }
226
limpbizkit349f6f22008-06-02 06:05:54 +0000227 public void testSingletonsInDevelopmentStage() {
limpbizkite81cf1b2008-06-02 16:41:22 +0000228 Guice.createInjector(Stage.DEVELOPMENT, singletonsModule);
limpbizkitf530b252008-05-27 23:03:42 +0000229
limpbizkit349f6f22008-06-02 06:05:54 +0000230 assertEquals(0, AnnotatedSingleton.nextInstanceId);
231 assertEquals(0, BoundAsSingleton.nextInstanceId);
232 assertEquals(1, EagerSingleton.nextInstanceId);
233 assertEquals(0, RealLinkedSingleton.nextInstanceId);
limpbizkite81cf1b2008-06-02 16:41:22 +0000234 assertEquals(0, JustInTimeSingleton.nextInstanceId);
235 assertEquals(0, NotASingleton.nextInstanceId);
limpbizkitf530b252008-05-27 23:03:42 +0000236 }
237
238 public void testSingletonScopeIsNotSerializable() throws IOException {
239 Asserts.assertNotSerializable(Scopes.SINGLETON);
240 }
241
242 public void testNoScopeIsNotSerializable() throws IOException {
243 Asserts.assertNotSerializable(Scopes.NO_SCOPE);
244 }
245
limpbizkit9266b992008-06-04 01:57:08 +0000246 public void testUnscopedProviderWorksOutsideOfRequestedScope() {
247 final RememberProviderScope scope = new RememberProviderScope();
sberlinb7a02b02011-07-08 00:34:16 +0000248
limpbizkit9266b992008-06-04 01:57:08 +0000249 Injector injector = Guice.createInjector(new AbstractModule() {
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700250 @Override protected void configure() {
limpbizkit9266b992008-06-04 01:57:08 +0000251 bindScope(CustomScoped.class, scope);
252 bind(List.class).to(ArrayList.class).in(CustomScoped.class);
253 }
254 });
255
256 injector.getInstance(List.class);
257 Provider<?> listProvider = scope.providers.get(Key.get(List.class));
258
259 // this line fails with a NullPointerException because the Providers
260 // passed to Scope.scope() don't work outside of the scope() method.
261 assertTrue(listProvider.get() instanceof ArrayList);
262 }
263
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700264 static class OuterRuntimeModule extends AbstractModule {
265 @Override protected void configure() {
266 install(new InnerRuntimeModule());
267 }
268 }
269 static class InnerRuntimeModule extends AbstractModule {
270 @Override protected void configure() {
271 bindScope(NotRuntimeRetainedScoped.class, Scopes.NO_SCOPE);
272 }
273 }
limpbizkit5789ef42008-08-04 06:44:30 +0000274 public void testScopeAnnotationWithoutRuntimeRetention() {
275 try {
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700276 Guice.createInjector(new OuterRuntimeModule());
limpbizkit5789ef42008-08-04 06:44:30 +0000277 fail();
278 } catch (CreationException expected) {
279 assertContains(expected.getMessage(),
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700280 "1) Please annotate " + NotRuntimeRetainedScoped.class.getName()
281 + " with @Retention(RUNTIME).",
Christian Edward Gruberba5acdf2013-10-05 14:05:39 -0700282 "at " + InnerRuntimeModule.class.getName() + getDeclaringSourcePart(getClass()),
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700283 asModuleChain(OuterRuntimeModule.class, InnerRuntimeModule.class));
limpbizkit5789ef42008-08-04 06:44:30 +0000284 }
285 }
286
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700287 static class OuterDeprecatedModule extends AbstractModule {
288 @Override protected void configure() {
289 install(new InnerDeprecatedModule());
290 }
291 }
292 static class InnerDeprecatedModule extends AbstractModule {
293 @Override protected void configure() {
294 bindScope(Deprecated.class, Scopes.NO_SCOPE);
295 }
296 }
limpbizkitd6becac2008-08-07 08:16:32 +0000297 public void testBindScopeToAnnotationWithoutScopeAnnotation() {
298 try {
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700299 Guice.createInjector(new OuterDeprecatedModule());
limpbizkitd6becac2008-08-07 08:16:32 +0000300 fail();
301 } catch (CreationException expected) {
302 assertContains(expected.getMessage(),
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700303 "1) Please annotate " + Deprecated.class.getName() + " with @ScopeAnnotation.",
Christian Edward Gruberba5acdf2013-10-05 14:05:39 -0700304 "at " + InnerDeprecatedModule.class.getName() + getDeclaringSourcePart(getClass()),
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700305 asModuleChain(OuterDeprecatedModule.class, InnerDeprecatedModule.class));
306 }
307 }
308
309 static class OuterScopeModule extends AbstractModule {
310 @Override protected void configure() {
311 install(new CustomNoScopeModule());
312 install(new CustomSingletonModule());
313 }
314 }
315 static class CustomNoScopeModule extends AbstractModule {
316 @Override protected void configure() {
317 bindScope(CustomScoped.class, Scopes.NO_SCOPE);
318 }
319 }
320 static class CustomSingletonModule extends AbstractModule {
321 @Override protected void configure() {
322 bindScope(CustomScoped.class, Scopes.SINGLETON);
limpbizkitd6becac2008-08-07 08:16:32 +0000323 }
324 }
325
326 public void testBindScopeTooManyTimes() {
327 try {
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700328 Guice.createInjector(new OuterScopeModule());
limpbizkitd6becac2008-08-07 08:16:32 +0000329 fail();
330 } catch (CreationException expected) {
331 assertContains(expected.getMessage(),
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700332 "1) Scope Scopes.NO_SCOPE is already bound to " + CustomScoped.class.getName()
Christian Edward Gruberba5acdf2013-10-05 14:05:39 -0700333 + " at " + CustomNoScopeModule.class.getName() + getDeclaringSourcePart(getClass()),
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700334 asModuleChain(OuterScopeModule.class, CustomNoScopeModule.class),
limpbizkita6e0e782008-09-03 06:19:56 +0000335 "Cannot bind Scopes.SINGLETON.",
Christian Edward Gruberba5acdf2013-10-05 14:05:39 -0700336 "at " + ScopesTest.class.getName(), getDeclaringSourcePart(getClass()),
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700337 asModuleChain(OuterScopeModule.class, CustomSingletonModule.class));
limpbizkitd6becac2008-08-07 08:16:32 +0000338 }
339 }
340
341 public void testDuplicateScopeAnnotations() {
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000342 Injector injector = Guice.createInjector(new AbstractModule() {
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700343 @Override protected void configure() {
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000344 bindScope(CustomScoped.class, Scopes.NO_SCOPE);
345 }
346 });
347
limpbizkitd6becac2008-08-07 08:16:32 +0000348 try {
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000349 injector.getInstance(SingletonAndCustomScoped.class);
limpbizkitd6becac2008-08-07 08:16:32 +0000350 fail();
limpbizkit490833f2008-11-02 00:12:39 +0000351 } catch (ConfigurationException expected) {
limpbizkitd6becac2008-08-07 08:16:32 +0000352 assertContains(expected.getMessage(),
limpbizkita6e0e782008-09-03 06:19:56 +0000353 "1) More than one scope annotation was found: ",
limpbizkit8ba97882008-11-04 02:52:54 +0000354 "while locating " + SingletonAndCustomScoped.class.getName());
limpbizkitd6becac2008-08-07 08:16:32 +0000355 }
356 }
357
limpbizkitb206d202008-12-02 06:16:53 +0000358 public void testNullScopedAsASingleton() {
359 Provider<String> unscoped = new Provider<String>() {
360 final Iterator<String> values = Arrays.asList(null, "A").iterator();
361 public String get() {
362 return values.next();
363 }
364 };
365
366 Provider<String> scoped = Scopes.SINGLETON.scope(Key.get(String.class), unscoped);
367 assertNull(scoped.get());
368 assertNull(scoped.get());
369 assertNull(scoped.get());
370 }
371
limpbizkit9266b992008-06-04 01:57:08 +0000372 class RememberProviderScope implements Scope {
limpbizkit185d2a22008-06-16 07:22:47 +0000373 final Map<Key<?>, Provider<?>> providers = Maps.newHashMap();
limpbizkit9266b992008-06-04 01:57:08 +0000374 public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
375 providers.put(key, unscoped);
376 return unscoped;
377 }
378 }
379
limpbizkit29ccacf2009-01-25 20:51:11 +0000380 public void testSingletonAnnotationOnParameterizedType() {
381 Injector injector = Guice.createInjector();
382 assertSame(injector.getInstance(new Key<Injected<String>>() {}),
383 injector.getInstance(new Key<Injected<String>>() {}));
384 assertSame(injector.getInstance(new Key<In<Integer>>() {}),
385 injector.getInstance(new Key<In<Short>>() {}));
386 }
387
388 @ImplementedBy(Injected.class) public interface In<T> {}
389 @Singleton public static class Injected<T> implements In<T> {}
390
limpbizkit9266b992008-06-04 01:57:08 +0000391 @Target({ ElementType.TYPE, ElementType.METHOD })
392 @Retention(RUNTIME)
393 @ScopeAnnotation
394 public @interface CustomScoped {}
395
Sam Berlin66094fb2012-01-13 18:21:27 -0500396 static final Scope CUSTOM_SCOPE = new Scope() {
397 public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
398 return Scopes.SINGLETON.scope(key, unscoped);
399 }
400 };
401
limpbizkit5789ef42008-08-04 06:44:30 +0000402 @Target({ ElementType.TYPE, ElementType.METHOD })
403 @ScopeAnnotation
404 public @interface NotRuntimeRetainedScoped {}
405
Sam Berlin66094fb2012-01-13 18:21:27 -0500406 @CustomScoped
407 static class AnnotatedCustomScoped {}
408
limpbizkitf530b252008-05-27 23:03:42 +0000409 @Singleton
410 static class AnnotatedSingleton {
limpbizkit349f6f22008-06-02 06:05:54 +0000411 static int nextInstanceId;
412 final int instanceId = nextInstanceId++;
limpbizkitf530b252008-05-27 23:03:42 +0000413 }
414
415 static class BoundAsSingleton {
limpbizkit349f6f22008-06-02 06:05:54 +0000416 static int nextInstanceId;
417 final int instanceId = nextInstanceId++;
418 }
419
420 static class EagerSingleton {
421 static int nextInstanceId;
422 final int instanceId = nextInstanceId++;
423 }
424
425 interface LinkedSingleton {}
426
427 @Singleton
428 static class RealLinkedSingleton implements LinkedSingleton {
429 static int nextInstanceId;
430 final int instanceId = nextInstanceId++;
crazybobleef33d23e2007-02-12 04:17:48 +0000431 }
limpbizkite81cf1b2008-06-02 16:41:22 +0000432
433 static class DependsOnJustInTimeSingleton {
434 @Inject JustInTimeSingleton justInTimeSingleton;
435 }
436
437 @Singleton
438 static class JustInTimeSingleton {
439 static int nextInstanceId;
440 final int instanceId = nextInstanceId++;
441 }
442
443 static class NotASingleton {
444 static int nextInstanceId;
445 final int instanceId = nextInstanceId++;
446 }
limpbizkitd6becac2008-08-07 08:16:32 +0000447
Christian Edward Gruber33736ec2013-09-18 10:59:00 -0700448 @SuppressWarnings("MoreThanOneScopeAnnotationOnClass") // suppress compiler error for testing
449 @Singleton
450 @CustomScoped
limpbizkitd6becac2008-08-07 08:16:32 +0000451 static class SingletonAndCustomScoped {}
limpbizkit27fc50d2008-08-08 07:43:39 +0000452
453 @ImplementedBy(Implementation.class)
454 static interface ImplementedBySingleton {}
455
456 @ProvidedBy(ImplementationProvider.class)
457 static class ProvidedBySingleton {
458 static int nextInstanceId;
459 final int instanceId = nextInstanceId++;
460 }
461
462 static class Implementation implements ImplementedBySingleton {
463 static int nextInstanceId;
464 final int instanceId = nextInstanceId++;
465 }
466
467 static class ImplementationProvider implements Provider<ProvidedBySingleton> {
468 public ProvidedBySingleton get() {
469 return new ProvidedBySingleton();
470 }
471 }
limpbizkit0afda9a2009-06-23 00:29:43 +0000472
473 public void testScopeThatGetsAnUnrelatedObject() {
474 Injector injector = Guice.createInjector(new AbstractModule() {
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700475 @Override protected void configure() {
limpbizkit0afda9a2009-06-23 00:29:43 +0000476 bind(B.class);
477 bind(C.class);
478 ProviderGetScope providerGetScope = new ProviderGetScope();
479 requestInjection(providerGetScope);
480 bindScope(CustomScoped.class, providerGetScope);
481 }
482 });
483
484 injector.getInstance(C.class);
485 }
486
487 class ProviderGetScope implements Scope {
488 @Inject Provider<B> bProvider;
489
490 public <T> Provider<T> scope(Key<T> key, final Provider<T> unscoped) {
491 return new Provider<T>() {
492 public T get() {
493 bProvider.get();
494 return unscoped.get();
495 }
496 };
497 }
498 }
limpbizkit52aad3b2009-06-23 03:16:48 +0000499
500 public void testIsSingletonPositive() {
501 final Key<String> a = Key.get(String.class, named("A"));
502 final Key<String> b = Key.get(String.class, named("B"));
503 final Key<String> c = Key.get(String.class, named("C"));
504 final Key<String> d = Key.get(String.class, named("D"));
505 final Key<String> e = Key.get(String.class, named("E"));
506 final Key<String> f = Key.get(String.class, named("F"));
507 final Key<String> g = Key.get(String.class, named("G"));
508 final Key<Object> h = Key.get(Object.class, named("H"));
sberlin09ab5f92010-10-25 00:41:10 +0000509 final Key<String> i = Key.get(String.class, named("I"));
limpbizkit52aad3b2009-06-23 03:16:48 +0000510
511 Module singletonBindings = new AbstractModule() {
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700512 @Override protected void configure() {
limpbizkit52aad3b2009-06-23 03:16:48 +0000513 bind(a).to(b);
514 bind(b).to(c);
515 bind(c).toProvider(Providers.of("c")).in(Scopes.SINGLETON);
516 bind(d).toInstance("d");
517 bind(e).toProvider(Providers.of("e")).asEagerSingleton();
518 bind(f).toProvider(Providers.of("f")).in(Singleton.class);
519 bind(h).to(AnnotatedSingleton.class);
sberlinb7a02b02011-07-08 00:34:16 +0000520 install(new PrivateModule() {
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700521 @Override protected void configure() {
sberlin09ab5f92010-10-25 00:41:10 +0000522 bind(i).toProvider(Providers.of("i")).in(Singleton.class);
523 expose(i);
524 }
525 });
limpbizkit52aad3b2009-06-23 03:16:48 +0000526 }
527
528 @Provides @Named("G") @Singleton String provideG() {
529 return "g";
530 }
531 };
532
533 @SuppressWarnings("unchecked") // we know the module contains only bindings
534 List<Element> moduleBindings = Elements.getElements(singletonBindings);
535 ImmutableMap<Key<?>, Binding<?>> map = indexBindings(moduleBindings);
536 assertFalse(Scopes.isSingleton(map.get(a))); // linked bindings are not followed by modules
537 assertFalse(Scopes.isSingleton(map.get(b)));
538 assertTrue(Scopes.isSingleton(map.get(c)));
539 assertTrue(Scopes.isSingleton(map.get(d)));
540 assertTrue(Scopes.isSingleton(map.get(e)));
541 assertTrue(Scopes.isSingleton(map.get(f)));
542 assertTrue(Scopes.isSingleton(map.get(g)));
543 assertFalse(Scopes.isSingleton(map.get(h))); // annotated classes are not followed by modules
sberlin09ab5f92010-10-25 00:41:10 +0000544 assertTrue(Scopes.isSingleton(map.get(i)));
limpbizkit52aad3b2009-06-23 03:16:48 +0000545
546 Injector injector = Guice.createInjector(singletonBindings);
547 assertTrue(Scopes.isSingleton(injector.getBinding(a)));
548 assertTrue(Scopes.isSingleton(injector.getBinding(b)));
549 assertTrue(Scopes.isSingleton(injector.getBinding(c)));
550 assertTrue(Scopes.isSingleton(injector.getBinding(d)));
551 assertTrue(Scopes.isSingleton(injector.getBinding(e)));
552 assertTrue(Scopes.isSingleton(injector.getBinding(f)));
553 assertTrue(Scopes.isSingleton(injector.getBinding(g)));
554 assertTrue(Scopes.isSingleton(injector.getBinding(h)));
sberlin09ab5f92010-10-25 00:41:10 +0000555 assertTrue(Scopes.isSingleton(injector.getBinding(i)));
limpbizkit52aad3b2009-06-23 03:16:48 +0000556 }
sberlinb7a02b02011-07-08 00:34:16 +0000557
limpbizkit52aad3b2009-06-23 03:16:48 +0000558 public void testIsSingletonNegative() {
559 final Key<String> a = Key.get(String.class, named("A"));
560 final Key<String> b = Key.get(String.class, named("B"));
561 final Key<String> c = Key.get(String.class, named("C"));
562 final Key<String> d = Key.get(String.class, named("D"));
563 final Key<String> e = Key.get(String.class, named("E"));
sberlin09ab5f92010-10-25 00:41:10 +0000564 final Key<String> f = Key.get(String.class, named("F"));
limpbizkit52aad3b2009-06-23 03:16:48 +0000565
566 Module singletonBindings = new AbstractModule() {
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700567 @Override protected void configure() {
limpbizkit52aad3b2009-06-23 03:16:48 +0000568 bind(a).to(b);
569 bind(b).to(c);
570 bind(c).toProvider(Providers.of("c")).in(Scopes.NO_SCOPE);
571 bind(d).toProvider(Providers.of("d")).in(CustomScoped.class);
572 bindScope(CustomScoped.class, Scopes.NO_SCOPE);
sberlinb7a02b02011-07-08 00:34:16 +0000573 install(new PrivateModule() {
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700574 @Override protected void configure() {
sberlin09ab5f92010-10-25 00:41:10 +0000575 bind(f).toProvider(Providers.of("f")).in(CustomScoped.class);
576 expose(f);
577 }
578 });
limpbizkit52aad3b2009-06-23 03:16:48 +0000579 }
580
581 @Provides @Named("E") @CustomScoped String provideE() {
582 return "e";
583 }
584 };
585
586 @SuppressWarnings("unchecked") // we know the module contains only bindings
587 List<Element> moduleBindings = Elements.getElements(singletonBindings);
588 ImmutableMap<Key<?>, Binding<?>> map = indexBindings(moduleBindings);
589 assertFalse(Scopes.isSingleton(map.get(a)));
590 assertFalse(Scopes.isSingleton(map.get(b)));
591 assertFalse(Scopes.isSingleton(map.get(c)));
592 assertFalse(Scopes.isSingleton(map.get(d)));
593 assertFalse(Scopes.isSingleton(map.get(e)));
sberlin09ab5f92010-10-25 00:41:10 +0000594 assertFalse(Scopes.isSingleton(map.get(f)));
limpbizkit52aad3b2009-06-23 03:16:48 +0000595
596 Injector injector = Guice.createInjector(singletonBindings);
597 assertFalse(Scopes.isSingleton(injector.getBinding(a)));
598 assertFalse(Scopes.isSingleton(injector.getBinding(b)));
599 assertFalse(Scopes.isSingleton(injector.getBinding(c)));
600 assertFalse(Scopes.isSingleton(injector.getBinding(d)));
601 assertFalse(Scopes.isSingleton(injector.getBinding(e)));
sberlin09ab5f92010-10-25 00:41:10 +0000602 assertFalse(Scopes.isSingleton(injector.getBinding(f)));
limpbizkit52aad3b2009-06-23 03:16:48 +0000603 }
604
Sam Berlin66094fb2012-01-13 18:21:27 -0500605 public void testIsScopedPositive() {
606 final Key<String> a = Key.get(String.class, named("A"));
607 final Key<String> b = Key.get(String.class, named("B"));
608 final Key<String> c = Key.get(String.class, named("C"));
609 final Key<String> d = Key.get(String.class, named("D"));
610 final Key<String> e = Key.get(String.class, named("E"));
611 final Key<Object> f = Key.get(Object.class, named("F"));
612 final Key<String> g = Key.get(String.class, named("G"));
613
614 Module customBindings = new AbstractModule() {
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700615 @Override protected void configure() {
Sam Berlin66094fb2012-01-13 18:21:27 -0500616 bindScope(CustomScoped.class, CUSTOM_SCOPE);
617 bind(a).to(b);
618 bind(b).to(c);
619 bind(c).toProvider(Providers.of("c")).in(CUSTOM_SCOPE);
620 bind(d).toProvider(Providers.of("d")).in(CustomScoped.class);
621 bind(f).to(AnnotatedCustomScoped.class);
622 install(new PrivateModule() {
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700623 @Override protected void configure() {
Sam Berlin66094fb2012-01-13 18:21:27 -0500624 bind(g).toProvider(Providers.of("g")).in(CustomScoped.class);
625 expose(g);
626 }
627 });
628 }
629
630 @Provides @Named("E") @CustomScoped String provideE() {
631 return "e";
632 }
633 };
634
635 @SuppressWarnings("unchecked") // we know the module contains only bindings
636 List<Element> moduleBindings = Elements.getElements(customBindings);
637 ImmutableMap<Key<?>, Binding<?>> map = indexBindings(moduleBindings);
638 assertFalse(isCustomScoped(map.get(a))); // linked bindings are not followed by modules
639 assertFalse(isCustomScoped(map.get(b)));
640 assertTrue(isCustomScoped(map.get(c)));
641 assertTrue(isCustomScoped(map.get(d)));
642 assertTrue(isCustomScoped(map.get(e)));
643 assertFalse(isCustomScoped(map.get(f))); // annotated classes are not followed by modules
644 assertTrue(isCustomScoped(map.get(g)));
645
646 Injector injector = Guice.createInjector(customBindings);
647 assertTrue(isCustomScoped(injector.getBinding(a)));
648 assertTrue(isCustomScoped(injector.getBinding(b)));
649 assertTrue(isCustomScoped(injector.getBinding(c)));
650 assertTrue(isCustomScoped(injector.getBinding(d)));
651 assertTrue(isCustomScoped(injector.getBinding(e)));
652 assertTrue(isCustomScoped(injector.getBinding(f)));
653 assertTrue(isCustomScoped(injector.getBinding(g)));
654 }
655
656 public void testIsScopedNegative() {
657 final Key<String> a = Key.get(String.class, named("A"));
658 final Key<String> b = Key.get(String.class, named("B"));
659 final Key<String> c = Key.get(String.class, named("C"));
660 final Key<String> d = Key.get(String.class, named("D"));
661 final Key<String> e = Key.get(String.class, named("E"));
662 final Key<String> f = Key.get(String.class, named("F"));
663 final Key<String> g = Key.get(String.class, named("G"));
664 final Key<String> h = Key.get(String.class, named("H"));
665
666 Module customBindings = new AbstractModule() {
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700667 @Override protected void configure() {
Sam Berlin66094fb2012-01-13 18:21:27 -0500668 bind(a).to(b);
669 bind(b).to(c);
670 bind(c).toProvider(Providers.of("c")).in(Scopes.NO_SCOPE);
671 bind(d).toProvider(Providers.of("d")).in(Singleton.class);
672 install(new PrivateModule() {
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700673 @Override protected void configure() {
Sam Berlin66094fb2012-01-13 18:21:27 -0500674 bind(f).toProvider(Providers.of("f")).in(Singleton.class);
675 expose(f);
676 }
677 });
678 bind(g).toInstance("g");
679 bind(h).toProvider(Providers.of("h")).asEagerSingleton();
680 }
681
682 @Provides @Named("E") @Singleton String provideE() {
683 return "e";
684 }
685 };
686
687 @SuppressWarnings("unchecked") // we know the module contains only bindings
688 List<Element> moduleBindings = Elements.getElements(customBindings);
689 ImmutableMap<Key<?>, Binding<?>> map = indexBindings(moduleBindings);
690 assertFalse(isCustomScoped(map.get(a)));
691 assertFalse(isCustomScoped(map.get(b)));
692 assertFalse(isCustomScoped(map.get(c)));
693 assertFalse(isCustomScoped(map.get(d)));
694 assertFalse(isCustomScoped(map.get(e)));
695 assertFalse(isCustomScoped(map.get(f)));
696 assertFalse(isCustomScoped(map.get(g)));
697 assertFalse(isCustomScoped(map.get(h)));
698
699 Injector injector = Guice.createInjector(customBindings);
700 assertFalse(isCustomScoped(injector.getBinding(a)));
701 assertFalse(isCustomScoped(injector.getBinding(b)));
702 assertFalse(isCustomScoped(injector.getBinding(c)));
703 assertFalse(isCustomScoped(injector.getBinding(d)));
704 assertFalse(isCustomScoped(injector.getBinding(e)));
705 assertFalse(isCustomScoped(injector.getBinding(f)));
706 assertFalse(isCustomScoped(injector.getBinding(g)));
707 assertFalse(isCustomScoped(injector.getBinding(h)));
708 }
709
710 private boolean isCustomScoped(Binding<?> binding) {
711 return Scopes.isScoped(binding, CUSTOM_SCOPE, CustomScoped.class);
712 }
713
limpbizkit52aad3b2009-06-23 03:16:48 +0000714 ImmutableMap<Key<?>, Binding<?>> indexBindings(Iterable<Element> elements) {
715 ImmutableMap.Builder<Key<?>, Binding<?>> builder = ImmutableMap.builder();
716 for (Element element : elements) {
717 if (element instanceof Binding) {
718 Binding<?> binding = (Binding<?>) element;
719 builder.put(binding.getKey(), binding);
sberlin09ab5f92010-10-25 00:41:10 +0000720 } else if (element instanceof PrivateElements) {
721 PrivateElements privateElements = (PrivateElements)element;
722 Map<Key<?>, Binding<?>> privateBindings = indexBindings(privateElements.getElements());
723 for(Key<?> exposed : privateElements.getExposedKeys()) {
724 builder.put(exposed, privateBindings.get(exposed));
725 }
limpbizkit52aad3b2009-06-23 03:16:48 +0000726 }
727 }
728 return builder.build();
729 }
limpbizkit1f1fca72009-07-23 17:46:52 +0000730
731 @Singleton
732 static class ThrowingSingleton {
733 static int nextInstanceId;
734 final int instanceId = nextInstanceId++;
735
736 ThrowingSingleton() {
737 if (instanceId == 0) {
738 throw new RuntimeException();
739 }
740 }
741 }
742
743 public void testSingletonConstructorThrows() {
744 Injector injector = Guice.createInjector();
745
746 try {
747 injector.getInstance(ThrowingSingleton.class);
748 fail();
749 } catch (ProvisionException expected) {
750 }
751
752 // this behaviour is unspecified. If we change Guice to re-throw the exception, this test
753 // should be changed
754 injector.getInstance(ThrowingSingleton.class);
755 assertEquals(2, ThrowingSingleton.nextInstanceId);
756 }
crazybobleef33d23e2007-02-12 04:17:48 +0000757}