1/**
2 * Copyright (C) 2007 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.inject.spi;
18
19import static com.google.inject.Asserts.assertContains;
20import static java.lang.annotation.RetentionPolicy.RUNTIME;
21
22import com.google.common.collect.ImmutableList;
23import com.google.common.collect.ImmutableSet;
24import com.google.common.collect.Iterables;
25import com.google.common.collect.Lists;
26import com.google.inject.AbstractModule;
27import com.google.inject.Binder;
28import com.google.inject.Binding;
29import 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;
37import com.google.inject.ProvisionException;
38import com.google.inject.Singleton;
39import com.google.inject.Stage;
40import com.google.inject.TypeLiteral;
41import com.google.inject.internal.Errors;
42import com.google.inject.internal.InternalFlags;
43import com.google.inject.internal.ProviderMethod;
44import com.google.inject.internal.ProviderMethodsModule;
45import com.google.inject.name.Named;
46import com.google.inject.name.Names;
47import com.google.inject.util.Providers;
48import com.google.inject.util.Types;
49
50import junit.framework.TestCase;
51
52import java.lang.annotation.ElementType;
53import java.lang.annotation.Retention;
54import java.lang.annotation.RetentionPolicy;
55import java.lang.annotation.Target;
56import java.lang.reflect.Method;
57import java.util.ArrayList;
58import java.util.Collection;
59import java.util.List;
60import java.util.Set;
61import java.util.concurrent.atomic.AtomicReference;
62import java.util.logging.Handler;
63import java.util.logging.LogRecord;
64import java.util.logging.Logger;
65
66/**
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() {
203        @Override protected void configure() {}
204
205        @Provides @Named("A") @Blue
206        public String provideString() {
207          return "a";
208        }
209      });
210      fail();
211    } catch (CreationException expected) {
212      assertContains(expected.getMessage(),
213          "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) {});
225
226    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
246    @Override protected void configure() {}
247
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  }
260
261  public void testAutomaticProviderMethods() {
262    Injector injector = Guice.createInjector((Module) new AbstractModule() {
263      @Override protected void configure() { }
264      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() {
283      @Override protected void configure() {
284        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  }
295
296  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() {
301      @Override protected void configure() {
302        @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  }
325
326  public void testProviderMethodDependenciesAreExposed() throws Exception {
327    Module module = new AbstractModule() {
328      @Override protected void configure() {
329        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      }
335    };
336    Injector injector = Guice.createInjector(module);
337
338    ProviderInstanceBinding<?> binding = (ProviderInstanceBinding<?>) injector.getBinding(
339        Key.get(String.class, Names.named("weight")));
340    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());
347  }
348
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;
376    javax.inject.Provider provider = binding.getUserSuppliedProvider();
377    assertTrue(provider instanceof ProviderMethod);
378    assertEquals(methodsObject, ((ProviderMethod) provider).getInstance());
379    assertSame(provider, binding.getProviderInstance());
380  }
381
382  public void testVoidProviderMethods() {
383    try {
384      Guice.createInjector(new AbstractModule() {
385        @Override protected void configure() {}
386
387        @Provides void provideFoo() {}
388      });
389      fail();
390    } catch (CreationException expected) {
391      assertContains(expected.getMessage(),
392          "1) Provider methods must return a value. Do not return void.",
393          getClass().getName(), ".provideFoo(ProviderMethodsTest.java:");
394    }
395  }
396
397  public void testInjectsJustOneLogger() {
398    AtomicReference<Logger> loggerRef = new AtomicReference<Logger>();
399    Injector injector = Guice.createInjector(new FooModule(loggerRef));
400
401    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());
407
408    assertEquals(FooModule.class.getName(), lastLogger.getName());
409  }
410
411  private static class FooModule extends AbstractModule {
412    private final AtomicReference<Logger> loggerRef;
413
414    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  }
426
427  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);
437
438    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());
448
449    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());
458
459  }
460
461  private static class BindingCapturer<T> extends DefaultBindingTargetVisitor<T, ProvidesMethodBinding<T>>
462      implements ProvidesMethodTargetVisitor<T, ProvidesMethodBinding<T>> {
463
464    @SuppressWarnings("unchecked")
465    public ProvidesMethodBinding<T> visit(
466        ProvidesMethodBinding<? extends T> providesMethodBinding) {
467      return (ProvidesMethodBinding<T>)providesMethodBinding;
468    }
469
470    @Override protected ProvidesMethodBinding<T> visitOther(Binding<? extends T> binding) {
471      throw new IllegalStateException("unexpected visit of: " + binding);
472    }
473  }
474
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
514      assertContains(expected.getMessage(),
515          "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);
580    assertEquals("Expected provider methods in the same class to share fastclass classes",
581        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  }
587
588
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]*/
600
601  static class SuperClassModule extends AbstractModule {
602    @Override protected void configure() {}
603    @Provides Number providerMethod() {
604      return 1D;
605    }
606    @Provides @Named("rawlist") List rawProvider(@Named("list") List<String> f) {
607      return f;
608    }
609
610    @Provides @Named("unrawlist") List<String> rawParameterProvider(@Named("rawlist") List f) {
611      return f;
612    }
613
614    @Provides @Named("list") List<String> annotatedGenericProviderMethod() {
615      return new ArrayList<String>();
616    }
617    @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) {
636      assertContains(e.getMessage(),
637          "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) {
653      assertContains(e.getMessage(),
654          "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) {
670      assertContains(e.getMessage(),
671          "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) {
686      assertContains(e.getMessage(),
687          "Overriding @Provides methods is not allowed.",
688          "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()",
689          "overridden by: " + SubClassModule.class.getName() + ".providerMethod()");
690    }
691  }
692
693
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) {
704      assertContains(e.getMessage(),
705          "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) {
721      assertContains(e.getMessage(),
722          "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) {
766      assertContains(e.getMessage(),
767          "Overriding @Provides methods is not allowed.",
768          "@Provides method: " + SuperClassModule.class.getName()
769              + ".annotatedGenericParameterProviderMethod()",
770          "overridden by: " + SubClassModule.class.getName()
771              + ".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) {
786      assertContains(e.getMessage(),
787          "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));
833  }
834
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  }
855
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 {}
952}
953