| /* |
| * Copyright (C) 2015 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.CompilerMode.DEFAULT_MODE; |
| import static dagger.internal.codegen.TestUtils.message; |
| import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.SUBCOMPONENT_BUILDER; |
| import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.SUBCOMPONENT_FACTORY; |
| import static dagger.internal.codegen.binding.ComponentCreatorKind.BUILDER; |
| import static dagger.internal.codegen.binding.ComponentCreatorKind.FACTORY; |
| import static dagger.internal.codegen.binding.ComponentKind.SUBCOMPONENT; |
| import static dagger.internal.codegen.binding.ErrorMessages.ComponentCreatorMessages.moreThanOneRefToSubcomponent; |
| import static dagger.internal.codegen.binding.ErrorMessages.componentMessagesFor; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.testing.compile.Compilation; |
| import com.google.testing.compile.JavaFileObjects; |
| import dagger.internal.codegen.binding.ComponentCreatorAnnotation; |
| import java.util.Collection; |
| import javax.tools.JavaFileObject; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| import org.junit.runners.Parameterized.Parameters; |
| |
| /** Tests for {@link dagger.Subcomponent.Builder} validation. */ |
| @RunWith(Parameterized.class) |
| public class SubcomponentCreatorValidationTest extends ComponentCreatorTestHelper { |
| @Parameters(name = "creatorKind={0}") |
| public static Collection<Object[]> parameters() { |
| return ImmutableList.copyOf(new Object[][] {{SUBCOMPONENT_BUILDER}, {SUBCOMPONENT_FACTORY}}); |
| } |
| |
| public SubcomponentCreatorValidationTest(ComponentCreatorAnnotation componentCreatorAnnotation) { |
| super(DEFAULT_MODE, componentCreatorAnnotation); |
| } |
| |
| @Test |
| public void testRefSubcomponentAndSubCreatorFails() { |
| JavaFileObject componentFile = preprocessedJavaFile("test.ParentComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "import javax.inject.Provider;", |
| "", |
| "@Component", |
| "interface ParentComponent {", |
| " ChildComponent child();", |
| " ChildComponent.Builder builder();", |
| "}"); |
| JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "interface ChildComponent {", |
| " @Subcomponent.Builder", |
| " static interface Builder {", |
| " ChildComponent build();", |
| " }", |
| "}"); |
| Compilation compilation = compile(componentFile, childComponentFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining( |
| String.format( |
| moreThanOneRefToSubcomponent(), |
| "test.ChildComponent", |
| process("[child(), builder()]"))) |
| .inFile(componentFile); |
| } |
| |
| @Test |
| public void testRefSubCreatorTwiceFails() { |
| JavaFileObject componentFile = preprocessedJavaFile("test.ParentComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "import javax.inject.Provider;", |
| "", |
| "@Component", |
| "interface ParentComponent {", |
| " ChildComponent.Builder builder1();", |
| " ChildComponent.Builder builder2();", |
| "}"); |
| JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "interface ChildComponent {", |
| " @Subcomponent.Builder", |
| " static interface Builder {", |
| " ChildComponent build();", |
| " }", |
| "}"); |
| Compilation compilation = compile(componentFile, childComponentFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining( |
| String.format( |
| moreThanOneRefToSubcomponent(), |
| "test.ChildComponent", process("[builder1(), builder2()]"))) |
| .inFile(componentFile); |
| } |
| |
| @Test |
| public void testMoreThanOneCreatorFails() { |
| JavaFileObject componentFile = preprocessedJavaFile("test.ParentComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "import javax.inject.Provider;", |
| "", |
| "@Component", |
| "interface ParentComponent {", |
| " ChildComponent.Builder1 build();", |
| "}"); |
| JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "interface ChildComponent {", |
| " @Subcomponent.Builder", |
| " static interface Builder1 {", |
| " ChildComponent build();", |
| " }", |
| "", |
| " @Subcomponent.Builder", |
| " static interface Builder2 {", |
| " ChildComponent build();", |
| " }", |
| "}"); |
| Compilation compilation = compile(componentFile, childComponentFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining( |
| String.format( |
| componentMessagesFor(SUBCOMPONENT).moreThanOne(), |
| process("[test.ChildComponent.Builder1, test.ChildComponent.Builder2]"))) |
| .inFile(childComponentFile); |
| } |
| |
| @Test |
| public void testMoreThanOneCreatorFails_differentTypes() { |
| JavaFileObject componentFile = preprocessedJavaFile("test.ParentComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "import javax.inject.Provider;", |
| "", |
| "@Component", |
| "interface ParentComponent {", |
| " ChildComponent.Builder build();", |
| "}"); |
| JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "interface ChildComponent {", |
| " @Subcomponent.Builder", |
| " static interface Builder {", |
| " ChildComponent build();", |
| " }", |
| "", |
| " @Subcomponent.Factory", |
| " static interface Factory {", |
| " ChildComponent create();", |
| " }", |
| "}"); |
| Compilation compilation = compile(componentFile, childComponentFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining( |
| String.format( |
| componentMessagesFor(SUBCOMPONENT).moreThanOne(), |
| "[test.ChildComponent.Builder, test.ChildComponent.Factory]")) |
| .inFile(childComponentFile); |
| } |
| |
| @Test |
| public void testCreatorGenericsFails() { |
| JavaFileObject componentFile = preprocessedJavaFile("test.ParentComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "import javax.inject.Provider;", |
| "", |
| "@Component", |
| "interface ParentComponent {", |
| " ChildComponent.Builder build();", |
| "}"); |
| JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "interface ChildComponent {", |
| " @Subcomponent.Builder", |
| " interface Builder<T> {", |
| " ChildComponent build();", |
| " }", |
| "}"); |
| Compilation compilation = compile(componentFile, childComponentFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation).hadErrorContaining(messages.generics()).inFile(childComponentFile); |
| } |
| |
| @Test |
| public void testCreatorNotInComponentFails() { |
| JavaFileObject builder = preprocessedJavaFile("test.Builder", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent.Builder", |
| "interface Builder {}"); |
| Compilation compilation = compile(builder); |
| assertThat(compilation).failed(); |
| assertThat(compilation).hadErrorContaining(messages.mustBeInComponent()).inFile(builder); |
| } |
| |
| @Test |
| public void testCreatorMissingFactoryMethodFails() { |
| JavaFileObject componentFile = preprocessedJavaFile("test.ParentComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "import javax.inject.Provider;", |
| "", |
| "@Component", |
| "interface ParentComponent {", |
| " ChildComponent.Builder builder();", |
| "}"); |
| JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "interface ChildComponent {", |
| " @Subcomponent.Builder", |
| " interface Builder {}", |
| "}"); |
| Compilation compilation = compile(componentFile, childComponentFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining(messages.missingFactoryMethod()) |
| .inFile(childComponentFile); |
| } |
| |
| @Test |
| public void testPrivateCreatorFails() { |
| JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "abstract class ChildComponent {", |
| " @Subcomponent.Builder", |
| " private interface Builder {}", |
| "}"); |
| Compilation compilation = compile(childComponentFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation).hadErrorContaining(messages.isPrivate()).inFile(childComponentFile); |
| } |
| |
| @Test |
| public void testNonStaticCreatorFails() { |
| JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "abstract class ChildComponent {", |
| " @Subcomponent.Builder", |
| " abstract class Builder {}", |
| "}"); |
| Compilation compilation = compile(childComponentFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation).hadErrorContaining(messages.mustBeStatic()).inFile(childComponentFile); |
| } |
| |
| @Test |
| public void testNonAbstractCreatorFails() { |
| JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "abstract class ChildComponent {", |
| " @Subcomponent.Builder", |
| " static class Builder {}", |
| "}"); |
| Compilation compilation = compile(childComponentFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining(messages.mustBeAbstract()) |
| .inFile(childComponentFile); |
| } |
| |
| @Test |
| public void testCreatorOneConstructorWithArgsFails() { |
| JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "abstract class ChildComponent {", |
| " @Subcomponent.Builder", |
| " static abstract class Builder {", |
| " Builder(String unused) {}", |
| " }", |
| "}"); |
| Compilation compilation = compile(childComponentFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining(messages.invalidConstructor()) |
| .inFile(childComponentFile); |
| } |
| |
| @Test |
| public void testCreatorMoreThanOneConstructorFails() { |
| JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "abstract class ChildComponent {", |
| " @Subcomponent.Builder", |
| " static abstract class Builder {", |
| " Builder() {}", |
| " Builder(String unused) {}", |
| " }", |
| "}"); |
| Compilation compilation = compile(childComponentFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining(messages.invalidConstructor()) |
| .inFile(childComponentFile); |
| } |
| |
| @Test |
| public void testCreatorEnumFails() { |
| JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "abstract class ChildComponent {", |
| " @Subcomponent.Builder", |
| " enum Builder {}", |
| "}"); |
| Compilation compilation = compile(childComponentFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining(messages.mustBeClassOrInterface()) |
| .inFile(childComponentFile); |
| } |
| |
| @Test |
| public void testCreatorFactoryMethodReturnsWrongTypeFails() { |
| JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "abstract class ChildComponent {", |
| " @Subcomponent.Builder", |
| " interface Builder {", |
| " String build();", |
| " }", |
| "}"); |
| Compilation compilation = compile(childComponentFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining(messages.factoryMethodMustReturnComponentType()) |
| .inFile(childComponentFile) |
| .onLine(9); |
| } |
| |
| @Test |
| public void testInheritedCreatorFactoryMethodReturnsWrongTypeFails() { |
| JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "abstract class ChildComponent {", |
| " interface Parent {", |
| " String build();", |
| " }", |
| "", |
| " @Subcomponent.Builder", |
| " interface Builder extends Parent {}", |
| "}"); |
| Compilation compilation = compile(childComponentFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining( |
| String.format( |
| messages.inheritedFactoryMethodMustReturnComponentType(), process("build"))) |
| .inFile(childComponentFile) |
| .onLine(12); |
| } |
| |
| @Test |
| public void testTwoFactoryMethodsFails() { |
| JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "abstract class ChildComponent {", |
| " @Subcomponent.Builder", |
| " interface Builder {", |
| " ChildComponent build();", |
| " ChildComponent build1();", |
| " }", |
| "}"); |
| Compilation compilation = compile(childComponentFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining(String.format(messages.twoFactoryMethods(), process("build()"))) |
| .inFile(childComponentFile) |
| .onLine(10); |
| } |
| |
| @Test |
| public void testInheritedTwoFactoryMethodsFails() { |
| JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "abstract class ChildComponent {", |
| " interface Parent {", |
| " ChildComponent build();", |
| " ChildComponent build1();", |
| " }", |
| "", |
| " @Subcomponent.Builder", |
| " interface Builder extends Parent {}", |
| "}"); |
| Compilation compilation = compile(childComponentFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining( |
| String.format( |
| messages.inheritedTwoFactoryMethods(), process("build()"), process("build1()"))) |
| .inFile(childComponentFile) |
| .onLine(13); |
| } |
| |
| @Test |
| public void testMultipleSettersPerTypeFails() { |
| JavaFileObject moduleFile = |
| JavaFileObjects.forSourceLines( |
| "test.TestModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "", |
| "@Module", |
| "final class TestModule {", |
| " @Provides String s() { return \"\"; }", |
| "}"); |
| JavaFileObject componentFile = |
| preprocessedJavaFile( |
| "test.ParentComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "", |
| "@Component", |
| "interface ParentComponent {", |
| " ChildComponent.Builder childComponentBuilder();", |
| "}"); |
| JavaFileObject childComponentFile = |
| javaFileBuilder("test.ChildComponent") |
| .addLines( |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "import javax.inject.Provider;", |
| "", |
| "@Subcomponent(modules = TestModule.class)", |
| "abstract class ChildComponent {", |
| " abstract String s();", |
| "") |
| .addLinesIf( |
| BUILDER, |
| " @Subcomponent.Builder", |
| " interface Builder {", |
| " ChildComponent build();", |
| " void set1(TestModule s);", |
| " void set2(TestModule s);", |
| " }") |
| .addLinesIf( |
| FACTORY, |
| " @Subcomponent.Factory", |
| " interface Factory {", |
| " ChildComponent create(TestModule m1, TestModule m2);", |
| " }") |
| .addLines( // |
| "}") |
| .build(); |
| Compilation compilation = compile(moduleFile, componentFile, childComponentFile); |
| assertThat(compilation).failed(); |
| String elements = |
| creatorKind.equals(BUILDER) |
| ? "[void test.ChildComponent.Builder.set1(test.TestModule), " |
| + "void test.ChildComponent.Builder.set2(test.TestModule)]" |
| : "[test.TestModule m1, test.TestModule m2]"; |
| assertThat(compilation) |
| .hadErrorContaining( |
| String.format( |
| messages.multipleSettersForModuleOrDependencyType(), "test.TestModule", elements)) |
| .inFile(childComponentFile) |
| .onLine(11); |
| } |
| |
| @Test |
| public void testMultipleSettersPerTypeIncludingResolvedGenericsFails() { |
| JavaFileObject moduleFile = |
| JavaFileObjects.forSourceLines( |
| "test.TestModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "", |
| "@Module", |
| "final class TestModule {", |
| " @Provides String s() { return \"\"; }", |
| "}"); |
| JavaFileObject componentFile = |
| preprocessedJavaFile( |
| "test.ParentComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "", |
| "@Component", |
| "interface ParentComponent {", |
| " ChildComponent.Builder childComponentBuilder();", |
| "}"); |
| JavaFileObject childComponentFile = |
| javaFileBuilder("test.ChildComponent") |
| .addLines( |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "import javax.inject.Provider;", |
| "", |
| "@Subcomponent(modules = TestModule.class)", |
| "abstract class ChildComponent {", |
| " abstract String s();", |
| "") |
| .addLinesIf( |
| BUILDER, |
| " interface Parent<T> {", |
| " void set1(T t);", |
| " }", |
| "", |
| " @Subcomponent.Builder", |
| " interface Builder extends Parent<TestModule> {", |
| " ChildComponent build();", |
| " void set2(TestModule s);", |
| " }") |
| .addLinesIf( |
| FACTORY, |
| " interface Parent<C, T> {", |
| " C create(TestModule m1, T t);", |
| " }", |
| "", |
| " @Subcomponent.Factory", |
| " interface Factory extends Parent<ChildComponent, TestModule> {}") |
| .addLines( // |
| "}") |
| .build(); |
| Compilation compilation = compile(moduleFile, componentFile, childComponentFile); |
| assertThat(compilation).failed(); |
| String elements = |
| creatorKind.equals(BUILDER) |
| ? "[void test.ChildComponent.Builder.set1(test.TestModule), " |
| + "void test.ChildComponent.Builder.set2(test.TestModule)]" |
| : "[test.TestModule m1, test.TestModule t]"; |
| assertThat(compilation) |
| .hadErrorContaining( |
| String.format( |
| messages.multipleSettersForModuleOrDependencyType(), "test.TestModule", elements)) |
| .inFile(childComponentFile) |
| .onLine(15); |
| } |
| |
| @Test |
| public void testMultipleSettersPerBoundInstanceTypeFails() { |
| JavaFileObject componentFile = |
| preprocessedJavaFile( |
| "test.ParentComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "", |
| "@Component", |
| "interface ParentComponent {", |
| " ChildComponent.Builder childComponentBuilder();", |
| "}"); |
| JavaFileObject childComponentFile = |
| javaFileBuilder("test.ChildComponent") |
| .addLines( |
| "package test;", |
| "", |
| "import dagger.BindsInstance;", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "abstract class ChildComponent {", |
| " abstract String s();", |
| "") |
| .addLinesIf( |
| BUILDER, |
| " @Subcomponent.Builder", |
| " interface Builder {", |
| " ChildComponent build();", |
| " @BindsInstance void set1(String s);", |
| " @BindsInstance void set2(String s);", |
| " }") |
| .addLinesIf( |
| FACTORY, |
| " @Subcomponent.Factory", |
| " interface Factory {", |
| " ChildComponent create(", |
| " @BindsInstance String s1, @BindsInstance String s2);", |
| " }") |
| .addLines( // |
| "}") |
| .build(); |
| |
| Compilation compilation = compile(componentFile, childComponentFile); |
| assertThat(compilation).failed(); |
| String firstBinding = creatorKind.equals(FACTORY) |
| ? "ChildComponent.Factory.create(s1, …)" |
| : "@BindsInstance void ChildComponent.Builder.set1(String)"; |
| String secondBinding = creatorKind.equals(FACTORY) |
| ? "ChildComponent.Factory.create(…, s2)" |
| : "@BindsInstance void ChildComponent.Builder.set2(String)"; |
| assertThat(compilation) |
| .hadErrorContaining( |
| message( |
| "String is bound multiple times:", |
| " " + firstBinding, |
| " " + secondBinding, |
| " in component: [ParentComponent → ChildComponent]")) |
| .inFile(componentFile) |
| .onLineContaining("interface ParentComponent {"); |
| } |
| |
| @Test |
| public void testExtraSettersFails() { |
| JavaFileObject componentFile = preprocessedJavaFile("test.ParentComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "import javax.inject.Provider;", |
| "", |
| "@Component", |
| "interface ParentComponent {", |
| " ChildComponent.Builder build();", |
| "}"); |
| JavaFileObject childComponentFile = |
| javaFileBuilder("test.ChildComponent") |
| .addLines( |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "import javax.inject.Provider;", |
| "", |
| "@Subcomponent", |
| "abstract class ChildComponent {") |
| .addLinesIf( |
| BUILDER, |
| " @Subcomponent.Builder", |
| " interface Builder {", |
| " ChildComponent build();", |
| " void set1(String s);", |
| " void set2(Integer s);", |
| " }") |
| .addLinesIf( |
| FACTORY, |
| " @Subcomponent.Factory", |
| " interface Factory {", |
| " ChildComponent create(String s, Integer i);", |
| " }") |
| .addLines( // |
| "}") |
| .build(); |
| Compilation compilation = compile(componentFile, childComponentFile); |
| assertThat(compilation).failed(); |
| String elements = |
| creatorKind.equals(FACTORY) |
| ? "[String s, Integer i]" |
| : "[void test.ChildComponent.Builder.set1(String)," |
| + " void test.ChildComponent.Builder.set2(Integer)]"; |
| assertThat(compilation) |
| .hadErrorContaining( |
| String.format( |
| messages.extraSetters(), |
| elements)) |
| .inFile(childComponentFile) |
| .onLine(9); |
| } |
| |
| @Test |
| public void testMissingSettersFail() { |
| JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "", |
| "@Module", |
| "final class TestModule {", |
| " TestModule(String unused) {}", |
| " @Provides String s() { return null; }", |
| "}"); |
| JavaFileObject module2File = JavaFileObjects.forSourceLines("test.Test2Module", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "", |
| "@Module", |
| "final class Test2Module {", |
| " @Provides Integer i() { return null; }", |
| "}"); |
| JavaFileObject module3File = JavaFileObjects.forSourceLines("test.Test3Module", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "", |
| "@Module", |
| "final class Test3Module {", |
| " Test3Module(String unused) {}", |
| " @Provides Double d() { return null; }", |
| "}"); |
| JavaFileObject componentFile = preprocessedJavaFile("test.ParentComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "import javax.inject.Provider;", |
| "", |
| "@Component", |
| "interface ParentComponent {", |
| " ChildComponent.Builder build();", |
| "}"); |
| JavaFileObject childComponentFile = |
| preprocessedJavaFile( |
| "test.TestComponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent(modules = {TestModule.class, Test2Module.class, Test3Module.class})", |
| "interface ChildComponent {", |
| " String string();", |
| " Integer integer();", |
| "", |
| " @Subcomponent.Builder", |
| " interface Builder {", |
| " ChildComponent build();", |
| " }", |
| "}"); |
| Compilation compilation = |
| compile(moduleFile, module2File, module3File, componentFile, childComponentFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining( |
| // Ignores Test2Module because we can construct it ourselves. |
| // TODO(sameb): Ignore Test3Module because it's not used within transitive dependencies. |
| String.format(messages.missingSetters(), "[test.TestModule, test.Test3Module]")) |
| .inFile(childComponentFile) |
| .onLine(11); |
| } |
| |
| @Test |
| public void covariantFactoryMethodReturnType() { |
| JavaFileObject foo = |
| JavaFileObjects.forSourceLines( |
| "test.Foo", |
| "package test;", |
| "", |
| "import javax.inject.Inject;", |
| "", |
| "class Foo {", |
| " @Inject Foo() {}", |
| "}"); |
| JavaFileObject supertype = |
| JavaFileObjects.forSourceLines( |
| "test.Supertype", |
| "package test;", |
| "", |
| "interface Supertype {", |
| " Foo foo();", |
| "}"); |
| |
| JavaFileObject subcomponent = |
| preprocessedJavaFile( |
| "test.HasSupertype", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "interface HasSupertype extends Supertype {", |
| " @Subcomponent.Builder", |
| " interface Builder {", |
| " Supertype build();", |
| " }", |
| "}"); |
| |
| Compilation compilation = compile(foo, supertype, subcomponent); |
| assertThat(compilation).succeededWithoutWarnings(); |
| } |
| |
| @Test |
| public void covariantFactoryMethodReturnType_hasNewMethod() { |
| JavaFileObject foo = |
| JavaFileObjects.forSourceLines( |
| "test.Foo", |
| "package test;", |
| "", |
| "import javax.inject.Inject;", |
| "", |
| "class Foo {", |
| " @Inject Foo() {}", |
| "}"); |
| JavaFileObject bar = |
| JavaFileObjects.forSourceLines( |
| "test.Bar", |
| "package test;", |
| "", |
| "import javax.inject.Inject;", |
| "", |
| "class Bar {", |
| " @Inject Bar() {}", |
| "}"); |
| JavaFileObject supertype = |
| JavaFileObjects.forSourceLines( |
| "test.Supertype", |
| "package test;", |
| "", |
| "interface Supertype {", |
| " Foo foo();", |
| "}"); |
| |
| JavaFileObject subcomponent = |
| preprocessedJavaFile( |
| "test.HasSupertype", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "interface HasSupertype extends Supertype {", |
| " Bar bar();", |
| "", |
| " @Subcomponent.Builder", |
| " interface Builder {", |
| " Supertype build();", |
| " }", |
| "}"); |
| |
| Compilation compilation = compile(foo, bar, supertype, subcomponent); |
| assertThat(compilation).succeeded(); |
| assertThat(compilation) |
| .hadWarningContaining( |
| process( |
| "test.HasSupertype.Builder.build() returns test.Supertype, but test.HasSupertype " |
| + "declares additional component method(s): bar(). In order to provide " |
| + "type-safe access to these methods, override build() to return " |
| + "test.HasSupertype")) |
| .inFile(subcomponent) |
| .onLine(11); |
| } |
| |
| @Test |
| public void covariantFactoryMethodReturnType_hasNewMethod_buildMethodInherited() { |
| JavaFileObject foo = |
| JavaFileObjects.forSourceLines( |
| "test.Foo", |
| "package test;", |
| "", |
| "import javax.inject.Inject;", |
| "", |
| "class Foo {", |
| " @Inject Foo() {}", |
| "}"); |
| JavaFileObject bar = |
| JavaFileObjects.forSourceLines( |
| "test.Bar", |
| "package test;", |
| "", |
| "import javax.inject.Inject;", |
| "", |
| "class Bar {", |
| " @Inject Bar() {}", |
| "}"); |
| JavaFileObject supertype = |
| JavaFileObjects.forSourceLines( |
| "test.Supertype", |
| "package test;", |
| "", |
| "interface Supertype {", |
| " Foo foo();", |
| "}"); |
| |
| JavaFileObject creatorSupertype = |
| preprocessedJavaFile( |
| "test.CreatorSupertype", |
| "package test;", |
| "", |
| "interface CreatorSupertype {", |
| " Supertype build();", |
| "}"); |
| |
| JavaFileObject subcomponent = |
| preprocessedJavaFile( |
| "test.HasSupertype", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "interface HasSupertype extends Supertype {", |
| " Bar bar();", |
| "", |
| " @Subcomponent.Builder", |
| " interface Builder extends CreatorSupertype {}", |
| "}"); |
| |
| Compilation compilation = compile(foo, bar, supertype, creatorSupertype, subcomponent); |
| assertThat(compilation).succeeded(); |
| assertThat(compilation) |
| .hadWarningContaining( |
| process( |
| "[test.CreatorSupertype.build()] test.HasSupertype.Builder.build() returns " |
| + "test.Supertype, but test.HasSupertype declares additional component " |
| + "method(s): bar(). In order to provide type-safe access to these methods, " |
| + "override build() to return test.HasSupertype")); |
| } |
| } |