blob: 3cf05ac9cbdfe8a6d9cd664d98e341c9e7d67d95 [file] [log] [blame]
/*
* 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.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<Object[]> 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;",
"",
GeneratedLines.generatedAnnotations(),
"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;",
"",
GeneratedLines.generatedImports(),
"",
GeneratedLines.generatedAnnotations(),
"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<T> {",
" 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;",
"",
GeneratedLines.generatedImports("import dagger.internal.Preconditions;"),
"",
GeneratedLines.generatedAnnotations(),
"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;",
"",
GeneratedLines.generatedImports("import dagger.internal.Preconditions;"),
"",
GeneratedLines.generatedAnnotations(),
"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<T> {",
" void set1(T t);",
" }",
"",
" @Component.Builder",
" interface Builder extends Parent<TestModule> {",
" SimpleComponent build();",
" void set2(TestModule s);",
" }")
.addLinesIf(
FACTORY,
" interface Parent<C, T> {",
" C create(TestModule m1, T t);",
" }",
"",
" @Component.Factory",
" interface Factory extends Parent<SimpleComponent, TestModule> {}")
.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 {",
" <T> SimpleComponent build();",
" }",
"}");
Compilation compilation = compile(componentFile);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(messages.methodsMayNotHaveTypeParameters())
.inFile(componentFile)
.onLineContaining(process("<T> 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 {",
" <T> SimpleComponent build();",
" }",
"",
" @Component.Builder",
" interface Builder extends Parent {}",
"}");
Compilation compilation = compile(componentFile);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
String.format(
messages.inheritedMethodsMayNotHaveTypeParameters(), process("<T>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<String> options =
ImmutableList.<String>builder().addAll(compilerMode.javacopts());
return compilerWithOptions(options.build()).compile(files);
}
}