/* * Copyright (C) 2014 The Dagger Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dagger.internal.codegen; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; import androidx.room.compiler.processing.util.CompilationResultSubject; import androidx.room.compiler.processing.util.Source; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.TypeSpec; import dagger.MembersInjector; import dagger.internal.codegen.javapoet.TypeNames; import dagger.testing.compile.CompilerTests; import dagger.testing.golden.GoldenFileRule; import java.util.Collection; import javax.inject.Inject; import javax.tools.Diagnostic; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) public class ComponentProcessorTest { @Parameters(name = "{0}") public static Collection parameters() { return CompilerMode.TEST_PARAMETERS; } private static final TypeSpec GENERATED_INJECT_TYPE = TypeSpec.classBuilder("GeneratedInjectType") .addMethod( MethodSpec.constructorBuilder() .addAnnotation(TypeNames.INJECT_JAVAX) .build()) .build(); private static final TypeSpec GENERATED_QUALIFIER = TypeSpec.annotationBuilder("GeneratedQualifier") .addAnnotation(AnnotationSpec.builder(TypeNames.QUALIFIER_JAVAX).build()) .build(); private static final TypeSpec GENERATED_MODULE = TypeSpec.classBuilder("GeneratedModule").addAnnotation(TypeNames.MODULE).build(); @Rule public GoldenFileRule goldenFileRule = new GoldenFileRule(); private final CompilerMode compilerMode; public ComponentProcessorTest(CompilerMode compilerMode) { this.compilerMode = compilerMode; } @Test public void doubleBindingFromResolvedModules() { Source parent = CompilerTests.javaSource("test.ParentModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "import java.util.List;", "", "@Module", "abstract class ParentModule {", " @Provides List provideListB(A a) { return null; }", "}"); Source child = CompilerTests.javaSource("test.ChildNumberModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "", "@Module", "class ChildNumberModule extends ParentModule {", " @Provides Integer provideInteger() { return null; }", "}"); Source another = CompilerTests.javaSource("test.AnotherModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "import java.util.List;", "", "@Module", "class AnotherModule {", " @Provides List provideListOfInteger() { return null; }", "}"); Source componentFile = CompilerTests.javaSource("test.BadComponent", "package test;", "", "import dagger.Component;", "import java.util.List;", "", "@Component(modules = {ChildNumberModule.class, AnotherModule.class})", "interface BadComponent {", " List listOfInteger();", "}"); CompilerTests.daggerCompiler(parent, child, another, componentFile) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining("List is bound multiple times"); subject.hasErrorContaining( "@Provides List ChildNumberModule.provideListB(Integer)"); subject.hasErrorContaining( "@Provides List AnotherModule.provideListOfInteger()"); }); } @Test public void privateNestedClassWithWarningThatIsAnErrorInComponent() { Source outerClass = CompilerTests.javaSource("test.OuterClass", "package test;", "", "import javax.inject.Inject;", "", "final class OuterClass {", " @Inject OuterClass(InnerClass innerClass) {}", "", " private static final class InnerClass {", " @Inject InnerClass() {}", " }", "}"); Source componentFile = CompilerTests.javaSource("test.BadComponent", "package test;", "", "import dagger.Component;", "", "@Component", "interface BadComponent {", " OuterClass outerClass();", "}"); CompilerTests.daggerCompiler(outerClass, componentFile) .withProcessingOptions( ImmutableMap.builder() .putAll(compilerMode.processorOptions()) .put("dagger.privateMemberValidation", "WARNING") .buildOrThrow()) .compile( subject -> // Because it is just a warning until it is used, the factory still gets generated // which has errors from referencing the private class, so there are extra errors. // Hence we don't assert on the number of errors. subject.hasErrorContaining( "Dagger does not support injection into private classes")); } @Test public void simpleComponent() throws Exception { Source injectableTypeFile = CompilerTests.javaSource("test/SomeInjectableType", "package test;", "", "import javax.inject.Inject;", "", "final class SomeInjectableType {", " @Inject SomeInjectableType() {}", "}"); Source componentFile = CompilerTests.javaSource("test/SimpleComponent", "package test;", "", "import dagger.Component;", "import dagger.Lazy;", "import javax.inject.Provider;", "", "@Component", "interface SimpleComponent {", " SomeInjectableType someInjectableType();", " Lazy lazySomeInjectableType();", " Provider someInjectableTypeProvider();", "}"); CompilerTests.daggerCompiler(injectableTypeFile, componentFile) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(0); subject.generatedSource(goldenFileRule.goldenSource("test/DaggerSimpleComponent")); }); } @Test public void componentWithScope() throws Exception { Source injectableTypeFile = CompilerTests.javaSource("test.SomeInjectableType", "package test;", "", "import javax.inject.Inject;", "import javax.inject.Singleton;", "", "@Singleton", "final class SomeInjectableType {", " @Inject SomeInjectableType() {}", "}"); Source componentFile = CompilerTests.javaSource("test.SimpleComponent", "package test;", "", "import dagger.Component;", "import dagger.Lazy;", "import javax.inject.Provider;", "import javax.inject.Singleton;", "", "@Singleton", "@Component", "interface SimpleComponent {", " SomeInjectableType someInjectableType();", " Lazy lazySomeInjectableType();", " Provider someInjectableTypeProvider();", "}"); CompilerTests.daggerCompiler(injectableTypeFile, componentFile) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(0); subject.generatedSource(goldenFileRule.goldenSource("test/DaggerSimpleComponent")); }); } @Test public void simpleComponentWithNesting() throws Exception { Source nestedTypesFile = CompilerTests.javaSource("test.OuterType", "package test;", "", "import dagger.Component;", "import javax.inject.Inject;", "", "final class OuterType {", " static class A {", " @Inject A() {}", " }", " static class B {", " @Inject A a;", " }", " @Component interface SimpleComponent {", " A a();", " void inject(B b);", " }", "}"); CompilerTests.daggerCompiler(nestedTypesFile) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(0); subject.generatedSource( goldenFileRule.goldenSource("test/DaggerOuterType_SimpleComponent")); }); } @Test public void componentWithModule() throws Exception { Source aFile = CompilerTests.javaSource("test.A", "package test;", "", "import javax.inject.Inject;", "", "final class A {", " @Inject A(B b) {}", "}"); Source bFile = CompilerTests.javaSource("test.B", "package test;", "", "interface B {}"); Source cFile = CompilerTests.javaSource("test.C", "package test;", "", "import javax.inject.Inject;", "", "final class C {", " @Inject C() {}", "}"); Source moduleFile = CompilerTests.javaSource("test.TestModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "", "@Module", "final class TestModule {", " @Provides B b(C c) { return null; }", "}"); Source componentFile = CompilerTests.javaSource("test.TestComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component(modules = TestModule.class)", "interface TestComponent {", " A a();", "}"); CompilerTests.daggerCompiler(aFile, bFile, cFile, moduleFile, componentFile) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(0); subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent")); }); } @Test public void componentWithAbstractModule() throws Exception { Source aFile = CompilerTests.javaSource( "test.A", "package test;", "", "import javax.inject.Inject;", "", "final class A {", " @Inject A(B b) {}", "}"); Source bFile = CompilerTests.javaSource("test.B", "package test;", "", "interface B {}"); Source cFile = CompilerTests.javaSource( "test.C", "package test;", "", "import javax.inject.Inject;", "", "final class C {", " @Inject C() {}", "}"); Source moduleFile = CompilerTests.javaSource( "test.TestModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "", "@Module", "abstract class TestModule {", " @Provides static B b(C c) { return null; }", "}"); Source componentFile = CompilerTests.javaSource( "test.TestComponent", "package test;", "", "import dagger.Component;", "", "@Component(modules = TestModule.class)", "interface TestComponent {", " A a();", "}"); CompilerTests.daggerCompiler(aFile, bFile, cFile, moduleFile, componentFile) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(0); subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent")); }); } @Test public void transitiveModuleDeps() throws Exception { Source always = CompilerTests.javaSource("test.AlwaysIncluded", "package test;", "", "import dagger.Module;", "", "@Module", "final class AlwaysIncluded {}"); Source testModule = CompilerTests.javaSource("test.TestModule", "package test;", "", "import dagger.Module;", "", "@Module(includes = {DepModule.class, AlwaysIncluded.class})", "final class TestModule extends ParentTestModule {}"); Source parentTest = CompilerTests.javaSource("test.ParentTestModule", "package test;", "", "import dagger.Module;", "", "@Module(includes = {ParentTestIncluded.class, AlwaysIncluded.class})", "class ParentTestModule {}"); Source parentTestIncluded = CompilerTests.javaSource("test.ParentTestIncluded", "package test;", "", "import dagger.Module;", "", "@Module(includes = AlwaysIncluded.class)", "final class ParentTestIncluded {}"); Source depModule = CompilerTests.javaSource("test.DepModule", "package test;", "", "import dagger.Module;", "", "@Module(includes = {RefByDep.class, AlwaysIncluded.class})", "final class DepModule extends ParentDepModule {}"); Source refByDep = CompilerTests.javaSource("test.RefByDep", "package test;", "", "import dagger.Module;", "", "@Module(includes = AlwaysIncluded.class)", "final class RefByDep extends ParentDepModule {}"); Source parentDep = CompilerTests.javaSource("test.ParentDepModule", "package test;", "", "import dagger.Module;", "", "@Module(includes = {ParentDepIncluded.class, AlwaysIncluded.class})", "class ParentDepModule {}"); Source parentDepIncluded = CompilerTests.javaSource("test.ParentDepIncluded", "package test;", "", "import dagger.Module;", "", "@Module(includes = AlwaysIncluded.class)", "final class ParentDepIncluded {}"); Source componentFile = CompilerTests.javaSource("test.TestComponent", "package test;", "", "import dagger.Component;", "", "@Component(modules = TestModule.class)", "interface TestComponent {", "}"); CompilerTests.daggerCompiler( always, testModule, parentTest, parentTestIncluded, depModule, refByDep, parentDep, parentDepIncluded, componentFile) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(0); subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent")); }); } @Test public void generatedTransitiveModule() { Source rootModule = CompilerTests.javaSource( "test.RootModule", "package test;", "", "import dagger.Module;", "", "@Module(includes = GeneratedModule.class)", "final class RootModule {}"); Source component = CompilerTests.javaSource( "test.TestComponent", "package test;", "", "import dagger.Component;", "", "@Component(modules = RootModule.class)", "interface TestComponent {}"); CompilerTests.daggerCompiler(rootModule, component) .withProcessingOptions(compilerMode.processorOptions()) .compile(subject -> subject.hasError()); CompilerTests.daggerCompiler(rootModule, component) .withProcessingOptions(compilerMode.processorOptions()) .withProcessingSteps(() -> new GeneratingProcessingStep("test", GENERATED_MODULE)) .compile(subject -> subject.hasErrorCount(0)); } @Test public void generatedModuleInSubcomponent() { Source subcomponent = CompilerTests.javaSource( "test.ChildComponent", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent(modules = GeneratedModule.class)", "interface ChildComponent {}"); Source component = CompilerTests.javaSource( "test.TestComponent", "package test;", "", "import dagger.Component;", "", "@Component", "interface TestComponent {", " ChildComponent childComponent();", "}"); CompilerTests.daggerCompiler(subcomponent, component) .withProcessingOptions(compilerMode.processorOptions()) .compile(subject -> subject.hasError()); CompilerTests.daggerCompiler(subcomponent, component) .withProcessingOptions(compilerMode.processorOptions()) .withProcessingSteps(() -> new GeneratingProcessingStep("test", GENERATED_MODULE)) .compile(subject -> subject.hasErrorCount(0)); } @Test public void subcomponentNotGeneratedIfNotUsedInGraph() throws Exception { Source component = CompilerTests.javaSource( "test.Parent", "package test;", "", "import dagger.Component;", "", "@Component(modules = ParentModule.class)", "interface Parent {", " String notSubcomponent();", "}"); Source module = CompilerTests.javaSource( "test.ParentModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "", "@Module(subcomponents = Child.class)", "class ParentModule {", " @Provides static String notSubcomponent() { return new String(); }", "}"); Source subcomponent = CompilerTests.javaSource( "test.Child", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent", "interface Child {", " @Subcomponent.Builder", " interface Builder {", " Child build();", " }", "}"); CompilerTests.daggerCompiler(component, module, subcomponent) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(0); subject.generatedSource(goldenFileRule.goldenSource("test/DaggerParent")); }); } @Test public void testDefaultPackage() { Source aClass = CompilerTests.javaSource("AClass", "class AClass {}"); Source bClass = CompilerTests.javaSource("BClass", "import javax.inject.Inject;", "", "class BClass {", " @Inject BClass(AClass a) {}", "}"); Source aModule = CompilerTests.javaSource("AModule", "import dagger.Module;", "import dagger.Provides;", "", "@Module class AModule {", " @Provides AClass aClass() {", " return new AClass();", " }", "}"); Source component = CompilerTests.javaSource("SomeComponent", "import dagger.Component;", "", "@Component(modules = AModule.class)", "interface SomeComponent {", " BClass bClass();", "}"); CompilerTests.daggerCompiler(aModule, aClass, bClass, component) .withProcessingOptions(compilerMode.processorOptions()) .compile(subject -> subject.hasErrorCount(0)); } @Test public void membersInjection() throws Exception { Source injectableTypeFile = CompilerTests.javaSource("test.SomeInjectableType", "package test;", "", "import javax.inject.Inject;", "", "final class SomeInjectableType {", " @Inject SomeInjectableType() {}", "}"); Source injectedTypeFile = CompilerTests.javaSource("test.SomeInjectedType", "package test;", "", "import javax.inject.Inject;", "", "final class SomeInjectedType {", " @Inject SomeInjectableType injectedField;", " SomeInjectedType() {}", "}"); Source componentFile = CompilerTests.javaSource("test.SimpleComponent", "package test;", "", "import dagger.Component;", "import dagger.Lazy;", "import javax.inject.Provider;", "", "@Component", "interface SimpleComponent {", " void inject(SomeInjectedType instance);", " SomeInjectedType injectAndReturn(SomeInjectedType instance);", "}"); CompilerTests.daggerCompiler(injectableTypeFile, injectedTypeFile, componentFile) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(0); subject.generatedSource(goldenFileRule.goldenSource("test/DaggerSimpleComponent")); }); } @Test public void componentInjection() throws Exception { Source injectableTypeFile = CompilerTests.javaSource( "test.SomeInjectableType", "package test;", "", "import javax.inject.Inject;", "", "final class SomeInjectableType {", " @Inject SomeInjectableType(SimpleComponent component) {}", "}"); Source componentFile = CompilerTests.javaSource( "test.SimpleComponent", "package test;", "", "import dagger.Component;", "import dagger.Lazy;", "import javax.inject.Provider;", "", "@Component", "interface SimpleComponent {", " SomeInjectableType someInjectableType();", " Provider selfProvider();", "}"); CompilerTests.daggerCompiler(injectableTypeFile, componentFile) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(0); subject.generatedSource(goldenFileRule.goldenSource("test/DaggerSimpleComponent")); }); } @Test public void membersInjectionInsideProvision() throws Exception { Source injectableTypeFile = CompilerTests.javaSource( "test.SomeInjectableType", "package test;", "", "import javax.inject.Inject;", "", "final class SomeInjectableType {", " @Inject SomeInjectableType() {}", "}"); Source injectedTypeFile = CompilerTests.javaSource( "test.SomeInjectedType", "package test;", "", "import javax.inject.Inject;", "", "final class SomeInjectedType {", " @Inject SomeInjectableType injectedField;", " @Inject SomeInjectedType() {}", "}"); Source componentFile = CompilerTests.javaSource( "test.SimpleComponent", "package test;", "", "import dagger.Component;", "", "@Component", "interface SimpleComponent {", " SomeInjectedType createAndInject();", "}"); CompilerTests.daggerCompiler(injectableTypeFile, injectedTypeFile, componentFile) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(0); subject.generatedSourceFileWithPath("test/DaggerSimpleComponent.java"); }); } @Test public void componentDependency() throws Exception { Source aFile = CompilerTests.javaSource("test.A", "package test;", "", "import javax.inject.Inject;", "", "final class A {", " @Inject A() {}", "}"); Source bFile = CompilerTests.javaSource("test.B", "package test;", "", "import javax.inject.Inject;", "import javax.inject.Provider;", "", "final class B {", " @Inject B(Provider a) {}", "}"); Source aComponentFile = CompilerTests.javaSource("test.AComponent", "package test;", "", "import dagger.Component;", "", "@Component", "interface AComponent {", " A a();", "}"); Source bComponentFile = CompilerTests.javaSource("test.BComponent", "package test;", "", "import dagger.Component;", "", "@Component(dependencies = AComponent.class)", "interface BComponent {", " B b();", "}"); CompilerTests.daggerCompiler(aFile, bFile, aComponentFile, bComponentFile) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(0); subject.generatedSource(goldenFileRule.goldenSource("test/DaggerBComponent")); }); } @Test public void primitiveComponentDependency() throws Exception { Source bFile = CompilerTests.javaSource("test.B", "package test;", "", "import javax.inject.Inject;", "import javax.inject.Provider;", "", "final class B {", " @Inject B(Provider i) {}", "}"); Source intComponentFile = CompilerTests.javaSource("test.IntComponent", "package test;", "", "interface IntComponent {", " int i();", "}"); Source bComponentFile = CompilerTests.javaSource("test.BComponent", "package test;", "", "import dagger.Component;", "", "@Component(dependencies = IntComponent.class)", "interface BComponent {", " B b();", "}"); CompilerTests.daggerCompiler(bFile, intComponentFile, bComponentFile) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(0); subject.generatedSource(goldenFileRule.goldenSource("test/DaggerBComponent")); }); } @Test public void arrayComponentDependency() throws Exception { Source bFile = CompilerTests.javaSource("test.B", "package test;", "", "import javax.inject.Inject;", "import javax.inject.Provider;", "", "final class B {", " @Inject B(Provider i) {}", "}"); Source arrayComponentFile = CompilerTests.javaSource("test.ArrayComponent", "package test;", "", "interface ArrayComponent {", " String[] strings();", "}"); Source bComponentFile = CompilerTests.javaSource("test.BComponent", "package test;", "", "import dagger.Component;", "", "@Component(dependencies = ArrayComponent.class)", "interface BComponent {", " B b();", "}"); CompilerTests.daggerCompiler(bFile, arrayComponentFile, bComponentFile) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(0); subject.generatedSource(goldenFileRule.goldenSource("test/DaggerBComponent")); }); } @Test public void dependencyNameCollision() throws Exception { Source a1 = CompilerTests.javaSource("pkg1.A", "package pkg1;", "", "import javax.inject.Inject;", "", "public final class A {", " @Inject A() {}", "}"); Source a2 = CompilerTests.javaSource("pkg2.A", "package pkg2;", "", "import javax.inject.Inject;", "", "public final class A {", " @Inject A() {}", "}"); Source a1Component = CompilerTests.javaSource("pkg1.AComponent", "package pkg1;", "", "import dagger.Component;", "", "@Component", "public interface AComponent {", " A a();", "}"); Source a2Component = CompilerTests.javaSource("pkg2.AComponent", "package pkg2;", "", "import dagger.Component;", "", "@Component", "public interface AComponent {", " A a();", "}"); Source bComponent = CompilerTests.javaSource("test.BComponent", "package test;", "", "import dagger.Component;", "", "@Component(dependencies = {pkg1.AComponent.class, pkg2.AComponent.class})", "interface BComponent {", " B b();", "}"); Source b = CompilerTests.javaSource("test.B", "package test;", "", "import javax.inject.Inject;", "import javax.inject.Provider;", "", "final class B {", " @Inject B(Provider a1, Provider a2) {}", "}"); CompilerTests.daggerCompiler(a1, a2, b, a1Component, a2Component, bComponent) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(0); subject.generatedSource(goldenFileRule.goldenSource("test/DaggerBComponent")); }); } @Test public void moduleNameCollision() throws Exception { Source aFile = CompilerTests.javaSource( "test.A", "package test;", "", "public final class A {}"); Source otherAFile = CompilerTests.javaSource( "other.test.A", "package other.test;", "", "public final class A {}"); Source moduleFile = CompilerTests.javaSource( "test.TestModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "", "@Module", "public final class TestModule {", " @Provides A a() { return null; }", "}"); Source otherModuleFile = CompilerTests.javaSource( "other.test.TestModule", "package other.test;", "", "import dagger.Module;", "import dagger.Provides;", "", "@Module", "public final class TestModule {", " @Provides A a() { return null; }", "}"); Source componentFile = CompilerTests.javaSource( "test.TestComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component(modules = {TestModule.class, other.test.TestModule.class})", "interface TestComponent {", " A a();", " other.test.A otherA();", "}"); CompilerTests.daggerCompiler(aFile, otherAFile, moduleFile, otherModuleFile, componentFile) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(0); subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent")); }); } @Test public void ignoresDependencyMethodsFromObject() throws Exception { Source injectedTypeFile = CompilerTests.javaSource( "test.InjectedType", "package test;", "", "import javax.inject.Inject;", "import javax.inject.Provider;", "", "final class InjectedType {", " @Inject InjectedType(", " String stringInjection,", " int intInjection,", " AComponent aComponent,", " Class aClass) {}", "}"); Source aComponentFile = CompilerTests.javaSource( "test.AComponent", "package test;", "", "class AComponent {", " String someStringInjection() {", " return \"injectedString\";", " }", "", " int someIntInjection() {", " return 123;", " }", "", " Class someClassInjection() {", " return AComponent.class;", " }", "", " @Override", " public String toString() {", " return null;", " }", "", " @Override", " public int hashCode() {", " return 456;", " }", "", " @Override", " public AComponent clone() {", " return null;", " }", "}"); Source bComponentFile = CompilerTests.javaSource( "test.BComponent", "package test;", "", "import dagger.Component;", "", "@Component(dependencies = AComponent.class)", "interface BComponent {", " InjectedType injectedType();", "}"); CompilerTests.daggerCompiler(injectedTypeFile, aComponentFile, bComponentFile) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(0); subject.generatedSource(goldenFileRule.goldenSource("test/DaggerBComponent")); }); } @Test public void resolutionOrder() throws Exception { Source aFile = CompilerTests.javaSource("test.A", "package test;", "", "import javax.inject.Inject;", "", "final class A {", " @Inject A(B b) {}", "}"); Source bFile = CompilerTests.javaSource("test.B", "package test;", "", "import javax.inject.Inject;", "", "final class B {", " @Inject B(C c) {}", "}"); Source cFile = CompilerTests.javaSource("test.C", "package test;", "", "import javax.inject.Inject;", "", "final class C {", " @Inject C() {}", "}"); Source xFile = CompilerTests.javaSource("test.X", "package test;", "", "import javax.inject.Inject;", "", "final class X {", " @Inject X(C c) {}", "}"); Source componentFile = CompilerTests.javaSource("test.TestComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "interface TestComponent {", " A a();", " C c();", " X x();", "}"); CompilerTests.daggerCompiler(aFile, bFile, cFile, xFile, componentFile) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(0); subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent")); }); } @Test public void simpleComponent_redundantComponentMethod() throws Exception { Source injectableTypeFile = CompilerTests.javaSource( "test.SomeInjectableType", "package test;", "", "import javax.inject.Inject;", "", "final class SomeInjectableType {", " @Inject SomeInjectableType() {}", "}"); Source componentSupertypeAFile = CompilerTests.javaSource( "test.SupertypeA", "package test;", "", "import dagger.Component;", "import dagger.Lazy;", "import javax.inject.Provider;", "", "@Component", "interface SupertypeA {", " SomeInjectableType someInjectableType();", "}"); Source componentSupertypeBFile = CompilerTests.javaSource( "test.SupertypeB", "package test;", "", "import dagger.Component;", "import dagger.Lazy;", "import javax.inject.Provider;", "", "@Component", "interface SupertypeB {", " SomeInjectableType someInjectableType();", "}"); Source componentFile = CompilerTests.javaSource( "test.SimpleComponent", "package test;", "", "import dagger.Component;", "import dagger.Lazy;", "import javax.inject.Provider;", "", "@Component", "interface SimpleComponent extends SupertypeA, SupertypeB {", "}"); CompilerTests.daggerCompiler( injectableTypeFile, componentSupertypeAFile, componentSupertypeBFile, componentFile) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(0); subject.generatedSource(goldenFileRule.goldenSource("test/DaggerSimpleComponent")); }); } @Test public void simpleComponent_inheritedComponentMethodDep() throws Exception { Source injectableTypeFile = CompilerTests.javaSource("test.SomeInjectableType", "package test;", "", "import javax.inject.Inject;", "", "final class SomeInjectableType {", " @Inject SomeInjectableType() {}", "}"); Source componentSupertype = CompilerTests.javaSource("test.Supertype", "package test;", "", "import dagger.Component;", "", "@Component", "interface Supertype {", " SomeInjectableType someInjectableType();", "}"); Source depComponentFile = CompilerTests.javaSource("test.SimpleComponent", "package test;", "", "import dagger.Component;", "", "@Component", "interface SimpleComponent extends Supertype {", "}"); CompilerTests.daggerCompiler(injectableTypeFile, componentSupertype, depComponentFile) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(0); subject.generatedSource(goldenFileRule.goldenSource("test/DaggerSimpleComponent")); }); } @Test public void wildcardGenericsRequiresAtProvides() { Source aFile = CompilerTests.javaSource("test.A", "package test;", "", "import javax.inject.Inject;", "", "final class A {", " @Inject A() {}", "}"); Source bFile = CompilerTests.javaSource("test.B", "package test;", "", "import javax.inject.Inject;", "import javax.inject.Provider;", "", "final class B {", " @Inject B(T t) {}", "}"); Source cFile = CompilerTests.javaSource("test.C", "package test;", "", "import javax.inject.Inject;", "import javax.inject.Provider;", "", "final class C {", " @Inject C(B bA) {}", "}"); Source componentFile = CompilerTests.javaSource("test.SimpleComponent", "package test;", "", "import dagger.Component;", "import dagger.Lazy;", "import javax.inject.Provider;", "", "@Component", "interface SimpleComponent {", " C c();", "}"); CompilerTests.daggerCompiler(aFile, bFile, cFile, componentFile) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining( "test.B cannot be provided without an @Provides-annotated " + "method"); }); } // https://github.com/google/dagger/issues/630 @Test public void arrayKeyRequiresAtProvides() { Source component = CompilerTests.javaSource( "test.TestComponent", "package test;", "", "import dagger.Component;", "", "@Component", "interface TestComponent {", " String[] array();", "}"); CompilerTests.daggerCompiler(component) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining( "String[] cannot be provided without an @Provides-annotated method"); }); } @Test public void componentImplicitlyDependsOnGeneratedType() { Source injectableTypeFile = CompilerTests.javaSource( "test.SomeInjectableType", "package test;", "", "import javax.inject.Inject;", "", "final class SomeInjectableType {", " @Inject SomeInjectableType(GeneratedInjectType generatedInjectType) {}", "}"); Source componentFile = CompilerTests.javaSource( "test.SimpleComponent", "package test;", "", "import dagger.Component;", "", "@Component", "interface SimpleComponent {", " SomeInjectableType someInjectableType();", "}"); CompilerTests.daggerCompiler(injectableTypeFile, componentFile) .withProcessingOptions(compilerMode.processorOptions()) .withProcessingSteps(() -> new GeneratingProcessingStep("test", GENERATED_INJECT_TYPE)) .compile( subject -> { subject.hasErrorCount(0); subject.generatedSourceFileWithPath("test/DaggerSimpleComponent.java"); }); } @Test public void componentSupertypeDependsOnGeneratedType() { Source componentFile = CompilerTests.javaSource( "test.SimpleComponent", "package test;", "", "import dagger.Component;", "", "@Component", "interface SimpleComponent extends SimpleComponentInterface {}"); Source interfaceFile = CompilerTests.javaSource( "test.SimpleComponentInterface", "package test;", "", "interface SimpleComponentInterface {", " GeneratedInjectType generatedInjectType();", "}"); CompilerTests.daggerCompiler(componentFile, interfaceFile) .withProcessingOptions(compilerMode.processorOptions()) .withProcessingSteps(() -> new GeneratingProcessingStep("test", GENERATED_INJECT_TYPE)) .compile( subject -> { subject.hasErrorCount(0); subject.generatedSourceFileWithPath("test/DaggerSimpleComponent.java"); }); } /** * We warn when generating a {@link MembersInjector} for a type post-hoc (i.e., if Dagger wasn't * invoked when compiling the type). But Dagger only generates {@link MembersInjector}s for types * with {@link Inject @Inject} constructors if they have any injection sites, and it only * generates them for types without {@link Inject @Inject} constructors if they have local * (non-inherited) injection sites. So make sure we warn in only those cases where running the * Dagger processor actually generates a {@link MembersInjector}. */ @Test public void unprocessedMembersInjectorNotes() { Source component = CompilerTests.javaSource( "test.TestComponent", "package test;", "", "import dagger.Component;", "import dagger.internal.codegen.ComponentProcessorTestClasses;", "", "@Component(modules = TestModule.class)", "interface TestComponent {", " void inject(ComponentProcessorTestClasses.NoInjectMemberNoConstructor object);", " void inject(ComponentProcessorTestClasses.NoInjectMemberWithConstructor object);", " void inject(ComponentProcessorTestClasses.LocalInjectMemberNoConstructor object);", " void inject(ComponentProcessorTestClasses.LocalInjectMemberWithConstructor object);", " void inject(ComponentProcessorTestClasses.ParentInjectMemberNoConstructor object);", " void inject(", " ComponentProcessorTestClasses.ParentInjectMemberWithConstructor object);", "}"); Source module = CompilerTests.javaSource( "test.TestModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "", "@Module", "class TestModule {", " @Provides static Object object() {", " return \"object\";", " }", "}"); CompilerTests.daggerCompiler(module, component) .withProcessingOptions( ImmutableMap.builder() .putAll(compilerMode.processorOptions()) .put("dagger.warnIfInjectionFactoryNotGeneratedUpstream", "enabled") .buildOrThrow()) .compile( subject -> { subject.hasErrorCount(0); subject.hasWarningCount(0); String generatedFileTemplate = "dagger/internal/codegen/ComponentProcessorTestClasses_%s_MembersInjector.java"; String noteTemplate = "Generating a MembersInjector for " + "dagger.internal.codegen.ComponentProcessorTestClasses.%s."; // Assert that we generate sources and notes for the following classes. ImmutableList.of( "LocalInjectMemberNoConstructor", "LocalInjectMemberWithConstructor", "ParentInjectMemberWithConstructor") .forEach( className -> { subject.generatedSourceFileWithPath( String.format(generatedFileTemplate, className)); subject.hasNoteContaining(String.format(noteTemplate, className)); }); // Assert that we **do not** generate sources and notes for the following classes. ImmutableList.of( "ParentInjectMemberNoConstructor", "NoInjectMemberNoConstructor", "NoInjectMemberWithConstructor") .forEach( className -> { assertFileNotGenerated( subject, String.format(generatedFileTemplate, className)); assertDoesNotHaveNoteContaining( subject, String.format(noteTemplate, className)); }); }); } private void assertFileNotGenerated(CompilationResultSubject subject, String filePath) { // TODO(b/303653163): replace this with better XProcessing API once we have the ability to get a // list of all generated sources. AssertionError error = assertThrows( AssertionError.class, () -> subject.generatedSourceFileWithPath(filePath)); assertThat(error).hasMessageThat().contains("Didn't generate file"); } private void assertDoesNotHaveNoteContaining(CompilationResultSubject subject, String content) { assertThat( subject.getCompilationResult().getDiagnostics().get(Diagnostic.Kind.NOTE).stream() .filter(diagnostic -> diagnostic.getMsg().contains(content))) .isEmpty(); } @Test public void scopeAnnotationOnInjectConstructorNotValid() { Source aScope = CompilerTests.javaSource( "test.AScope", "package test;", "", "import javax.inject.Scope;", "", "@Scope", "@interface AScope {}"); Source aClass = CompilerTests.javaSource( "test.AClass", "package test;", "", "import javax.inject.Inject;", "", "final class AClass {", " @Inject @AScope AClass() {}", "}"); CompilerTests.daggerCompiler(aScope, aClass) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(1); subject .hasErrorContaining("@Scope annotations are not allowed on @Inject constructors") .onSource(aClass) .onLine(6); }); } @Test public void unusedSubcomponents_dontResolveExtraBindingsInParentComponents() throws Exception { Source foo = CompilerTests.javaSource( "test.Foo", "package test;", "", "import javax.inject.Inject;", "import javax.inject.Singleton;", "", "@Singleton", "class Foo {", " @Inject Foo() {}", "}"); Source module = CompilerTests.javaSource( "test.TestModule", "package test;", "", "import dagger.Module;", "", "@Module(subcomponents = Pruned.class)", "class TestModule {}"); Source component = CompilerTests.javaSource( "test.Parent", "package test;", "", "import dagger.Component;", "import javax.inject.Singleton;", "", "@Singleton", "@Component(modules = TestModule.class)", "interface Parent {}"); Source prunedSubcomponent = CompilerTests.javaSource( "test.Pruned", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent", "interface Pruned {", " @Subcomponent.Builder", " interface Builder {", " Pruned build();", " }", "", " Foo foo();", "}"); CompilerTests.daggerCompiler(foo, module, component, prunedSubcomponent) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(0); subject.generatedSource(goldenFileRule.goldenSource("test/DaggerParent")); }); } @Test public void bindsToDuplicateBinding_bindsKeyIsNotDuplicated() { Source firstModule = CompilerTests.javaSource( "test.FirstModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "", "@Module", "abstract class FirstModule {", " @Provides static String first() { return \"first\"; }", "}"); Source secondModule = CompilerTests.javaSource( "test.SecondModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "", "@Module", "abstract class SecondModule {", " @Provides static String second() { return \"second\"; }", "}"); Source bindsModule = CompilerTests.javaSource( "test.BindsModule", "package test;", "", "import dagger.Binds;", "import dagger.Module;", "", "@Module", "abstract class BindsModule {", " @Binds abstract Object bindToDuplicateBinding(String duplicate);", "}"); Source component = CompilerTests.javaSource( "test.TestComponent", "package test;", "", "import dagger.Component;", "", "@Component(modules = {FirstModule.class, SecondModule.class, BindsModule.class})", "interface TestComponent {", " Object notDuplicated();", "}"); CompilerTests.daggerCompiler(firstModule, secondModule, bindsModule, component) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining("String is bound multiple times") .onSource(component) .onLineContaining("interface TestComponent"); }); } @Test public void nullIncorrectlyReturnedFromNonNullableInlinedProvider() throws Exception { CompilerTests.daggerCompiler( CompilerTests.javaSource( "test.TestModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "", "@Module", "public abstract class TestModule {", " @Provides static String nonNullableString() { return \"string\"; }", "}"), CompilerTests.javaSource( "test.InjectsMember", "package test;", "", "import javax.inject.Inject;", "", "public class InjectsMember {", " @Inject String member;", "}"), CompilerTests.javaSource( "test.TestComponent", "package test;", "", "import dagger.Component;", "", "@Component(modules = TestModule.class)", "interface TestComponent {", " String nonNullableString();", " void inject(InjectsMember member);", "}")) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { // TODO(bcorso): Replace with subject.succeededWithoutWarnings() subject.hasErrorCount(0); subject.hasWarningCount(0); subject.generatedSource( goldenFileRule.goldenSource("test/TestModule_NonNullableStringFactory")); subject.generatedSource( goldenFileRule.goldenSource("test/DaggerTestComponent")); }); } @Test public void nullCheckingIgnoredWhenProviderReturnsPrimitive() throws Exception { CompilerTests.daggerCompiler( CompilerTests.javaSource( "test.TestModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "", "@Module", "public abstract class TestModule {", " @Provides static int primitiveInteger() { return 1; }", "}"), CompilerTests.javaSource( "test.InjectsMember", "package test;", "", "import javax.inject.Inject;", "", "public class InjectsMember {", " @Inject Integer member;", "}"), CompilerTests.javaSource( "test.TestComponent", "package test;", "", "import dagger.Component;", "", "@Component(modules = TestModule.class)", "interface TestComponent {", " Integer nonNullableInteger();", " void inject(InjectsMember member);", "}")) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { // TODO(bcorso): Replace with subject.succeededWithoutWarnings() subject.hasErrorCount(0); subject.hasWarningCount(0); subject.generatedSource( goldenFileRule.goldenSource("test/TestModule_PrimitiveIntegerFactory")); subject.generatedSource( goldenFileRule.goldenSource("test/DaggerTestComponent")); }); } @Test public void privateMethodUsedOnlyInChildDoesNotUseQualifiedThis() throws Exception { Source parent = CompilerTests.javaSource( "test.Parent", "package test;", "", "import dagger.Component;", "import javax.inject.Singleton;", "", "@Singleton", "@Component(modules=TestModule.class)", "interface Parent {", " Child child();", "}"); Source testModule = CompilerTests.javaSource( "test.TestModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "import javax.inject.Singleton;", "", "@Module", "abstract class TestModule {", " @Provides @Singleton static Number number() {", " return 3;", " }", "", " @Provides static String string(Number number) {", " return number.toString();", " }", "}"); Source child = CompilerTests.javaSource( "test.Child", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent", "interface Child {", " String string();", "}"); CompilerTests.daggerCompiler(parent, testModule, child) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { // TODO(bcorso): Replace with subject.succeededWithoutWarnings() subject.hasErrorCount(0); subject.hasWarningCount(0); subject.generatedSource(goldenFileRule.goldenSource("test/DaggerParent")); }); } @Test public void componentMethodInChildCallsComponentMethodInParent() throws Exception { Source supertype = CompilerTests.javaSource( "test.Supertype", "package test;", "", "interface Supertype {", " String string();", "}"); Source parent = CompilerTests.javaSource( "test.Parent", "package test;", "", "import dagger.Component;", "import javax.inject.Singleton;", "", "@Singleton", "@Component(modules=TestModule.class)", "interface Parent extends Supertype {", " Child child();", "}"); Source testModule = CompilerTests.javaSource( "test.TestModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "import javax.inject.Singleton;", "", "@Module", "abstract class TestModule {", " @Provides @Singleton static Number number() {", " return 3;", " }", "", " @Provides static String string(Number number) {", " return number.toString();", " }", "}"); Source child = CompilerTests.javaSource( "test.Child", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent", "interface Child extends Supertype {}"); CompilerTests.daggerCompiler(supertype, parent, testModule, child) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { // TODO(bcorso): Replace with subject.succeededWithoutWarnings() subject.hasErrorCount(0); subject.hasWarningCount(0); subject.generatedSource(goldenFileRule.goldenSource("test/DaggerParent")); }); } @Test public void justInTimeAtInjectConstructor_hasGeneratedQualifier() throws Exception { Source injected = CompilerTests.javaSource( "test.Injected", "package test;", "", "import javax.inject.Inject;", "", "class Injected {", " @Inject Injected(@GeneratedQualifier String string) {}", "}"); Source module = CompilerTests.javaSource( "test.TestModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "", "@Module", "interface TestModule {", " @Provides", " static String unqualified() {", " return new String();", " }", "", " @Provides", " @GeneratedQualifier", " static String qualified() {", " return new String();", " }", "}"); Source component = CompilerTests.javaSource( "test.TestComponent", "package test;", "", "import dagger.Component;", "", "@Component(modules = TestModule.class)", "interface TestComponent {", " Injected injected();", "}"); CompilerTests.daggerCompiler(injected, module, component) .withProcessingOptions(compilerMode.processorOptions()) .withProcessingSteps(() -> new GeneratingProcessingStep("test", GENERATED_QUALIFIER)) .compile( subject -> { subject.hasErrorCount(0); subject.hasWarningCount(0); subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent")); }); } @Test public void moduleHasGeneratedQualifier() throws Exception { Source module = CompilerTests.javaSource( "test.TestModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "", "@Module", "interface TestModule {", " @Provides", " static String unqualified() {", " return new String();", " }", "", " @Provides", " @GeneratedQualifier", " static String qualified() {", " return new String();", " }", "}"); Source component = CompilerTests.javaSource( "test.TestComponent", "package test;", "", "import dagger.Component;", "", "@Component(modules = TestModule.class)", "interface TestComponent {", " String unqualified();", "}"); CompilerTests.daggerCompiler(module, component) .withProcessingOptions(compilerMode.processorOptions()) .withProcessingSteps(() -> new GeneratingProcessingStep("test", GENERATED_QUALIFIER)) .compile( subject -> { subject.hasErrorCount(0); subject.hasWarningCount(0); subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent")); }); } @Test public void publicComponentType() throws Exception { Source publicComponent = CompilerTests.javaSource( "test.PublicComponent", "package test;", "", "import dagger.Component;", "", "@Component", "public interface PublicComponent {}"); CompilerTests.daggerCompiler(publicComponent) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(0); subject.generatedSource(goldenFileRule.goldenSource("test/DaggerPublicComponent")); }); } @Test public void componentFactoryInterfaceTest() { Source parentInterface = CompilerTests.javaSource( "test.ParentInterface", "package test;", "", "interface ParentInterface extends ChildInterface.Factory {}"); Source childInterface = CompilerTests.javaSource( "test.ChildInterface", "package test;", "", "interface ChildInterface {", " interface Factory {", " ChildInterface child(ChildModule childModule);", " }", "}"); Source parent = CompilerTests.javaSource( "test.Parent", "package test;", "", "import dagger.Component;", "", "@Component", "interface Parent extends ParentInterface, Child.Factory {}"); Source child = CompilerTests.javaSource( "test.Child", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent(modules = ChildModule.class)", "interface Child extends ChildInterface {", " interface Factory extends ChildInterface.Factory {", " @Override Child child(ChildModule childModule);", " }", "}"); Source childModule = CompilerTests.javaSource( "test.ChildModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "", "@Module", "class ChildModule {", " @Provides", " int provideInt() {", " return 0;", " }", "}"); CompilerTests.daggerCompiler(parentInterface, childInterface, parent, child, childModule) .withProcessingOptions(compilerMode.processorOptions()) .compile(subject -> subject.hasErrorCount(0)); } @Test public void providerComponentType() throws Exception { Source entryPoint = CompilerTests.javaSource( "test.SomeEntryPoint", "package test;", "", "import javax.inject.Inject;", "import javax.inject.Provider;", "", "public class SomeEntryPoint {", " @Inject SomeEntryPoint(Foo foo, Provider fooProvider) {}", "}"); Source foo = CompilerTests.javaSource( "test.Foo", "package test;", "", "import javax.inject.Inject;", "", "public class Foo {", " @Inject Foo(Bar bar) {}", "}"); Source bar = CompilerTests.javaSource( "test.Bar", "package test;", "", "import javax.inject.Inject;", "", "public class Bar {", " @Inject Bar() {}", "}"); Source component = CompilerTests.javaSource( "test.TestComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "public interface TestComponent {", " SomeEntryPoint someEntryPoint();", "}"); CompilerTests.daggerCompiler(component, foo, bar, entryPoint) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(0); subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent")); }); } @Test public void injectedTypeHasGeneratedParam() { Source foo = CompilerTests.javaSource( "test.Foo", "package test;", "", "import javax.inject.Inject;", "", "public final class Foo {", "", " @Inject", " public Foo(GeneratedInjectType param) {}", "", " @Inject", " public void init() {}", "}"); Source component = CompilerTests.javaSource( "test.TestComponent", "package test;", "", "import dagger.Component;", "import java.util.Set;", "", "@Component", "interface TestComponent {", " Foo foo();", "}"); CompilerTests.daggerCompiler(foo, component) .withProcessingOptions(compilerMode.processorOptions()) .withProcessingSteps(() -> new GeneratingProcessingStep("test", GENERATED_INJECT_TYPE)) .compile( subject -> { subject.hasErrorCount(0); subject.hasWarningCount(0); }); } @Test public void abstractVarFieldInComponent_failsValidation() { Source component = CompilerTests.kotlinSource( "test.TestComponent.kt", "package test", "", "import dagger.Component", "", "@Component(modules = [TestModule::class])", "interface TestComponent {", " var foo: String", "}"); Source module = CompilerTests.javaSource( "test.TestModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "", "@Module", "abstract class TestModule {", " @Provides", " static String provideString() { return \"hello\"; }", "}"); CompilerTests.daggerCompiler(component, module) .withProcessingOptions(compilerMode.processorOptions()) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining( "Cannot use 'abstract var' property in a component declaration to get a binding." + " Use 'val' or 'fun' instead: foo"); }); } @Test public void nonAbstractVarFieldInComponent_passesValidation() { Source component = CompilerTests.kotlinSource( "test.TestComponent.kt", "package test", "", "import dagger.Component", "", "@Component(modules = [TestModule::class])", "abstract class TestComponent {", " var foo: String = \"hello\"", "}"); Source module = CompilerTests.javaSource( "test.TestModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "", "@Module", "abstract class TestModule {", " @Provides", " static String provideString() { return \"hello\"; }", "}"); CompilerTests.daggerCompiler(component, module) .withProcessingOptions(compilerMode.processorOptions()) .compile(subject -> subject.hasErrorCount(0)); } }