/core/test/com/google/inject/spi/ProviderMethodsTest.java
Java | 952 lines | 737 code | 128 blank | 87 comment | 1 complexity | 21f1746f856fce1c7a0a21f40a8df968 MD5 | raw file
- /**
- * Copyright (C) 2007 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package com.google.inject.spi;
- import static com.google.inject.Asserts.assertContains;
- import static java.lang.annotation.RetentionPolicy.RUNTIME;
- import com.google.common.collect.ImmutableList;
- import com.google.common.collect.ImmutableSet;
- import com.google.common.collect.Iterables;
- import com.google.common.collect.Lists;
- import com.google.inject.AbstractModule;
- import com.google.inject.Binder;
- import com.google.inject.Binding;
- import com.google.inject.BindingAnnotation;
- import com.google.inject.CreationException;
- import com.google.inject.Guice;
- import com.google.inject.Inject;
- import com.google.inject.Injector;
- import com.google.inject.Key;
- import com.google.inject.Module;
- import com.google.inject.Provides;
- import com.google.inject.ProvisionException;
- import com.google.inject.Singleton;
- import com.google.inject.Stage;
- import com.google.inject.TypeLiteral;
- import com.google.inject.internal.Errors;
- import com.google.inject.internal.InternalFlags;
- import com.google.inject.internal.ProviderMethod;
- import com.google.inject.internal.ProviderMethodsModule;
- import com.google.inject.name.Named;
- import com.google.inject.name.Names;
- import com.google.inject.util.Providers;
- import com.google.inject.util.Types;
- import junit.framework.TestCase;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- import java.lang.reflect.Method;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.List;
- import java.util.Set;
- import java.util.concurrent.atomic.AtomicReference;
- import java.util.logging.Handler;
- import java.util.logging.LogRecord;
- import java.util.logging.Logger;
- /**
- * @author crazybob@google.com (Bob Lee)
- */
- public class ProviderMethodsTest extends TestCase implements Module {
- @SuppressWarnings("unchecked")
- public void testProviderMethods() {
- Injector injector = Guice.createInjector(this);
- Bob bob = injector.getInstance(Bob.class);
- assertEquals("A Bob", bob.getName());
- Bob clone = injector.getInstance(Bob.class);
- assertEquals("A Bob", clone.getName());
- assertNotSame(bob, clone);
- assertSame(bob.getDaughter(), clone.getDaughter());
- Key soleBobKey = Key.get(Bob.class, Sole.class);
- assertSame(
- injector.getInstance(soleBobKey),
- injector.getInstance(soleBobKey)
- );
- }
- public void configure(Binder binder) {}
- interface Bob {
- String getName();
- Dagny getDaughter();
- }
- interface Dagny {
- int getAge();
- }
- @Provides
- Bob provideBob(final Dagny dagny) {
- return new Bob() {
- public String getName() {
- return "A Bob";
- }
- public Dagny getDaughter() {
- return dagny;
- }
- };
- }
- @Provides
- @Singleton
- @Sole
- Bob provideSoleBob(final Dagny dagny) {
- return new Bob() {
- public String getName() {
- return "Only Bob";
- }
- public Dagny getDaughter() {
- return dagny;
- }
- };
- }
- @Provides
- @Singleton
- Dagny provideDagny() {
- return new Dagny() {
- public int getAge() {
- return 1;
- }
- };
- }
- @Retention(RUNTIME)
- @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
- @BindingAnnotation
- @interface Sole {}
- // We'll have to make getProvider() support circular dependencies before this
- // will work.
- //
- // public void testCircularDependency() {
- // Injector injector = Guice.createInjector(new Module() {
- // public void configure(Binder binder) {
- // binder.install(ProviderMethods.from(ProviderMethodsTest.this));
- // }
- // });
- //
- // Foo foo = injector.getInstance(Foo.class);
- // assertEquals(5, foo.getI());
- // assertEquals(10, foo.getBar().getI());
- // assertEquals(5, foo.getBar().getFoo().getI());
- // }
- //
- // interface Foo {
- // Bar getBar();
- // int getI();
- // }
- //
- // interface Bar {
- // Foo getFoo();
- // int getI();
- // }
- //
- // @Provides Foo newFoo(final Bar bar) {
- // return new Foo() {
- //
- // public Bar getBar() {
- // return bar;
- // }
- //
- // public int getI() {
- // return 5;
- // }
- // };
- // }
- //
- // @Provides Bar newBar(final Foo foo) {
- // return new Bar() {
- //
- // public Foo getFoo() {
- // return foo;
- // }
- //
- // public int getI() {
- // return 10;
- // }
- // };
- // }
- public void testMultipleBindingAnnotations() {
- try {
- Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {}
- @Provides @Named("A") @Blue
- public String provideString() {
- return "a";
- }
- });
- fail();
- } catch (CreationException expected) {
- assertContains(expected.getMessage(),
- "more than one annotation annotated with @BindingAnnotation:", "Named", "Blue",
- "at " + getClass().getName(), ".provideString(ProviderMethodsTest.java:");
- }
- }
- @Retention(RUNTIME)
- @BindingAnnotation @interface Blue {}
- public void testGenericProviderMethods() {
- Injector injector = Guice.createInjector(
- new ProvideTs<String>("A", "B") {}, new ProvideTs<Integer>(1, 2) {});
- assertEquals("A", injector.getInstance(Key.get(String.class, Names.named("First"))));
- assertEquals("B", injector.getInstance(Key.get(String.class, Names.named("Second"))));
- assertEquals(ImmutableSet.of("A", "B"),
- injector.getInstance(Key.get(Types.setOf(String.class))));
- assertEquals(1, injector.getInstance(Key.get(Integer.class, Names.named("First"))).intValue());
- assertEquals(2, injector.getInstance(Key.get(Integer.class, Names.named("Second"))).intValue());
- assertEquals(ImmutableSet.of(1, 2),
- injector.getInstance(Key.get(Types.setOf(Integer.class))));
- }
- abstract class ProvideTs<T> extends AbstractModule {
- final T first;
- final T second;
- protected ProvideTs(T first, T second) {
- this.first = first;
- this.second = second;
- }
- @Override protected void configure() {}
- @Named("First") @Provides T provideFirst() {
- return first;
- }
- @Named("Second") @Provides T provideSecond() {
- return second;
- }
- @Provides Set<T> provideBoth(@Named("First") T first, @Named("Second") T second) {
- return ImmutableSet.of(first, second);
- }
- }
- public void testAutomaticProviderMethods() {
- Injector injector = Guice.createInjector((Module) new AbstractModule() {
- @Override protected void configure() { }
- private int next = 1;
- @Provides @Named("count")
- public Integer provideCount() {
- return next++;
- }
- });
- assertEquals(1, injector.getInstance(Key.get(Integer.class, Names.named("count"))).intValue());
- assertEquals(2, injector.getInstance(Key.get(Integer.class, Names.named("count"))).intValue());
- assertEquals(3, injector.getInstance(Key.get(Integer.class, Names.named("count"))).intValue());
- }
- /**
- * If the user installs provider methods for the module manually, that shouldn't cause a double
- * binding of the provider methods' types.
- */
- public void testAutomaticProviderMethodsDoNotCauseDoubleBinding() {
- Module installsSelf = new AbstractModule() {
- @Override protected void configure() {
- install(this);
- bind(Integer.class).toInstance(5);
- }
- @Provides public String provideString(Integer count) {
- return "A" + count;
- }
- };
- Injector injector = Guice.createInjector(installsSelf);
- assertEquals("A5", injector.getInstance(String.class));
- }
- public void testWildcardProviderMethods() {
- final List<String> strings = ImmutableList.of("A", "B", "C");
- final List<Number> numbers = ImmutableList.<Number>of(1, 2, 3);
- Injector injector = Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- @SuppressWarnings("unchecked")
- Key<List<? super Integer>> listOfSupertypesOfInteger = (Key<List<? super Integer>>)
- Key.get(Types.listOf(Types.supertypeOf(Integer.class)));
- bind(listOfSupertypesOfInteger).toInstance(numbers);
- }
- @Provides public List<? extends CharSequence> provideCharSequences() {
- return strings;
- }
- @Provides public Class<?> provideType() {
- return Float.class;
- }
- });
- assertSame(strings, injector.getInstance(HasWildcardInjection.class).charSequences);
- assertSame(numbers, injector.getInstance(HasWildcardInjection.class).numbers);
- assertSame(Float.class, injector.getInstance(HasWildcardInjection.class).type);
- }
- static class HasWildcardInjection {
- @Inject List<? extends CharSequence> charSequences;
- @Inject List<? super Integer> numbers;
- @Inject Class<?> type;
- }
- public void testProviderMethodDependenciesAreExposed() throws Exception {
- Module module = new AbstractModule() {
- @Override protected void configure() {
- bind(Integer.class).toInstance(50);
- bindConstant().annotatedWith(Names.named("units")).to("Kg");
- }
- @Provides @Named("weight") String provideWeight(Integer count, @Named("units") String units) {
- return count + units;
- }
- };
- Injector injector = Guice.createInjector(module);
- ProviderInstanceBinding<?> binding = (ProviderInstanceBinding<?>) injector.getBinding(
- Key.get(String.class, Names.named("weight")));
- Method method =
- module.getClass().getDeclaredMethod("provideWeight", Integer.class, String.class);
- InjectionPoint point = new InjectionPoint(TypeLiteral.get(module.getClass()), method, false);
- assertEquals(ImmutableSet.<Dependency<?>>of(
- new Dependency<Integer>(point, Key.get(Integer.class), false, 0),
- new Dependency<String>(point, Key.get(String.class, Names.named("units")), false, 1)),
- binding.getDependencies());
- }
- public void testNonModuleProviderMethods() {
- final Object methodsObject = new Object() {
- @Provides @Named("foo") String provideFoo() {
- return "foo-value";
- }
- };
- Module module = new AbstractModule() {
- @Override protected void configure() {
- install(ProviderMethodsModule.forObject(methodsObject));
- }
- };
- Injector injector = Guice.createInjector(module);
- Key<String> key = Key.get(String.class, Names.named("foo"));
- assertEquals("foo-value", injector.getInstance(key));
- // Test the provider method object itself. This makes sure getInstance works, since GIN uses it
- List<Element> elements = Elements.getElements(module);
- assertEquals(1, elements.size());
- Element element = elements.get(0);
- assertTrue(element + " instanceof ProviderInstanceBinding",
- element instanceof ProviderInstanceBinding);
- ProviderInstanceBinding binding = (ProviderInstanceBinding) element;
- javax.inject.Provider provider = binding.getUserSuppliedProvider();
- assertTrue(provider instanceof ProviderMethod);
- assertEquals(methodsObject, ((ProviderMethod) provider).getInstance());
- assertSame(provider, binding.getProviderInstance());
- }
- public void testVoidProviderMethods() {
- try {
- Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {}
- @Provides void provideFoo() {}
- });
- fail();
- } catch (CreationException expected) {
- assertContains(expected.getMessage(),
- "1) Provider methods must return a value. Do not return void.",
- getClass().getName(), ".provideFoo(ProviderMethodsTest.java:");
- }
- }
- public void testInjectsJustOneLogger() {
- AtomicReference<Logger> loggerRef = new AtomicReference<Logger>();
- Injector injector = Guice.createInjector(new FooModule(loggerRef));
- assertNull(loggerRef.get());
- injector.getInstance(Integer.class);
- Logger lastLogger = loggerRef.getAndSet(null);
- assertNotNull(lastLogger);
- injector.getInstance(Integer.class);
- assertSame(lastLogger, loggerRef.get());
- assertEquals(FooModule.class.getName(), lastLogger.getName());
- }
- private static class FooModule extends AbstractModule {
- private final AtomicReference<Logger> loggerRef;
- public FooModule(AtomicReference<Logger> loggerRef) {
- this.loggerRef = loggerRef;
- }
- @Override protected void configure() {}
- @SuppressWarnings("unused")
- @Provides Integer foo(Logger logger) {
- loggerRef.set(logger);
- return 42;
- }
- }
- public void testSpi() throws Exception {
- Module m1 = new AbstractModule() {
- @Override protected void configure() {}
- @Provides @Named("foo") String provideFoo(Integer dep) { return "foo"; }
- };
- Module m2 = new AbstractModule() {
- @Override protected void configure() {}
- @Provides Integer provideInt(@Named("foo") String dep) { return 42; }
- };
- Injector injector = Guice.createInjector(m1, m2);
- Binding<String> stringBinding =
- injector.getBinding(Key.get(String.class, Names.named("foo")));
- ProvidesMethodBinding<String> stringMethod =
- stringBinding.acceptTargetVisitor(new BindingCapturer<String>());
- assertEquals(m1, stringMethod.getEnclosingInstance());
- assertEquals(m1.getClass().getDeclaredMethod("provideFoo", Integer.class),
- stringMethod.getMethod());
- assertEquals(((HasDependencies) stringBinding).getDependencies(),
- stringMethod.getDependencies());
- assertEquals(Key.get(String.class, Names.named("foo")), stringMethod.getKey());
- Binding<Integer> intBinding = injector.getBinding(Integer.class);
- ProvidesMethodBinding<Integer> intMethod =
- intBinding.acceptTargetVisitor(new BindingCapturer<Integer>());
- assertEquals(m2, intMethod.getEnclosingInstance());
- assertEquals(m2.getClass().getDeclaredMethod("provideInt", String.class),
- intMethod.getMethod());
- assertEquals(((HasDependencies) intBinding).getDependencies(),
- intMethod.getDependencies());
- assertEquals(Key.get(Integer.class), intMethod.getKey());
- }
- private static class BindingCapturer<T> extends DefaultBindingTargetVisitor<T, ProvidesMethodBinding<T>>
- implements ProvidesMethodTargetVisitor<T, ProvidesMethodBinding<T>> {
- @SuppressWarnings("unchecked")
- public ProvidesMethodBinding<T> visit(
- ProvidesMethodBinding<? extends T> providesMethodBinding) {
- return (ProvidesMethodBinding<T>)providesMethodBinding;
- }
- @Override protected ProvidesMethodBinding<T> visitOther(Binding<? extends T> binding) {
- throw new IllegalStateException("unexpected visit of: " + binding);
- }
- }
- public void testProvidesMethodVisibility() {
- Injector injector = Guice.createInjector(new VisibilityModule());
- assertEquals(42, injector.getInstance(Integer.class).intValue());
- assertEquals(42L, injector.getInstance(Long.class).longValue());
- assertEquals(42D, injector.getInstance(Double.class).doubleValue());
- assertEquals(42F, injector.getInstance(Float.class).floatValue());
- }
- private static class VisibilityModule extends AbstractModule {
- @Override protected void configure() {}
- @SuppressWarnings("unused")
- @Provides Integer foo() {
- return 42;
- }
- @SuppressWarnings("unused")
- @Provides private Long bar() {
- return 42L;
- }
- @SuppressWarnings("unused")
- @Provides protected Double baz() {
- return 42D;
- }
- @SuppressWarnings("unused")
- @Provides public Float quux() {
- return 42F;
- }
- }
- public void testProvidesMethodInheritenceHierarchy() {
- try {
- Guice.createInjector(new Sub1Module(), new Sub2Module());
- fail("Expected injector creation failure");
- } catch (CreationException expected) {
- // both of our super class bindings cause errors
- assertContains(expected.getMessage(),
- "A binding to java.lang.Long was already configured",
- "A binding to java.lang.Integer was already configured");
- }
- }
- public void testProvidesMethodsDefinedInSuperClass() {
- Injector injector = Guice.createInjector(new Sub1Module());
- assertEquals(42, injector.getInstance(Integer.class).intValue());
- assertEquals(42L, injector.getInstance(Long.class).longValue());
- assertEquals(42D, injector.getInstance(Double.class).doubleValue());
- }
- private static class BaseModule extends AbstractModule {
- @Override protected void configure() {}
- @Provides Integer foo() {
- return 42;
- }
- @Provides Long bar() {
- return 42L;
- }
- }
- private static class Sub1Module extends BaseModule {
- @Provides Double baz() {
- return 42D;
- }
- }
- private static class Sub2Module extends BaseModule {
- @Provides Float quux() {
- return 42F;
- }
- }
- /*if[AOP]*/
- public void testShareFastClass() {
- CallerInspecterModule module = new CallerInspecterModule();
- Guice.createInjector(Stage.PRODUCTION, module);
- assertEquals(module.fooCallerClass, module.barCallerClass);
- assertTrue(module.fooCallerClass.contains("$$FastClassByGuice$$"));
- }
- private static class CallerInspecterModule extends AbstractModule {
- // start them off as unequal
- String barCallerClass = "not_set_bar";
- String fooCallerClass = "not_set_foo";
- @Override protected void configure() {}
- @Provides @Singleton Integer foo() {
- this.fooCallerClass = new Exception().getStackTrace()[1].getClassName();
- return 42;
- }
- @Provides @Singleton Long bar() {
- this.barCallerClass = new Exception().getStackTrace()[1].getClassName();
- return 42L;
- }
- }
- public void testShareFastClassWithSuperClass() {
- CallerInspecterSubClassModule module = new CallerInspecterSubClassModule();
- Guice.createInjector(Stage.PRODUCTION, module);
- assertEquals("Expected provider methods in the same class to share fastclass classes",
- module.fooCallerClass, module.barCallerClass);
- assertFalse(
- "Did not expect provider methods in the subclasses to share fastclass classes "
- + "with their parent classes",
- module.bazCallerClass.equals(module.barCallerClass));
- }
- private static class CallerInspecterSubClassModule extends CallerInspecterModule {
- String bazCallerClass;
- @Override protected void configure() {}
- @Provides @Singleton Double baz() {
- this.bazCallerClass = new Exception().getStackTrace()[1].getClassName();
- return 42D;
- }
- }
- /*end[AOP]*/
- static class SuperClassModule extends AbstractModule {
- @Override protected void configure() {}
- @Provides Number providerMethod() {
- return 1D;
- }
- @Provides @Named("rawlist") List rawProvider(@Named("list") List<String> f) {
- return f;
- }
- @Provides @Named("unrawlist") List<String> rawParameterProvider(@Named("rawlist") List f) {
- return f;
- }
- @Provides @Named("list") List<String> annotatedGenericProviderMethod() {
- return new ArrayList<String>();
- }
- @Provides @Named("collection") Collection<String> annotatedGenericParameterProviderMethod(
- @Named("list") List<String> foo) {
- return foo;
- }
- @Provides private String privateProviderMethod() {
- return "hello";
- }
- }
- public void testOverrideProviderMethod_overrideHasProvides() {
- class SubClassModule extends SuperClassModule {
- @Override @Provides Number providerMethod() {
- return 2D;
- }
- }
- try {
- Guice.createInjector(new SubClassModule());
- fail();
- } catch (CreationException e) {
- assertContains(e.getMessage(),
- "Overriding @Provides methods is not allowed.",
- "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()",
- "overridden by: " + SubClassModule.class.getName() + ".providerMethod()");
- }
- }
- public void testOverrideProviderMethod_overrideHasProvides_withNewAnnotation() {
- class SubClassModule extends SuperClassModule {
- @Override @Provides @Named("foo") Number providerMethod() {
- return 2D;
- }
- }
- try {
- Guice.createInjector(new SubClassModule());
- fail();
- } catch (CreationException e) {
- assertContains(e.getMessage(),
- "Overriding @Provides methods is not allowed.",
- "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()",
- "overridden by: " + SubClassModule.class.getName() + ".providerMethod()");
- }
- }
- public void testOverrideProviderMethod_overrideDoesntHaveProvides() {
- class SubClassModule extends SuperClassModule {
- @Override Number providerMethod() {
- return 2D;
- }
- }
- try {
- Guice.createInjector(new SubClassModule());
- fail();
- } catch (CreationException e) {
- assertContains(e.getMessage(),
- "Overriding @Provides methods is not allowed.",
- "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()",
- "overridden by: " + SubClassModule.class.getName() + ".providerMethod()");
- }
- }
- public void testOverrideProviderMethod_overrideDoesntHaveProvides_withNewAnnotation() {
- class SubClassModule extends SuperClassModule {
- @Override @Named("foo") Number providerMethod() {
- return 2D;
- }
- }
- try {
- Guice.createInjector(new SubClassModule());
- fail();
- } catch (CreationException e) {
- assertContains(e.getMessage(),
- "Overriding @Provides methods is not allowed.",
- "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()",
- "overridden by: " + SubClassModule.class.getName() + ".providerMethod()");
- }
- }
- public void testOverrideProviderMethod_covariantOverrideDoesntHaveProvides() {
- class SubClassModule extends SuperClassModule {
- @Override Double providerMethod() {
- return 2D;
- }
- }
- try {
- Guice.createInjector(new SubClassModule());
- fail();
- } catch (CreationException e) {
- assertContains(e.getMessage(),
- "Overriding @Provides methods is not allowed.",
- "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()",
- "overridden by: " + SubClassModule.class.getName() + ".providerMethod()");
- }
- }
- public void testOverrideProviderMethod_covariantOverrideHasProvides() {
- class SubClassModule extends SuperClassModule {
- @Override @Provides Double providerMethod() {
- return 2D;
- }
- }
- try {
- Guice.createInjector(new SubClassModule());
- fail();
- } catch (CreationException e) {
- assertContains(e.getMessage(),
- "Overriding @Provides methods is not allowed.",
- "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()",
- "overridden by: " + SubClassModule.class.getName() + ".providerMethod()");
- }
- }
- public void testOverrideProviderMethod_fakeOverridePrivateMethod() {
- class SubClassModule extends SuperClassModule {
- // not actually an override, just looks like it
- String privateProviderMethod() {
- return "sub";
- }
- }
- assertEquals("hello", Guice.createInjector(new SubClassModule()).getInstance(String.class));
- }
- public void testOverrideProviderMethod_subclassRawTypes_returnType() {
- class SubClassModule extends SuperClassModule {
- @Override List annotatedGenericProviderMethod() {
- return super.annotatedGenericProviderMethod();
- }
- }
- try {
- Guice.createInjector(new SubClassModule());
- fail();
- } catch (CreationException e) {
- assertContains(e.getMessage(),
- "Overriding @Provides methods is not allowed.",
- "@Provides method: " + SuperClassModule.class.getName()
- + ".annotatedGenericProviderMethod()",
- "overridden by: " + SubClassModule.class.getName() + ".annotatedGenericProviderMethod()");
- }
- }
- public void testOverrideProviderMethod_subclassRawTypes_parameterType() {
- class SubClassModule extends SuperClassModule {
- @Override Collection<String> annotatedGenericParameterProviderMethod(List foo) {
- return super.annotatedGenericParameterProviderMethod(foo);
- }
- }
- try {
- Guice.createInjector(new SubClassModule());
- fail();
- } catch (CreationException e) {
- assertContains(e.getMessage(),
- "Overriding @Provides methods is not allowed.",
- "@Provides method: " + SuperClassModule.class.getName()
- + ".annotatedGenericParameterProviderMethod()",
- "overridden by: " + SubClassModule.class.getName()
- + ".annotatedGenericParameterProviderMethod()");
- }
- }
- public void testOverrideProviderMethod_superclassRawTypes_returnType() {
- class SubClassModule extends SuperClassModule {
- // remove the rawtype from the override
- @Override List<String> rawProvider(List<String> f) {
- return f;
- }
- }
- try {
- Guice.createInjector(new SubClassModule());
- fail();
- } catch (CreationException e) {
- assertContains(e.getMessage(),
- "Overriding @Provides methods is not allowed.",
- "@Provides method: " + SuperClassModule.class.getName() + ".rawProvider()",
- "overridden by: " + SubClassModule.class.getName() + ".rawProvider()");
- }
- }
- abstract static class GenericSuperModule<T> extends AbstractModule {
- @Provides String provide(T thing) {
- return thing.toString();
- }
- }
- // This is a tricky case where signatures don't match, but it is an override (facilitated via a
- // bridge method)
- public void testOverrideProviderMethod_erasureBasedOverrides() {
- class SubClassModule extends GenericSuperModule<Integer> {
- @Override String provide(Integer thing) {
- return thing.toString();
- }
- @Override protected void configure() {
- bind(Integer.class).toInstance(3);
- }
- }
- try {
- Guice.createInjector(new SubClassModule());
- fail();
- } catch (CreationException e) {
- assertContains(e.getMessage(),
- "Overriding @Provides methods is not allowed.",
- "@Provides method: " + GenericSuperModule.class.getName() + ".provide()",
- "overridden by: " + SubClassModule.class.getName() + ".provide()");
- }
- }
- class RestrictedSuper extends AbstractModule {
- @Provides public String provideFoo() { return "foo"; }
- @Override protected void configure() {}
- }
- public class ExposedSub extends RestrictedSuper {}
- public void testOverrideProviderMethod_increasedVisibility() {
- // ensure we don't detect the synthetic provideFoo method in ExposedSub as an override (it is,
- // but since it is synthetic it would be annoying to throw an error on it).
- assertEquals("foo", Guice.createInjector(new ExposedSub()).getInstance(String.class));
- }
- interface ProviderInterface<T> {
- T getT();
- }
- static class ModuleImpl extends AbstractModule implements ProviderInterface<String> {
- @Override protected void configure() {}
- @Provides public String getT() {
- return "string";
- }
- @Provides public Object getObject() {
- return new Object();
- }
- /* javac will synthesize a bridge method for getT with the types erased, equivalent to:
- * @Provides public Object getT() { ... }
- */
- }
- public void testIgnoreSyntheticBridgeMethods() {
- Guice.createInjector(new ModuleImpl());
- }
- public void testNullability() throws Exception {
- Module module = new AbstractModule() {
- @Override
- protected void configure() {
- bind(String.class).toProvider(Providers.<String>of(null));
- }
- @SuppressWarnings("unused")
- @Provides
- Integer fail(String foo) {
- return 1;
- }
- @SuppressWarnings("unused")
- @Provides
- Long succeed(@Nullable String foo) {
- return 2L;
- }
- };
- Injector injector = Guice.createInjector(module);
- InjectionPoint fooPoint = InjectionPoint.forMethod(
- module.getClass().getDeclaredMethod("fail", String.class),
- TypeLiteral.get(module.getClass()));
- Dependency<?> fooDependency = Iterables.getOnlyElement(fooPoint.getDependencies());
- runNullableTest(injector, fooDependency, module);
- injector.getInstance(Long.class);
- }
- private void runNullableTest(Injector injector, Dependency<?> dependency, Module module) {
- switch (InternalFlags.getNullableProvidesOption()) {
- case ERROR:
- validateNullableFails(injector, module);
- break;
- case IGNORE:
- validateNullableIgnored(injector);
- break;
- case WARN:
- validateNullableWarns(injector, dependency);
- break;
- }
- }
- private void validateNullableFails(Injector injector, Module module) {
- try {
- injector.getInstance(Integer.class);
- fail();
- } catch (ProvisionException expected) {
- assertContains(expected.getMessage(),
- "1) null returned by binding at " + module.getClass().getName() + ".configure(",
- "but parameter 0 of " + module.getClass().getName() + ".fail() is not @Nullable",
- "while locating java.lang.String",
- "for parameter 0 at " + module.getClass().getName() + ".fail(",
- "while locating java.lang.Integer");
- assertEquals(1, expected.getErrorMessages().size());
- }
- }
- private void validateNullableIgnored(Injector injector) {
- injector.getInstance(Integer.class); // no exception
- }
- private void validateNullableWarns(Injector injector, Dependency<?> dependency) {
- final List<LogRecord> logRecords = Lists.newArrayList();
- final Handler fakeHandler = new Handler() {
- @Override
- public void publish(LogRecord logRecord) {
- logRecords.add(logRecord);
- }
- @Override
- public void flush() {}
- @Override
- public void close() throws SecurityException {}
- };
- Logger.getLogger(Guice.class.getName()).addHandler(fakeHandler);
- try {
- injector.getInstance(Integer.class); // no exception, but assert it does log.
- LogRecord record = Iterables.getOnlyElement(logRecords);
- assertEquals(
- "Guice injected null into parameter {0} of {1} (a {2}), please mark it @Nullable."
- + " Use -Dguice_check_nullable_provides_params=ERROR to turn this into an"
- + " error.",
- record.getMessage());
- assertEquals(dependency.getParameterIndex(), record.getParameters()[0]);
- assertEquals(Errors.convert(dependency.getInjectionPoint().getMember()),
- record.getParameters()[1]);
- assertEquals(Errors.convert(dependency.getKey()), record.getParameters()[2]);
- } finally {
- Logger.getLogger(Guice.class.getName()).removeHandler(fakeHandler);
- }
- }
- @Retention(RetentionPolicy.RUNTIME)
- @interface Nullable {}
- }