/* * 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.common.truth.TruthJUnit.assume; import static com.google.testing.compile.CompilationSubject.assertThat; import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE; import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE; import static dagger.internal.codegen.Compilers.compilerWithOptions; import static dagger.internal.codegen.Compilers.daggerCompiler; import static dagger.internal.codegen.ComponentCreatorTest.CompilerType.JAVAC; import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS; import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION; import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.COMPONENT_BUILDER; import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.COMPONENT_FACTORY; import static dagger.internal.codegen.binding.ComponentCreatorKind.BUILDER; import static dagger.internal.codegen.binding.ComponentCreatorKind.FACTORY; import static dagger.internal.codegen.binding.ComponentKind.COMPONENT; 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 properties of component creators shared by both builders and factories. */ @RunWith(Parameterized.class) public class ComponentCreatorTest extends ComponentCreatorTestHelper { enum CompilerType { JAVAC } private final CompilerType compilerType; private final CompilerMode compilerMode; @Parameters(name = "compilerMode={0}, creatorKind={1}") public static Collection parameters() { return ImmutableList.of( new Object[]{DEFAULT_MODE, COMPONENT_BUILDER, JAVAC}, new Object[]{DEFAULT_MODE, COMPONENT_FACTORY, JAVAC}, new Object[]{FAST_INIT_MODE, COMPONENT_BUILDER, JAVAC}, new Object[]{FAST_INIT_MODE, COMPONENT_FACTORY, JAVAC}); } public ComponentCreatorTest( CompilerMode compilerMode, ComponentCreatorAnnotation componentCreatorAnnotation, CompilerType compilerType) { super(compilerMode, componentCreatorAnnotation); this.compilerMode = compilerMode; this.compilerType = compilerType; } @Test public void testEmptyCreator() { assume().that(compilerType).isEqualTo(JAVAC); JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines( "test.SomeInjectableType", "package test;", "", "import javax.inject.Inject;", "", "final class SomeInjectableType {", " @Inject SomeInjectableType() {}", "}"); JavaFileObject componentFile = preprocessedJavaFile( "test.SimpleComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "interface SimpleComponent {", " SomeInjectableType someInjectableType();", "", " @Component.Builder", " static interface Builder {", " SimpleComponent build();", " }", "}"); JavaFileObject generatedComponent = preprocessedJavaFile( "test.DaggerSimpleComponent", "package test;", "", GENERATED_CODE_ANNOTATIONS, "final class DaggerSimpleComponent implements SimpleComponent {", " private static final class Builder implements SimpleComponent.Builder {", " @Override", " public SimpleComponent build() {", " return new DaggerSimpleComponent();", " }", " }", "}"); Compilation compilation = compile(injectableTypeFile, componentFile); assertThat(compilation).succeeded(); assertThat(compilation) .generatedSourceFile("test.DaggerSimpleComponent") .containsElementsIn(generatedComponent); } @Test public void testCanInstantiateModulesUserCannotSet() { assume().that(compilerType).isEqualTo(JAVAC); JavaFileObject module = JavaFileObjects.forSourceLines( "test.TestModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "", "@Module", "final class TestModule {", " @Provides String string() { return null; }", "}"); JavaFileObject componentFile = preprocessedJavaFile( "test.TestComponent", "package test;", "", "import dagger.Component;", "", "@Component(modules = TestModule.class)", "interface TestComponent {", " String string();", "", " @Component.Builder", " interface Builder {", " TestComponent build();", " }", "}"); JavaFileObject generatedComponent = preprocessedJavaFile( "test.DaggerTestComponent", "package test;", "", IMPORT_GENERATED_ANNOTATION, "", GENERATED_CODE_ANNOTATIONS, "final class DaggerTestComponent implements TestComponent {", " private final TestModule testModule;", "", " private DaggerTestComponent(TestModule testModuleParam) {", " this.testModule = testModuleParam;", " }", "", " public static TestComponent.Builder builder() {", " return new Builder();", " }", "", " public static TestComponent create() {", " return new Builder().build();", " }", "", " @Override", " public String string() {", " return TestModule_StringFactory.string(testModule);", " }", "", " private static final class Builder implements TestComponent.Builder {", " @Override", " public TestComponent build() {", " return new DaggerTestComponent(new TestModule());", " }", " }", "}"); Compilation compilation = compile(module, componentFile); assertThat(compilation).succeeded(); assertThat(compilation) .generatedSourceFile("test.DaggerTestComponent") .hasSourceEquivalentTo(generatedComponent); } @Test public void testMoreThanOneCreatorOfSameTypeFails() { JavaFileObject componentFile = preprocessedJavaFile( "test.SimpleComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "interface SimpleComponent {", " @Component.Builder", " static interface Builder {", " SimpleComponent build();", " }", "", " @Component.Builder", " interface Builder2 {", " SimpleComponent build();", " }", "}"); Compilation compilation = compile(componentFile); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining( String.format( componentMessagesFor(COMPONENT).moreThanOne(), process("[test.SimpleComponent.Builder, test.SimpleComponent.Builder2]"))) .inFile(componentFile); } @Test public void testBothBuilderAndFactoryFails() { JavaFileObject componentFile = JavaFileObjects.forSourceLines( "test.SimpleComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "interface SimpleComponent {", " @Component.Builder", " static interface Builder {", " SimpleComponent build();", " }", "", " @Component.Factory", " interface Factory {", " SimpleComponent create();", " }", "}"); Compilation compilation = compile(componentFile); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining( String.format( componentMessagesFor(COMPONENT).moreThanOne(), "[test.SimpleComponent.Builder, test.SimpleComponent.Factory]")) .inFile(componentFile); } @Test public void testGenericCreatorTypeFails() { JavaFileObject componentFile = preprocessedJavaFile( "test.SimpleComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "interface SimpleComponent {", " @Component.Builder", " interface Builder {", " SimpleComponent build();", " }", "}"); Compilation compilation = compile(componentFile); assertThat(compilation).failed(); assertThat(compilation).hadErrorContaining(messages.generics()).inFile(componentFile); } @Test public void testCreatorNotInComponentFails() { JavaFileObject builder = preprocessedJavaFile( "test.Builder", "package test;", "", "import dagger.Component;", "", "@Component.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.SimpleComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "interface SimpleComponent {", " @Component.Builder", " interface Builder {}", "}"); Compilation compilation = compile(componentFile); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining(messages.missingFactoryMethod()) .inFile(componentFile); } @Test public void testCreatorWithBindsInstanceNoStaticCreateGenerated() { assume().that(compilerType).isEqualTo(JAVAC); JavaFileObject componentFile = javaFileBuilder("test.SimpleComponent") .addLines( "package test;", "", "import dagger.BindsInstance;", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "interface SimpleComponent {", " Object object();", "") .addLinesIf( BUILDER, " @Component.Builder", " interface Builder {", " @BindsInstance Builder object(Object object);", " SimpleComponent build();", " }") .addLinesIf( FACTORY, " @Component.Factory", " interface Factory {", " SimpleComponent create(@BindsInstance Object object);", " }") .addLines("}") .build(); JavaFileObject generatedComponent = javaFileBuilder("test.DaggerSimpleComponent") .addLines( "package test;", "", "import dagger.internal.Preconditions;", IMPORT_GENERATED_ANNOTATION, "", GENERATED_CODE_ANNOTATIONS, "final class DaggerSimpleComponent implements SimpleComponent {", " private final Object object;", "", " private DaggerSimpleComponent(Object objectParam) {", " this.object = objectParam;", " }", "") .addLinesIf( BUILDER, " public static SimpleComponent.Builder builder() {", " return new Builder();", " }") .addLinesIf( FACTORY, " public static SimpleComponent.Factory factory() {", " return new Factory();", " }") .addLines( "", // " @Override", " public Object object() {", " return object;", " }", "") .addLinesIf( BUILDER, " private static final class Builder implements SimpleComponent.Builder {", " private Object object;", "", " @Override", " public Builder object(Object object) {", " this.object = Preconditions.checkNotNull(object);", " return this;", " }", "", " @Override", " public SimpleComponent build() {", " Preconditions.checkBuilderRequirement(object, Object.class);", " return new DaggerSimpleComponent(object);", " }", " }") .addLinesIf( FACTORY, " private static final class Factory implements SimpleComponent.Factory {", " @Override", " public SimpleComponent create(Object object) {", " Preconditions.checkNotNull(object);", " return new DaggerSimpleComponent(object);", " }", " }") .addLines("}") .build(); Compilation compilation = compile(componentFile); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) .generatedSourceFile("test.DaggerSimpleComponent") .hasSourceEquivalentTo(generatedComponent); } @Test public void testCreatorWithPrimitiveBindsInstance() { assume().that(compilerType).isEqualTo(JAVAC); JavaFileObject componentFile = javaFileBuilder("test.SimpleComponent") .addLines( "package test;", "", "import dagger.BindsInstance;", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "interface SimpleComponent {", " int anInt();", "") .addLinesIf( BUILDER, " @Component.Builder", " interface Builder {", " @BindsInstance Builder i(int i);", " SimpleComponent build();", " }") .addLinesIf( FACTORY, " @Component.Factory", " interface Factory {", " SimpleComponent create(@BindsInstance int i);", " }") .addLines( "}") .build(); JavaFileObject generatedComponent = javaFileBuilder("test.DaggerSimpleComponent") .addLines( "package test;", "", "import dagger.internal.Preconditions;", IMPORT_GENERATED_ANNOTATION, "", GENERATED_CODE_ANNOTATIONS, "final class DaggerSimpleComponent implements SimpleComponent {", " private final Integer i;", "", " private DaggerSimpleComponent(Integer iParam) {", " this.i = iParam;", " }", "", " @Override", " public int anInt() {", " return i;", " }", "") .addLinesIf( BUILDER, " private static final class Builder implements SimpleComponent.Builder {", " private Integer i;", "", " @Override", " public Builder i(int i) {", " this.i = Preconditions.checkNotNull(i);", " return this;", " }", "", " @Override", " public SimpleComponent build() {", " Preconditions.checkBuilderRequirement(i, Integer.class);", " return new DaggerSimpleComponent(i);", " }", " }") .addLinesIf( FACTORY, " private static final class Factory implements SimpleComponent.Factory {", " @Override", " public SimpleComponent create(int i) {", " Preconditions.checkNotNull(i);", " return new DaggerSimpleComponent(i);", " }", " }") .addLines( "}") .build(); Compilation compilation = compile(componentFile); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) .generatedSourceFile("test.DaggerSimpleComponent") .containsElementsIn(generatedComponent); } @Test public void testPrivateCreatorFails() { JavaFileObject componentFile = preprocessedJavaFile( "test.SimpleComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "abstract class SimpleComponent {", " @Component.Builder", " private interface Builder {}", "}"); Compilation compilation = compile(componentFile); assertThat(compilation).failed(); assertThat(compilation).hadErrorContaining(messages.isPrivate()).inFile(componentFile); } @Test public void testNonStaticCreatorFails() { JavaFileObject componentFile = preprocessedJavaFile( "test.SimpleComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "abstract class SimpleComponent {", " @Component.Builder", " abstract class Builder {}", "}"); Compilation compilation = compile(componentFile); assertThat(compilation).failed(); assertThat(compilation).hadErrorContaining(messages.mustBeStatic()).inFile(componentFile); } @Test public void testNonAbstractCreatorFails() { JavaFileObject componentFile = preprocessedJavaFile( "test.SimpleComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "abstract class SimpleComponent {", " @Component.Builder", " static class Builder {}", "}"); Compilation compilation = compile(componentFile); assertThat(compilation).failed(); assertThat(compilation).hadErrorContaining(messages.mustBeAbstract()).inFile(componentFile); } @Test public void testCreatorOneConstructorWithArgsFails() { JavaFileObject componentFile = preprocessedJavaFile( "test.SimpleComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "abstract class SimpleComponent {", " @Component.Builder", " static abstract class Builder {", " Builder(String unused) {}", " }", "}"); Compilation compilation = compile(componentFile); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining(messages.invalidConstructor()) .inFile(componentFile); } @Test public void testCreatorMoreThanOneConstructorFails() { JavaFileObject componentFile = preprocessedJavaFile( "test.SimpleComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "abstract class SimpleComponent {", " @Component.Builder", " static abstract class Builder {", " Builder() {}", " Builder(String unused) {}", " }", "}"); Compilation compilation = compile(componentFile); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining(messages.invalidConstructor()) .inFile(componentFile); } @Test public void testCreatorEnumFails() { JavaFileObject componentFile = preprocessedJavaFile( "test.SimpleComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "abstract class SimpleComponent {", " @Component.Builder", " enum Builder {}", "}"); Compilation compilation = compile(componentFile); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining(messages.mustBeClassOrInterface()) .inFile(componentFile); } @Test public void testCreatorFactoryMethodReturnsWrongTypeFails() { JavaFileObject componentFile = preprocessedJavaFile( "test.SimpleComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "abstract class SimpleComponent {", " @Component.Builder", " interface Builder {", " String build();", " }", "}"); Compilation compilation = compile(componentFile); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining(messages.factoryMethodMustReturnComponentType()) .inFile(componentFile) .onLineContaining(process("String build();")); } @Test public void testCreatorSetterForNonBindsInstancePrimitiveFails() { JavaFileObject component = javaFileBuilder("test.TestComponent") .addLines( "package test;", "", "import dagger.Component;", "", "@Component", "interface TestComponent {", " Object object();", "") .addLinesIf( BUILDER, " @Component.Builder", " interface Builder {", " Builder primitive(long l);", " TestComponent build();", " }") .addLinesIf( FACTORY, " @Component.Factory", " interface Factory {", " TestComponent create(long l);", " }") .addLines( // "}") .build(); Compilation compilation = compile(component); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining(messages.nonBindsInstanceParametersMayNotBePrimitives()) .inFile(component) .onLineContaining("(long l)"); } @Test public void testInheritedBuilderBuildReturnsWrongTypeFails() { JavaFileObject componentFile = preprocessedJavaFile( "test.SimpleComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "abstract class SimpleComponent {", " interface Parent {", " String build();", " }", "", " @Component.Builder", " interface Builder extends Parent {}", "}"); Compilation compilation = compile(componentFile); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining( String.format( messages.inheritedFactoryMethodMustReturnComponentType(), process("build"))) .inFile(componentFile) .onLineContaining(process("interface Builder")); } @Test public void testTwoFactoryMethodsFails() { JavaFileObject componentFile = preprocessedJavaFile( "test.SimpleComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "abstract class SimpleComponent {", " @Component.Builder", " interface Builder {", " SimpleComponent build();", " SimpleComponent newSimpleComponent();", " }", "}"); Compilation compilation = compile(componentFile); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining(String.format(messages.twoFactoryMethods(), process("build"))) .inFile(componentFile) .onLineContaining("SimpleComponent newSimpleComponent();"); } @Test public void testInheritedTwoFactoryMethodsFails() { JavaFileObject componentFile = preprocessedJavaFile( "test.SimpleComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "abstract class SimpleComponent {", " interface Parent {", " SimpleComponent build();", " SimpleComponent newSimpleComponent();", " }", "", " @Component.Builder", " interface Builder extends Parent {}", "}"); Compilation compilation = compile(componentFile); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining( String.format( messages.inheritedTwoFactoryMethods(), process("build()"), "newSimpleComponent()")) .inFile(componentFile) .onLineContaining(process("interface Builder")); } @Test public void testMultipleSettersPerTypeFails() { assume().that(compilerType).isEqualTo(JAVAC); JavaFileObject moduleFile = JavaFileObjects.forSourceLines( "test.TestModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "", "@Module", "final class TestModule {", " @Provides String s() { return \"\"; }", "}"); JavaFileObject componentFile = javaFileBuilder("test.SimpleComponent") .addLines( "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component(modules = TestModule.class)", "abstract class SimpleComponent {", " abstract String s();", "") .addLinesIf( BUILDER, " @Component.Builder", " interface Builder {", " SimpleComponent build();", " void set1(TestModule s);", " void set2(TestModule s);", " }") .addLinesIf( FACTORY, " @Component.Factory", " interface Factory {", " SimpleComponent create(TestModule m1, TestModule m2);", " }") .addLines( // "}") .build(); Compilation compilation = compile(moduleFile, componentFile); assertThat(compilation).failed(); String elements = creatorKind.equals(BUILDER) ? "[void test.SimpleComponent.Builder.set1(test.TestModule), " + "void test.SimpleComponent.Builder.set2(test.TestModule)]" : "[test.TestModule m1, test.TestModule m2]"; assertThat(compilation) .hadErrorContaining( String.format( messages.multipleSettersForModuleOrDependencyType(), "test.TestModule", elements)) .inFile(componentFile) .onLineContaining(process("interface Builder")); } @Test public void testMultipleSettersPerTypeIncludingResolvedGenericsFails() { assume().that(compilerType).isEqualTo(JAVAC); JavaFileObject moduleFile = JavaFileObjects.forSourceLines( "test.TestModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "", "@Module", "final class TestModule {", " @Provides String s() { return \"\"; }", "}"); JavaFileObject componentFile = javaFileBuilder("test.SimpleComponent") .addLines( "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component(modules = TestModule.class)", "abstract class SimpleComponent {", " abstract String s();", "") .addLinesIf( BUILDER, " interface Parent {", " void set1(T t);", " }", "", " @Component.Builder", " interface Builder extends Parent {", " SimpleComponent build();", " void set2(TestModule s);", " }") .addLinesIf( FACTORY, " interface Parent {", " C create(TestModule m1, T t);", " }", "", " @Component.Factory", " interface Factory extends Parent {}") .addLines( // "}") .build(); Compilation compilation = compile(moduleFile, componentFile); assertThat(compilation).failed(); String elements = creatorKind.equals(BUILDER) ? "[void test.SimpleComponent.Builder.set1(test.TestModule), " + "void test.SimpleComponent.Builder.set2(test.TestModule)]" : "[test.TestModule m1, test.TestModule t]"; assertThat(compilation) .hadErrorContaining( String.format( messages.multipleSettersForModuleOrDependencyType(), "test.TestModule", elements)) .inFile(componentFile) .onLineContaining(process("interface Builder")); } @Test public void testExtraSettersFails() { assume().that(compilerType).isEqualTo(JAVAC); JavaFileObject componentFile = javaFileBuilder("test.SimpleComponent") .addLines( "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component(modules = AbstractModule.class)", "abstract class SimpleComponent {") .addLinesIf( BUILDER, " @Component.Builder", " interface Builder {", " SimpleComponent build();", " void abstractModule(AbstractModule abstractModule);", " void other(String s);", " }") .addLinesIf( FACTORY, " @Component.Factory", " interface Factory {", " SimpleComponent create(AbstractModule abstractModule, String s);", " }") .addLines("}") .build(); JavaFileObject abstractModule = JavaFileObjects.forSourceLines( "test.AbstractModule", "package test;", "", "import dagger.Module;", "", "@Module", "abstract class AbstractModule {}"); Compilation compilation = compile(componentFile, abstractModule); assertThat(compilation).failed(); String elements = creatorKind.equals(BUILDER) ? "[void test.SimpleComponent.Builder.abstractModule(test.AbstractModule), " + "void test.SimpleComponent.Builder.other(String)]" : "[test.AbstractModule abstractModule, String s]"; assertThat(compilation) .hadErrorContaining(String.format(messages.extraSetters(), elements)) .inFile(componentFile) .onLineContaining(process("interface Builder")); } @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.TestComponent", "package test;", "", "import dagger.Component;", "", "@Component(modules = {TestModule.class, Test2Module.class, Test3Module.class},", " dependencies = OtherComponent.class)", "interface TestComponent {", " String string();", " Integer integer();", "", " @Component.Builder", " interface Builder {", " TestComponent create();", " }", "}"); JavaFileObject otherComponent = JavaFileObjects.forSourceLines( "test.OtherComponent", "package test;", "", "import dagger.Component;", "", "@Component", "interface OtherComponent {}"); Compilation compilation = daggerCompiler() .compile(moduleFile, module2File, module3File, componentFile, otherComponent); 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, test.OtherComponent]")) .inFile(componentFile) .onLineContaining(process("interface Builder")); } @Test public void covariantFactoryMethodReturnType() { assume().that(compilerType).isEqualTo(JAVAC); 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 component = preprocessedJavaFile( "test.HasSupertype", "package test;", "", "import dagger.Component;", "", "@Component", "interface HasSupertype extends Supertype {", " @Component.Builder", " interface Builder {", " Supertype build();", " }", "}"); Compilation compilation = compile(foo, supertype, component); assertThat(compilation).succeededWithoutWarnings(); } @Test public void covariantFactoryMethodReturnType_hasNewMethod() { assume().that(compilerType).isEqualTo(JAVAC); 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 component = preprocessedJavaFile( "test.HasSupertype", "package test;", "", "import dagger.Component;", "", "@Component", "interface HasSupertype extends Supertype {", " Bar bar();", "", " @Component.Builder", " interface Builder {", " Supertype build();", " }", "}"); Compilation compilation = compile(foo, bar, supertype, component); 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(component) .onLine(11); } @Test public void covariantFactoryMethodReturnType_hasNewMethod_factoryMethodInherited() { assume().that(compilerType).isEqualTo(JAVAC); 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 component = preprocessedJavaFile( "test.HasSupertype", "package test;", "", "import dagger.Component;", "", "@Component", "interface HasSupertype extends Supertype {", " Bar bar();", "", " @Component.Builder", " interface Builder extends CreatorSupertype {}", "}"); Compilation compilation = compile(foo, bar, supertype, creatorSupertype, component); 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")); } @Test public void testGenericsOnFactoryMethodFails() { JavaFileObject componentFile = preprocessedJavaFile( "test.SimpleComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "abstract class SimpleComponent {", " @Component.Builder", " interface Builder {", " SimpleComponent build();", " }", "}"); Compilation compilation = compile(componentFile); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining(messages.methodsMayNotHaveTypeParameters()) .inFile(componentFile) .onLineContaining(process(" SimpleComponent build();")); } @Test public void testGenericsOnInheritedFactoryMethodFails() { JavaFileObject componentFile = preprocessedJavaFile( "test.SimpleComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "abstract class SimpleComponent {", " interface Parent {", " SimpleComponent build();", " }", "", " @Component.Builder", " interface Builder extends Parent {}", "}"); Compilation compilation = compile(componentFile); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining( String.format( messages.inheritedMethodsMayNotHaveTypeParameters(), process("build()"))) .inFile(componentFile) .onLineContaining(process("interface Builder")); } /** Compiles the given files with the set compiler mode's javacopts. */ @Override Compilation compile(JavaFileObject... files) { ImmutableList.Builder options = ImmutableList.builder().addAll(compilerMode.javacopts()); return compilerWithOptions(options.build()).compile(files); } }