932 lines
32 KiB
Java
932 lines
32 KiB
Java
/*
|
|
* 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.daggerCompiler;
|
|
import static dagger.internal.codegen.TestUtils.message;
|
|
|
|
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.JUnit4;
|
|
|
|
@RunWith(JUnit4.class)
|
|
public class MissingBindingValidationTest {
|
|
@Test
|
|
public void dependOnInterface() {
|
|
JavaFileObject component = JavaFileObjects.forSourceLines("test.MyComponent",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@Component",
|
|
"interface MyComponent {",
|
|
" Foo getFoo();",
|
|
"}");
|
|
JavaFileObject injectable = JavaFileObjects.forSourceLines("test.Foo",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Inject;",
|
|
"",
|
|
"class Foo {",
|
|
" @Inject Foo(Bar bar) {}",
|
|
"}");
|
|
JavaFileObject nonInjectable = JavaFileObjects.forSourceLines("test.Bar",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Inject;",
|
|
"",
|
|
"interface Bar {}");
|
|
Compilation compilation = daggerCompiler().compile(component, injectable, nonInjectable);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation).hadErrorCount(1);
|
|
assertThat(compilation)
|
|
.hadErrorContaining("Bar cannot be provided without an @Provides-annotated method.")
|
|
.inFile(component)
|
|
.onLineContaining("interface MyComponent");
|
|
}
|
|
|
|
@Test
|
|
public void entryPointDependsOnInterface() {
|
|
JavaFileObject component =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.TestClass",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"",
|
|
"final class TestClass {",
|
|
" interface A {}",
|
|
"",
|
|
" @Component()",
|
|
" interface AComponent {",
|
|
" A getA();",
|
|
" }",
|
|
"}");
|
|
Compilation compilation = daggerCompiler().compile(component);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation).hadErrorCount(1);
|
|
assertThat(compilation)
|
|
.hadErrorContaining(
|
|
"\033[1;31m[Dagger/MissingBinding]\033[0m TestClass.A cannot be provided "
|
|
+ "without an @Provides-annotated method.")
|
|
.inFile(component)
|
|
.onLineContaining("interface AComponent");
|
|
}
|
|
|
|
@Test
|
|
public void entryPointDependsOnQualifiedInterface() {
|
|
JavaFileObject component =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.TestClass",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"import javax.inject.Qualifier;",
|
|
"",
|
|
"final class TestClass {",
|
|
" @Qualifier @interface Q {}",
|
|
" interface A {}",
|
|
"",
|
|
" @Component()",
|
|
" interface AComponent {",
|
|
" @Q A qualifiedA();",
|
|
" }",
|
|
"}");
|
|
Compilation compilation = daggerCompiler().compile(component);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation).hadErrorCount(1);
|
|
assertThat(compilation)
|
|
.hadErrorContaining(
|
|
"\033[1;31m[Dagger/MissingBinding]\033[0m @TestClass.Q TestClass.A cannot be provided "
|
|
+ "without an @Provides-annotated method.")
|
|
.inFile(component)
|
|
.onLineContaining("interface AComponent");
|
|
}
|
|
|
|
@Test public void constructorInjectionWithoutAnnotation() {
|
|
JavaFileObject component = JavaFileObjects.forSourceLines("test.TestClass",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"import dagger.Module;",
|
|
"import dagger.Provides;",
|
|
"import javax.inject.Inject;",
|
|
"",
|
|
"final class TestClass {",
|
|
" static class A {",
|
|
" A() {}",
|
|
" }",
|
|
"",
|
|
" @Component()",
|
|
" interface AComponent {",
|
|
" A getA();",
|
|
" }",
|
|
"}");
|
|
|
|
Compilation compilation = daggerCompiler().compile(component);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation).hadErrorCount(1);
|
|
assertThat(compilation)
|
|
.hadErrorContaining(
|
|
"TestClass.A cannot be provided without an @Inject constructor or an "
|
|
+ "@Provides-annotated method.")
|
|
.inFile(component)
|
|
.onLineContaining("interface AComponent");
|
|
}
|
|
|
|
@Test public void membersInjectWithoutProvision() {
|
|
JavaFileObject component = JavaFileObjects.forSourceLines("test.TestClass",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"import dagger.Module;",
|
|
"import dagger.Provides;",
|
|
"import javax.inject.Inject;",
|
|
"",
|
|
"final class TestClass {",
|
|
" static class A {",
|
|
" @Inject A() {}",
|
|
" }",
|
|
"",
|
|
" static class B {",
|
|
" @Inject A a;",
|
|
" }",
|
|
"",
|
|
" @Component()",
|
|
" interface AComponent {",
|
|
" B getB();",
|
|
" }",
|
|
"}");
|
|
|
|
Compilation compilation = daggerCompiler().compile(component);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation).hadErrorCount(1);
|
|
assertThat(compilation)
|
|
.hadErrorContaining(
|
|
"TestClass.B cannot be provided without an @Inject constructor or an "
|
|
+ "@Provides-annotated method. This type supports members injection but cannot be "
|
|
+ "implicitly provided.")
|
|
.inFile(component)
|
|
.onLineContaining("interface AComponent");
|
|
}
|
|
|
|
@Test
|
|
public void missingBindingWithSameKeyAsMembersInjectionMethod() {
|
|
JavaFileObject self =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.Self",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Inject;",
|
|
"import javax.inject.Provider;",
|
|
"",
|
|
"class Self {",
|
|
" @Inject Provider<Self> selfProvider;",
|
|
"}");
|
|
JavaFileObject component =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.SelfComponent",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@Component",
|
|
"interface SelfComponent {",
|
|
" void inject(Self target);",
|
|
"}");
|
|
|
|
Compilation compilation = daggerCompiler().compile(self, component);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation).hadErrorCount(1);
|
|
assertThat(compilation)
|
|
.hadErrorContaining("Self cannot be provided without an @Inject constructor")
|
|
.inFile(component)
|
|
.onLineContaining("interface SelfComponent");
|
|
}
|
|
|
|
@Test
|
|
public void genericInjectClassWithWildcardDependencies() {
|
|
JavaFileObject component =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.TestComponent",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@Component",
|
|
"interface TestComponent {",
|
|
" Foo<? extends Number> foo();",
|
|
"}");
|
|
JavaFileObject foo =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.Foo",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Inject;",
|
|
"",
|
|
"final class Foo<T> {",
|
|
" @Inject Foo(T t) {}",
|
|
"}");
|
|
Compilation compilation = daggerCompiler().compile(component, foo);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation).hadErrorCount(1);
|
|
assertThat(compilation)
|
|
.hadErrorContaining(
|
|
"Foo<? extends Number> cannot be provided "
|
|
+ "without an @Provides-annotated method");
|
|
}
|
|
|
|
@Test public void longChainOfDependencies() {
|
|
JavaFileObject component =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.TestClass",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"import dagger.Lazy;",
|
|
"import dagger.Module;",
|
|
"import dagger.Provides;",
|
|
"import javax.inject.Inject;",
|
|
"import javax.inject.Named;",
|
|
"import javax.inject.Provider;",
|
|
"",
|
|
"final class TestClass {",
|
|
" interface A {}",
|
|
"",
|
|
" static class B {",
|
|
" @Inject B(A a) {}",
|
|
" }",
|
|
"",
|
|
" static class C {",
|
|
" @Inject B b;",
|
|
" @Inject C(X x) {}",
|
|
" }",
|
|
"",
|
|
" interface D { }",
|
|
"",
|
|
" static class DImpl implements D {",
|
|
" @Inject DImpl(C c, B b) {}",
|
|
" }",
|
|
"",
|
|
" static class X {",
|
|
" @Inject X() {}",
|
|
" }",
|
|
"",
|
|
" @Module",
|
|
" static class DModule {",
|
|
" @Provides @Named(\"slim shady\") D d(X x1, DImpl impl, X x2) { return impl; }",
|
|
" }",
|
|
"",
|
|
" @Component(modules = { DModule.class })",
|
|
" interface AComponent {",
|
|
" @Named(\"slim shady\") D getFoo();",
|
|
" C injectC(C c);",
|
|
" Provider<C> cProvider();",
|
|
" Lazy<C> lazyC();",
|
|
" Provider<Lazy<C>> lazyCProvider();",
|
|
" }",
|
|
"}");
|
|
|
|
Compilation compilation = daggerCompiler().compile(component);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation).hadErrorCount(1);
|
|
assertThat(compilation)
|
|
.hadErrorContaining(
|
|
message(
|
|
"TestClass.A cannot be provided without an @Provides-annotated method.",
|
|
" TestClass.A is injected at",
|
|
" TestClass.B(a)",
|
|
" TestClass.B is injected at",
|
|
" TestClass.C.b",
|
|
" TestClass.C is injected at",
|
|
" TestClass.AComponent.injectC(TestClass.C)",
|
|
"The following other entry points also depend on it:",
|
|
" TestClass.AComponent.getFoo()",
|
|
" TestClass.AComponent.cProvider()",
|
|
" TestClass.AComponent.lazyC()",
|
|
" TestClass.AComponent.lazyCProvider()"))
|
|
.inFile(component)
|
|
.onLineContaining("interface AComponent");
|
|
}
|
|
|
|
@Test
|
|
public void bindsMethodAppearsInTrace() {
|
|
JavaFileObject component =
|
|
JavaFileObjects.forSourceLines(
|
|
"TestComponent",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@Component(modules = TestModule.class)",
|
|
"interface TestComponent {",
|
|
" TestInterface testInterface();",
|
|
"}");
|
|
JavaFileObject interfaceFile =
|
|
JavaFileObjects.forSourceLines("TestInterface", "interface TestInterface {}");
|
|
JavaFileObject implementationFile =
|
|
JavaFileObjects.forSourceLines(
|
|
"TestImplementation",
|
|
"import javax.inject.Inject;",
|
|
"",
|
|
"final class TestImplementation implements TestInterface {",
|
|
" @Inject TestImplementation(String missingBinding) {}",
|
|
"}");
|
|
JavaFileObject module =
|
|
JavaFileObjects.forSourceLines(
|
|
"TestModule",
|
|
"import dagger.Binds;",
|
|
"import dagger.Module;",
|
|
"",
|
|
"@Module",
|
|
"interface TestModule {",
|
|
" @Binds abstract TestInterface bindTestInterface(TestImplementation implementation);",
|
|
"}");
|
|
|
|
Compilation compilation =
|
|
daggerCompiler().compile(component, module, interfaceFile, implementationFile);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation).hadErrorCount(1);
|
|
assertThat(compilation)
|
|
.hadErrorContaining(
|
|
message(
|
|
"String cannot be provided without an @Inject constructor or an "
|
|
+ "@Provides-annotated method.",
|
|
" String is injected at",
|
|
" TestImplementation(missingBinding)",
|
|
" TestImplementation is injected at",
|
|
" TestModule.bindTestInterface(implementation)",
|
|
" TestInterface is requested at",
|
|
" TestComponent.testInterface()"))
|
|
.inFile(component)
|
|
.onLineContaining("interface TestComponent");
|
|
}
|
|
|
|
@Test public void resolvedParametersInDependencyTrace() {
|
|
JavaFileObject generic = JavaFileObjects.forSourceLines("test.Generic",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Inject;",
|
|
"import javax.inject.Provider;",
|
|
"",
|
|
"final class Generic<T> {",
|
|
" @Inject Generic(T t) {}",
|
|
"}");
|
|
JavaFileObject testClass = JavaFileObjects.forSourceLines("test.TestClass",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Inject;",
|
|
"import java.util.List;",
|
|
"",
|
|
"final class TestClass {",
|
|
" @Inject TestClass(List list) {}",
|
|
"}");
|
|
JavaFileObject usesTest = JavaFileObjects.forSourceLines("test.UsesTest",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Inject;",
|
|
"",
|
|
"final class UsesTest {",
|
|
" @Inject UsesTest(Generic<TestClass> genericTestClass) {}",
|
|
"}");
|
|
JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@Component",
|
|
"interface TestComponent {",
|
|
" UsesTest usesTest();",
|
|
"}");
|
|
|
|
Compilation compilation = daggerCompiler().compile(generic, testClass, usesTest, component);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation).hadErrorCount(1);
|
|
assertThat(compilation)
|
|
.hadErrorContaining(
|
|
message(
|
|
"List cannot be provided without an @Provides-annotated method.",
|
|
" List is injected at",
|
|
" TestClass(list)",
|
|
" TestClass is injected at",
|
|
" Generic(t)",
|
|
" Generic<TestClass> is injected at",
|
|
" UsesTest(genericTestClass)",
|
|
" UsesTest is requested at",
|
|
" TestComponent.usesTest()"));
|
|
}
|
|
|
|
@Test public void resolvedVariablesInDependencyTrace() {
|
|
JavaFileObject generic = JavaFileObjects.forSourceLines("test.Generic",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Inject;",
|
|
"import javax.inject.Provider;",
|
|
"",
|
|
"final class Generic<T> {",
|
|
" @Inject T t;",
|
|
" @Inject Generic() {}",
|
|
"}");
|
|
JavaFileObject testClass = JavaFileObjects.forSourceLines("test.TestClass",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Inject;",
|
|
"import java.util.List;",
|
|
"",
|
|
"final class TestClass {",
|
|
" @Inject TestClass(List list) {}",
|
|
"}");
|
|
JavaFileObject usesTest = JavaFileObjects.forSourceLines("test.UsesTest",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Inject;",
|
|
"",
|
|
"final class UsesTest {",
|
|
" @Inject UsesTest(Generic<TestClass> genericTestClass) {}",
|
|
"}");
|
|
JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@Component",
|
|
"interface TestComponent {",
|
|
" UsesTest usesTest();",
|
|
"}");
|
|
|
|
Compilation compilation = daggerCompiler().compile(generic, testClass, usesTest, component);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation).hadErrorCount(1);
|
|
assertThat(compilation)
|
|
.hadErrorContaining(
|
|
message(
|
|
"List cannot be provided without an @Provides-annotated method.",
|
|
" List is injected at",
|
|
" TestClass(list)",
|
|
" TestClass is injected at",
|
|
" Generic.t",
|
|
" Generic<TestClass> is injected at",
|
|
" UsesTest(genericTestClass)",
|
|
" UsesTest is requested at",
|
|
" TestComponent.usesTest()"));
|
|
}
|
|
|
|
@Test
|
|
public void bindingUsedOnlyInSubcomponentDependsOnBindingOnlyInSubcomponent() {
|
|
JavaFileObject parent =
|
|
JavaFileObjects.forSourceLines(
|
|
"Parent",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@Component(modules = ParentModule.class)",
|
|
"interface Parent {",
|
|
" Child child();",
|
|
"}");
|
|
JavaFileObject parentModule =
|
|
JavaFileObjects.forSourceLines(
|
|
"ParentModule",
|
|
"import dagger.Module;",
|
|
"import dagger.Provides;",
|
|
"",
|
|
"@Module",
|
|
"class ParentModule {",
|
|
" @Provides static Object needsString(String string) {",
|
|
" return \"needs string: \" + string;",
|
|
" }",
|
|
"}");
|
|
JavaFileObject child =
|
|
JavaFileObjects.forSourceLines(
|
|
"Child",
|
|
"import dagger.Subcomponent;",
|
|
"",
|
|
"@Subcomponent(modules = ChildModule.class)",
|
|
"interface Child {",
|
|
" String string();",
|
|
" Object needsString();",
|
|
"}");
|
|
JavaFileObject childModule =
|
|
JavaFileObjects.forSourceLines(
|
|
"ChildModule",
|
|
"import dagger.Module;",
|
|
"import dagger.Provides;",
|
|
"",
|
|
"@Module",
|
|
"class ChildModule {",
|
|
" @Provides static String string() {",
|
|
" return \"child string\";",
|
|
" }",
|
|
"}");
|
|
|
|
Compilation compilation = daggerCompiler().compile(parent, parentModule, child, childModule);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation).hadErrorCount(1);
|
|
assertThat(compilation)
|
|
.hadErrorContainingMatch(
|
|
"(?s)\\QString cannot be provided\\E.*\\QChild.needsString()\\E")
|
|
.inFile(parent)
|
|
.onLineContaining("interface Parent");
|
|
}
|
|
|
|
@Test
|
|
public void multibindingContributionBetweenAncestorComponentAndEntrypointComponent() {
|
|
JavaFileObject parent =
|
|
JavaFileObjects.forSourceLines(
|
|
"Parent",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@Component(modules = ParentModule.class)",
|
|
"interface Parent {",
|
|
" Child child();",
|
|
"}");
|
|
JavaFileObject child =
|
|
JavaFileObjects.forSourceLines(
|
|
"Child",
|
|
"import dagger.Subcomponent;",
|
|
"",
|
|
"@Subcomponent(modules = ChildModule.class)",
|
|
"interface Child {",
|
|
" Grandchild grandchild();",
|
|
"}");
|
|
JavaFileObject grandchild =
|
|
JavaFileObjects.forSourceLines(
|
|
"Grandchild",
|
|
"import dagger.Subcomponent;",
|
|
"",
|
|
"@Subcomponent",
|
|
"interface Grandchild {",
|
|
" Object object();",
|
|
"}");
|
|
|
|
JavaFileObject parentModule =
|
|
JavaFileObjects.forSourceLines(
|
|
"ParentModule",
|
|
"import dagger.Module;",
|
|
"import dagger.Provides;",
|
|
"import dagger.multibindings.IntoSet;",
|
|
"import java.util.Set;",
|
|
"",
|
|
"@Module",
|
|
"class ParentModule {",
|
|
" @Provides static Object dependsOnSet(Set<String> strings) {",
|
|
" return \"needs strings: \" + strings;",
|
|
" }",
|
|
"",
|
|
" @Provides @IntoSet static String contributesToSet() {",
|
|
" return \"parent string\";",
|
|
" }",
|
|
"",
|
|
" @Provides int missingDependency(double dub) {",
|
|
" return 4;",
|
|
" }",
|
|
"}");
|
|
JavaFileObject childModule =
|
|
JavaFileObjects.forSourceLines(
|
|
"ChildModule",
|
|
"import dagger.Module;",
|
|
"import dagger.Provides;",
|
|
"import dagger.multibindings.IntoSet;",
|
|
"",
|
|
"@Module",
|
|
"class ChildModule {",
|
|
" @Provides @IntoSet static String contributesToSet(int i) {",
|
|
" return \"\" + i;",
|
|
" }",
|
|
"}");
|
|
Compilation compilation =
|
|
daggerCompiler().compile(parent, parentModule, child, childModule, grandchild);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation).hadErrorCount(1);
|
|
assertThat(compilation)
|
|
.hadErrorContainingMatch(
|
|
"(?s)\\QDouble cannot be provided\\E.*"
|
|
+ "\\QGrandchild.object() [Parent → Child → Grandchild]\\E$")
|
|
.inFile(parent)
|
|
.onLineContaining("interface Parent");
|
|
}
|
|
|
|
@Test
|
|
public void manyDependencies() {
|
|
JavaFileObject component =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.TestComponent",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@Component(modules = TestModule.class)",
|
|
"interface TestComponent {",
|
|
" Object object();",
|
|
" String string();",
|
|
"}");
|
|
JavaFileObject module =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.TestModule",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Binds;",
|
|
"import dagger.Module;",
|
|
"import dagger.Provides;",
|
|
"",
|
|
"@Module",
|
|
"abstract class TestModule {",
|
|
" @Binds abstract Object object(NotBound notBound);",
|
|
"",
|
|
" @Provides static String string(NotBound notBound, Object object) {",
|
|
" return notBound.toString();",
|
|
" }",
|
|
"}");
|
|
JavaFileObject notBound =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.NotBound", //
|
|
"package test;",
|
|
"",
|
|
"interface NotBound {}");
|
|
Compilation compilation = daggerCompiler().compile(component, module, notBound);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation).hadErrorCount(1);
|
|
assertThat(compilation)
|
|
.hadErrorContaining(
|
|
message(
|
|
"\033[1;31m[Dagger/MissingBinding]\033[0m "
|
|
+ "NotBound cannot be provided without an @Provides-annotated method.",
|
|
" NotBound is injected at",
|
|
" TestModule.object(notBound)",
|
|
" Object is requested at",
|
|
" TestComponent.object()",
|
|
"It is also requested at:",
|
|
" TestModule.string(notBound, …)",
|
|
"The following other entry points also depend on it:",
|
|
" TestComponent.string()"))
|
|
.inFile(component)
|
|
.onLineContaining("interface TestComponent");
|
|
assertThat(compilation).hadErrorCount(1);
|
|
}
|
|
|
|
@Test
|
|
public void tooManyRequests() {
|
|
JavaFileObject foo =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.Foo",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Inject;",
|
|
"",
|
|
"final class Foo {",
|
|
" @Inject Foo(",
|
|
" String one,",
|
|
" String two,",
|
|
" String three,",
|
|
" String four,",
|
|
" String five,",
|
|
" String six,",
|
|
" String seven,",
|
|
" String eight,",
|
|
" String nine,",
|
|
" String ten,",
|
|
" String eleven,",
|
|
" String twelve,",
|
|
" String thirteen) {",
|
|
" }",
|
|
"}");
|
|
JavaFileObject component =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.TestComponent",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@Component",
|
|
"interface TestComponent {",
|
|
" String string();",
|
|
" Foo foo();",
|
|
"}");
|
|
|
|
Compilation compilation = daggerCompiler().compile(foo, component);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation).hadErrorCount(1);
|
|
assertThat(compilation)
|
|
.hadErrorContaining(
|
|
message(
|
|
"\033[1;31m[Dagger/MissingBinding]\033[0m String cannot be provided without an "
|
|
+ "@Inject constructor or an @Provides-annotated method.",
|
|
" String is requested at",
|
|
" TestComponent.string()",
|
|
"It is also requested at:",
|
|
" Foo(one, …)",
|
|
" Foo(…, two, …)",
|
|
" Foo(…, three, …)",
|
|
" Foo(…, four, …)",
|
|
" Foo(…, five, …)",
|
|
" Foo(…, six, …)",
|
|
" Foo(…, seven, …)",
|
|
" Foo(…, eight, …)",
|
|
" Foo(…, nine, …)",
|
|
" Foo(…, ten, …)",
|
|
" and 3 others"))
|
|
.inFile(component)
|
|
.onLineContaining("interface TestComponent");
|
|
}
|
|
|
|
@Test
|
|
public void tooManyEntryPoints() {
|
|
JavaFileObject component =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.TestComponent",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@Component",
|
|
"interface TestComponent {",
|
|
" String string1();",
|
|
" String string2();",
|
|
" String string3();",
|
|
" String string4();",
|
|
" String string5();",
|
|
" String string6();",
|
|
" String string7();",
|
|
" String string8();",
|
|
" String string9();",
|
|
" String string10();",
|
|
" String string11();",
|
|
" String string12();",
|
|
"}");
|
|
|
|
Compilation compilation = daggerCompiler().compile(component);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation).hadErrorCount(1);
|
|
assertThat(compilation)
|
|
.hadErrorContaining(
|
|
message(
|
|
"\033[1;31m[Dagger/MissingBinding]\033[0m String cannot be provided without an "
|
|
+ "@Inject constructor or an @Provides-annotated method.",
|
|
" String is requested at",
|
|
" TestComponent.string1()",
|
|
"The following other entry points also depend on it:",
|
|
" TestComponent.string2()",
|
|
" TestComponent.string3()",
|
|
" TestComponent.string4()",
|
|
" TestComponent.string5()",
|
|
" TestComponent.string6()",
|
|
" TestComponent.string7()",
|
|
" TestComponent.string8()",
|
|
" TestComponent.string9()",
|
|
" TestComponent.string10()",
|
|
" TestComponent.string11()",
|
|
" and 1 other"))
|
|
.inFile(component)
|
|
.onLineContaining("interface TestComponent");
|
|
}
|
|
|
|
@Test
|
|
public void missingBindingInAllComponentsAndEntryPoints() {
|
|
JavaFileObject parent =
|
|
JavaFileObjects.forSourceLines(
|
|
"Parent",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@Component",
|
|
"interface Parent {",
|
|
" Foo foo();",
|
|
" Bar bar();",
|
|
" Child child();",
|
|
"}");
|
|
JavaFileObject child =
|
|
JavaFileObjects.forSourceLines(
|
|
"Child",
|
|
"import dagger.Subcomponent;",
|
|
"",
|
|
"@Subcomponent",
|
|
"interface Child {",
|
|
" Foo foo();",
|
|
" Baz baz();",
|
|
"}");
|
|
JavaFileObject foo =
|
|
JavaFileObjects.forSourceLines(
|
|
"Foo",
|
|
"import javax.inject.Inject;",
|
|
"",
|
|
"class Foo {",
|
|
" @Inject Foo(Bar bar) {}",
|
|
"}");
|
|
JavaFileObject bar =
|
|
JavaFileObjects.forSourceLines(
|
|
"Bar",
|
|
"import javax.inject.Inject;",
|
|
"",
|
|
"class Bar {",
|
|
" @Inject Bar(Baz baz) {}",
|
|
"}");
|
|
JavaFileObject baz = JavaFileObjects.forSourceLines("Baz", "class Baz {}");
|
|
|
|
Compilation compilation = daggerCompiler().compile(parent, child, foo, bar, baz);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation).hadErrorCount(1);
|
|
assertThat(compilation)
|
|
.hadErrorContaining(
|
|
message(
|
|
"\033[1;31m[Dagger/MissingBinding]\033[0m Baz cannot be provided without an "
|
|
+ "@Inject constructor or an @Provides-annotated method.",
|
|
" Baz is injected at",
|
|
" Bar(baz)",
|
|
" Bar is requested at",
|
|
" Parent.bar()",
|
|
"The following other entry points also depend on it:",
|
|
" Parent.foo()",
|
|
" Child.foo() [Parent → Child]",
|
|
" Child.baz() [Parent → Child]"))
|
|
.inFile(parent)
|
|
.onLineContaining("interface Parent");
|
|
}
|
|
|
|
// Regression test for b/147423208 where if the same subcomponent was used
|
|
// in two different parts of the hierarchy and only one side had a missing binding
|
|
// incorrect caching during binding graph conversion might cause validation to pass
|
|
// incorrectly.
|
|
@Test
|
|
public void sameSubcomponentUsedInDifferentHierarchies() {
|
|
JavaFileObject parent = JavaFileObjects.forSourceLines("test.Parent",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@Component",
|
|
"interface Parent {",
|
|
" Child1 getChild1();",
|
|
" Child2 getChild2();",
|
|
"}");
|
|
JavaFileObject child1 = JavaFileObjects.forSourceLines("test.Child1",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Subcomponent;",
|
|
"",
|
|
"@Subcomponent(modules = LongModule.class)",
|
|
"interface Child1 {",
|
|
" RepeatedSub getSub();",
|
|
"}");
|
|
JavaFileObject child2 = JavaFileObjects.forSourceLines("test.Child2",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Subcomponent;",
|
|
"",
|
|
"@Subcomponent",
|
|
"interface Child2 {",
|
|
" RepeatedSub getSub();",
|
|
"}");
|
|
JavaFileObject repeatedSub = JavaFileObjects.forSourceLines("test.RepeatedSub",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Subcomponent;",
|
|
"",
|
|
"@Subcomponent",
|
|
"interface RepeatedSub {",
|
|
" Foo getFoo();",
|
|
"}");
|
|
JavaFileObject injectable = JavaFileObjects.forSourceLines("test.Foo",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Inject;",
|
|
"",
|
|
"class Foo {",
|
|
" @Inject Foo(Long value) {}",
|
|
"}");
|
|
JavaFileObject module = JavaFileObjects.forSourceLines("test.LongModule",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Module;",
|
|
"import dagger.Provides;",
|
|
"",
|
|
"@Module",
|
|
"interface LongModule {",
|
|
" @Provides static Long provideLong() {",
|
|
" return 0L;",
|
|
" }",
|
|
"}");
|
|
Compilation compilation = daggerCompiler().compile(
|
|
parent, child1, child2, repeatedSub, injectable, module);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation).hadErrorCount(1);
|
|
assertThat(compilation)
|
|
.hadErrorContaining("Long cannot be provided without an @Inject constructor")
|
|
.inFile(parent)
|
|
.onLineContaining("interface Parent");
|
|
}
|
|
|
|
}
|