| /* |
| * Copyright (C) 2016 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.testing.compile.CompilationSubject.assertThat; |
| import static dagger.internal.codegen.Compilers.daggerCompiler; |
| import static dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatModuleMethod; |
| |
| import com.google.testing.compile.Compilation; |
| import com.google.testing.compile.JavaFileObjects; |
| import dagger.Module; |
| import dagger.producers.ProducerModule; |
| import java.lang.annotation.Annotation; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import javax.tools.JavaFileObject; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| |
| @RunWith(Parameterized.class) |
| public final class ModuleValidationTest { |
| |
| @Parameterized.Parameters(name = "{0}") |
| public static Collection<Object[]> parameters() { |
| return Arrays.asList(new Object[][] {{ModuleType.MODULE}, {ModuleType.PRODUCER_MODULE}}); |
| } |
| |
| private enum ModuleType { |
| MODULE(Module.class), |
| PRODUCER_MODULE(ProducerModule.class), |
| ; |
| |
| private final Class<? extends Annotation> annotation; |
| |
| ModuleType(Class<? extends Annotation> annotation) { |
| this.annotation = annotation; |
| } |
| |
| String annotationWithSubcomponent(String subcomponent) { |
| return String.format("@%s(subcomponents = %s)", annotation.getSimpleName(), subcomponent); |
| } |
| |
| String importStatement() { |
| return String.format("import %s;", annotation.getName()); |
| } |
| |
| String simpleName() { |
| return annotation.getSimpleName(); |
| } |
| } |
| |
| private final ModuleType moduleType; |
| |
| public ModuleValidationTest(ModuleType moduleType) { |
| this.moduleType = moduleType; |
| } |
| |
| @Test |
| public void moduleSubcomponents_notASubcomponent() { |
| JavaFileObject module = |
| JavaFileObjects.forSourceLines( |
| "test.TestModule", |
| "package test;", |
| "", |
| moduleType.importStatement(), |
| "", |
| moduleType.annotationWithSubcomponent("NotASubcomponent.class"), |
| "class TestModule {}"); |
| JavaFileObject notASubcomponent = |
| JavaFileObjects.forSourceLines( |
| "test.NotASubcomponent", "package test;", "", "class NotASubcomponent {}"); |
| Compilation compilation = daggerCompiler().compile(module, notASubcomponent); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining( |
| "test.NotASubcomponent is not a @Subcomponent or @ProductionSubcomponent") |
| .inFile(module) |
| .onLine(5); |
| } |
| |
| @Test |
| public void moduleSubcomponents_listsSubcomponentBuilder() { |
| JavaFileObject module = |
| JavaFileObjects.forSourceLines( |
| "test.TestModule", |
| "package test;", |
| "", |
| moduleType.importStatement(), |
| "", |
| moduleType.annotationWithSubcomponent("Sub.Builder.class"), |
| "class TestModule {}"); |
| JavaFileObject subcomponent = |
| JavaFileObjects.forSourceLines( |
| "test.Sub", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "interface Sub {", |
| " @Subcomponent.Builder", |
| " interface Builder {", |
| " Sub build();", |
| " }", |
| "}"); |
| Compilation compilation = daggerCompiler().compile(module, subcomponent); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining( |
| "test.Sub.Builder is a @Subcomponent.Builder. Did you mean to use test.Sub?") |
| .inFile(module) |
| .onLine(5); |
| } |
| |
| @Test |
| public void moduleSubcomponents_listsSubcomponentFactory() { |
| JavaFileObject module = |
| JavaFileObjects.forSourceLines( |
| "test.TestModule", |
| "package test;", |
| "", |
| moduleType.importStatement(), |
| "", |
| moduleType.annotationWithSubcomponent("Sub.Factory.class"), |
| "class TestModule {}"); |
| JavaFileObject subcomponent = |
| JavaFileObjects.forSourceLines( |
| "test.Sub", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "interface Sub {", |
| " @Subcomponent.Factory", |
| " interface Factory {", |
| " Sub creator();", |
| " }", |
| "}"); |
| Compilation compilation = daggerCompiler().compile(module, subcomponent); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining( |
| "test.Sub.Factory is a @Subcomponent.Factory. Did you mean to use test.Sub?") |
| .inFile(module) |
| .onLine(5); |
| } |
| |
| @Test |
| public void moduleSubcomponents_listsProductionSubcomponentBuilder() { |
| JavaFileObject module = |
| JavaFileObjects.forSourceLines( |
| "test.TestModule", |
| "package test;", |
| "", |
| moduleType.importStatement(), |
| "", |
| moduleType.annotationWithSubcomponent("Sub.Builder.class"), |
| "class TestModule {}"); |
| JavaFileObject subcomponent = |
| JavaFileObjects.forSourceLines( |
| "test.Sub", |
| "package test;", |
| "", |
| "import dagger.producers.ProductionSubcomponent;", |
| "", |
| "@ProductionSubcomponent", |
| "interface Sub {", |
| " @ProductionSubcomponent.Builder", |
| " interface Builder {", |
| " Sub build();", |
| " }", |
| "}"); |
| Compilation compilation = daggerCompiler().compile(module, subcomponent); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining( |
| "test.Sub.Builder is a @ProductionSubcomponent.Builder. Did you mean to use test.Sub?") |
| .inFile(module) |
| .onLine(5); |
| } |
| |
| @Test |
| public void moduleSubcomponents_listsProductionSubcomponentFactory() { |
| JavaFileObject module = |
| JavaFileObjects.forSourceLines( |
| "test.TestModule", |
| "package test;", |
| "", |
| moduleType.importStatement(), |
| "", |
| moduleType.annotationWithSubcomponent("Sub.Factory.class"), |
| "class TestModule {}"); |
| JavaFileObject subcomponent = |
| JavaFileObjects.forSourceLines( |
| "test.Sub", |
| "package test;", |
| "", |
| "import dagger.producers.ProductionSubcomponent;", |
| "", |
| "@ProductionSubcomponent", |
| "interface Sub {", |
| " @ProductionSubcomponent.Factory", |
| " interface Factory {", |
| " Sub create();", |
| " }", |
| "}"); |
| Compilation compilation = daggerCompiler().compile(module, subcomponent); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining( |
| "test.Sub.Factory is a @ProductionSubcomponent.Factory. Did you mean to use test.Sub?") |
| .inFile(module) |
| .onLine(5); |
| } |
| |
| @Test |
| public void moduleSubcomponents_noSubcomponentCreator() { |
| JavaFileObject module = |
| JavaFileObjects.forSourceLines( |
| "test.TestModule", |
| "package test;", |
| "", |
| moduleType.importStatement(), |
| "", |
| moduleType.annotationWithSubcomponent("NoBuilder.class"), |
| "class TestModule {}"); |
| JavaFileObject subcomponent = |
| JavaFileObjects.forSourceLines( |
| "test.NoBuilder", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "interface NoBuilder {}"); |
| Compilation compilation = daggerCompiler().compile(module, subcomponent); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining( |
| "test.NoBuilder doesn't have a @Subcomponent.Builder or @Subcomponent.Factory, which " |
| + "is required when used with @" |
| + moduleType.simpleName() |
| + ".subcomponents") |
| .inFile(module) |
| .onLine(5); |
| } |
| |
| @Test |
| public void moduleSubcomponents_noProductionSubcomponentCreator() { |
| JavaFileObject module = |
| JavaFileObjects.forSourceLines( |
| "test.TestModule", |
| "package test;", |
| "", |
| moduleType.importStatement(), |
| "", |
| moduleType.annotationWithSubcomponent("NoBuilder.class"), |
| "class TestModule {}"); |
| JavaFileObject subcomponent = |
| JavaFileObjects.forSourceLines( |
| "test.NoBuilder", |
| "package test;", |
| "", |
| "import dagger.producers.ProductionSubcomponent;", |
| "", |
| "@ProductionSubcomponent", |
| "interface NoBuilder {}"); |
| Compilation compilation = daggerCompiler().compile(module, subcomponent); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining( |
| "test.NoBuilder doesn't have a @ProductionSubcomponent.Builder or " |
| + "@ProductionSubcomponent.Factory, which is required when used with @" |
| + moduleType.simpleName() |
| + ".subcomponents") |
| .inFile(module) |
| .onLine(5); |
| } |
| |
| @Test |
| public void moduleSubcomponentsAreTypes() { |
| JavaFileObject module = |
| JavaFileObjects.forSourceLines( |
| "test.TestModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "", |
| "@Module(subcomponents = int.class)", |
| "class TestModule {}"); |
| Compilation compilation = daggerCompiler().compile(module); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining("int is not a valid subcomponent type") |
| .inFile(module) |
| .onLine(5); |
| } |
| |
| @Test |
| public void tooManyAnnotations() { |
| assertThatModuleMethod( |
| "@BindsOptionalOf @Multibinds abstract Set<Object> tooManyAnnotations();") |
| .hasError("is annotated with more than one of"); |
| } |
| |
| @Test |
| public void invalidIncludedModule() { |
| JavaFileObject badModule = |
| JavaFileObjects.forSourceLines( |
| "test.BadModule", |
| "package test;", |
| "", |
| "import dagger.Binds;", |
| "import dagger.Module;", |
| "", |
| "@Module", |
| "abstract class BadModule {", |
| " @Binds abstract Object noParameters();", |
| "}"); |
| JavaFileObject module = |
| JavaFileObjects.forSourceLines( |
| "test.IncludesBadModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "", |
| "@Module(includes = BadModule.class)", |
| "abstract class IncludesBadModule {}"); |
| Compilation compilation = daggerCompiler().compile(badModule, module); |
| assertThat(compilation).hadErrorCount(2); |
| assertThat(compilation) |
| .hadErrorContaining("test.BadModule has errors") |
| .inFile(module) |
| .onLine(5); |
| assertThat(compilation) |
| .hadErrorContaining( |
| "@Binds methods must have exactly one parameter, whose type is assignable to the " |
| + "return type") |
| .inFile(badModule) |
| .onLine(8); |
| } |
| |
| @Test |
| public void scopeOnModule() { |
| JavaFileObject badModule = |
| JavaFileObjects.forSourceLines( |
| "test.BadModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import javax.inject.Singleton;", |
| "", |
| "@Singleton", |
| "@Module", |
| "interface BadModule {}"); |
| Compilation compilation = daggerCompiler().compile(badModule); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining("@Modules cannot be scoped") |
| .inFile(badModule) |
| .onLineContaining("@Singleton"); |
| } |
| |
| @Test |
| public void moduleIncludesSelfCycle() { |
| JavaFileObject module = |
| JavaFileObjects.forSourceLines( |
| "test.TestModule", |
| "package test;", |
| "", |
| moduleType.importStatement(), |
| "import dagger.Provides;", |
| "", |
| String.format("@%s(", moduleType.simpleName()), |
| " includes = {", |
| " TestModule.class, // first", |
| " OtherModule.class,", |
| " TestModule.class, // second", |
| " }", |
| ")", |
| "class TestModule {", |
| " @Provides int i() { return 0; }", |
| "}"); |
| |
| JavaFileObject otherModule = |
| JavaFileObjects.forSourceLines( |
| "test.OtherModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "", |
| "@Module", |
| "class OtherModule {}"); |
| |
| Compilation compilation = daggerCompiler().compile(module, otherModule); |
| assertThat(compilation).failed(); |
| String error = String.format("@%s cannot include themselves", moduleType.simpleName()); |
| assertThat(compilation).hadErrorContaining(error).inFile(module).onLineContaining("Module("); |
| } |
| } |