900 lines
30 KiB
Java
900 lines
30 KiB
Java
/*
|
|
* 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.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 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 ScopingValidationTest {
|
|
@Test
|
|
public void componentWithoutScopeIncludesScopedBindings_Fail() {
|
|
JavaFileObject componentFile =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.MyComponent",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"import javax.inject.Singleton;",
|
|
"",
|
|
"@Component(modules = ScopedModule.class)",
|
|
"interface MyComponent {",
|
|
" ScopedType string();",
|
|
"}");
|
|
JavaFileObject typeFile =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ScopedType",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Inject;",
|
|
"import javax.inject.Singleton;",
|
|
"",
|
|
"@Singleton",
|
|
"class ScopedType {",
|
|
" @Inject ScopedType(String s, long l, float f) {}",
|
|
"}");
|
|
JavaFileObject moduleFile =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ScopedModule",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Module;",
|
|
"import dagger.Provides;",
|
|
"import javax.inject.Singleton;",
|
|
"",
|
|
"@Module",
|
|
"class ScopedModule {",
|
|
" @Provides @Singleton String string() { return \"a string\"; }",
|
|
" @Provides long integer() { return 0L; }",
|
|
" @Provides float floatingPoint() { return 0.0f; }",
|
|
"}");
|
|
|
|
Compilation compilation = daggerCompiler().compile(componentFile, typeFile, moduleFile);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation)
|
|
.hadErrorContaining(
|
|
message(
|
|
"MyComponent (unscoped) may not reference scoped bindings:",
|
|
" @Singleton class ScopedType",
|
|
" @Provides @Singleton String ScopedModule.string()"));
|
|
}
|
|
|
|
@Test // b/79859714
|
|
public void bindsWithChildScope_inParentModule_notAllowed() {
|
|
JavaFileObject childScope =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ChildScope",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Scope;",
|
|
"",
|
|
"@Scope",
|
|
"@interface ChildScope {}");
|
|
|
|
JavaFileObject foo =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.Foo",
|
|
"package test;",
|
|
"", //
|
|
"interface Foo {}");
|
|
|
|
JavaFileObject fooImpl =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ChildModule",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Inject;",
|
|
"",
|
|
"class FooImpl implements Foo {",
|
|
" @Inject FooImpl() {}",
|
|
"}");
|
|
|
|
JavaFileObject parentModule =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ParentModule",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Binds;",
|
|
"import dagger.Module;",
|
|
"",
|
|
"@Module",
|
|
"interface ParentModule {",
|
|
" @Binds @ChildScope Foo bind(FooImpl fooImpl);",
|
|
"}");
|
|
|
|
JavaFileObject parent =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ParentComponent",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"import javax.inject.Singleton;",
|
|
"",
|
|
"@Singleton",
|
|
"@Component(modules = ParentModule.class)",
|
|
"interface Parent {",
|
|
" Child child();",
|
|
"}");
|
|
|
|
JavaFileObject child =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.Child",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Subcomponent;",
|
|
"",
|
|
"@ChildScope",
|
|
"@Subcomponent",
|
|
"interface Child {",
|
|
" Foo foo();",
|
|
"}");
|
|
|
|
Compilation compilation =
|
|
daggerCompiler().compile(childScope, foo, fooImpl, parentModule, parent, child);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation)
|
|
.hadErrorContaining(
|
|
message(
|
|
"Parent scoped with @Singleton may not reference bindings with different "
|
|
+ "scopes:",
|
|
" @Binds @ChildScope Foo ParentModule.bind(FooImpl)"));
|
|
}
|
|
|
|
@Test
|
|
public void componentWithScopeIncludesIncompatiblyScopedBindings_Fail() {
|
|
JavaFileObject componentFile =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.MyComponent",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"import javax.inject.Singleton;",
|
|
"",
|
|
"@Singleton",
|
|
"@Component(modules = ScopedModule.class)",
|
|
"interface MyComponent {",
|
|
" ScopedType string();",
|
|
"}");
|
|
JavaFileObject scopeFile =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.PerTest",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Scope;",
|
|
"",
|
|
"@Scope",
|
|
"@interface PerTest {}");
|
|
JavaFileObject scopeWithAttribute =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.Per",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Scope;",
|
|
"",
|
|
"@Scope",
|
|
"@interface Per {",
|
|
" Class<?> value();",
|
|
"}");
|
|
JavaFileObject typeFile =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ScopedType",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Inject;",
|
|
"",
|
|
"@PerTest", // incompatible scope
|
|
"class ScopedType {",
|
|
" @Inject ScopedType(String s, long l, float f, boolean b) {}",
|
|
"}");
|
|
JavaFileObject moduleFile =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ScopedModule",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Module;",
|
|
"import dagger.Provides;",
|
|
"import javax.inject.Singleton;",
|
|
"",
|
|
"@Module",
|
|
"class ScopedModule {",
|
|
" @Provides @PerTest String string() { return \"a string\"; }", // incompatible scope
|
|
" @Provides long integer() { return 0L; }", // unscoped - valid
|
|
" @Provides @Singleton float floatingPoint() { return 0.0f; }", // same scope - valid
|
|
" @Provides @Per(MyComponent.class) boolean bool() { return false; }", // incompatible
|
|
"}");
|
|
|
|
Compilation compilation =
|
|
daggerCompiler()
|
|
.compile(componentFile, scopeFile, scopeWithAttribute, typeFile, moduleFile);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation)
|
|
.hadErrorContaining(
|
|
message(
|
|
"MyComponent scoped with @Singleton "
|
|
+ "may not reference bindings with different scopes:",
|
|
" @PerTest class ScopedType",
|
|
" @Provides @PerTest String ScopedModule.string()",
|
|
" @Provides @Per(MyComponent.class) boolean "
|
|
+ "ScopedModule.bool()"))
|
|
.inFile(componentFile)
|
|
.onLineContaining("interface MyComponent");
|
|
|
|
compilation =
|
|
compilerWithOptions("-Adagger.fullBindingGraphValidation=ERROR")
|
|
.compile(componentFile, scopeFile, scopeWithAttribute, typeFile, moduleFile);
|
|
// The @Inject binding for ScopedType should not appear here, but the @Singleton binding should.
|
|
assertThat(compilation)
|
|
.hadErrorContaining(
|
|
message(
|
|
"ScopedModule contains bindings with different scopes:",
|
|
" @Provides @PerTest String ScopedModule.string()",
|
|
" @Provides @Singleton float ScopedModule.floatingPoint()",
|
|
" @Provides @Per(MyComponent.class) boolean "
|
|
+ "ScopedModule.bool()"))
|
|
.inFile(moduleFile)
|
|
.onLineContaining("class ScopedModule");
|
|
}
|
|
|
|
@Test
|
|
public void fullBindingGraphValidationDoesNotReportForOneScope() {
|
|
Compilation compilation =
|
|
compilerWithOptions(
|
|
"-Adagger.fullBindingGraphValidation=ERROR",
|
|
"-Adagger.moduleHasDifferentScopesValidation=ERROR")
|
|
.compile(
|
|
JavaFileObjects.forSourceLines(
|
|
"test.TestModule",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Module;",
|
|
"import dagger.Provides;",
|
|
"import javax.inject.Singleton;",
|
|
"",
|
|
"@Module",
|
|
"interface TestModule {",
|
|
" @Provides @Singleton static Object object() { return \"object\"; }",
|
|
" @Provides @Singleton static String string() { return \"string\"; }",
|
|
" @Provides static int integer() { return 4; }",
|
|
"}"));
|
|
assertThat(compilation).succeededWithoutWarnings();
|
|
}
|
|
|
|
@Test
|
|
public void fullBindingGraphValidationDoesNotReportInjectBindings() {
|
|
Compilation compilation =
|
|
compilerWithOptions(
|
|
"-Adagger.fullBindingGraphValidation=ERROR",
|
|
"-Adagger.moduleHasDifferentScopesValidation=ERROR")
|
|
.compile(
|
|
JavaFileObjects.forSourceLines(
|
|
"test.UsedInRootRedScoped",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Inject;",
|
|
"",
|
|
"@RedScope",
|
|
"final class UsedInRootRedScoped {",
|
|
" @Inject UsedInRootRedScoped() {}",
|
|
"}"),
|
|
JavaFileObjects.forSourceLines(
|
|
"test.UsedInRootBlueScoped",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Inject;",
|
|
"",
|
|
"@BlueScope",
|
|
"final class UsedInRootBlueScoped {",
|
|
" @Inject UsedInRootBlueScoped() {}",
|
|
"}"),
|
|
JavaFileObjects.forSourceLines(
|
|
"test.RedScope",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Scope;",
|
|
"",
|
|
"@Scope",
|
|
"@interface RedScope {}"),
|
|
JavaFileObjects.forSourceLines(
|
|
"test.BlueScope",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Scope;",
|
|
"",
|
|
"@Scope",
|
|
"@interface BlueScope {}"),
|
|
JavaFileObjects.forSourceLines(
|
|
"test.TestModule",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Module;",
|
|
"import dagger.Provides;",
|
|
"import javax.inject.Singleton;",
|
|
"",
|
|
"@Module(subcomponents = Child.class)",
|
|
"interface TestModule {",
|
|
" @Provides @Singleton",
|
|
" static Object object(",
|
|
" UsedInRootRedScoped usedInRootRedScoped,",
|
|
" UsedInRootBlueScoped usedInRootBlueScoped) {",
|
|
" return \"object\";",
|
|
" }",
|
|
"}"),
|
|
JavaFileObjects.forSourceLines(
|
|
"test.Child",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Subcomponent;",
|
|
"",
|
|
"@Subcomponent",
|
|
"interface Child {",
|
|
" UsedInChildRedScoped usedInChildRedScoped();",
|
|
" UsedInChildBlueScoped usedInChildBlueScoped();",
|
|
"",
|
|
" @Subcomponent.Builder",
|
|
" interface Builder {",
|
|
" Child child();",
|
|
" }",
|
|
"}"),
|
|
JavaFileObjects.forSourceLines(
|
|
"test.UsedInChildRedScoped",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Inject;",
|
|
"",
|
|
"@RedScope",
|
|
"final class UsedInChildRedScoped {",
|
|
" @Inject UsedInChildRedScoped() {}",
|
|
"}"),
|
|
JavaFileObjects.forSourceLines(
|
|
"test.UsedInChildBlueScoped",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Inject;",
|
|
"",
|
|
"@BlueScope",
|
|
"final class UsedInChildBlueScoped {",
|
|
" @Inject UsedInChildBlueScoped() {}",
|
|
"}"));
|
|
assertThat(compilation).succeededWithoutWarnings();
|
|
}
|
|
|
|
@Test
|
|
public void componentWithScopeCanDependOnMultipleScopedComponents() {
|
|
// If a scoped component will have dependencies, they can include multiple scoped component
|
|
JavaFileObject type =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.SimpleType",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Inject;",
|
|
"",
|
|
"class SimpleType {",
|
|
" @Inject SimpleType() {}",
|
|
" static class A { @Inject A() {} }",
|
|
" static class B { @Inject B() {} }",
|
|
"}");
|
|
JavaFileObject simpleScope =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.SimpleScope",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Scope;",
|
|
"",
|
|
"@Scope @interface SimpleScope {}");
|
|
JavaFileObject singletonScopedA =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.SingletonComponentA",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"import javax.inject.Singleton;",
|
|
"",
|
|
"@Singleton",
|
|
"@Component",
|
|
"interface SingletonComponentA {",
|
|
" SimpleType.A type();",
|
|
"}");
|
|
JavaFileObject singletonScopedB =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.SingletonComponentB",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"import javax.inject.Singleton;",
|
|
"",
|
|
"@Singleton",
|
|
"@Component",
|
|
"interface SingletonComponentB {",
|
|
" SimpleType.B type();",
|
|
"}");
|
|
JavaFileObject scopeless =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ScopelessComponent",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@Component",
|
|
"interface ScopelessComponent {",
|
|
" SimpleType type();",
|
|
"}");
|
|
JavaFileObject simpleScoped =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.SimpleScopedComponent",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@SimpleScope",
|
|
"@Component(dependencies = {SingletonComponentA.class, SingletonComponentB.class})",
|
|
"interface SimpleScopedComponent {",
|
|
" SimpleType.A type();",
|
|
"}");
|
|
|
|
Compilation compilation =
|
|
daggerCompiler()
|
|
.compile(
|
|
type, simpleScope, simpleScoped, singletonScopedA, singletonScopedB, scopeless);
|
|
assertThat(compilation).succeededWithoutWarnings();
|
|
}
|
|
|
|
|
|
|
|
// Tests the following component hierarchy:
|
|
//
|
|
// @ScopeA
|
|
// ComponentA
|
|
// [SimpleType getSimpleType()]
|
|
// / \
|
|
// / \
|
|
// @ScopeB @ScopeB
|
|
// ComponentB1 ComponentB2
|
|
// \ [SimpleType getSimpleType()]
|
|
// \ /
|
|
// \ /
|
|
// @ScopeC
|
|
// ComponentC
|
|
// [SimpleType getSimpleType()]
|
|
@Test
|
|
public void componentWithScopeCanDependOnMultipleScopedComponentsEvenDoingADiamond() {
|
|
JavaFileObject type =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.SimpleType",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Inject;",
|
|
"",
|
|
"class SimpleType {",
|
|
" @Inject SimpleType() {}",
|
|
"}");
|
|
JavaFileObject simpleScope =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.SimpleScope",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Scope;",
|
|
"",
|
|
"@Scope @interface SimpleScope {}");
|
|
JavaFileObject scopeA =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ScopeA",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Scope;",
|
|
"",
|
|
"@Scope @interface ScopeA {}");
|
|
JavaFileObject scopeB =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ScopeB",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Scope;",
|
|
"",
|
|
"@Scope @interface ScopeB {}");
|
|
JavaFileObject componentA =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ComponentA",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@ScopeA",
|
|
"@Component",
|
|
"interface ComponentA {",
|
|
" SimpleType type();",
|
|
"}");
|
|
JavaFileObject componentB1 =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ComponentB1",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@ScopeB",
|
|
"@Component(dependencies = ComponentA.class)",
|
|
"interface ComponentB1 {",
|
|
" SimpleType type();",
|
|
"}");
|
|
JavaFileObject componentB2 =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ComponentB2",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@ScopeB",
|
|
"@Component(dependencies = ComponentA.class)",
|
|
"interface ComponentB2 {",
|
|
"}");
|
|
JavaFileObject componentC =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ComponentC",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@SimpleScope",
|
|
"@Component(dependencies = {ComponentB1.class, ComponentB2.class})",
|
|
"interface ComponentC {",
|
|
" SimpleType type();",
|
|
"}");
|
|
|
|
Compilation compilation =
|
|
daggerCompiler()
|
|
.compile(
|
|
type, simpleScope, scopeA, scopeB,
|
|
componentA, componentB1, componentB2, componentC);
|
|
assertThat(compilation).succeededWithoutWarnings();
|
|
}
|
|
|
|
@Test
|
|
public void componentWithoutScopeCannotDependOnScopedComponent() {
|
|
JavaFileObject type =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.SimpleType",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Inject;",
|
|
"",
|
|
"class SimpleType {",
|
|
" @Inject SimpleType() {}",
|
|
"}");
|
|
JavaFileObject scopedComponent =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ScopedComponent",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"import javax.inject.Singleton;",
|
|
"",
|
|
"@Singleton",
|
|
"@Component",
|
|
"interface ScopedComponent {",
|
|
" SimpleType type();",
|
|
"}");
|
|
JavaFileObject unscopedComponent =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.UnscopedComponent",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"import javax.inject.Singleton;",
|
|
"",
|
|
"@Component(dependencies = ScopedComponent.class)",
|
|
"interface UnscopedComponent {",
|
|
" SimpleType type();",
|
|
"}");
|
|
|
|
Compilation compilation = daggerCompiler().compile(type, scopedComponent, unscopedComponent);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation)
|
|
.hadErrorContaining(
|
|
message(
|
|
"test.UnscopedComponent (unscoped) cannot depend on scoped components:",
|
|
" @Singleton test.ScopedComponent"));
|
|
}
|
|
|
|
@Test
|
|
public void componentWithSingletonScopeMayNotDependOnOtherScope() {
|
|
// Singleton must be the widest lifetime of present scopes.
|
|
JavaFileObject type =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.SimpleType",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Inject;",
|
|
"",
|
|
"class SimpleType {",
|
|
" @Inject SimpleType() {}",
|
|
"}");
|
|
JavaFileObject simpleScope =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.SimpleScope",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Scope;",
|
|
"",
|
|
"@Scope @interface SimpleScope {}");
|
|
JavaFileObject simpleScoped =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.SimpleScopedComponent",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@SimpleScope",
|
|
"@Component",
|
|
"interface SimpleScopedComponent {",
|
|
" SimpleType type();",
|
|
"}");
|
|
JavaFileObject singletonScoped =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.SingletonComponent",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"import javax.inject.Singleton;",
|
|
"",
|
|
"@Singleton",
|
|
"@Component(dependencies = SimpleScopedComponent.class)",
|
|
"interface SingletonComponent {",
|
|
" SimpleType type();",
|
|
"}");
|
|
|
|
Compilation compilation =
|
|
daggerCompiler().compile(type, simpleScope, simpleScoped, singletonScoped);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation)
|
|
.hadErrorContaining(
|
|
message(
|
|
"This @Singleton component cannot depend on scoped components:",
|
|
" @test.SimpleScope test.SimpleScopedComponent"));
|
|
}
|
|
|
|
@Test
|
|
public void componentScopeWithMultipleScopedDependenciesMustNotCycle() {
|
|
JavaFileObject type =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.SimpleType",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Inject;",
|
|
"",
|
|
"class SimpleType {",
|
|
" @Inject SimpleType() {}",
|
|
"}");
|
|
JavaFileObject scopeA =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ScopeA",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Scope;",
|
|
"",
|
|
"@Scope @interface ScopeA {}");
|
|
JavaFileObject scopeB =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ScopeB",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Scope;",
|
|
"",
|
|
"@Scope @interface ScopeB {}");
|
|
JavaFileObject longLifetime =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ComponentLong",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@ScopeA",
|
|
"@Component",
|
|
"interface ComponentLong {",
|
|
" SimpleType type();",
|
|
"}");
|
|
JavaFileObject mediumLifetime1 =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ComponentMedium1",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@ScopeB",
|
|
"@Component(dependencies = ComponentLong.class)",
|
|
"interface ComponentMedium1 {",
|
|
" SimpleType type();",
|
|
"}");
|
|
JavaFileObject mediumLifetime2 =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ComponentMedium2",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@ScopeB",
|
|
"@Component",
|
|
"interface ComponentMedium2 {",
|
|
"}");
|
|
JavaFileObject shortLifetime =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ComponentShort",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@ScopeA",
|
|
"@Component(dependencies = {ComponentMedium1.class, ComponentMedium2.class})",
|
|
"interface ComponentShort {",
|
|
" SimpleType type();",
|
|
"}");
|
|
|
|
Compilation compilation =
|
|
daggerCompiler()
|
|
.compile(
|
|
type, scopeA, scopeB,
|
|
longLifetime, mediumLifetime1, mediumLifetime2, shortLifetime);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation)
|
|
.hadErrorContaining(
|
|
message(
|
|
"test.ComponentShort depends on scoped components in a non-hierarchical scope "
|
|
+ "ordering:",
|
|
" @test.ScopeA test.ComponentLong",
|
|
" @test.ScopeB test.ComponentMedium1",
|
|
" @test.ScopeA test.ComponentShort"));
|
|
}
|
|
|
|
@Test
|
|
public void componentScopeAncestryMustNotCycle() {
|
|
// The dependency relationship of components is necessarily from shorter lifetimes to
|
|
// longer lifetimes. The scoping annotations must reflect this, and so one cannot declare
|
|
// scopes on components such that they cycle.
|
|
JavaFileObject type =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.SimpleType",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Inject;",
|
|
"",
|
|
"class SimpleType {",
|
|
" @Inject SimpleType() {}",
|
|
"}");
|
|
JavaFileObject scopeA =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ScopeA",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Scope;",
|
|
"",
|
|
"@Scope @interface ScopeA {}");
|
|
JavaFileObject scopeB =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ScopeB",
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Scope;",
|
|
"",
|
|
"@Scope @interface ScopeB {}");
|
|
JavaFileObject longLifetime =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ComponentLong",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@ScopeA",
|
|
"@Component",
|
|
"interface ComponentLong {",
|
|
" SimpleType type();",
|
|
"}");
|
|
JavaFileObject mediumLifetime =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ComponentMedium",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@ScopeB",
|
|
"@Component(dependencies = ComponentLong.class)",
|
|
"interface ComponentMedium {",
|
|
" SimpleType type();",
|
|
"}");
|
|
JavaFileObject shortLifetime =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.ComponentShort",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"",
|
|
"@ScopeA",
|
|
"@Component(dependencies = ComponentMedium.class)",
|
|
"interface ComponentShort {",
|
|
" SimpleType type();",
|
|
"}");
|
|
|
|
Compilation compilation =
|
|
daggerCompiler().compile(type, scopeA, scopeB, longLifetime, mediumLifetime, shortLifetime);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation)
|
|
.hadErrorContaining(
|
|
message(
|
|
"test.ComponentShort depends on scoped components in a non-hierarchical scope "
|
|
+ "ordering:",
|
|
" @test.ScopeA test.ComponentLong",
|
|
" @test.ScopeB test.ComponentMedium",
|
|
" @test.ScopeA test.ComponentShort"));
|
|
|
|
|
|
// Test that compilation succeeds when transitive validation is disabled because the scope cycle
|
|
// cannot be detected.
|
|
compilation =
|
|
compilerWithOptions("-Adagger.validateTransitiveComponentDependencies=DISABLED")
|
|
.compile(type, scopeA, scopeB, longLifetime, mediumLifetime, shortLifetime);
|
|
assertThat(compilation).succeeded();
|
|
}
|
|
|
|
@Test
|
|
public void reusableNotAllowedOnComponent() {
|
|
JavaFileObject someComponent =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.SomeComponent",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"import dagger.Reusable;",
|
|
"",
|
|
"@Reusable",
|
|
"@Component",
|
|
"interface SomeComponent {}");
|
|
Compilation compilation = daggerCompiler().compile(someComponent);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation)
|
|
.hadErrorContaining("@Reusable cannot be applied to components or subcomponents")
|
|
.inFile(someComponent)
|
|
.onLine(6);
|
|
}
|
|
|
|
@Test
|
|
public void reusableNotAllowedOnSubcomponent() {
|
|
JavaFileObject someSubcomponent =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.SomeComponent",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Reusable;",
|
|
"import dagger.Subcomponent;",
|
|
"",
|
|
"@Reusable",
|
|
"@Subcomponent",
|
|
"interface SomeSubcomponent {}");
|
|
Compilation compilation = daggerCompiler().compile(someSubcomponent);
|
|
assertThat(compilation).failed();
|
|
assertThat(compilation)
|
|
.hadErrorContaining("@Reusable cannot be applied to components or subcomponents")
|
|
.inFile(someSubcomponent)
|
|
.onLine(6);
|
|
}
|
|
}
|