| |
| /* |
| * Copyright (C) 2014 The Dagger Authors. |
| * |
| * 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 dagger.internal.codegen; |
| |
| import static com.google.common.truth.Truth.assertAbout; |
| import static com.google.testing.compile.CompilationSubject.assertThat; |
| import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource; |
| import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources; |
| import static dagger.internal.codegen.Compilers.daggerCompiler; |
| import static dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatMethodInUnannotatedClass; |
| import static dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatModuleMethod; |
| import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS; |
| import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.testing.compile.Compilation; |
| import com.google.testing.compile.JavaFileObjects; |
| import javax.tools.JavaFileObject; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| @RunWith(JUnit4.class) |
| public class ModuleFactoryGeneratorTest { |
| |
| private static final JavaFileObject NULLABLE = |
| JavaFileObjects.forSourceLines( |
| "test.Nullable", "package test;", "public @interface Nullable {}"); |
| |
| // TODO(gak): add tests for invalid combinations of scope and qualifier annotations like we have |
| // for @Inject |
| |
| @Test public void providesMethodNotInModule() { |
| assertThatMethodInUnannotatedClass("@Provides String provideString() { return null; }") |
| .hasError("@Provides methods can only be present within a @Module or @ProducerModule"); |
| } |
| |
| @Test public void providesMethodAbstract() { |
| assertThatModuleMethod("@Provides abstract String abstractMethod();") |
| .hasError("@Provides methods cannot be abstract"); |
| } |
| |
| @Test public void providesMethodPrivate() { |
| assertThatModuleMethod("@Provides private String privateMethod() { return null; }") |
| .hasError("@Provides methods cannot be private"); |
| } |
| |
| @Test public void providesMethodReturnVoid() { |
| assertThatModuleMethod("@Provides void voidMethod() {}") |
| .hasError("@Provides methods must return a value (not void)"); |
| } |
| |
| @Test |
| public void providesMethodReturnsProvider() { |
| assertThatModuleMethod("@Provides Provider<String> provideProvider() {}") |
| .hasError("@Provides methods must not return framework types"); |
| } |
| |
| @Test |
| public void providesMethodReturnsLazy() { |
| assertThatModuleMethod("@Provides Lazy<String> provideLazy() {}") |
| .hasError("@Provides methods must not return framework types"); |
| } |
| |
| @Test |
| public void providesMethodReturnsMembersInjector() { |
| assertThatModuleMethod("@Provides MembersInjector<String> provideMembersInjector() {}") |
| .hasError("@Provides methods must not return framework types"); |
| } |
| |
| @Test |
| public void providesMethodReturnsProducer() { |
| assertThatModuleMethod("@Provides Producer<String> provideProducer() {}") |
| .hasError("@Provides methods must not return framework types"); |
| } |
| |
| @Test |
| public void providesMethodReturnsProduced() { |
| assertThatModuleMethod("@Provides Produced<String> provideProduced() {}") |
| .hasError("@Provides methods must not return framework types"); |
| } |
| |
| @Test public void providesMethodWithTypeParameter() { |
| assertThatModuleMethod("@Provides <T> String typeParameter() { return null; }") |
| .hasError("@Provides methods may not have type parameters"); |
| } |
| |
| @Test public void providesMethodSetValuesWildcard() { |
| assertThatModuleMethod("@Provides @ElementsIntoSet Set<?> provideWildcard() { return null; }") |
| .hasError( |
| "@Provides methods must return a primitive, an array, a type variable, " |
| + "or a declared type"); |
| } |
| |
| @Test public void providesMethodSetValuesRawSet() { |
| assertThatModuleMethod("@Provides @ElementsIntoSet Set provideSomething() { return null; }") |
| .hasError("@Provides methods annotated with @ElementsIntoSet cannot return a raw Set"); |
| } |
| |
| @Test public void providesMethodSetValuesNotASet() { |
| assertThatModuleMethod( |
| "@Provides @ElementsIntoSet List<String> provideStrings() { return null; }") |
| .hasError("@Provides methods annotated with @ElementsIntoSet must return a Set"); |
| } |
| |
| @Test public void modulesWithTypeParamsMustBeAbstract() { |
| JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "", |
| "@Module", |
| "final class TestModule<A> {}"); |
| Compilation compilation = daggerCompiler().compile(moduleFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining("Modules with type parameters must be abstract") |
| .inFile(moduleFile) |
| .onLine(6); |
| } |
| |
| @Test public void provideOverriddenByNoProvide() { |
| JavaFileObject parent = JavaFileObjects.forSourceLines("test.Parent", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "", |
| "@Module", |
| "class Parent {", |
| " @Provides String foo() { return null; }", |
| "}"); |
| assertThatModuleMethod("String foo() { return null; }") |
| .withDeclaration("@Module class %s extends Parent { %s }") |
| .withAdditionalSources(parent) |
| .hasError( |
| "Binding methods may not be overridden in modules. Overrides: " |
| + "@Provides String test.Parent.foo()"); |
| } |
| |
| @Test public void provideOverriddenByProvide() { |
| JavaFileObject parent = JavaFileObjects.forSourceLines("test.Parent", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "", |
| "@Module", |
| "class Parent {", |
| " @Provides String foo() { return null; }", |
| "}"); |
| assertThatModuleMethod("@Provides String foo() { return null; }") |
| .withDeclaration("@Module class %s extends Parent { %s }") |
| .withAdditionalSources(parent) |
| .hasError( |
| "Binding methods may not override another method. Overrides: " |
| + "@Provides String test.Parent.foo()"); |
| } |
| |
| @Test public void providesOverridesNonProvides() { |
| JavaFileObject parent = JavaFileObjects.forSourceLines("test.Parent", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "", |
| "@Module", |
| "class Parent {", |
| " String foo() { return null; }", |
| "}"); |
| assertThatModuleMethod("@Provides String foo() { return null; }") |
| .withDeclaration("@Module class %s extends Parent { %s }") |
| .withAdditionalSources(parent) |
| .hasError( |
| "Binding methods may not override another method. Overrides: " |
| + "String test.Parent.foo()"); |
| } |
| |
| @Test public void validatesIncludedModules() { |
| JavaFileObject module = JavaFileObjects.forSourceLines("test.Parent", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "", |
| "@Module(includes = Void.class)", |
| "class TestModule {}"); |
| |
| Compilation compilation = daggerCompiler().compile(module); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining( |
| "java.lang.Void is listed as a module, but is not annotated with @Module"); |
| } |
| |
| @Test public void singleProvidesMethodNoArgs() { |
| JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "", |
| "@Module", |
| "final class TestModule {", |
| " @Provides String provideString() {", |
| " return \"\";", |
| " }", |
| "}"); |
| JavaFileObject factoryFile = |
| JavaFileObjects.forSourceLines( |
| "TestModule_ProvideStringFactory", |
| "package test;", |
| "", |
| "import dagger.internal.Factory;", |
| "import dagger.internal.Preconditions;", |
| IMPORT_GENERATED_ANNOTATION, |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "public final class TestModule_ProvideStringFactory implements Factory<String> {", |
| " private final TestModule module;", |
| "", |
| " public TestModule_ProvideStringFactory(TestModule module) {", |
| " this.module = module;", |
| " }", |
| "", |
| " @Override public String get() {", |
| " return provideString(module);", |
| " }", |
| "", |
| " public static TestModule_ProvideStringFactory create(TestModule module) {", |
| " return new TestModule_ProvideStringFactory(module);", |
| " }", |
| "", |
| " public static String provideString(TestModule instance) {", |
| " return Preconditions.checkNotNullFromProvides(instance.provideString());", |
| " }", |
| "}"); |
| assertAbout(javaSource()).that(moduleFile) |
| .processedWith(new ComponentProcessor()) |
| .compilesWithoutError() |
| .and().generatesSources(factoryFile); |
| } |
| |
| @Test public void singleProvidesMethodNoArgs_disableNullable() { |
| JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "", |
| "@Module", |
| "final class TestModule {", |
| " @Provides String provideString() {", |
| " return \"\";", |
| " }", |
| "}"); |
| JavaFileObject factoryFile = |
| JavaFileObjects.forSourceLines( |
| "TestModule_ProvideStringFactory", |
| "package test;", |
| "", |
| "import dagger.internal.Factory;", |
| IMPORT_GENERATED_ANNOTATION, |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "public final class TestModule_ProvideStringFactory implements Factory<String> {", |
| " private final TestModule module;", |
| "", |
| " public TestModule_ProvideStringFactory(TestModule module) {", |
| " this.module = module;", |
| " }", |
| "", |
| " @Override public String get() {", |
| " return provideString(module);", |
| " }", |
| "", |
| " public static TestModule_ProvideStringFactory create(TestModule module) {", |
| " return new TestModule_ProvideStringFactory(module);", |
| " }", |
| "", |
| " public static String provideString(TestModule instance) {", |
| " return instance.provideString();", |
| " }", |
| "}"); |
| assertAbout(javaSource()).that(moduleFile) |
| .withCompilerOptions("-Adagger.nullableValidation=WARNING") |
| .processedWith(new ComponentProcessor()) |
| .compilesWithoutError() |
| .and().generatesSources(factoryFile); |
| } |
| |
| @Test public void nullableProvides() { |
| JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "", |
| "@Module", |
| "final class TestModule {", |
| " @Provides @Nullable String provideString() { return null; }", |
| "}"); |
| JavaFileObject factoryFile = |
| JavaFileObjects.forSourceLines( |
| "TestModule_ProvideStringFactory", |
| "package test;", |
| "", |
| "import dagger.internal.Factory;", |
| IMPORT_GENERATED_ANNOTATION, |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "public final class TestModule_ProvideStringFactory implements Factory<String> {", |
| " private final TestModule module;", |
| "", |
| " public TestModule_ProvideStringFactory(TestModule module) {", |
| " this.module = module;", |
| " }", |
| "", |
| " @Override", |
| " @Nullable", |
| " public String get() {", |
| " return provideString(module);", |
| " }", |
| "", |
| " public static TestModule_ProvideStringFactory create(TestModule module) {", |
| " return new TestModule_ProvideStringFactory(module);", |
| " }", |
| "", |
| " @Nullable", |
| " public static String provideString(TestModule instance) {", |
| " return instance.provideString();", |
| " }", |
| "}"); |
| assertAbout(javaSources()).that(ImmutableList.of(moduleFile, NULLABLE)) |
| .processedWith(new ComponentProcessor()) |
| .compilesWithoutError() |
| .and().generatesSources(factoryFile); |
| } |
| |
| @Test public void multipleProvidesMethods() { |
| JavaFileObject classXFile = JavaFileObjects.forSourceLines("test.X", |
| "package test;", |
| "", |
| "import javax.inject.Inject;", |
| "", |
| "class X {", |
| " @Inject public String s;", |
| "}"); |
| JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule", |
| "package test;", |
| "", |
| "import dagger.MembersInjector;", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "import java.util.Arrays;", |
| "import java.util.List;", |
| "", |
| "@Module", |
| "final class TestModule {", |
| " @Provides List<Object> provideObjects(", |
| " @QualifierA Object a, @QualifierB Object b, MembersInjector<X> xInjector) {", |
| " return Arrays.asList(a, b);", |
| " }", |
| "", |
| " @Provides @QualifierA Object provideAObject() {", |
| " return new Object();", |
| " }", |
| "", |
| " @Provides @QualifierB Object provideBObject() {", |
| " return new Object();", |
| " }", |
| "}"); |
| JavaFileObject listFactoryFile = |
| JavaFileObjects.forSourceLines( |
| "TestModule_ProvideObjectsFactory", |
| "package test;", |
| "", |
| "import dagger.MembersInjector;", |
| "import dagger.internal.Factory;", |
| "import dagger.internal.Preconditions;", |
| "import java.util.List;", |
| IMPORT_GENERATED_ANNOTATION, |
| "import javax.inject.Provider;", |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "public final class TestModule_ProvideObjectsFactory", |
| " implements Factory<List<Object>> {", |
| " private final TestModule module;", |
| " private final Provider<Object> aProvider;", |
| " private final Provider<Object> bProvider;", |
| " private final Provider<MembersInjector<X>> xInjectorProvider;", |
| "", |
| " public TestModule_ProvideObjectsFactory(", |
| " TestModule module,", |
| " Provider<Object> aProvider,", |
| " Provider<Object> bProvider,", |
| " Provider<MembersInjector<X>> xInjectorProvider) {", |
| " this.module = module;", |
| " this.aProvider = aProvider;", |
| " this.bProvider = bProvider;", |
| " this.xInjectorProvider = xInjectorProvider;", |
| " }", |
| "", |
| " @Override public List<Object> get() {", |
| " return provideObjects(", |
| " module, aProvider.get(), bProvider.get(), xInjectorProvider.get());", |
| " }", |
| "", |
| " public static TestModule_ProvideObjectsFactory create(", |
| " TestModule module,", |
| " Provider<Object> aProvider,", |
| " Provider<Object> bProvider,", |
| " Provider<MembersInjector<X>> xInjectorProvider) {", |
| " return new TestModule_ProvideObjectsFactory(", |
| " module, aProvider, bProvider, xInjectorProvider);", |
| " }", |
| "", |
| " public static List<Object> provideObjects(", |
| " TestModule instance, Object a, Object b, MembersInjector<X> xInjector) {", |
| " return Preconditions.checkNotNullFromProvides(", |
| " instance.provideObjects(a, b, xInjector));", |
| " }", |
| "}"); |
| assertAbout(javaSources()).that( |
| ImmutableList.of(classXFile, moduleFile, QUALIFIER_A, QUALIFIER_B)) |
| .processedWith(new ComponentProcessor()) |
| .compilesWithoutError() |
| .and().generatesSources(listFactoryFile); |
| } |
| |
| @Test public void providesSetElement() { |
| JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule", |
| "package test;", |
| "", |
| "import java.util.logging.Logger;", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "import dagger.multibindings.IntoSet;", |
| "", |
| "@Module", |
| "final class TestModule {", |
| " @Provides @IntoSet String provideString() {", |
| " return \"\";", |
| " }", |
| "}"); |
| JavaFileObject factoryFile = |
| JavaFileObjects.forSourceLines( |
| "TestModule_ProvideStringFactory", |
| "package test;", |
| "", |
| "import dagger.internal.Factory;", |
| "import dagger.internal.Preconditions;", |
| IMPORT_GENERATED_ANNOTATION, |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "public final class TestModule_ProvideStringFactory implements Factory<String> {", |
| " private final TestModule module;", |
| "", |
| " public TestModule_ProvideStringFactory(TestModule module) {", |
| " this.module = module;", |
| " }", |
| "", |
| " @Override public String get() {", |
| " return provideString(module);", |
| " }", |
| "", |
| " public static TestModule_ProvideStringFactory create(TestModule module) {", |
| " return new TestModule_ProvideStringFactory(module);", |
| " }", |
| "", |
| " public static String provideString(TestModule instance) {", |
| " return Preconditions.checkNotNullFromProvides(instance.provideString());", |
| " }", |
| "}"); |
| assertAbout(javaSource()).that(moduleFile) |
| .processedWith(new ComponentProcessor()) |
| .compilesWithoutError() |
| .and().generatesSources(factoryFile); |
| } |
| |
| @Test public void providesSetElementWildcard() { |
| JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule", |
| "package test;", |
| "", |
| "import java.util.logging.Logger;", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "import dagger.multibindings.IntoSet;", |
| "import java.util.ArrayList;", |
| "import java.util.List;", |
| "", |
| "@Module", |
| "final class TestModule {", |
| " @Provides @IntoSet List<List<?>> provideWildcardList() {", |
| " return new ArrayList<>();", |
| " }", |
| "}"); |
| JavaFileObject factoryFile = |
| JavaFileObjects.forSourceLines( |
| "TestModule_ProvideWildcardListFactory", |
| "package test;", |
| "", |
| "import dagger.internal.Factory;", |
| "import dagger.internal.Preconditions;", |
| "import java.util.List;", |
| IMPORT_GENERATED_ANNOTATION, |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "public final class TestModule_ProvideWildcardListFactory implements " |
| + "Factory<List<List<?>>> {", |
| " private final TestModule module;", |
| "", |
| " public TestModule_ProvideWildcardListFactory(TestModule module) {", |
| " this.module = module;", |
| " }", |
| "", |
| " @Override public List<List<?>> get() {", |
| " return provideWildcardList(module);", |
| " }", |
| "", |
| " public static TestModule_ProvideWildcardListFactory create(TestModule module) {", |
| " return new TestModule_ProvideWildcardListFactory(module);", |
| " }", |
| "", |
| " public static List<List<?>> provideWildcardList(TestModule instance) {", |
| " return Preconditions.checkNotNullFromProvides(", |
| " instance.provideWildcardList());", |
| " }", |
| "}"); |
| assertAbout(javaSource()).that(moduleFile) |
| .processedWith(new ComponentProcessor()) |
| .compilesWithoutError() |
| .and().generatesSources(factoryFile); |
| } |
| |
| @Test public void providesSetValues() { |
| JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "import dagger.multibindings.ElementsIntoSet;", |
| "import java.util.Set;", |
| "", |
| "@Module", |
| "final class TestModule {", |
| " @Provides @ElementsIntoSet Set<String> provideStrings() {", |
| " return null;", |
| " }", |
| "}"); |
| JavaFileObject factoryFile = |
| JavaFileObjects.forSourceLines( |
| "TestModule_ProvideStringsFactory", |
| "package test;", |
| "", |
| "import dagger.internal.Factory;", |
| "import dagger.internal.Preconditions;", |
| "import java.util.Set;", |
| IMPORT_GENERATED_ANNOTATION, |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "public final class TestModule_ProvideStringsFactory implements Factory<Set<String>> {", |
| " private final TestModule module;", |
| "", |
| " public TestModule_ProvideStringsFactory(TestModule module) {", |
| " this.module = module;", |
| " }", |
| "", |
| " @Override public Set<String> get() {", |
| " return provideStrings(module);", |
| " }", |
| "", |
| " public static TestModule_ProvideStringsFactory create(TestModule module) {", |
| " return new TestModule_ProvideStringsFactory(module);", |
| " }", |
| "", |
| " public static Set<String> provideStrings(TestModule instance) {", |
| " return Preconditions.checkNotNullFromProvides(", |
| " instance.provideStrings());", |
| " }", |
| "}"); |
| assertAbout(javaSource()).that(moduleFile) |
| .processedWith(new ComponentProcessor()) |
| .compilesWithoutError() |
| .and().generatesSources(factoryFile); |
| } |
| |
| @Test public void multipleProvidesMethodsWithSameName() { |
| JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "", |
| "@Module", |
| "final class TestModule {", |
| " @Provides Object provide(int i) {", |
| " return i;", |
| " }", |
| "", |
| " @Provides String provide() {", |
| " return \"\";", |
| " }", |
| "}"); |
| Compilation compilation = daggerCompiler().compile(moduleFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining( |
| "Cannot have more than one binding method with the same name in a single module") |
| .inFile(moduleFile) |
| .onLine(8); |
| assertThat(compilation) |
| .hadErrorContaining( |
| "Cannot have more than one binding method with the same name in a single module") |
| .inFile(moduleFile) |
| .onLine(12); |
| } |
| |
| @Test |
| public void providesMethodThrowsChecked() { |
| JavaFileObject moduleFile = |
| JavaFileObjects.forSourceLines( |
| "test.TestModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "", |
| "@Module", |
| "final class TestModule {", |
| " @Provides int i() throws Exception {", |
| " return 0;", |
| " }", |
| "", |
| " @Provides String s() throws Throwable {", |
| " return \"\";", |
| " }", |
| "}"); |
| Compilation compilation = daggerCompiler().compile(moduleFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining("@Provides methods may only throw unchecked exceptions") |
| .inFile(moduleFile) |
| .onLine(8); |
| assertThat(compilation) |
| .hadErrorContaining("@Provides methods may only throw unchecked exceptions") |
| .inFile(moduleFile) |
| .onLine(12); |
| } |
| |
| @Test |
| public void providedTypes() { |
| JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "import java.io.Closeable;", |
| "import java.util.Set;", |
| "", |
| "@Module", |
| "final class TestModule {", |
| " @Provides String string() {", |
| " return null;", |
| " }", |
| "", |
| " @Provides Set<String> strings() {", |
| " return null;", |
| " }", |
| "", |
| " @Provides Set<? extends Closeable> closeables() {", |
| " return null;", |
| " }", |
| "", |
| " @Provides String[] stringArray() {", |
| " return null;", |
| " }", |
| "", |
| " @Provides int integer() {", |
| " return 0;", |
| " }", |
| "", |
| " @Provides int[] integers() {", |
| " return null;", |
| " }", |
| "}"); |
| Compilation compilation = daggerCompiler().compile(moduleFile); |
| assertThat(compilation).succeeded(); |
| } |
| |
| @Test |
| public void privateModule() { |
| JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.Enclosing", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "", |
| "final class Enclosing {", |
| " @Module private static final class PrivateModule {", |
| " }", |
| "}"); |
| Compilation compilation = daggerCompiler().compile(moduleFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining("Modules cannot be private") |
| .inFile(moduleFile) |
| .onLine(6); |
| } |
| |
| |
| @Test |
| public void enclosedInPrivateModule() { |
| JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.Enclosing", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "", |
| "final class Enclosing {", |
| " private static final class PrivateEnclosing {", |
| " @Module static final class TestModule {", |
| " }", |
| " }", |
| "}"); |
| Compilation compilation = daggerCompiler().compile(moduleFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining("Modules cannot be enclosed in private types") |
| .inFile(moduleFile) |
| .onLine(7); |
| } |
| |
| @Test |
| public void publicModuleNonPublicIncludes() { |
| JavaFileObject publicModuleFile = JavaFileObjects.forSourceLines("test.PublicModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "", |
| "@Module(includes = {", |
| " BadNonPublicModule.class, OtherPublicModule.class, OkNonPublicModule.class", |
| "})", |
| "public final class PublicModule {", |
| "}"); |
| JavaFileObject badNonPublicModuleFile = |
| JavaFileObjects.forSourceLines( |
| "test.BadNonPublicModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "", |
| "@Module", |
| "final class BadNonPublicModule {", |
| " @Provides", |
| " int provideInt() {", |
| " return 42;", |
| " }", |
| "}"); |
| JavaFileObject okNonPublicModuleFile = JavaFileObjects.forSourceLines("test.OkNonPublicModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "", |
| "@Module", |
| "final class OkNonPublicModule {", |
| " @Provides", |
| " static String provideString() {", |
| " return \"foo\";", |
| " }", |
| "}"); |
| JavaFileObject otherPublicModuleFile = JavaFileObjects.forSourceLines("test.OtherPublicModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "", |
| "@Module", |
| "public final class OtherPublicModule {", |
| "}"); |
| Compilation compilation = |
| daggerCompiler() |
| .compile( |
| publicModuleFile, |
| badNonPublicModuleFile, |
| okNonPublicModuleFile, |
| otherPublicModuleFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining( |
| "This module is public, but it includes non-public (or effectively non-public) modules " |
| + "(test.BadNonPublicModule) that have non-static, non-abstract binding methods. " |
| + "Either reduce the visibility of this module, make the included modules public, " |
| + "or make all of the binding methods on the included modules abstract or static.") |
| .inFile(publicModuleFile) |
| .onLine(8); |
| } |
| |
| @Test |
| public void genericSubclassedModule() { |
| JavaFileObject parent = |
| JavaFileObjects.forSourceLines( |
| "test.ParentModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "import dagger.multibindings.IntoSet;", |
| "import dagger.multibindings.IntoMap;", |
| "import dagger.multibindings.StringKey;", |
| "import java.util.List;", |
| "import java.util.ArrayList;", |
| "", |
| "@Module", |
| "abstract class ParentModule<A extends CharSequence,", |
| " B,", |
| " C extends Number & Comparable<C>> {", |
| " @Provides List<B> provideListB(B b) {", |
| " List<B> list = new ArrayList<B>();", |
| " list.add(b);", |
| " return list;", |
| " }", |
| "", |
| " @Provides @IntoSet B provideBElement(B b) {", |
| " return b;", |
| " }", |
| "", |
| " @Provides @IntoMap @StringKey(\"b\") B provideBEntry(B b) {", |
| " return b;", |
| " }", |
| "}"); |
| JavaFileObject numberChild = JavaFileObjects.forSourceLines("test.ChildNumberModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "", |
| "@Module", |
| "class ChildNumberModule extends ParentModule<String, Number, Double> {", |
| " @Provides Number provideNumber() { return 1; }", |
| "}"); |
| JavaFileObject integerChild = JavaFileObjects.forSourceLines("test.ChildIntegerModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "", |
| "@Module", |
| "class ChildIntegerModule extends ParentModule<StringBuilder, Integer, Float> {", |
| " @Provides Integer provideInteger() { return 2; }", |
| "}"); |
| JavaFileObject component = JavaFileObjects.forSourceLines("test.C", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "import java.util.List;", |
| "", |
| "@Component(modules={ChildNumberModule.class, ChildIntegerModule.class})", |
| "interface C {", |
| " List<Number> numberList();", |
| " List<Integer> integerList();", |
| "}"); |
| JavaFileObject listBFactory = |
| JavaFileObjects.forSourceLines( |
| "test.ParentModule_ProvideListBFactory", |
| "package test;", |
| "", |
| "import dagger.internal.Factory;", |
| "import dagger.internal.Preconditions;", |
| "import java.util.List;", |
| IMPORT_GENERATED_ANNOTATION, |
| "import javax.inject.Provider;", |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "public final class ParentModule_ProvideListBFactory<A extends CharSequence,", |
| " B, C extends Number & Comparable<C>> implements Factory<List<B>> {", |
| " private final ParentModule<A, B, C> module;", |
| " private final Provider<B> bProvider;", |
| "", |
| " public ParentModule_ProvideListBFactory(", |
| " ParentModule<A, B, C> module, Provider<B> bProvider) {", |
| " this.module = module;", |
| " this.bProvider = bProvider;", |
| " }", |
| "", |
| " @Override", |
| " public List<B> get() { ", |
| " return provideListB(module, bProvider.get());", |
| " }", |
| "", |
| " public static <A extends CharSequence, B, C extends Number & Comparable<C>>", |
| " ParentModule_ProvideListBFactory<A, B, C> create(", |
| " ParentModule<A, B, C> module, Provider<B> bProvider) {", |
| " return new ParentModule_ProvideListBFactory<A, B, C>(module, bProvider);", |
| " }", |
| "", |
| " public static <A extends CharSequence, B, C extends Number & Comparable<C>> List<B>", |
| " provideListB(ParentModule<A, B, C> instance, B b) {", |
| " return Preconditions.checkNotNullFromProvides(instance.provideListB(b));", |
| " }", |
| "}"); |
| JavaFileObject bElementFactory = |
| JavaFileObjects.forSourceLines( |
| "test.ParentModule_ProvideBElementFactory", |
| "package test;", |
| "", |
| "import dagger.internal.Factory;", |
| "import dagger.internal.Preconditions;", |
| IMPORT_GENERATED_ANNOTATION, |
| "import javax.inject.Provider;", |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "public final class ParentModule_ProvideBElementFactory<A extends CharSequence,", |
| " B, C extends Number & Comparable<C>> implements Factory<B> {", |
| " private final ParentModule<A, B, C> module;", |
| " private final Provider<B> bProvider;", |
| "", |
| " public ParentModule_ProvideBElementFactory(", |
| " ParentModule<A, B, C> module, Provider<B> bProvider) {", |
| " this.module = module;", |
| " this.bProvider = bProvider;", |
| " }", |
| "", |
| " @Override", |
| " public B get() { ", |
| " return provideBElement(module, bProvider.get());", |
| " }", |
| "", |
| " public static <A extends CharSequence, B, C extends Number & Comparable<C>>", |
| " ParentModule_ProvideBElementFactory<A, B, C> create(", |
| " ParentModule<A, B, C> module, Provider<B> bProvider) {", |
| " return new ParentModule_ProvideBElementFactory<A, B, C>(module, bProvider);", |
| " }", |
| "", |
| " public static <A extends CharSequence, B, C extends Number & Comparable<C>>", |
| " B provideBElement(", |
| " ParentModule<A, B, C> instance, B b) {", |
| " return Preconditions.checkNotNullFromProvides(instance.provideBElement(b));", |
| " }", |
| "}"); |
| JavaFileObject bEntryFactory = |
| JavaFileObjects.forSourceLines( |
| "test.ParentModule_ProvideBEntryFactory", |
| "package test;", |
| "", |
| "import dagger.internal.Factory;", |
| "import dagger.internal.Preconditions;", |
| IMPORT_GENERATED_ANNOTATION, |
| "import javax.inject.Provider;", |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "public final class ParentModule_ProvideBEntryFactory<A extends CharSequence,", |
| " B, C extends Number & Comparable<C>> implements Factory<B>> {", |
| " private final ParentModule<A, B, C> module;", |
| " private final Provider<B> bProvider;", |
| "", |
| " public ParentModule_ProvideBEntryFactory(", |
| " ParentModule<A, B, C> module, Provider<B> bProvider) {", |
| " this.module = module;", |
| " this.bProvider = bProvider;", |
| " }", |
| "", |
| " @Override", |
| " public B get() { ", |
| " return provideBEntry(module, bProvider.get());", |
| " }", |
| "", |
| " public static <A extends CharSequence, B, C extends Number & Comparable<C>>", |
| " ParentModule_ProvideBEntryFactory<A, B, C> create(", |
| " ParentModule<A, B, C> module, Provider<B> bProvider) {", |
| " return new ParentModule_ProvideBEntryFactory<A, B, C>(module, bProvider);", |
| " }", |
| "", |
| " public static <A extends CharSequence, B, C extends Number & Comparable<C>>", |
| " B provideBEntry(", |
| " ParentModule<A, B, C> instance, B b) {", |
| " return Preconditions.checkNotNullFromProvides(instance.provideBEntry(b));", |
| " }", |
| "}"); |
| JavaFileObject numberFactory = |
| JavaFileObjects.forSourceLines( |
| "test.ChildNumberModule_ProvideNumberFactory", |
| "package test;", |
| "", |
| "import dagger.internal.Factory;", |
| "import dagger.internal.Preconditions;", |
| IMPORT_GENERATED_ANNOTATION, |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "public final class ChildNumberModule_ProvideNumberFactory", |
| " implements Factory<Number> {", |
| " private final ChildNumberModule module;", |
| "", |
| " public ChildNumberModule_ProvideNumberFactory(ChildNumberModule module) {", |
| " this.module = module;", |
| " }", |
| "", |
| " @Override", |
| " public Number get() { ", |
| " return provideNumber(module);", |
| " }", |
| "", |
| " public static ChildNumberModule_ProvideNumberFactory create(", |
| " ChildNumberModule module) {", |
| " return new ChildNumberModule_ProvideNumberFactory(module);", |
| " }", |
| "", |
| " public static Number provideNumber(ChildNumberModule instance) {", |
| " return Preconditions.checkNotNullFromProvides(instance.provideNumber());", |
| " }", |
| "}"); |
| JavaFileObject integerFactory = |
| JavaFileObjects.forSourceLines( |
| "test.ChildIntegerModule_ProvideIntegerFactory", |
| "package test;", |
| "", |
| "import dagger.internal.Factory;", |
| "import dagger.internal.Preconditions;", |
| IMPORT_GENERATED_ANNOTATION, |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "public final class ChildIntegerModule_ProvideIntegerFactory", |
| " implements Factory<Integer> {", |
| " private final ChildIntegerModule module;", |
| "", |
| " public ChildIntegerModule_ProvideIntegerFactory(ChildIntegerModule module) {", |
| " this.module = module;", |
| " }", |
| "", |
| " @Override", |
| " public Integer get() { ", |
| " return provideInteger(module);", |
| " }", |
| "", |
| " public static ChildIntegerModule_ProvideIntegerFactory create(", |
| " ChildIntegerModule module) {", |
| " return new ChildIntegerModule_ProvideIntegerFactory(module);", |
| " }", |
| "", |
| " public static Integer provideInteger(ChildIntegerModule instance) {", |
| " return Preconditions.checkNotNullFromProvides(", |
| " instance.provideInteger());", |
| " }", |
| "}"); |
| assertAbout(javaSources()) |
| .that(ImmutableList.of(parent, numberChild, integerChild, component)) |
| .processedWith(new ComponentProcessor()) |
| .compilesWithoutError() |
| .and() |
| .generatesSources( |
| listBFactory, bElementFactory, bEntryFactory, numberFactory, integerFactory); |
| } |
| |
| @Test public void parameterizedModuleWithStaticProvidesMethodOfGenericType() { |
| JavaFileObject moduleFile = |
| JavaFileObjects.forSourceLines( |
| "test.ParameterizedModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "import java.util.List;", |
| "import java.util.ArrayList;", |
| "import java.util.Map;", |
| "import java.util.HashMap;", |
| "", |
| "@Module abstract class ParameterizedModule<T> {", |
| " @Provides List<T> provideListT() {", |
| " return new ArrayList<>();", |
| " }", |
| "", |
| " @Provides static Map<String, Number> provideMapStringNumber() {", |
| " return new HashMap<>();", |
| " }", |
| "", |
| " @Provides static Object provideNonGenericType() {", |
| " return new Object();", |
| " }", |
| "", |
| " @Provides static String provideNonGenericTypeWithDeps(Object o) {", |
| " return o.toString();", |
| " }", |
| "}"); |
| |
| JavaFileObject provideMapStringNumberFactory = |
| JavaFileObjects.forSourceLines( |
| "test.ParameterizedModule_ProvideMapStringNumberFactory;", |
| "package test;", |
| "", |
| "import dagger.internal.Factory;", |
| "import dagger.internal.Preconditions;", |
| "import java.util.Map;", |
| IMPORT_GENERATED_ANNOTATION, |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "public final class ParameterizedModule_ProvideMapStringNumberFactory", |
| " implements Factory<Map<String, Number>> {", |
| " @Override", |
| " public Map<String, Number> get() {", |
| " return provideMapStringNumber();", |
| " }", |
| "", |
| " public static ParameterizedModule_ProvideMapStringNumberFactory create() {", |
| " return InstanceHolder.INSTANCE;", |
| " }", |
| "", |
| " public static Map<String, Number> provideMapStringNumber() {", |
| " return Preconditions.checkNotNullFromProvides(", |
| " ParameterizedModule.provideMapStringNumber());", |
| " }", |
| "", |
| " private static final class InstanceHolder {", |
| " private static final ParameterizedModule_ProvideMapStringNumberFactory INSTANCE =", |
| " new ParameterizedModule_ProvideMapStringNumberFactory();", |
| " }", |
| "}"); |
| |
| JavaFileObject provideNonGenericTypeFactory = |
| JavaFileObjects.forSourceLines( |
| "test.ParameterizedModule_ProvideNonGenericTypeFactory;", |
| "package test;", |
| "", |
| "import dagger.internal.Factory;", |
| "import dagger.internal.Preconditions;", |
| IMPORT_GENERATED_ANNOTATION, |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "public final class ParameterizedModule_ProvideNonGenericTypeFactory", |
| " implements Factory<Object> {", |
| " @Override", |
| " public Object get() {", |
| " return provideNonGenericType();", |
| " }", |
| "", |
| " public static ParameterizedModule_ProvideNonGenericTypeFactory create() {", |
| " return InstanceHolder.INSTANCE;", |
| " }", |
| "", |
| " public static Object provideNonGenericType() {", |
| " return Preconditions.checkNotNullFromProvides(", |
| " ParameterizedModule.provideNonGenericType());", |
| " }", |
| "", |
| " private static final class InstanceHolder {", |
| " private static final ParameterizedModule_ProvideNonGenericTypeFactory INSTANCE =", |
| " new ParameterizedModule_ProvideNonGenericTypeFactory();", |
| " }", |
| "}"); |
| |
| JavaFileObject provideNonGenericTypeWithDepsFactory = |
| JavaFileObjects.forSourceLines( |
| "test.ParameterizedModule_ProvideNonGenericTypeWithDepsFactory;", |
| "package test;", |
| "", |
| "import dagger.internal.Factory;", |
| "import dagger.internal.Preconditions;", |
| IMPORT_GENERATED_ANNOTATION, |
| "import javax.inject.Provider;", |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "public final class ParameterizedModule_ProvideNonGenericTypeWithDepsFactory", |
| " implements Factory<String> {", |
| " private final Provider<Object> oProvider;", |
| "", |
| " public ParameterizedModule_ProvideNonGenericTypeWithDepsFactory(", |
| " Provider<Object> oProvider) {", |
| " this.oProvider = oProvider;", |
| " }", |
| "", |
| " @Override", |
| " public String get() {", |
| " return provideNonGenericTypeWithDeps(oProvider.get());", |
| " }", |
| "", |
| " public static ParameterizedModule_ProvideNonGenericTypeWithDepsFactory create(", |
| " Provider<Object> oProvider) {", |
| " return new ParameterizedModule_ProvideNonGenericTypeWithDepsFactory(oProvider);", |
| " }", |
| "", |
| " public static String provideNonGenericTypeWithDeps(Object o) {", |
| " return Preconditions.checkNotNullFromProvides(", |
| " ParameterizedModule.provideNonGenericTypeWithDeps(o));", |
| " }", |
| "}"); |
| |
| assertAbout(javaSource()) |
| .that(moduleFile) |
| .processedWith(new ComponentProcessor()) |
| .compilesWithoutError() |
| .and() |
| .generatesSources( |
| provideMapStringNumberFactory, |
| provideNonGenericTypeFactory, |
| provideNonGenericTypeWithDepsFactory); |
| } |
| |
| private static final JavaFileObject QUALIFIER_A = |
| JavaFileObjects.forSourceLines( |
| "test.QualifierA", |
| "package test;", |
| "", |
| "import javax.inject.Qualifier;", |
| "", |
| "@Qualifier @interface QualifierA {}"); |
| |
| private static final JavaFileObject QUALIFIER_B = |
| JavaFileObjects.forSourceLines( |
| "test.QualifierB", |
| "package test;", |
| "", |
| "import javax.inject.Qualifier;", |
| "", |
| "@Qualifier @interface QualifierB {}"); |
| |
| @Test |
| public void providesMethodMultipleQualifiersOnMethod() { |
| JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "", |
| "@Module", |
| "final class TestModule {", |
| " @Provides @QualifierA @QualifierB String provideString() {", |
| " return \"foo\";", |
| " }", |
| "}"); |
| Compilation compilation = daggerCompiler().compile(moduleFile, QUALIFIER_A, QUALIFIER_B); |
| assertThat(compilation).failed(); |
| assertThat(compilation).hadErrorContaining("may not use more than one @Qualifier"); |
| } |
| |
| @Test |
| public void providesMethodMultipleQualifiersOnParameter() { |
| JavaFileObject moduleFile = |
| JavaFileObjects.forSourceLines( |
| "test.TestModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "", |
| "@Module", |
| "final class TestModule {", |
| " @Provides static String provideString(@QualifierA @QualifierB Object object) {", |
| " return \"foo\";", |
| " }", |
| "}"); |
| Compilation compilation = daggerCompiler().compile(moduleFile, QUALIFIER_A, QUALIFIER_B); |
| assertThat(compilation).failed(); |
| assertThat(compilation).hadErrorContaining("may not use more than one @Qualifier"); |
| } |
| |
| @Test |
| public void providesMethodWildcardDependency() { |
| JavaFileObject moduleFile = |
| JavaFileObjects.forSourceLines( |
| "test.TestModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "import javax.inject.Provider;", |
| "", |
| "@Module", |
| "final class TestModule {", |
| " @Provides static String provideString(Provider<? extends Number> numberProvider) {", |
| " return \"foo\";", |
| " }", |
| "}"); |
| Compilation compilation = daggerCompiler().compile(moduleFile, QUALIFIER_A, QUALIFIER_B); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining( |
| "Dagger does not support injecting Provider<T>, Lazy<T>, Producer<T>, or Produced<T> " |
| + "when T is a wildcard type such as ? extends java.lang.Number"); |
| } |
| |
| private static final JavaFileObject SCOPE_A = |
| JavaFileObjects.forSourceLines( |
| "test.ScopeA", |
| "package test;", |
| "", |
| "import javax.inject.Scope;", |
| "", |
| "@Scope @interface ScopeA {}"); |
| |
| private static final JavaFileObject SCOPE_B = |
| JavaFileObjects.forSourceLines( |
| "test.ScopeB", |
| "package test;", |
| "", |
| "import javax.inject.Scope;", |
| "", |
| "@Scope @interface ScopeB {}"); |
| |
| @Test |
| public void providesMethodMultipleScopes() { |
| JavaFileObject moduleFile = |
| JavaFileObjects.forSourceLines( |
| "test.TestModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "", |
| "@Module", |
| "final class TestModule {", |
| " @Provides", |
| " @ScopeA", |
| " @ScopeB", |
| " String provideString() {", |
| " return \"foo\";", |
| " }", |
| "}"); |
| Compilation compilation = daggerCompiler().compile(moduleFile, SCOPE_A, SCOPE_B); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining("cannot use more than one @Scope") |
| .inFile(moduleFile) |
| .onLineContaining("@ScopeA"); |
| assertThat(compilation) |
| .hadErrorContaining("cannot use more than one @Scope") |
| .inFile(moduleFile) |
| .onLineContaining("@ScopeB"); |
| } |
| |
| @Test public void providerDependsOnProduced() { |
| JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "import dagger.producers.Producer;", |
| "", |
| "@Module", |
| "final class TestModule {", |
| " @Provides String provideString(Producer<Integer> producer) {", |
| " return \"foo\";", |
| " }", |
| "}"); |
| Compilation compilation = daggerCompiler().compile(moduleFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining("Producer may only be injected in @Produces methods"); |
| } |
| |
| @Test public void providerDependsOnProducer() { |
| JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "import dagger.producers.Produced;", |
| "", |
| "@Module", |
| "final class TestModule {", |
| " @Provides String provideString(Produced<Integer> produced) {", |
| " return \"foo\";", |
| " }", |
| "}"); |
| Compilation compilation = daggerCompiler().compile(moduleFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining("Produced may only be injected in @Produces methods"); |
| } |
| |
| @Test |
| public void proxyMethodsConflictWithOtherFactoryMethods() { |
| JavaFileObject module = |
| JavaFileObjects.forSourceLines( |
| "test.TestModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "", |
| "@Module", |
| "interface TestModule {", |
| " @Provides", |
| " static int get() { return 1; }", |
| "", |
| " @Provides", |
| " static boolean create() { return true; }", |
| "}"); |
| |
| Compilation compilation = daggerCompiler().compile(module); |
| assertThat(compilation).succeededWithoutWarnings(); |
| assertThat(compilation) |
| .generatedSourceFile("test.TestModule_GetFactory") |
| .containsElementsIn( |
| JavaFileObjects.forSourceLines( |
| "test.TestModule_GetFactory", |
| "package test;", |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "public final class TestModule_GetFactory implements Factory<Integer> {", |
| " @Override", |
| " public Integer get() {", |
| " return proxyGet();", |
| " }", |
| "", |
| " public static TestModule_GetFactory create() {", |
| " return InstanceHolder.INSTANCE;", |
| " }", |
| "", |
| " public static int proxyGet() {", |
| " return TestModule.get();", |
| " }", |
| "}")); |
| |
| assertThat(compilation) |
| .generatedSourceFile("test.TestModule_CreateFactory") |
| .containsElementsIn( |
| JavaFileObjects.forSourceLines( |
| "test.TestModule_CreateFactory", |
| "package test;", |
| "", |
| GENERATED_CODE_ANNOTATIONS, |
| "public final class TestModule_CreateFactory implements Factory<Boolean> {", |
| " @Override", |
| " public Boolean get() {", |
| " return proxyCreate();", |
| " }", |
| "", |
| " public static TestModule_CreateFactory create() {", |
| " return InstanceHolder.INSTANCE;", |
| " }", |
| "", |
| " public static boolean proxyCreate() {", |
| " return TestModule.create();", |
| " }", |
| "}")); |
| } |
| |
| private static final String BINDS_METHOD = "@Binds abstract Foo bindFoo(FooImpl impl);"; |
| private static final String MULTIBINDS_METHOD = "@Multibinds abstract Set<Foo> foos();"; |
| private static final String STATIC_PROVIDES_METHOD = |
| "@Provides static Bar provideBar() { return new Bar(); }"; |
| private static final String INSTANCE_PROVIDES_METHOD = |
| "@Provides Baz provideBaz() { return new Baz(); }"; |
| private static final String SOME_ABSTRACT_METHOD = "abstract void blah();"; |
| |
| @Test |
| public void bindsWithInstanceProvides() { |
| Compilation compilation = compileMethodCombination(BINDS_METHOD, INSTANCE_PROVIDES_METHOD); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining( |
| "A @Module may not contain both non-static and abstract binding methods"); |
| } |
| |
| @Test |
| public void multibindsWithInstanceProvides() { |
| Compilation compilation = compileMethodCombination(MULTIBINDS_METHOD, INSTANCE_PROVIDES_METHOD); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining( |
| "A @Module may not contain both non-static and abstract binding methods"); |
| } |
| |
| @Test |
| public void bindsWithStaticProvides() { |
| assertThat(compileMethodCombination(BINDS_METHOD, STATIC_PROVIDES_METHOD)).succeeded(); |
| } |
| |
| @Test |
| public void bindsWithMultibinds() { |
| assertThat(compileMethodCombination(BINDS_METHOD, MULTIBINDS_METHOD)).succeeded(); |
| } |
| |
| @Test |
| public void multibindsWithStaticProvides() { |
| assertThat(compileMethodCombination(MULTIBINDS_METHOD, STATIC_PROVIDES_METHOD)).succeeded(); |
| } |
| |
| @Test |
| public void instanceProvidesWithAbstractMethod() { |
| assertThat(compileMethodCombination(INSTANCE_PROVIDES_METHOD, SOME_ABSTRACT_METHOD)) |
| .succeeded(); |
| } |
| |
| private Compilation compileMethodCombination(String... methodLines) { |
| JavaFileObject fooFile = |
| JavaFileObjects.forSourceLines( |
| "test.Foo", |
| "package test;", |
| "", |
| "interface Foo {}"); |
| JavaFileObject fooImplFile = |
| JavaFileObjects.forSourceLines( |
| "test.FooImpl", |
| "package test;", |
| "", |
| "import javax.inject.Inject;", |
| "", |
| "final class FooImpl implements Foo {", |
| " @Inject FooImpl() {}", |
| "}"); |
| JavaFileObject barFile = |
| JavaFileObjects.forSourceLines( |
| "test.Bar", |
| "package test;", |
| "", |
| "final class Bar {}"); |
| JavaFileObject bazFile = |
| JavaFileObjects.forSourceLines( |
| "test.Baz", |
| "package test;", |
| "", |
| "final class Baz {}"); |
| |
| ImmutableList<String> moduleLines = |
| new ImmutableList.Builder<String>() |
| .add( |
| "package test;", |
| "", |
| "import dagger.Binds;", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "import dagger.multibindings.Multibinds;", |
| "import java.util.Set;", |
| "", |
| "@Module abstract class TestModule {") |
| .add(methodLines) |
| .add("}") |
| .build(); |
| |
| JavaFileObject bindsMethodAndInstanceProvidesMethodModuleFile = |
| JavaFileObjects.forSourceLines("test.TestModule", moduleLines); |
| return daggerCompiler() |
| .compile( |
| fooFile, fooImplFile, barFile, bazFile, bindsMethodAndInstanceProvidesMethodModuleFile); |
| } |
| } |