blob: 58149670f0f6507ce051e182906f6318652668cf [file] [log] [blame]
limpbizkitb3a8f0b2008-09-05 22:30:40 +00001/**
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
17package com.google.inject.spi;
18
limpbizkit759662b2009-04-26 21:00:24 +000019import static com.google.inject.Asserts.assertContains;
sberlinb7a02b02011-07-08 00:34:16 +000020import static java.lang.annotation.RetentionPolicy.RUNTIME;
21
22import com.google.common.collect.ImmutableList;
23import com.google.common.collect.ImmutableSet;
sameb9867f9c2015-02-02 12:45:25 -080024import com.google.common.collect.Iterables;
25import com.google.common.collect.Lists;
sberlinb7a02b02011-07-08 00:34:16 +000026import com.google.inject.AbstractModule;
limpbizkitb3a8f0b2008-09-05 22:30:40 +000027import com.google.inject.Binder;
Sam Berlin29ce12b2014-03-10 12:52:01 -040028import com.google.inject.Binding;
limpbizkitb3a8f0b2008-09-05 22:30:40 +000029import com.google.inject.BindingAnnotation;
30import com.google.inject.CreationException;
31import com.google.inject.Guice;
32import com.google.inject.Inject;
33import com.google.inject.Injector;
34import com.google.inject.Key;
35import com.google.inject.Module;
36import com.google.inject.Provides;
sameb9867f9c2015-02-02 12:45:25 -080037import com.google.inject.ProvisionException;
limpbizkitb3a8f0b2008-09-05 22:30:40 +000038import com.google.inject.Singleton;
Sam Berlin409e0f52014-05-10 10:34:16 -040039import com.google.inject.Stage;
sameb9867f9c2015-02-02 12:45:25 -080040import com.google.inject.TypeLiteral;
41import com.google.inject.internal.Errors;
42import com.google.inject.internal.InternalFlags;
limpbizkit714df3c2009-01-08 02:40:04 +000043import com.google.inject.internal.ProviderMethod;
44import com.google.inject.internal.ProviderMethodsModule;
limpbizkitb3a8f0b2008-09-05 22:30:40 +000045import com.google.inject.name.Named;
46import com.google.inject.name.Names;
sameb9867f9c2015-02-02 12:45:25 -080047import com.google.inject.util.Providers;
limpbizkitb3a8f0b2008-09-05 22:30:40 +000048import com.google.inject.util.Types;
sberlinb7a02b02011-07-08 00:34:16 +000049
50import junit.framework.TestCase;
51
limpbizkitb3a8f0b2008-09-05 22:30:40 +000052import java.lang.annotation.ElementType;
53import java.lang.annotation.Retention;
sameb9867f9c2015-02-02 12:45:25 -080054import java.lang.annotation.RetentionPolicy;
limpbizkitb3a8f0b2008-09-05 22:30:40 +000055import java.lang.annotation.Target;
sameb9867f9c2015-02-02 12:45:25 -080056import java.lang.reflect.Method;
Sam Berlin76be88e2014-07-01 16:56:13 -040057import java.util.ArrayList;
Sam Berlin409e0f52014-05-10 10:34:16 -040058import java.util.Collection;
limpbizkitb3a8f0b2008-09-05 22:30:40 +000059import java.util.List;
60import java.util.Set;
sberlin82cbc6d2010-05-19 03:12:07 +000061import java.util.concurrent.atomic.AtomicReference;
sameb9867f9c2015-02-02 12:45:25 -080062import java.util.logging.Handler;
63import java.util.logging.LogRecord;
sberlin82cbc6d2010-05-19 03:12:07 +000064import java.util.logging.Logger;
65
limpbizkitb3a8f0b2008-09-05 22:30:40 +000066/**
67 * @author crazybob@google.com (Bob Lee)
68 */
69public class ProviderMethodsTest extends TestCase implements Module {
70
71 @SuppressWarnings("unchecked")
72 public void testProviderMethods() {
73 Injector injector = Guice.createInjector(this);
74
75 Bob bob = injector.getInstance(Bob.class);
76 assertEquals("A Bob", bob.getName());
77
78 Bob clone = injector.getInstance(Bob.class);
79 assertEquals("A Bob", clone.getName());
80
81 assertNotSame(bob, clone);
82 assertSame(bob.getDaughter(), clone.getDaughter());
83
84 Key soleBobKey = Key.get(Bob.class, Sole.class);
85 assertSame(
86 injector.getInstance(soleBobKey),
87 injector.getInstance(soleBobKey)
88 );
89 }
90
91 public void configure(Binder binder) {}
92
93 interface Bob {
94 String getName();
95 Dagny getDaughter();
96 }
97
98 interface Dagny {
99 int getAge();
100 }
101
102 @Provides
103 Bob provideBob(final Dagny dagny) {
104 return new Bob() {
105 public String getName() {
106 return "A Bob";
107 }
108
109 public Dagny getDaughter() {
110 return dagny;
111 }
112 };
113 }
114
115 @Provides
116 @Singleton
117 @Sole
118 Bob provideSoleBob(final Dagny dagny) {
119 return new Bob() {
120 public String getName() {
121 return "Only Bob";
122 }
123
124 public Dagny getDaughter() {
125 return dagny;
126 }
127 };
128 }
129
130 @Provides
131 @Singleton
132 Dagny provideDagny() {
133 return new Dagny() {
134 public int getAge() {
135 return 1;
136 }
137 };
138 }
139
140 @Retention(RUNTIME)
141 @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
142 @BindingAnnotation
143 @interface Sole {}
144
145
146
147// We'll have to make getProvider() support circular dependencies before this
148// will work.
149//
150// public void testCircularDependency() {
151// Injector injector = Guice.createInjector(new Module() {
152// public void configure(Binder binder) {
153// binder.install(ProviderMethods.from(ProviderMethodsTest.this));
154// }
155// });
156//
157// Foo foo = injector.getInstance(Foo.class);
158// assertEquals(5, foo.getI());
159// assertEquals(10, foo.getBar().getI());
160// assertEquals(5, foo.getBar().getFoo().getI());
161// }
162//
163// interface Foo {
164// Bar getBar();
165// int getI();
166// }
167//
168// interface Bar {
169// Foo getFoo();
170// int getI();
171// }
172//
173// @Provides Foo newFoo(final Bar bar) {
174// return new Foo() {
175//
176// public Bar getBar() {
177// return bar;
178// }
179//
180// public int getI() {
181// return 5;
182// }
183// };
184// }
185//
186// @Provides Bar newBar(final Foo foo) {
187// return new Bar() {
188//
189// public Foo getFoo() {
190// return foo;
191// }
192//
193// public int getI() {
194// return 10;
195// }
196// };
197// }
198
199
200 public void testMultipleBindingAnnotations() {
201 try {
202 Guice.createInjector(new AbstractModule() {
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700203 @Override protected void configure() {}
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000204
205 @Provides @Named("A") @Blue
206 public String provideString() {
207 return "a";
208 }
209 });
210 fail();
211 } catch (CreationException expected) {
limpbizkit759662b2009-04-26 21:00:24 +0000212 assertContains(expected.getMessage(),
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000213 "more than one annotation annotated with @BindingAnnotation:", "Named", "Blue",
214 "at " + getClass().getName(), ".provideString(ProviderMethodsTest.java:");
215 }
216
217 }
218
219 @Retention(RUNTIME)
220 @BindingAnnotation @interface Blue {}
221
222 public void testGenericProviderMethods() {
223 Injector injector = Guice.createInjector(
224 new ProvideTs<String>("A", "B") {}, new ProvideTs<Integer>(1, 2) {});
sameb9867f9c2015-02-02 12:45:25 -0800225
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000226 assertEquals("A", injector.getInstance(Key.get(String.class, Names.named("First"))));
227 assertEquals("B", injector.getInstance(Key.get(String.class, Names.named("Second"))));
228 assertEquals(ImmutableSet.of("A", "B"),
229 injector.getInstance(Key.get(Types.setOf(String.class))));
230
231 assertEquals(1, injector.getInstance(Key.get(Integer.class, Names.named("First"))).intValue());
232 assertEquals(2, injector.getInstance(Key.get(Integer.class, Names.named("Second"))).intValue());
233 assertEquals(ImmutableSet.of(1, 2),
234 injector.getInstance(Key.get(Types.setOf(Integer.class))));
235 }
236
237 abstract class ProvideTs<T> extends AbstractModule {
238 final T first;
239 final T second;
240
241 protected ProvideTs(T first, T second) {
242 this.first = first;
243 this.second = second;
244 }
245
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700246 @Override protected void configure() {}
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000247
248 @Named("First") @Provides T provideFirst() {
249 return first;
250 }
251
252 @Named("Second") @Provides T provideSecond() {
253 return second;
254 }
255
256 @Provides Set<T> provideBoth(@Named("First") T first, @Named("Second") T second) {
257 return ImmutableSet.of(first, second);
258 }
259 }
sameb9867f9c2015-02-02 12:45:25 -0800260
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000261 public void testAutomaticProviderMethods() {
262 Injector injector = Guice.createInjector((Module) new AbstractModule() {
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700263 @Override protected void configure() { }
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000264 private int next = 1;
265
266 @Provides @Named("count")
267 public Integer provideCount() {
268 return next++;
269 }
270 });
271
272 assertEquals(1, injector.getInstance(Key.get(Integer.class, Names.named("count"))).intValue());
273 assertEquals(2, injector.getInstance(Key.get(Integer.class, Names.named("count"))).intValue());
274 assertEquals(3, injector.getInstance(Key.get(Integer.class, Names.named("count"))).intValue());
275 }
276
277 /**
278 * If the user installs provider methods for the module manually, that shouldn't cause a double
279 * binding of the provider methods' types.
280 */
281 public void testAutomaticProviderMethodsDoNotCauseDoubleBinding() {
282 Module installsSelf = new AbstractModule() {
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700283 @Override protected void configure() {
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000284 install(this);
285 bind(Integer.class).toInstance(5);
286 }
287 @Provides public String provideString(Integer count) {
288 return "A" + count;
289 }
290 };
291
292 Injector injector = Guice.createInjector(installsSelf);
293 assertEquals("A5", injector.getInstance(String.class));
294 }
sameb9867f9c2015-02-02 12:45:25 -0800295
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000296 public void testWildcardProviderMethods() {
297 final List<String> strings = ImmutableList.of("A", "B", "C");
298 final List<Number> numbers = ImmutableList.<Number>of(1, 2, 3);
299
300 Injector injector = Guice.createInjector(new AbstractModule() {
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700301 @Override protected void configure() {
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000302 @SuppressWarnings("unchecked")
303 Key<List<? super Integer>> listOfSupertypesOfInteger = (Key<List<? super Integer>>)
304 Key.get(Types.listOf(Types.supertypeOf(Integer.class)));
305 bind(listOfSupertypesOfInteger).toInstance(numbers);
306 }
307 @Provides public List<? extends CharSequence> provideCharSequences() {
308 return strings;
309 }
310 @Provides public Class<?> provideType() {
311 return Float.class;
312 }
313 });
314
315 assertSame(strings, injector.getInstance(HasWildcardInjection.class).charSequences);
316 assertSame(numbers, injector.getInstance(HasWildcardInjection.class).numbers);
317 assertSame(Float.class, injector.getInstance(HasWildcardInjection.class).type);
318 }
319
320 static class HasWildcardInjection {
321 @Inject List<? extends CharSequence> charSequences;
322 @Inject List<? super Integer> numbers;
323 @Inject Class<?> type;
324 }
limpbizkit76c24b12008-12-25 04:32:41 +0000325
sameb9867f9c2015-02-02 12:45:25 -0800326 public void testProviderMethodDependenciesAreExposed() throws Exception {
327 Module module = new AbstractModule() {
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700328 @Override protected void configure() {
limpbizkit76c24b12008-12-25 04:32:41 +0000329 bind(Integer.class).toInstance(50);
330 bindConstant().annotatedWith(Names.named("units")).to("Kg");
331 }
332 @Provides @Named("weight") String provideWeight(Integer count, @Named("units") String units) {
333 return count + units;
334 }
sameb9867f9c2015-02-02 12:45:25 -0800335 };
336 Injector injector = Guice.createInjector(module);
limpbizkit76c24b12008-12-25 04:32:41 +0000337
338 ProviderInstanceBinding<?> binding = (ProviderInstanceBinding<?>) injector.getBinding(
339 Key.get(String.class, Names.named("weight")));
sameb9867f9c2015-02-02 12:45:25 -0800340 Method method =
341 module.getClass().getDeclaredMethod("provideWeight", Integer.class, String.class);
342 InjectionPoint point = new InjectionPoint(TypeLiteral.get(module.getClass()), method, false);
343 assertEquals(ImmutableSet.<Dependency<?>>of(
344 new Dependency<Integer>(point, Key.get(Integer.class), false, 0),
345 new Dependency<String>(point, Key.get(String.class, Names.named("units")), false, 1)),
346 binding.getDependencies());
limpbizkit76c24b12008-12-25 04:32:41 +0000347 }
limpbizkit714df3c2009-01-08 02:40:04 +0000348
349 public void testNonModuleProviderMethods() {
350 final Object methodsObject = new Object() {
351 @Provides @Named("foo") String provideFoo() {
352 return "foo-value";
353 }
354 };
355
356 Module module = new AbstractModule() {
357 @Override protected void configure() {
358 install(ProviderMethodsModule.forObject(methodsObject));
359 }
360 };
361
362 Injector injector = Guice.createInjector(module);
363
364 Key<String> key = Key.get(String.class, Names.named("foo"));
365 assertEquals("foo-value", injector.getInstance(key));
366
367 // Test the provider method object itself. This makes sure getInstance works, since GIN uses it
368 List<Element> elements = Elements.getElements(module);
369 assertEquals(1, elements.size());
370
371 Element element = elements.get(0);
372 assertTrue(element + " instanceof ProviderInstanceBinding",
373 element instanceof ProviderInstanceBinding);
374
375 ProviderInstanceBinding binding = (ProviderInstanceBinding) element;
sameb9867f9c2015-02-02 12:45:25 -0800376 javax.inject.Provider provider = binding.getUserSuppliedProvider();
Sam Berlinc00df282014-07-01 16:53:41 -0400377 assertTrue(provider instanceof ProviderMethod);
sameb9867f9c2015-02-02 12:45:25 -0800378 assertEquals(methodsObject, ((ProviderMethod) provider).getInstance());
Sam Berlin8ad60eb2014-03-10 12:49:46 -0400379 assertSame(provider, binding.getProviderInstance());
limpbizkit714df3c2009-01-08 02:40:04 +0000380 }
limpbizkit759662b2009-04-26 21:00:24 +0000381
382 public void testVoidProviderMethods() {
383 try {
384 Guice.createInjector(new AbstractModule() {
Christian Edward Gruber2e39ef72013-10-05 14:04:53 -0700385 @Override protected void configure() {}
limpbizkit759662b2009-04-26 21:00:24 +0000386
387 @Provides void provideFoo() {}
388 });
389 fail();
390 } catch (CreationException expected) {
sameb9867f9c2015-02-02 12:45:25 -0800391 assertContains(expected.getMessage(),
limpbizkit759662b2009-04-26 21:00:24 +0000392 "1) Provider methods must return a value. Do not return void.",
393 getClass().getName(), ".provideFoo(ProviderMethodsTest.java:");
394 }
395 }
sameb9867f9c2015-02-02 12:45:25 -0800396
sberlin82cbc6d2010-05-19 03:12:07 +0000397 public void testInjectsJustOneLogger() {
398 AtomicReference<Logger> loggerRef = new AtomicReference<Logger>();
399 Injector injector = Guice.createInjector(new FooModule(loggerRef));
sameb9867f9c2015-02-02 12:45:25 -0800400
sberlin82cbc6d2010-05-19 03:12:07 +0000401 assertNull(loggerRef.get());
402 injector.getInstance(Integer.class);
403 Logger lastLogger = loggerRef.getAndSet(null);
404 assertNotNull(lastLogger);
405 injector.getInstance(Integer.class);
406 assertSame(lastLogger, loggerRef.get());
sameb9867f9c2015-02-02 12:45:25 -0800407
408 assertEquals(FooModule.class.getName(), lastLogger.getName());
sberlin82cbc6d2010-05-19 03:12:07 +0000409 }
sameb9867f9c2015-02-02 12:45:25 -0800410
sberlin82cbc6d2010-05-19 03:12:07 +0000411 private static class FooModule extends AbstractModule {
412 private final AtomicReference<Logger> loggerRef;
sameb9867f9c2015-02-02 12:45:25 -0800413
sberlin82cbc6d2010-05-19 03:12:07 +0000414 public FooModule(AtomicReference<Logger> loggerRef) {
415 this.loggerRef = loggerRef;
416 }
417
418 @Override protected void configure() {}
419
420 @SuppressWarnings("unused")
421 @Provides Integer foo(Logger logger) {
422 loggerRef.set(logger);
423 return 42;
424 }
425 }
sameb9867f9c2015-02-02 12:45:25 -0800426
Sam Berlin29ce12b2014-03-10 12:52:01 -0400427 public void testSpi() throws Exception {
428 Module m1 = new AbstractModule() {
429 @Override protected void configure() {}
430 @Provides @Named("foo") String provideFoo(Integer dep) { return "foo"; }
431 };
432 Module m2 = new AbstractModule() {
433 @Override protected void configure() {}
434 @Provides Integer provideInt(@Named("foo") String dep) { return 42; }
435 };
436 Injector injector = Guice.createInjector(m1, m2);
sameb9867f9c2015-02-02 12:45:25 -0800437
Sam Berlin29ce12b2014-03-10 12:52:01 -0400438 Binding<String> stringBinding =
439 injector.getBinding(Key.get(String.class, Names.named("foo")));
440 ProvidesMethodBinding<String> stringMethod =
441 stringBinding.acceptTargetVisitor(new BindingCapturer<String>());
442 assertEquals(m1, stringMethod.getEnclosingInstance());
443 assertEquals(m1.getClass().getDeclaredMethod("provideFoo", Integer.class),
444 stringMethod.getMethod());
445 assertEquals(((HasDependencies) stringBinding).getDependencies(),
446 stringMethod.getDependencies());
447 assertEquals(Key.get(String.class, Names.named("foo")), stringMethod.getKey());
sameb9867f9c2015-02-02 12:45:25 -0800448
Sam Berlin29ce12b2014-03-10 12:52:01 -0400449 Binding<Integer> intBinding = injector.getBinding(Integer.class);
450 ProvidesMethodBinding<Integer> intMethod =
451 intBinding.acceptTargetVisitor(new BindingCapturer<Integer>());
452 assertEquals(m2, intMethod.getEnclosingInstance());
453 assertEquals(m2.getClass().getDeclaredMethod("provideInt", String.class),
454 intMethod.getMethod());
455 assertEquals(((HasDependencies) intBinding).getDependencies(),
456 intMethod.getDependencies());
457 assertEquals(Key.get(Integer.class), intMethod.getKey());
sameb9867f9c2015-02-02 12:45:25 -0800458
Sam Berlin29ce12b2014-03-10 12:52:01 -0400459 }
sameb9867f9c2015-02-02 12:45:25 -0800460
Sam Berlin29ce12b2014-03-10 12:52:01 -0400461 private static class BindingCapturer<T> extends DefaultBindingTargetVisitor<T, ProvidesMethodBinding<T>>
462 implements ProvidesMethodTargetVisitor<T, ProvidesMethodBinding<T>> {
sameb9867f9c2015-02-02 12:45:25 -0800463
Sam Berlin29ce12b2014-03-10 12:52:01 -0400464 @SuppressWarnings("unchecked")
465 public ProvidesMethodBinding<T> visit(
466 ProvidesMethodBinding<? extends T> providesMethodBinding) {
467 return (ProvidesMethodBinding<T>)providesMethodBinding;
468 }
sameb9867f9c2015-02-02 12:45:25 -0800469
Sam Berlin29ce12b2014-03-10 12:52:01 -0400470 @Override protected ProvidesMethodBinding<T> visitOther(Binding<? extends T> binding) {
471 throw new IllegalStateException("unexpected visit of: " + binding);
sameb9867f9c2015-02-02 12:45:25 -0800472 }
Sam Berlin29ce12b2014-03-10 12:52:01 -0400473 }
Sam Berlin409e0f52014-05-10 10:34:16 -0400474
475 public void testProvidesMethodVisibility() {
476 Injector injector = Guice.createInjector(new VisibilityModule());
477
478 assertEquals(42, injector.getInstance(Integer.class).intValue());
479 assertEquals(42L, injector.getInstance(Long.class).longValue());
480 assertEquals(42D, injector.getInstance(Double.class).doubleValue());
481 assertEquals(42F, injector.getInstance(Float.class).floatValue());
482 }
483
484 private static class VisibilityModule extends AbstractModule {
485 @Override protected void configure() {}
486
487 @SuppressWarnings("unused")
488 @Provides Integer foo() {
489 return 42;
490 }
491
492 @SuppressWarnings("unused")
493 @Provides private Long bar() {
494 return 42L;
495 }
496
497 @SuppressWarnings("unused")
498 @Provides protected Double baz() {
499 return 42D;
500 }
501
502 @SuppressWarnings("unused")
503 @Provides public Float quux() {
504 return 42F;
505 }
506 }
507
508 public void testProvidesMethodInheritenceHierarchy() {
509 try {
510 Guice.createInjector(new Sub1Module(), new Sub2Module());
511 fail("Expected injector creation failure");
512 } catch (CreationException expected) {
513 // both of our super class bindings cause errors
sameb9867f9c2015-02-02 12:45:25 -0800514 assertContains(expected.getMessage(),
Sam Berlin409e0f52014-05-10 10:34:16 -0400515 "A binding to java.lang.Long was already configured",
516 "A binding to java.lang.Integer was already configured");
517 }
518 }
519
520 public void testProvidesMethodsDefinedInSuperClass() {
521 Injector injector = Guice.createInjector(new Sub1Module());
522 assertEquals(42, injector.getInstance(Integer.class).intValue());
523 assertEquals(42L, injector.getInstance(Long.class).longValue());
524 assertEquals(42D, injector.getInstance(Double.class).doubleValue());
525 }
526
527 private static class BaseModule extends AbstractModule {
528 @Override protected void configure() {}
529
530 @Provides Integer foo() {
531 return 42;
532 }
533
534 @Provides Long bar() {
535 return 42L;
536 }
537 }
538
539 private static class Sub1Module extends BaseModule {
540 @Provides Double baz() {
541 return 42D;
542 }
543 }
544
545 private static class Sub2Module extends BaseModule {
546 @Provides Float quux() {
547 return 42F;
548 }
549 }
550
551 /*if[AOP]*/
552 public void testShareFastClass() {
553 CallerInspecterModule module = new CallerInspecterModule();
554 Guice.createInjector(Stage.PRODUCTION, module);
555 assertEquals(module.fooCallerClass, module.barCallerClass);
556 assertTrue(module.fooCallerClass.contains("$$FastClassByGuice$$"));
557 }
558
559 private static class CallerInspecterModule extends AbstractModule {
560 // start them off as unequal
561 String barCallerClass = "not_set_bar";
562 String fooCallerClass = "not_set_foo";
563
564 @Override protected void configure() {}
565
566 @Provides @Singleton Integer foo() {
567 this.fooCallerClass = new Exception().getStackTrace()[1].getClassName();
568 return 42;
569 }
570
571 @Provides @Singleton Long bar() {
572 this.barCallerClass = new Exception().getStackTrace()[1].getClassName();
573 return 42L;
574 }
575 }
576
577 public void testShareFastClassWithSuperClass() {
578 CallerInspecterSubClassModule module = new CallerInspecterSubClassModule();
579 Guice.createInjector(Stage.PRODUCTION, module);
sameb9867f9c2015-02-02 12:45:25 -0800580 assertEquals("Expected provider methods in the same class to share fastclass classes",
Sam Berlin409e0f52014-05-10 10:34:16 -0400581 module.fooCallerClass, module.barCallerClass);
582 assertFalse(
583 "Did not expect provider methods in the subclasses to share fastclass classes "
584 + "with their parent classes",
585 module.bazCallerClass.equals(module.barCallerClass));
586 }
sameb9867f9c2015-02-02 12:45:25 -0800587
Sam Berlin409e0f52014-05-10 10:34:16 -0400588
589 private static class CallerInspecterSubClassModule extends CallerInspecterModule {
590 String bazCallerClass;
591
592 @Override protected void configure() {}
593
594 @Provides @Singleton Double baz() {
595 this.bazCallerClass = new Exception().getStackTrace()[1].getClassName();
596 return 42D;
597 }
598 }
599 /*end[AOP]*/
Sam Berlin409e0f52014-05-10 10:34:16 -0400600
Sam Berlin76be88e2014-07-01 16:56:13 -0400601 static class SuperClassModule extends AbstractModule {
Sam Berlin409e0f52014-05-10 10:34:16 -0400602 @Override protected void configure() {}
Sam Berlin76be88e2014-07-01 16:56:13 -0400603 @Provides Number providerMethod() {
Sam Berlin409e0f52014-05-10 10:34:16 -0400604 return 1D;
605 }
Sam Berlin76be88e2014-07-01 16:56:13 -0400606 @Provides @Named("rawlist") List rawProvider(@Named("list") List<String> f) {
607 return f;
Sam Berlin409e0f52014-05-10 10:34:16 -0400608 }
609
Sam Berlin76be88e2014-07-01 16:56:13 -0400610 @Provides @Named("unrawlist") List<String> rawParameterProvider(@Named("rawlist") List f) {
611 return f;
Sam Berlin409e0f52014-05-10 10:34:16 -0400612 }
sameb9867f9c2015-02-02 12:45:25 -0800613
Sam Berlin76be88e2014-07-01 16:56:13 -0400614 @Provides @Named("list") List<String> annotatedGenericProviderMethod() {
615 return new ArrayList<String>();
Sam Berlin409e0f52014-05-10 10:34:16 -0400616 }
Sam Berlin76be88e2014-07-01 16:56:13 -0400617 @Provides @Named("collection") Collection<String> annotatedGenericParameterProviderMethod(
618 @Named("list") List<String> foo) {
619 return foo;
620 }
621 @Provides private String privateProviderMethod() {
622 return "hello";
623 }
624 }
625
626 public void testOverrideProviderMethod_overrideHasProvides() {
627 class SubClassModule extends SuperClassModule {
628 @Override @Provides Number providerMethod() {
629 return 2D;
630 }
631 }
632 try {
633 Guice.createInjector(new SubClassModule());
634 fail();
635 } catch (CreationException e) {
sameb9867f9c2015-02-02 12:45:25 -0800636 assertContains(e.getMessage(),
Sam Berlin76be88e2014-07-01 16:56:13 -0400637 "Overriding @Provides methods is not allowed.",
638 "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()",
639 "overridden by: " + SubClassModule.class.getName() + ".providerMethod()");
640 }
641 }
642
643 public void testOverrideProviderMethod_overrideHasProvides_withNewAnnotation() {
644 class SubClassModule extends SuperClassModule {
645 @Override @Provides @Named("foo") Number providerMethod() {
646 return 2D;
647 }
648 }
649 try {
650 Guice.createInjector(new SubClassModule());
651 fail();
652 } catch (CreationException e) {
sameb9867f9c2015-02-02 12:45:25 -0800653 assertContains(e.getMessage(),
Sam Berlin76be88e2014-07-01 16:56:13 -0400654 "Overriding @Provides methods is not allowed.",
655 "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()",
656 "overridden by: " + SubClassModule.class.getName() + ".providerMethod()");
657 }
658 }
659
660 public void testOverrideProviderMethod_overrideDoesntHaveProvides() {
661 class SubClassModule extends SuperClassModule {
662 @Override Number providerMethod() {
663 return 2D;
664 }
665 }
666 try {
667 Guice.createInjector(new SubClassModule());
668 fail();
669 } catch (CreationException e) {
sameb9867f9c2015-02-02 12:45:25 -0800670 assertContains(e.getMessage(),
Sam Berlin76be88e2014-07-01 16:56:13 -0400671 "Overriding @Provides methods is not allowed.",
672 "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()",
673 "overridden by: " + SubClassModule.class.getName() + ".providerMethod()");
674 }
675 }
676 public void testOverrideProviderMethod_overrideDoesntHaveProvides_withNewAnnotation() {
677 class SubClassModule extends SuperClassModule {
678 @Override @Named("foo") Number providerMethod() {
679 return 2D;
680 }
681 }
682 try {
683 Guice.createInjector(new SubClassModule());
684 fail();
685 } catch (CreationException e) {
sameb9867f9c2015-02-02 12:45:25 -0800686 assertContains(e.getMessage(),
Sam Berlin76be88e2014-07-01 16:56:13 -0400687 "Overriding @Provides methods is not allowed.",
688 "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()",
689 "overridden by: " + SubClassModule.class.getName() + ".providerMethod()");
690 }
691 }
sameb9867f9c2015-02-02 12:45:25 -0800692
Sam Berlin76be88e2014-07-01 16:56:13 -0400693
694 public void testOverrideProviderMethod_covariantOverrideDoesntHaveProvides() {
695 class SubClassModule extends SuperClassModule {
696 @Override Double providerMethod() {
697 return 2D;
698 }
699 }
700 try {
701 Guice.createInjector(new SubClassModule());
702 fail();
703 } catch (CreationException e) {
sameb9867f9c2015-02-02 12:45:25 -0800704 assertContains(e.getMessage(),
Sam Berlin76be88e2014-07-01 16:56:13 -0400705 "Overriding @Provides methods is not allowed.",
706 "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()",
707 "overridden by: " + SubClassModule.class.getName() + ".providerMethod()");
708 }
709 }
710
711 public void testOverrideProviderMethod_covariantOverrideHasProvides() {
712 class SubClassModule extends SuperClassModule {
713 @Override @Provides Double providerMethod() {
714 return 2D;
715 }
716 }
717 try {
718 Guice.createInjector(new SubClassModule());
719 fail();
720 } catch (CreationException e) {
sameb9867f9c2015-02-02 12:45:25 -0800721 assertContains(e.getMessage(),
Sam Berlin76be88e2014-07-01 16:56:13 -0400722 "Overriding @Provides methods is not allowed.",
723 "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()",
724 "overridden by: " + SubClassModule.class.getName() + ".providerMethod()");
725 }
726 }
727
728 public void testOverrideProviderMethod_fakeOverridePrivateMethod() {
729 class SubClassModule extends SuperClassModule {
730 // not actually an override, just looks like it
731 String privateProviderMethod() {
732 return "sub";
733 }
734 }
735 assertEquals("hello", Guice.createInjector(new SubClassModule()).getInstance(String.class));
736 }
737
738 public void testOverrideProviderMethod_subclassRawTypes_returnType() {
739 class SubClassModule extends SuperClassModule {
740 @Override List annotatedGenericProviderMethod() {
741 return super.annotatedGenericProviderMethod();
742 }
743 }
744 try {
745 Guice.createInjector(new SubClassModule());
746 fail();
747 } catch (CreationException e) {
748 assertContains(e.getMessage(),
749 "Overriding @Provides methods is not allowed.",
750 "@Provides method: " + SuperClassModule.class.getName()
751 + ".annotatedGenericProviderMethod()",
752 "overridden by: " + SubClassModule.class.getName() + ".annotatedGenericProviderMethod()");
753 }
754 }
755
756 public void testOverrideProviderMethod_subclassRawTypes_parameterType() {
757 class SubClassModule extends SuperClassModule {
758 @Override Collection<String> annotatedGenericParameterProviderMethod(List foo) {
759 return super.annotatedGenericParameterProviderMethod(foo);
760 }
761 }
762 try {
763 Guice.createInjector(new SubClassModule());
764 fail();
765 } catch (CreationException e) {
sameb9867f9c2015-02-02 12:45:25 -0800766 assertContains(e.getMessage(),
Sam Berlin76be88e2014-07-01 16:56:13 -0400767 "Overriding @Provides methods is not allowed.",
sameb9867f9c2015-02-02 12:45:25 -0800768 "@Provides method: " + SuperClassModule.class.getName()
Sam Berlin76be88e2014-07-01 16:56:13 -0400769 + ".annotatedGenericParameterProviderMethod()",
sameb9867f9c2015-02-02 12:45:25 -0800770 "overridden by: " + SubClassModule.class.getName()
Sam Berlin76be88e2014-07-01 16:56:13 -0400771 + ".annotatedGenericParameterProviderMethod()");
772 }
773 }
774
775 public void testOverrideProviderMethod_superclassRawTypes_returnType() {
776 class SubClassModule extends SuperClassModule {
777 // remove the rawtype from the override
778 @Override List<String> rawProvider(List<String> f) {
779 return f;
780 }
781 }
782 try {
783 Guice.createInjector(new SubClassModule());
784 fail();
785 } catch (CreationException e) {
sameb9867f9c2015-02-02 12:45:25 -0800786 assertContains(e.getMessage(),
Sam Berlin76be88e2014-07-01 16:56:13 -0400787 "Overriding @Provides methods is not allowed.",
788 "@Provides method: " + SuperClassModule.class.getName() + ".rawProvider()",
789 "overridden by: " + SubClassModule.class.getName() + ".rawProvider()");
790 }
791 }
792
793 abstract static class GenericSuperModule<T> extends AbstractModule {
794 @Provides String provide(T thing) {
795 return thing.toString();
796 }
797 }
798
799 // This is a tricky case where signatures don't match, but it is an override (facilitated via a
800 // bridge method)
801 public void testOverrideProviderMethod_erasureBasedOverrides() {
802 class SubClassModule extends GenericSuperModule<Integer> {
803 @Override String provide(Integer thing) {
804 return thing.toString();
805 }
806
807 @Override protected void configure() {
808 bind(Integer.class).toInstance(3);
809 }
810 }
811 try {
812 Guice.createInjector(new SubClassModule());
813 fail();
814 } catch (CreationException e) {
815 assertContains(e.getMessage(),
816 "Overriding @Provides methods is not allowed.",
817 "@Provides method: " + GenericSuperModule.class.getName() + ".provide()",
818 "overridden by: " + SubClassModule.class.getName() + ".provide()");
819 }
820 }
821
822 class RestrictedSuper extends AbstractModule {
823 @Provides public String provideFoo() { return "foo"; }
824 @Override protected void configure() {}
825 }
826
827 public class ExposedSub extends RestrictedSuper {}
828
829 public void testOverrideProviderMethod_increasedVisibility() {
830 // ensure we don't detect the synthetic provideFoo method in ExposedSub as an override (it is,
831 // but since it is synthetic it would be annoying to throw an error on it).
832 assertEquals("foo", Guice.createInjector(new ExposedSub()).getInstance(String.class));
Sam Berlin409e0f52014-05-10 10:34:16 -0400833 }
Sam Berlin53a59362014-05-23 18:19:12 -0400834
835 interface ProviderInterface<T> {
836 T getT();
837 }
838
839 static class ModuleImpl extends AbstractModule implements ProviderInterface<String> {
840 @Override protected void configure() {}
841 @Provides public String getT() {
842 return "string";
843 }
844 @Provides public Object getObject() {
845 return new Object();
846 }
847 /* javac will synthesize a bridge method for getT with the types erased, equivalent to:
848 * @Provides public Object getT() { ... }
849 */
850 }
851
852 public void testIgnoreSyntheticBridgeMethods() {
853 Guice.createInjector(new ModuleImpl());
854 }
sameb9867f9c2015-02-02 12:45:25 -0800855
856 public void testNullability() throws Exception {
857 Module module = new AbstractModule() {
858 @Override
859 protected void configure() {
860 bind(String.class).toProvider(Providers.<String>of(null));
861 }
862
863 @SuppressWarnings("unused")
864 @Provides
865 Integer fail(String foo) {
866 return 1;
867 }
868
869 @SuppressWarnings("unused")
870 @Provides
871 Long succeed(@Nullable String foo) {
872 return 2L;
873 }
874 };
875 Injector injector = Guice.createInjector(module);
876 InjectionPoint fooPoint = InjectionPoint.forMethod(
877 module.getClass().getDeclaredMethod("fail", String.class),
878 TypeLiteral.get(module.getClass()));
879 Dependency<?> fooDependency = Iterables.getOnlyElement(fooPoint.getDependencies());
880
881 runNullableTest(injector, fooDependency, module);
882
883 injector.getInstance(Long.class);
884 }
885
886 private void runNullableTest(Injector injector, Dependency<?> dependency, Module module) {
887 switch (InternalFlags.getNullableProvidesOption()) {
888 case ERROR:
889 validateNullableFails(injector, module);
890 break;
891 case IGNORE:
892 validateNullableIgnored(injector);
893 break;
894 case WARN:
895 validateNullableWarns(injector, dependency);
896 break;
897 }
898 }
899
900 private void validateNullableFails(Injector injector, Module module) {
901 try {
902 injector.getInstance(Integer.class);
903 fail();
904 } catch (ProvisionException expected) {
905 assertContains(expected.getMessage(),
906 "1) null returned by binding at " + module.getClass().getName() + ".configure(",
907 "but parameter 0 of " + module.getClass().getName() + ".fail() is not @Nullable",
908 "while locating java.lang.String",
909 "for parameter 0 at " + module.getClass().getName() + ".fail(",
910 "while locating java.lang.Integer");
911
912 assertEquals(1, expected.getErrorMessages().size());
913 }
914 }
915
916 private void validateNullableIgnored(Injector injector) {
917 injector.getInstance(Integer.class); // no exception
918 }
919
920 private void validateNullableWarns(Injector injector, Dependency<?> dependency) {
921 final List<LogRecord> logRecords = Lists.newArrayList();
922 final Handler fakeHandler = new Handler() {
923 @Override
924 public void publish(LogRecord logRecord) {
925 logRecords.add(logRecord);
926 }
927 @Override
928 public void flush() {}
929 @Override
930 public void close() throws SecurityException {}
931 };
932 Logger.getLogger(Guice.class.getName()).addHandler(fakeHandler);
933 try {
934 injector.getInstance(Integer.class); // no exception, but assert it does log.
935 LogRecord record = Iterables.getOnlyElement(logRecords);
936 assertEquals(
937 "Guice injected null into parameter {0} of {1} (a {2}), please mark it @Nullable."
938 + " Use -Dguice_check_nullable_provides_params=ERROR to turn this into an"
939 + " error.",
940 record.getMessage());
941 assertEquals(dependency.getParameterIndex(), record.getParameters()[0]);
942 assertEquals(Errors.convert(dependency.getInjectionPoint().getMember()),
943 record.getParameters()[1]);
944 assertEquals(Errors.convert(dependency.getKey()), record.getParameters()[2]);
945 } finally {
946 Logger.getLogger(Guice.class.getName()).removeHandler(fakeHandler);
947 }
948 }
949
950 @Retention(RetentionPolicy.RUNTIME)
951 @interface Nullable {}
Sam Berlin8ad60eb2014-03-10 12:49:46 -0400952}