| /* |
| * Copyright (C) 2015 The Dagger Authors. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package dagger.internal.codegen; |
| |
| import static com.google.testing.compile.CompilationSubject.assertThat; |
| import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE; |
| import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE; |
| import static dagger.internal.codegen.Compilers.compilerWithOptions; |
| import static dagger.internal.codegen.Compilers.daggerCompiler; |
| |
| import com.google.testing.compile.Compilation; |
| import com.google.testing.compile.JavaFileObjects; |
| 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; |
| |
| @RunWith(Parameterized.class) |
| public class SubcomponentValidationTest { |
| @Parameters(name = "{0}") |
| public static Collection<Object[]> parameters() { |
| return CompilerMode.TEST_PARAMETERS; |
| } |
| |
| private final CompilerMode compilerMode; |
| |
| public SubcomponentValidationTest(CompilerMode compilerMode) { |
| this.compilerMode = compilerMode; |
| } |
| |
| @Test public void factoryMethod_missingModulesWithParameters() { |
| JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "", |
| "@Component", |
| "interface TestComponent {", |
| " ChildComponent newChildComponent();", |
| "}"); |
| JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent(modules = ModuleWithParameters.class)", |
| "interface ChildComponent {", |
| " Object object();", |
| "}"); |
| JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.ModuleWithParameters", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "", |
| "@Module", |
| "final class ModuleWithParameters {", |
| " private final Object object;", |
| "", |
| " ModuleWithParameters(Object object) {", |
| " this.object = object;", |
| " }", |
| "", |
| " @Provides Object object() {", |
| " return object;", |
| " }", |
| "}"); |
| Compilation compilation = |
| compilerWithOptions(compilerMode.javacopts()) |
| .compile(componentFile, childComponentFile, moduleFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining( |
| "test.ChildComponent requires modules which have no visible default constructors. " |
| + "Add the following modules as parameters to this method: " |
| + "test.ModuleWithParameters") |
| .inFile(componentFile) |
| .onLineContaining("ChildComponent newChildComponent();"); |
| } |
| |
| @Test |
| public void factoryMethod_grandchild() { |
| JavaFileObject component = |
| JavaFileObjects.forSourceLines( |
| "test.TestComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "", |
| "@Component", |
| "interface TestComponent {", |
| " ChildComponent newChildComponent();", |
| "}"); |
| JavaFileObject childComponent = |
| JavaFileObjects.forSourceLines( |
| "test.ChildComponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "interface ChildComponent {", |
| " GrandchildComponent newGrandchildComponent();", |
| "}"); |
| JavaFileObject grandchildComponent = |
| JavaFileObjects.forSourceLines( |
| "test.GrandchildComponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent(modules = GrandchildModule.class)", |
| "interface GrandchildComponent {", |
| " Object object();", |
| "}"); |
| JavaFileObject grandchildModule = |
| JavaFileObjects.forSourceLines( |
| "test.GrandchildModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "", |
| "@Module", |
| "final class GrandchildModule {", |
| " private final Object object;", |
| "", |
| " GrandchildModule(Object object) {", |
| " this.object = object;", |
| " }", |
| "", |
| " @Provides Object object() {", |
| " return object;", |
| " }", |
| "}"); |
| Compilation compilation = |
| compilerWithOptions(compilerMode.javacopts()) |
| .compile(component, childComponent, grandchildComponent, grandchildModule); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining( |
| "[ChildComponent.newGrandchildComponent()] " |
| + "GrandchildComponent requires modules which have no visible default " |
| + "constructors. Add the following modules as parameters to this method: " |
| + "GrandchildModule") |
| .inFile(component) |
| .onLineContaining("interface TestComponent"); |
| } |
| |
| @Test public void factoryMethod_nonModuleParameter() { |
| JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "", |
| "@Component", |
| "interface TestComponent {", |
| " ChildComponent newChildComponent(String someRandomString);", |
| "}"); |
| JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "interface ChildComponent {}"); |
| Compilation compilation = |
| compilerWithOptions(compilerMode.javacopts()) |
| .compile(componentFile, childComponentFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining( |
| "Subcomponent factory methods may only accept modules, but java.lang.String is not.") |
| .inFile(componentFile) |
| .onLine(7) |
| .atColumn(43); |
| } |
| |
| @Test public void factoryMethod_duplicateParameter() { |
| JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "", |
| "@Module", |
| "final class TestModule {}"); |
| JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "", |
| "@Component", |
| "interface TestComponent {", |
| " ChildComponent newChildComponent(TestModule testModule1, TestModule testModule2);", |
| "}"); |
| JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent(modules = TestModule.class)", |
| "interface ChildComponent {}"); |
| Compilation compilation = |
| compilerWithOptions(compilerMode.javacopts()) |
| .compile(moduleFile, componentFile, childComponentFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining( |
| "A module may only occur once an an argument in a Subcomponent factory method, " |
| + "but test.TestModule was already passed.") |
| .inFile(componentFile) |
| .onLine(7) |
| .atColumn(71); |
| } |
| |
| @Test public void factoryMethod_superflouousModule() { |
| JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "", |
| "@Module", |
| "final class TestModule {}"); |
| JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "", |
| "@Component", |
| "interface TestComponent {", |
| " ChildComponent newChildComponent(TestModule testModule);", |
| "}"); |
| JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "interface ChildComponent {}"); |
| Compilation compilation = |
| compilerWithOptions(compilerMode.javacopts()) |
| .compile(moduleFile, componentFile, childComponentFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining( |
| "test.TestModule is present as an argument to the test.ChildComponent factory method, " |
| + "but is not one of the modules used to implement the subcomponent.") |
| .inFile(componentFile) |
| .onLine(7); |
| } |
| |
| @Test public void missingBinding() { |
| JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "", |
| "@Module", |
| "final class TestModule {", |
| " @Provides String provideString(int i) {", |
| " return Integer.toString(i);", |
| " }", |
| "}"); |
| JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "", |
| "@Component", |
| "interface TestComponent {", |
| " ChildComponent newChildComponent();", |
| "}"); |
| JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent(modules = TestModule.class)", |
| "interface ChildComponent {", |
| " String string();", |
| "}"); |
| Compilation compilation = |
| compilerWithOptions(compilerMode.javacopts()) |
| .compile(moduleFile, componentFile, childComponentFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation) |
| .hadErrorContaining( |
| "Integer cannot be provided without an @Inject constructor or an " |
| + "@Provides-annotated method") |
| .inFile(componentFile) |
| .onLineContaining("interface TestComponent"); |
| } |
| |
| @Test public void subcomponentOnConcreteType() { |
| JavaFileObject subcomponentFile = JavaFileObjects.forSourceLines("test.NotASubcomponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "final class NotASubcomponent {}"); |
| Compilation compilation = |
| compilerWithOptions(compilerMode.javacopts()).compile(subcomponentFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation).hadErrorContaining("interface"); |
| } |
| |
| @Test public void scopeMismatch() { |
| JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.ParentComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "import javax.inject.Singleton;", |
| "", |
| "@Component", |
| "@Singleton", |
| "interface ParentComponent {", |
| " ChildComponent childComponent();", |
| "}"); |
| JavaFileObject subcomponentFile = JavaFileObjects.forSourceLines("test.ChildComponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent(modules = ChildModule.class)", |
| "interface ChildComponent {", |
| " Object object();", |
| "}"); |
| JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.ChildModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "import javax.inject.Singleton;", |
| "", |
| "@Module", |
| "final class ChildModule {", |
| " @Provides @Singleton Object provideObject() { return null; }", |
| "}"); |
| Compilation compilation = |
| compilerWithOptions(compilerMode.javacopts()) |
| .compile(componentFile, subcomponentFile, moduleFile); |
| assertThat(compilation).failed(); |
| assertThat(compilation).hadErrorContaining("@Singleton"); |
| } |
| |
| @Test |
| public void delegateFactoryNotCreatedForSubcomponentWhenProviderExistsInParent() { |
| JavaFileObject parentComponentFile = |
| JavaFileObjects.forSourceLines( |
| "test.ParentComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "import javax.inject.Singleton;", |
| "", |
| "@Singleton", |
| "@Component", |
| "interface ParentComponent {", |
| " ChildComponent childComponent();", |
| " Dep1 dep1();", |
| " Dep2 dep2();", |
| "}"); |
| JavaFileObject childComponentFile = |
| JavaFileObjects.forSourceLines( |
| "test.ChildComponent", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent(modules = ChildModule.class)", |
| "interface ChildComponent {", |
| " Object object();", |
| "}"); |
| JavaFileObject childModuleFile = |
| JavaFileObjects.forSourceLines( |
| "test.ChildModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "", |
| "@Module", |
| "final class ChildModule {", |
| " @Provides Object provideObject(A a) { return null; }", |
| "}"); |
| JavaFileObject aFile = |
| JavaFileObjects.forSourceLines( |
| "test.A", |
| "package test;", |
| "", |
| "import javax.inject.Inject;", |
| "", |
| "final class A {", |
| " @Inject public A(NeedsDep1 a, Dep1 b, Dep2 c) { }", |
| " @Inject public void methodA() { }", |
| "}"); |
| JavaFileObject needsDep1File = |
| JavaFileObjects.forSourceLines( |
| "test.NeedsDep1", |
| "package test;", |
| "", |
| "import javax.inject.Inject;", |
| "", |
| "final class NeedsDep1 {", |
| " @Inject public NeedsDep1(Dep1 d) { }", |
| "}"); |
| JavaFileObject dep1File = |
| JavaFileObjects.forSourceLines( |
| "test.Dep1", |
| "package test;", |
| "", |
| "import javax.inject.Inject;", |
| "import javax.inject.Singleton;", |
| "", |
| "@Singleton", |
| "final class Dep1 {", |
| " @Inject public Dep1() { }", |
| " @Inject public void dep1Method() { }", |
| "}"); |
| JavaFileObject dep2File = |
| JavaFileObjects.forSourceLines( |
| "test.Dep2", |
| "package test;", |
| "", |
| "import javax.inject.Inject;", |
| "import javax.inject.Singleton;", |
| "", |
| "@Singleton", |
| "final class Dep2 {", |
| " @Inject public Dep2() { }", |
| " @Inject public void dep2Method() { }", |
| "}"); |
| |
| JavaFileObject generatedComponent = |
| compilerMode |
| .javaFileBuilder("test.DaggerParentComponent") |
| .addLines( |
| "package test;", |
| "", |
| GeneratedLines.generatedAnnotations(), |
| "final class DaggerParentComponent implements ParentComponent {") |
| .addLinesIn( |
| DEFAULT_MODE, |
| " @SuppressWarnings(\"unchecked\")", |
| " private void initialize() {", |
| " this.dep1Provider = DoubleCheck.provider(Dep1_Factory.create());", |
| " this.dep2Provider = DoubleCheck.provider(Dep2_Factory.create());", |
| " }", |
| "") |
| .addLines( |
| " @Override", // |
| " public Dep1 dep1() {") |
| .addLinesIn( |
| FAST_INIT_MODE, |
| " Object local = dep1;", |
| " if (local instanceof MemoizedSentinel) {", |
| " synchronized (local) {", |
| " local = dep1;", |
| " if (local instanceof MemoizedSentinel) {", |
| " local = injectDep1(Dep1_Factory.newInstance());", |
| " dep1 = DoubleCheck.reentrantCheck(dep1, local);", |
| " }", |
| " }", |
| " }", |
| " return (Dep1) local;") |
| .addLinesIn( |
| DEFAULT_MODE, // |
| " return dep1Provider.get();") |
| .addLines( |
| " }", // |
| "", |
| " @Override", |
| " public Dep2 dep2() {") |
| .addLinesIn( |
| FAST_INIT_MODE, |
| " Object local = dep2;", |
| " if (local instanceof MemoizedSentinel) {", |
| " synchronized (local) {", |
| " local = dep2;", |
| " if (local instanceof MemoizedSentinel) {", |
| " local = injectDep2(Dep2_Factory.newInstance());", |
| " dep2 = DoubleCheck.reentrantCheck(dep2, local);", |
| " }", |
| " }", |
| " }", |
| " return (Dep2) local;") |
| .addLinesIn( |
| DEFAULT_MODE, // |
| " return dep2Provider.get();") |
| .addLines( |
| " }", |
| "", |
| " @Override", |
| " public ChildComponent childComponent() {", |
| " return new ChildComponentImpl();", |
| " }", |
| "") |
| .addLinesIn( |
| FAST_INIT_MODE, |
| " @CanIgnoreReturnValue", |
| " private Dep1 injectDep1(Dep1 instance) {", |
| " Dep1_MembersInjector.injectDep1Method(instance);", |
| " return instance;", |
| " }", |
| "", |
| " @CanIgnoreReturnValue", |
| " private Dep2 injectDep2(Dep2 instance) {", |
| " Dep2_MembersInjector.injectDep2Method(instance);", |
| " return instance;", |
| " }") |
| .addLines( |
| "", |
| " private final class ChildComponentImpl implements ChildComponent {", |
| " private final ChildModule childModule;", |
| "", |
| " private ChildComponentImpl() {", |
| " this.childModule = new ChildModule();", |
| " }", |
| "") |
| .addLinesIn( |
| DEFAULT_MODE, |
| " private NeedsDep1 needsDep1() {", |
| " return new NeedsDep1(DaggerParentComponent.this.dep1Provider.get());", |
| " }") |
| .addLinesIn( |
| FAST_INIT_MODE, |
| " private NeedsDep1 needsDep1() {", |
| " return new NeedsDep1(DaggerParentComponent.this.dep1());", |
| " }") |
| .addLines( |
| " private A a() {", |
| " return injectA(", |
| " A_Factory.newInstance(", |
| " needsDep1(),") |
| .addLinesIn( |
| DEFAULT_MODE, |
| " DaggerParentComponent.this.dep1Provider.get(),", |
| " DaggerParentComponent.this.dep2Provider.get()));") |
| .addLinesIn( |
| FAST_INIT_MODE, |
| " DaggerParentComponent.this.dep1(),", |
| " DaggerParentComponent.this.dep2()));") |
| .addLines( |
| " }", |
| "", |
| " @Override", |
| " public Object object() {", |
| " return ChildModule_ProvideObjectFactory.provideObject(", |
| " childModule, a());", |
| " }", |
| "", |
| " @CanIgnoreReturnValue", |
| " private A injectA(A instance) {", |
| " A_MembersInjector.injectMethodA(instance);", |
| " return instance;", |
| " }", |
| " }", |
| "}") |
| .build(); |
| |
| Compilation compilation = |
| compilerWithOptions(compilerMode.javacopts()) |
| .compile( |
| parentComponentFile, |
| childComponentFile, |
| childModuleFile, |
| aFile, |
| needsDep1File, |
| dep1File, |
| dep2File); |
| assertThat(compilation).succeeded(); |
| assertThat(compilation) |
| .generatedSourceFile("test.DaggerParentComponent") |
| .containsElementsIn(generatedComponent); |
| } |
| |
| @Test |
| public void multipleSubcomponentsWithSameSimpleNamesCanExistInSameComponent() { |
| JavaFileObject parent = |
| JavaFileObjects.forSourceLines( |
| "test.ParentComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "", |
| "@Component", |
| "interface ParentComponent {", |
| " Foo.Sub newInstanceSubcomponent();", |
| " NoConflict newNoConflictSubcomponent();", |
| "}"); |
| JavaFileObject foo = |
| JavaFileObjects.forSourceLines( |
| "test.Foo", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "interface Foo {", |
| " @Subcomponent interface Sub {", |
| " Bar.Sub newBarSubcomponent();", |
| " }", |
| "}"); |
| JavaFileObject bar = |
| JavaFileObjects.forSourceLines( |
| "test.Bar", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "interface Bar {", |
| " @Subcomponent interface Sub {", |
| " test.subpackage.Sub newSubcomponentInSubpackage();", |
| " }", |
| "}"); |
| JavaFileObject baz = |
| JavaFileObjects.forSourceLines( |
| "test.subpackage.Sub", |
| "package test.subpackage;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent public interface Sub {}"); |
| JavaFileObject noConflict = |
| JavaFileObjects.forSourceLines( |
| "test.NoConflict", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent interface NoConflict {}"); |
| |
| JavaFileObject componentGeneratedFile = |
| JavaFileObjects.forSourceLines( |
| "test.DaggerParentComponent", |
| "package test;", |
| "", |
| "import test.subpackage.Sub;", |
| "", |
| GeneratedLines.generatedAnnotations(), |
| "final class DaggerParentComponent implements ParentComponent {", |
| " @Override", |
| " public Foo.Sub newInstanceSubcomponent() {", |
| " return new F_SubImpl();", |
| " }", |
| "", |
| " @Override", |
| " public NoConflict newNoConflictSubcomponent() {", |
| " return new NoConflictImpl();", |
| " }", |
| "", |
| " static final class Builder {", |
| " public ParentComponent build() {", |
| " return new DaggerParentComponent();", |
| " }", |
| " }", |
| "", |
| " private final class F_SubImpl implements Foo.Sub {", |
| " @Override", |
| " public Bar.Sub newBarSubcomponent() {", |
| " return new B_SubImpl();", |
| " }", |
| "", |
| " private final class B_SubImpl implements Bar.Sub {", |
| " @Override", |
| " public Sub newSubcomponentInSubpackage() {", |
| " return new ts_SubI();", |
| " }", |
| "", |
| " private final class ts_SubI implements Sub {}", |
| " }", |
| " }", |
| "", |
| " private final class NoConflictImpl implements NoConflict {}", |
| "}"); |
| Compilation compilation = |
| compilerWithOptions(compilerMode.javacopts()) |
| .compile(parent, foo, bar, baz, noConflict); |
| assertThat(compilation).succeeded(); |
| assertThat(compilation) |
| .generatedSourceFile("test.DaggerParentComponent") |
| .containsElementsIn(componentGeneratedFile); |
| } |
| |
| @Test |
| public void subcomponentSimpleNamesDisambiguated() { |
| JavaFileObject parent = |
| JavaFileObjects.forSourceLines( |
| "test.ParentComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "", |
| "@Component", |
| "interface ParentComponent {", |
| " Sub newSubcomponent();", |
| "}"); |
| JavaFileObject sub = |
| JavaFileObjects.forSourceLines( |
| "test.Sub", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent interface Sub {", |
| " test.deep.many.levels.that.match.test.Sub newDeepSubcomponent();", |
| "}"); |
| JavaFileObject deepSub = |
| JavaFileObjects.forSourceLines( |
| "test.deep.many.levels.that.match.test.Sub", |
| "package test.deep.many.levels.that.match.test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent public interface Sub {}"); |
| |
| JavaFileObject componentGeneratedFile = |
| JavaFileObjects.forSourceLines( |
| "test.DaggerParentComponent", |
| "package test;", |
| "", |
| GeneratedLines.generatedAnnotations(), |
| "final class DaggerParentComponent implements ParentComponent {", |
| " @Override", |
| " public Sub newSubcomponent() {", |
| " return new t_SubImpl();", |
| " }", |
| "", |
| " static final class Builder {", |
| " public ParentComponent build() {", |
| " return new DaggerParentComponent();", |
| " }", |
| " }", |
| "", |
| " private final class t_SubImpl implements Sub {", |
| " @Override", |
| " public test.deep.many.levels.that.match.test.Sub newDeepSubcomponent() {", |
| " return new tdmltmt_SubImpl();", |
| " }", |
| "", |
| " private final class tdmltmt_SubImpl implements ", |
| " test.deep.many.levels.that.match.test.Sub {", |
| " private tdmltmt_SubImpl() {}", |
| " }", |
| " }", |
| "}"); |
| Compilation compilation = |
| compilerWithOptions(compilerMode.javacopts()).compile(parent, sub, deepSub); |
| assertThat(compilation).succeeded(); |
| assertThat(compilation) |
| .generatedSourceFile("test.DaggerParentComponent") |
| .containsElementsIn(componentGeneratedFile); |
| } |
| |
| @Test |
| public void subcomponentSimpleNamesDisambiguatedInRoot() { |
| JavaFileObject parent = |
| JavaFileObjects.forSourceLines( |
| "ParentComponent", |
| "import dagger.Component;", |
| "", |
| "@Component", |
| "interface ParentComponent {", |
| " Sub newSubcomponent();", |
| "}"); |
| JavaFileObject sub = |
| JavaFileObjects.forSourceLines( |
| "Sub", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent interface Sub {", |
| " test.deep.many.levels.that.match.test.Sub newDeepSubcomponent();", |
| "}"); |
| JavaFileObject deepSub = |
| JavaFileObjects.forSourceLines( |
| "test.deep.many.levels.that.match.test.Sub", |
| "package test.deep.many.levels.that.match.test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent public interface Sub {}"); |
| |
| JavaFileObject componentGeneratedFile = |
| JavaFileObjects.forSourceLines( |
| "DaggerParentComponent", |
| "", |
| GeneratedLines.generatedAnnotations(), |
| "final class DaggerParentComponent implements ParentComponent {", |
| " @Override", |
| " public Sub newSubcomponent() {", |
| " return new $_SubImpl();", |
| " }", |
| "", |
| " private final class $_SubImpl implements Sub {", |
| " private $_SubImpl() {}", |
| "", |
| " @Override", |
| " public test.deep.many.levels.that.match.test.Sub newDeepSubcomponent() {", |
| " return new tdmltmt_SubImpl();", |
| " }", |
| "", |
| " private final class tdmltmt_SubImpl implements ", |
| " test.deep.many.levels.that.match.test.Sub {", |
| " private tdmltmt_SubImpl() {}", |
| " }", |
| " }", |
| "}", |
| ""); |
| |
| Compilation compilation = |
| compilerWithOptions(compilerMode.javacopts()).compile(parent, sub, deepSub); |
| assertThat(compilation).succeeded(); |
| assertThat(compilation) |
| .generatedSourceFile("DaggerParentComponent") |
| .containsElementsIn(componentGeneratedFile); |
| } |
| |
| @Test |
| public void subcomponentImplNameUsesFullyQualifiedClassNameIfNecessary() { |
| JavaFileObject parent = |
| JavaFileObjects.forSourceLines( |
| "test.ParentComponent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "", |
| "@Component", |
| "interface ParentComponent {", |
| " top1.a.b.c.d.E.F.Sub top1();", |
| " top2.a.b.c.d.E.F.Sub top2();", |
| "}"); |
| JavaFileObject top1 = |
| JavaFileObjects.forSourceLines( |
| "top1.a.b.c.d.E", |
| "package top1.a.b.c.d;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "public interface E {", |
| " interface F {", |
| " @Subcomponent interface Sub {}", |
| " }", |
| "}"); |
| JavaFileObject top2 = |
| JavaFileObjects.forSourceLines( |
| "top2.a.b.c.d.E", |
| "package top2.a.b.c.d;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "public interface E {", |
| " interface F {", |
| " @Subcomponent interface Sub {}", |
| " }", |
| "}"); |
| |
| JavaFileObject componentGeneratedFile = |
| JavaFileObjects.forSourceLines( |
| "test.DaggerParentComponent", |
| "package test;", |
| "", |
| "import top1.a.b.c.d.E;", |
| "", |
| GeneratedLines.generatedAnnotations(), |
| "final class DaggerParentComponent implements ParentComponent {", |
| " @Override", |
| " public E.F.Sub top1() {", |
| " return new F_SubImpl();", |
| " }", |
| "", |
| " @Override", |
| " public top2.a.b.c.d.E.F.Sub top2() {", |
| " return new F2_SubImpl();", |
| " }", |
| "", |
| " private final class F_SubImpl implements E.F.Sub {", |
| " private F_SubImpl() {}", |
| " }", |
| " private final class F2_SubImpl implements top2.a.b.c.d.E.F.Sub {", |
| " private F2_SubImpl() {}", |
| " }", |
| "}"); |
| |
| Compilation compilation = |
| compilerWithOptions(compilerMode.javacopts()).compile(parent, top1, top2); |
| assertThat(compilation).succeeded(); |
| assertThat(compilation) |
| .generatedSourceFile("test.DaggerParentComponent") |
| .containsElementsIn(componentGeneratedFile); |
| } |
| |
| @Test |
| public void parentComponentNameShouldNotBeDisambiguatedWhenItConflictsWithASubcomponent() { |
| JavaFileObject parent = |
| JavaFileObjects.forSourceLines( |
| "test.C", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "", |
| "@Component", |
| "interface C {", |
| " test.Foo.C newInstanceC();", |
| "}"); |
| JavaFileObject subcomponentWithSameSimpleNameAsParent = |
| JavaFileObjects.forSourceLines( |
| "test.Foo", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "interface Foo {", |
| " @Subcomponent interface C {}", |
| "}"); |
| |
| JavaFileObject componentGeneratedFile = |
| JavaFileObjects.forSourceLines( |
| "test.DaggerC", |
| "package test;", |
| "", |
| GeneratedLines.generatedAnnotations(), |
| "final class DaggerC implements C {", |
| " @Override", |
| " public Foo.C newInstanceC() {", |
| " return new F_CImpl();", |
| " }", |
| "", |
| " private final class F_CImpl implements Foo.C {}", |
| "}"); |
| |
| Compilation compilation = |
| compilerWithOptions(compilerMode.javacopts()) |
| .compile(parent, subcomponentWithSameSimpleNameAsParent); |
| assertThat(compilation).succeeded(); |
| assertThat(compilation) |
| .generatedSourceFile("test.DaggerC") |
| .containsElementsIn(componentGeneratedFile); |
| } |
| |
| @Test |
| public void subcomponentBuilderNamesShouldNotConflict() { |
| JavaFileObject parent = |
| JavaFileObjects.forSourceLines( |
| "test.C", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "import dagger.Subcomponent;", |
| "", |
| "@Component", |
| "interface C {", |
| " Foo.Sub.Builder fooBuilder();", |
| " Bar.Sub.Builder barBuilder();", |
| "", |
| " interface Foo {", |
| " @Subcomponent", |
| " interface Sub {", |
| " @Subcomponent.Builder", |
| " interface Builder {", |
| " Sub build();", |
| " }", |
| " }", |
| " }", |
| "", |
| " interface Bar {", |
| " @Subcomponent", |
| " interface Sub {", |
| " @Subcomponent.Builder", |
| " interface Builder {", |
| " Sub build();", |
| " }", |
| " }", |
| " }", |
| "}"); |
| JavaFileObject componentGeneratedFile = |
| JavaFileObjects.forSourceLines( |
| "test.DaggerC", |
| "package test;", |
| "", |
| GeneratedLines.generatedImports(), |
| "", |
| GeneratedLines.generatedAnnotations(), |
| "final class DaggerC implements C {", |
| " @Override", |
| " public C.Foo.Sub.Builder fooBuilder() {", |
| " return new F_SubBuilder();", |
| " }", |
| "", |
| " @Override", |
| " public C.Bar.Sub.Builder barBuilder() {", |
| " return new B_SubBuilder();", |
| " }", |
| "", |
| // TODO(bcorso): Reverse the order of subcomponent and builder so that subcomponent |
| // comes first. |
| " private final class F_SubBuilder implements C.Foo.Sub.Builder {", |
| " @Override", |
| " public C.Foo.Sub build() {", |
| " return new F_SubImpl();", |
| " }", |
| " }", |
| "", |
| " private final class F_SubImpl implements C.Foo.Sub {", |
| " private F_SubImpl() {}", |
| " }", |
| "", |
| " private final class B_SubBuilder implements C.Bar.Sub.Builder {", |
| " @Override", |
| " public C.Bar.Sub build() {", |
| " return new B_SubImpl();", |
| " }", |
| " }", |
| "", |
| " private final class B_SubImpl implements C.Bar.Sub {", |
| " private B_SubImpl() {}", |
| " }", |
| "}"); |
| Compilation compilation = |
| compilerWithOptions(compilerMode.javacopts()).compile(parent); |
| assertThat(compilation).succeeded(); |
| assertThat(compilation) |
| .generatedSourceFile("test.DaggerC") |
| .containsElementsIn(componentGeneratedFile); |
| } |
| |
| @Test |
| public void duplicateBindingWithSubcomponentDeclaration() { |
| JavaFileObject module = |
| JavaFileObjects.forSourceLines( |
| "test.TestModule", |
| "package test;", |
| "", |
| "import dagger.Module;", |
| "import dagger.Provides;", |
| "", |
| "@Module(subcomponents = Sub.class)", |
| "class TestModule {", |
| " @Provides Sub.Builder providesConflictsWithModuleSubcomponents() { return null; }", |
| " @Provides Object usesSubcomponentBuilder(Sub.Builder builder) {", |
| " return new Builder().toString();", |
| " }", |
| "}"); |
| |
| JavaFileObject subcomponent = |
| JavaFileObjects.forSourceLines( |
| "test.Sub", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "interface Sub {", |
| " @Subcomponent.Builder", |
| " interface Builder {", |
| " Sub build();", |
| " }", |
| "}"); |
| |
| JavaFileObject component = |
| JavaFileObjects.forSourceLines( |
| "test.Sub", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "", |
| "@Component(modules = TestModule.class)", |
| "interface C {", |
| " Object dependsOnBuilder();", |
| "}"); |
| |
| Compilation compilation = |
| compilerWithOptions(compilerMode.javacopts()) |
| .compile(module, component, subcomponent); |
| assertThat(compilation).failed(); |
| assertThat(compilation).hadErrorContaining("Sub.Builder is bound multiple times:"); |
| assertThat(compilation) |
| .hadErrorContaining( |
| "@Provides Sub.Builder " |
| + "TestModule.providesConflictsWithModuleSubcomponents()"); |
| assertThat(compilation) |
| .hadErrorContaining("@Module(subcomponents = Sub.class) for TestModule"); |
| } |
| |
| @Test |
| public void subcomponentDependsOnGeneratedType() { |
| JavaFileObject parent = |
| JavaFileObjects.forSourceLines( |
| "test.Parent", |
| "package test;", |
| "", |
| "import dagger.Component;", |
| "", |
| "@Component", |
| "interface Parent {", |
| " Child.Builder childBuilder();", |
| "}"); |
| |
| JavaFileObject child = |
| JavaFileObjects.forSourceLines( |
| "test.Child", |
| "package test;", |
| "", |
| "import dagger.Subcomponent;", |
| "", |
| "@Subcomponent", |
| "interface Child extends ChildSupertype {", |
| " @Subcomponent.Builder", |
| " interface Builder {", |
| " Child build();", |
| " }", |
| "}"); |
| |
| JavaFileObject childSupertype = |
| JavaFileObjects.forSourceLines( |
| "test.ChildSupertype", |
| "package test;", |
| "", |
| "interface ChildSupertype {", |
| " GeneratedType generatedType();", |
| "}"); |
| |
| Compilation compilation = |
| daggerCompiler( |
| new GeneratingProcessor( |
| "test.GeneratedType", |
| "package test;", |
| "", |
| "import javax.inject.Inject;", |
| "", |
| "final class GeneratedType {", |
| " @Inject GeneratedType() {}", |
| "}")) |
| .compile(parent, child, childSupertype); |
| assertThat(compilation).succeededWithoutWarnings(); |
| } |
| } |