blob: fbda59d780d5618676c888540d44bbe9e42c115d [file] [log] [blame]
/*
* Copyright (C) 2018 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.Compilers.compilerWithOptions;
import static dagger.internal.codegen.Compilers.daggerCompiler;
import static dagger.internal.codegen.TestUtils.message;
import static org.junit.Assume.assumeFalse;
import com.google.common.collect.ImmutableList;
import com.google.testing.compile.Compilation;
import com.google.testing.compile.JavaFileObjects;
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 DuplicateBindingsValidationTest {
@Parameters(name = "fullBindingGraphValidation={0}")
public static ImmutableList<Object[]> parameters() {
return ImmutableList.copyOf(new Object[][] {{false}, {true}});
}
private final boolean fullBindingGraphValidation;
public DuplicateBindingsValidationTest(boolean fullBindingGraphValidation) {
this.fullBindingGraphValidation = fullBindingGraphValidation;
}
@Test public void duplicateExplicitBindings_ProvidesAndComponentProvision() {
assumeFalse(fullBindingGraphValidation);
JavaFileObject component = JavaFileObjects.forSourceLines("test.Outer",
"package test;",
"",
"import dagger.Component;",
"import dagger.Module;",
"import dagger.Provides;",
"",
"final class Outer {",
" interface A {}",
"",
" interface B {}",
"",
" @Module",
" static class AModule {",
" @Provides String provideString() { return \"\"; }",
" @Provides A provideA(String s) { return new A() {}; }",
" }",
"",
" @Component(modules = AModule.class)",
" interface Parent {",
" A getA();",
" }",
"",
" @Module",
" static class BModule {",
" @Provides B provideB(A a) { return new B() {}; }",
" }",
"",
" @Component(dependencies = Parent.class, modules = { BModule.class, AModule.class})",
" interface Child {",
" B getB();",
" }",
"}");
Compilation compilation =
compilerWithOptions(
fullBindingGraphValidationOption())
.compile(component);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
message(
"Outer.A is bound multiple times:",
" @Provides Outer.A Outer.AModule.provideA(String)",
" Outer.A Outer.Parent.getA()"))
.inFile(component)
.onLineContaining("interface Child");
}
@Test public void duplicateExplicitBindings_TwoProvidesMethods() {
JavaFileObject component =
JavaFileObjects.forSourceLines(
"test.Outer",
"package test;",
"",
"import dagger.Component;",
"import dagger.Module;",
"import dagger.Provides;",
"import javax.inject.Inject;",
"",
"final class Outer {",
" interface A {}",
"",
" static class B {",
" @Inject B(A a) {}",
" }",
"",
" @Module",
" static class Module1 {",
" @Provides A provideA1() { return new A() {}; }",
" }",
"",
" @Module",
" static class Module2 {",
" @Provides String provideString() { return \"\"; }",
" @Provides A provideA2(String s) { return new A() {}; }",
" }",
"",
" @Module(includes = { Module1.class, Module2.class})",
" abstract static class Module3 {}",
"",
" @Component(modules = { Module1.class, Module2.class})",
" interface TestComponent {",
" A getA();",
" B getB();",
" }",
"}");
Compilation compilation =
compilerWithOptions(
fullBindingGraphValidationOption())
.compile(component);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
message(
"Outer.A is bound multiple times:",
" @Provides Outer.A Outer.Module1.provideA1()",
" @Provides Outer.A Outer.Module2.provideA2(String)"))
.inFile(component)
.onLineContaining("interface TestComponent");
if (fullBindingGraphValidation) {
assertThat(compilation)
.hadErrorContaining(
message(
"Outer.A is bound multiple times:",
" @Provides Outer.A Outer.Module1.provideA1()",
" @Provides Outer.A Outer.Module2.provideA2(String)"))
.inFile(component)
.onLineContaining("class Module3");
}
// The duplicate bindngs are also requested from B, but we don't want to report them again.
assertThat(compilation).hadErrorCount(fullBindingGraphValidation ? 2 : 1);
}
@Test
public void duplicateExplicitBindings_ProvidesVsBinds() {
JavaFileObject component =
JavaFileObjects.forSourceLines(
"test.Outer",
"package test;",
"",
"import dagger.Binds;",
"import dagger.Component;",
"import dagger.Module;",
"import dagger.Provides;",
"import javax.inject.Inject;",
"",
"final class Outer {",
" interface A {}",
"",
" static final class B implements A {",
" @Inject B() {}",
" }",
"",
" @Module",
" static class Module1 {",
" @Provides A provideA1() { return new A() {}; }",
" }",
"",
" @Module",
" static abstract class Module2 {",
" @Binds abstract A bindA2(B b);",
" }",
"",
" @Module(includes = { Module1.class, Module2.class})",
" abstract static class Module3 {}",
"",
" @Component(modules = { Module1.class, Module2.class})",
" interface TestComponent {",
" A getA();",
" }",
"}");
Compilation compilation =
compilerWithOptions(
fullBindingGraphValidationOption())
.compile(component);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
message(
"Outer.A is bound multiple times:",
" @Provides Outer.A Outer.Module1.provideA1()",
" @Binds Outer.A Outer.Module2.bindA2(Outer.B)"))
.inFile(component)
.onLineContaining("interface TestComponent");
if (fullBindingGraphValidation) {
assertThat(compilation)
.hadErrorContaining(
message(
"Outer.A is bound multiple times:",
" @Provides Outer.A Outer.Module1.provideA1()",
" @Binds Outer.A Outer.Module2.bindA2(Outer.B)"))
.inFile(component)
.onLineContaining("class Module3");
}
}
@Test
public void duplicateExplicitBindings_multibindingsAndExplicitSets() {
JavaFileObject component =
JavaFileObjects.forSourceLines(
"test.Outer",
"package test;",
"",
"import dagger.Binds;",
"import dagger.Component;",
"import dagger.Module;",
"import dagger.Provides;",
"import dagger.multibindings.IntoSet;",
"import java.util.HashSet;",
"import java.util.Set;",
"import javax.inject.Qualifier;",
"",
"final class Outer {",
" @Qualifier @interface SomeQualifier {}",
"",
" @Module",
" abstract static class TestModule1 {",
" @Provides @IntoSet static String stringSetElement() { return \"\"; }",
"",
" @Binds",
" @IntoSet abstract String bindStringSetElement(@SomeQualifier String value);",
"",
" @Provides @SomeQualifier",
" static String provideSomeQualifiedString() { return \"\"; }",
" }",
"",
" @Module",
" static class TestModule2 {",
" @Provides Set<String> stringSet() { return new HashSet<String>(); }",
" }",
"",
" @Module(includes = { TestModule1.class, TestModule2.class})",
" abstract static class TestModule3 {}",
"",
" @Component(modules = { TestModule1.class, TestModule2.class })",
" interface TestComponent {",
" Set<String> getStringSet();",
" }",
"}");
Compilation compilation =
compilerWithOptions(
fullBindingGraphValidationOption())
.compile(component);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
message(
"Set<String> has incompatible bindings or declarations:",
" Set bindings and declarations:",
" @Binds @IntoSet String "
+ "Outer.TestModule1.bindStringSetElement(@Outer.SomeQualifier "
+ "String)",
" @Provides @IntoSet String "
+ "Outer.TestModule1.stringSetElement()",
" Unique bindings and declarations:",
" @Provides Set<String> Outer.TestModule2.stringSet()"))
.inFile(component)
.onLineContaining(
fullBindingGraphValidation ? "class TestModule3" : "interface TestComponent");
}
@Test
public void duplicateExplicitBindings_multibindingsAndExplicitMaps() {
JavaFileObject component =
JavaFileObjects.forSourceLines(
"test.Outer",
"package test;",
"",
"import dagger.Binds;",
"import dagger.Component;",
"import dagger.Module;",
"import dagger.Provides;",
"import dagger.multibindings.IntoMap;",
"import dagger.multibindings.StringKey;",
"import java.util.HashMap;",
"import java.util.Map;",
"import javax.inject.Qualifier;",
"",
"final class Outer {",
" @Qualifier @interface SomeQualifier {}",
"",
" @Module",
" abstract static class TestModule1 {",
" @Provides @IntoMap",
" @StringKey(\"foo\")",
" static String stringMapEntry() { return \"\"; }",
"",
" @Binds @IntoMap @StringKey(\"bar\")",
" abstract String bindStringMapEntry(@SomeQualifier String value);",
"",
" @Provides @SomeQualifier",
" static String provideSomeQualifiedString() { return \"\"; }",
" }",
"",
" @Module",
" static class TestModule2 {",
" @Provides Map<String, String> stringMap() {",
" return new HashMap<String, String>();",
" }",
" }",
"",
" @Module(includes = { TestModule1.class, TestModule2.class})",
" abstract static class TestModule3 {}",
"",
" @Component(modules = { TestModule1.class, TestModule2.class })",
" interface TestComponent {",
" Map<String, String> getStringMap();",
" }",
"}");
Compilation compilation =
compilerWithOptions(
fullBindingGraphValidationOption())
.compile(component);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
message(
"Map<String,String> has incompatible bindings "
+ "or declarations:",
" Map bindings and declarations:",
" @Binds @IntoMap @StringKey(\"bar\") String"
+ " Outer.TestModule1.bindStringMapEntry(@Outer.SomeQualifier "
+ "String)",
" @Provides @IntoMap @StringKey(\"foo\") String"
+ " Outer.TestModule1.stringMapEntry()",
" Unique bindings and declarations:",
" @Provides Map<String,String> Outer.TestModule2.stringMap()"))
.inFile(component)
.onLineContaining(
fullBindingGraphValidation ? "class TestModule3" : "interface TestComponent");
}
@Test
public void duplicateExplicitBindings_UniqueBindingAndMultibindingDeclaration_Set() {
JavaFileObject component =
JavaFileObjects.forSourceLines(
"test.Outer",
"package test;",
"",
"import dagger.Component;",
"import dagger.Module;",
"import dagger.Provides;",
"import dagger.multibindings.Multibinds;",
"import java.util.HashSet;",
"import java.util.Set;",
"",
"final class Outer {",
" @Module",
" abstract static class TestModule1 {",
" @Multibinds abstract Set<String> stringSet();",
" }",
"",
" @Module",
" static class TestModule2 {",
" @Provides Set<String> stringSet() { return new HashSet<String>(); }",
" }",
"",
" @Module(includes = { TestModule1.class, TestModule2.class})",
" abstract static class TestModule3 {}",
"",
" @Component(modules = { TestModule1.class, TestModule2.class })",
" interface TestComponent {",
" Set<String> getStringSet();",
" }",
"}");
Compilation compilation =
compilerWithOptions(
fullBindingGraphValidationOption())
.compile(component);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
message(
"Set<String> has incompatible bindings or declarations:",
" Set bindings and declarations:",
" @Multibinds Set<String> "
+ "Outer.TestModule1.stringSet()",
" Unique bindings and declarations:",
" @Provides Set<String> Outer.TestModule2.stringSet()"))
.inFile(component)
.onLineContaining(
fullBindingGraphValidation ? "class TestModule3" : "interface TestComponent");
}
@Test
public void duplicateExplicitBindings_UniqueBindingAndMultibindingDeclaration_Map() {
JavaFileObject component =
JavaFileObjects.forSourceLines(
"test.Outer",
"package test;",
"",
"import dagger.Component;",
"import dagger.Module;",
"import dagger.Provides;",
"import dagger.multibindings.Multibinds;",
"import java.util.HashMap;",
"import java.util.Map;",
"",
"final class Outer {",
" @Module",
" abstract static class TestModule1 {",
" @Multibinds abstract Map<String, String> stringMap();",
" }",
"",
" @Module",
" static class TestModule2 {",
" @Provides Map<String, String> stringMap() {",
" return new HashMap<String, String>();",
" }",
" }",
"",
" @Module(includes = { TestModule1.class, TestModule2.class})",
" abstract static class TestModule3 {}",
"",
" @Component(modules = { TestModule1.class, TestModule2.class })",
" interface TestComponent {",
" Map<String, String> getStringMap();",
" }",
"}");
Compilation compilation =
compilerWithOptions(
fullBindingGraphValidationOption())
.compile(component);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
message(
"Map<String,String> has incompatible bindings "
+ "or declarations:",
" Map bindings and declarations:",
" @Multibinds Map<String,String> "
+ "Outer.TestModule1.stringMap()",
" Unique bindings and declarations:",
" @Provides Map<String,String> Outer.TestModule2.stringMap()"))
.inFile(component)
.onLineContaining(
fullBindingGraphValidation ? "class TestModule3" : "interface TestComponent");
}
@Test public void duplicateBindings_TruncateAfterLimit() {
JavaFileObject component =
JavaFileObjects.forSourceLines(
"test.Outer",
"package test;",
"",
"import dagger.Component;",
"import dagger.Module;",
"import dagger.Provides;",
"import javax.inject.Inject;",
"",
"final class Outer {",
" interface A {}",
"",
" @Module",
" static class Module01 {",
" @Provides A provideA() { return new A() {}; }",
" }",
"",
" @Module",
" static class Module02 {",
" @Provides A provideA() { return new A() {}; }",
" }",
"",
" @Module",
" static class Module03 {",
" @Provides A provideA() { return new A() {}; }",
" }",
"",
" @Module",
" static class Module04 {",
" @Provides A provideA() { return new A() {}; }",
" }",
"",
" @Module",
" static class Module05 {",
" @Provides A provideA() { return new A() {}; }",
" }",
"",
" @Module",
" static class Module06 {",
" @Provides A provideA() { return new A() {}; }",
" }",
"",
" @Module",
" static class Module07 {",
" @Provides A provideA() { return new A() {}; }",
" }",
"",
" @Module",
" static class Module08 {",
" @Provides A provideA() { return new A() {}; }",
" }",
"",
" @Module",
" static class Module09 {",
" @Provides A provideA() { return new A() {}; }",
" }",
"",
" @Module",
" static class Module10 {",
" @Provides A provideA() { return new A() {}; }",
" }",
"",
" @Module",
" static class Module11 {",
" @Provides A provideA() { return new A() {}; }",
" }",
"",
" @Module",
" static class Module12 {",
" @Provides A provideA() { return new A() {}; }",
" }",
"",
" @Module(includes = {",
" Module01.class,",
" Module02.class,",
" Module03.class,",
" Module04.class,",
" Module05.class,",
" Module06.class,",
" Module07.class,",
" Module08.class,",
" Module09.class,",
" Module10.class,",
" Module11.class,",
" Module12.class",
" })",
" abstract static class Modules {}",
"",
" @Component(modules = {",
" Module01.class,",
" Module02.class,",
" Module03.class,",
" Module04.class,",
" Module05.class,",
" Module06.class,",
" Module07.class,",
" Module08.class,",
" Module09.class,",
" Module10.class,",
" Module11.class,",
" Module12.class",
" })",
" interface TestComponent {",
" A getA();",
" }",
"}");
Compilation compilation =
compilerWithOptions(
fullBindingGraphValidationOption())
.compile(component);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
message(
"Outer.A is bound multiple times:",
" @Provides Outer.A Outer.Module01.provideA()",
" @Provides Outer.A Outer.Module02.provideA()",
" @Provides Outer.A Outer.Module03.provideA()",
" @Provides Outer.A Outer.Module04.provideA()",
" @Provides Outer.A Outer.Module05.provideA()",
" @Provides Outer.A Outer.Module06.provideA()",
" @Provides Outer.A Outer.Module07.provideA()",
" @Provides Outer.A Outer.Module08.provideA()",
" @Provides Outer.A Outer.Module09.provideA()",
" @Provides Outer.A Outer.Module10.provideA()",
" and 2 others"))
.inFile(component)
.onLineContaining(fullBindingGraphValidation ? "class Modules" : "interface TestComponent");
}
@Test
public void childBindingConflictsWithParent() {
JavaFileObject aComponent =
JavaFileObjects.forSourceLines(
"test.A",
"package test;",
"",
"import dagger.Component;",
"import dagger.Module;",
"import dagger.Provides;",
"",
"@Component(modules = A.AModule.class)",
"interface A {",
" Object conflict();",
"",
" B.Builder b();",
"",
" @Module(subcomponents = B.class)",
" static class AModule {",
" @Provides static Object abConflict() {",
" return \"a\";",
" }",
" }",
"}");
JavaFileObject bComponent =
JavaFileObjects.forSourceLines(
"test.B",
"package test;",
"",
"import dagger.Module;",
"import dagger.Provides;",
"import dagger.Subcomponent;",
"",
"@Subcomponent(modules = B.BModule.class)",
"interface B {",
" Object conflict();",
"",
" @Subcomponent.Builder",
" interface Builder {",
" B build();",
" }",
"",
" @Module",
" static class BModule {",
" @Provides static Object abConflict() {",
" return \"b\";",
" }",
" }",
"}");
Compilation compilation =
compilerWithOptions(
fullBindingGraphValidationOption())
.compile(aComponent, bComponent);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
message(
"Object is bound multiple times:",
" @Provides Object test.A.AModule.abConflict()",
" @Provides Object test.B.BModule.abConflict()"))
.inFile(aComponent)
.onLineContaining(fullBindingGraphValidation ? "class AModule" : "interface A {");
}
@Test
public void grandchildBindingConflictsWithGrandparent() {
JavaFileObject aComponent =
JavaFileObjects.forSourceLines(
"test.A",
"package test;",
"",
"import dagger.Component;",
"import dagger.Module;",
"import dagger.Provides;",
"",
"@Component(modules = A.AModule.class)",
"interface A {",
" Object conflict();",
"",
" B.Builder b();",
"",
" @Module(subcomponents = B.class)",
" static class AModule {",
" @Provides static Object acConflict() {",
" return \"a\";",
" }",
" }",
"}");
JavaFileObject bComponent =
JavaFileObjects.forSourceLines(
"test.B",
"package test;",
"",
"import dagger.Subcomponent;",
"",
"@Subcomponent",
"interface B {",
" C.Builder c();",
"",
" @Subcomponent.Builder",
" interface Builder {",
" B build();",
" }",
"}");
JavaFileObject cComponent =
JavaFileObjects.forSourceLines(
"test.C",
"package test;",
"",
"import dagger.Module;",
"import dagger.Provides;",
"import dagger.Subcomponent;",
"",
"@Subcomponent(modules = C.CModule.class)",
"interface C {",
" Object conflict();",
"",
" @Subcomponent.Builder",
" interface Builder {",
" C build();",
" }",
"",
" @Module",
" static class CModule {",
" @Provides static Object acConflict() {",
" return \"c\";",
" }",
" }",
"}");
Compilation compilation =
compilerWithOptions(
fullBindingGraphValidationOption())
.compile(aComponent, bComponent, cComponent);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
message(
"Object is bound multiple times:",
" @Provides Object test.A.AModule.acConflict()",
" @Provides Object test.C.CModule.acConflict()"))
.inFile(aComponent)
.onLineContaining(fullBindingGraphValidation ? "class AModule" : "interface A {");
}
@Test
public void grandchildBindingConflictsWithChild() {
JavaFileObject aComponent =
JavaFileObjects.forSourceLines(
"test.A",
"package test;",
"",
"import dagger.Component;",
"",
"@Component",
"interface A {",
" B b();",
"}");
JavaFileObject bComponent =
JavaFileObjects.forSourceLines(
"test.B",
"package test;",
"",
"import dagger.Module;",
"import dagger.Provides;",
"import dagger.Subcomponent;",
"",
"@Subcomponent(modules = B.BModule.class)",
"interface B {",
" Object conflict();",
"",
" C.Builder c();",
"",
" @Module(subcomponents = C.class)",
" static class BModule {",
" @Provides static Object bcConflict() {",
" return \"b\";",
" }",
" }",
"}");
JavaFileObject cComponent =
JavaFileObjects.forSourceLines(
"test.C",
"package test;",
"",
"import dagger.Module;",
"import dagger.Provides;",
"import dagger.Subcomponent;",
"",
"@Subcomponent(modules = C.CModule.class)",
"interface C {",
" Object conflict();",
"",
" @Subcomponent.Builder",
" interface Builder {",
" C build();",
" }",
"",
" @Module",
" static class CModule {",
" @Provides static Object bcConflict() {",
" return \"c\";",
" }",
" }",
"}");
Compilation compilation =
compilerWithOptions(
fullBindingGraphValidationOption())
.compile(aComponent, bComponent, cComponent);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
message(
"Object is bound multiple times:",
" @Provides Object test.B.BModule.bcConflict()",
" @Provides Object test.C.CModule.bcConflict()"))
.inFile(fullBindingGraphValidation ? bComponent : aComponent)
.onLineContaining(fullBindingGraphValidation ? "class BModule" : "interface A {");
}
@Test
public void childProvidesConflictsWithParentInjects() {
assumeFalse(fullBindingGraphValidation);
JavaFileObject foo =
JavaFileObjects.forSourceLines(
"test.Foo",
"package test;",
"",
"import java.util.Set;",
"import javax.inject.Inject;",
"",
"final class Foo {",
" @Inject Foo(Set<String> strings) {}",
"}");
JavaFileObject injected1 =
JavaFileObjects.forSourceLines(
"test.Injected1",
"package test;",
"",
"import dagger.Component;",
"import dagger.Module;",
"import dagger.Provides;",
"import dagger.multibindings.IntoSet;",
"import java.util.Set;",
"",
"@Component(modules = Injected1.Injected1Module.class)",
"interface Injected1 {",
" Foo foo();",
" Injected2 injected2();",
"",
" @Module",
" interface Injected1Module {",
" @Provides @IntoSet static String string() {",
" return \"injected1\";",
" }",
" }",
"}");
JavaFileObject injected2 =
JavaFileObjects.forSourceLines(
"test.Injected2",
"package test;",
"",
"import dagger.Module;",
"import dagger.Provides;",
"import dagger.Subcomponent;",
"import dagger.multibindings.IntoSet;",
"import java.util.Set;",
"",
"@Subcomponent(modules = Injected2.Injected2Module.class)",
"interface Injected2 {",
" Foo foo();",
" Provided1 provided1();",
"",
" @Module",
" interface Injected2Module {",
" @Provides @IntoSet static String string() {",
" return \"injected2\";",
" }",
" }",
"}");
JavaFileObject provided1 =
JavaFileObjects.forSourceLines(
"test.Provided1",
"package test;",
"",
"import dagger.Module;",
"import dagger.Provides;",
"import dagger.Subcomponent;",
"import dagger.multibindings.IntoSet;",
"import java.util.Set;",
"",
"@Subcomponent(modules = Provided1.Provided1Module.class)",
"interface Provided1 {",
" Foo foo();",
" Provided2 provided2();",
"",
" @Module",
" static class Provided1Module {",
" @Provides static Foo provideFoo(Set<String> strings) {",
" return new Foo(strings);",
" }",
"",
" @Provides @IntoSet static String string() {",
" return \"provided1\";",
" }",
" }",
"}");
JavaFileObject provided2 =
JavaFileObjects.forSourceLines(
"test.Provided2",
"package test;",
"",
"import dagger.Module;",
"import dagger.Provides;",
"import dagger.Subcomponent;",
"import dagger.multibindings.IntoSet;",
"",
"@Subcomponent(modules = Provided2.Provided2Module.class)",
"interface Provided2 {",
" Foo foo();",
"",
" @Module",
" static class Provided2Module {",
" @Provides @IntoSet static String string() {",
" return \"provided2\";",
" }",
" }",
"}");
Compilation compilation =
daggerCompiler().compile(foo, injected1, injected2, provided1, provided2);
assertThat(compilation).succeeded();
assertThat(compilation)
.hadWarningContaining(
message(
"Foo is bound multiple times:",
" @Inject Foo(Set<String>) [Injected1]",
" @Provides Foo Provided1.Provided1Module.provideFoo(Set<String>) "
+ "[Injected1 → Injected2 → Provided1]"))
.inFile(injected1)
.onLineContaining("interface Injected1 {");
}
@Test
public void grandchildBindingConflictsWithParentWithNullableViolationAsWarning() {
JavaFileObject parentConflictsWithChild =
JavaFileObjects.forSourceLines(
"test.ParentConflictsWithChild",
"package test;",
"",
"import dagger.Component;",
"import dagger.Module;",
"import dagger.Provides;",
"import javax.annotation.Nullable;",
"",
"@Component(modules = ParentConflictsWithChild.ParentModule.class)",
"interface ParentConflictsWithChild {",
" Child.Builder child();",
"",
" @Module(subcomponents = Child.class)",
" static class ParentModule {",
" @Provides @Nullable static Object nullableParentChildConflict() {",
" return \"parent\";",
" }",
" }",
"}");
JavaFileObject child =
JavaFileObjects.forSourceLines(
"test.Child",
"package test;",
"",
"import dagger.Module;",
"import dagger.Provides;",
"import dagger.Subcomponent;",
"",
"@Subcomponent(modules = Child.ChildModule.class)",
"interface Child {",
" Object parentChildConflictThatViolatesNullability();",
"",
" @Subcomponent.Builder",
" interface Builder {",
" Child build();",
" }",
"",
" @Module",
" static class ChildModule {",
" @Provides static Object nonNullableParentChildConflict() {",
" return \"child\";",
" }",
" }",
"}");
Compilation compilation =
compilerWithOptions(
"-Adagger.nullableValidation=WARNING",
fullBindingGraphValidationOption())
.compile(parentConflictsWithChild, child);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
message(
"Object is bound multiple times:",
" @Provides Object Child.ChildModule.nonNullableParentChildConflict()",
" @Provides @Nullable Object"
+ " ParentConflictsWithChild.ParentModule.nullableParentChildConflict()"))
.inFile(parentConflictsWithChild)
.onLineContaining(
fullBindingGraphValidation
? "class ParentModule"
: "interface ParentConflictsWithChild");
}
private String fullBindingGraphValidationOption() {
return "-Adagger.fullBindingGraphValidation=" + (fullBindingGraphValidation ? "ERROR" : "NONE");
}
@Test
public void reportedInParentAndChild() {
JavaFileObject parent =
JavaFileObjects.forSourceLines(
"test.Parent",
"package test;",
"",
"import dagger.Component;",
"",
"@Component(modules = ParentModule.class)",
"interface Parent {",
" Child.Builder childBuilder();",
" String duplicated();",
"}");
JavaFileObject parentModule =
JavaFileObjects.forSourceLines(
"test.ParentModule",
"package test;",
"",
"import dagger.BindsOptionalOf;",
"import dagger.Module;",
"import dagger.Provides;",
"import java.util.Optional;",
"",
"@Module",
"interface ParentModule {",
" @Provides static String one(Optional<Object> optional) { return \"one\"; }",
" @Provides static String two() { return \"two\"; }",
" @BindsOptionalOf Object optional();",
"}");
JavaFileObject child =
JavaFileObjects.forSourceLines(
"test.Child",
"package test;",
"",
"import dagger.Subcomponent;",
"",
"@Subcomponent(modules = ChildModule.class)",
"interface Child {",
" String duplicated();",
"",
" @Subcomponent.Builder",
" interface Builder {",
" Child build();",
" }",
"}");
JavaFileObject childModule =
JavaFileObjects.forSourceLines(
"test.ChildModule",
"package test;",
"",
"import dagger.Module;",
"import dagger.Provides;",
"import java.util.Optional;",
"",
"@Module",
"interface ChildModule {",
" @Provides static Object object() { return \"object\"; }",
"}");
Compilation compilation = daggerCompiler().compile(parent, parentModule, child, childModule);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining("String is bound multiple times")
.inFile(parent)
.onLineContaining("interface Parent");
assertThat(compilation).hadErrorCount(1);
}
}