1*f585d8a3SJacky Wang /* 2*f585d8a3SJacky Wang * Copyright (C) 2018 The Dagger Authors. 3*f585d8a3SJacky Wang * 4*f585d8a3SJacky Wang * Licensed under the Apache License, Version 2.0 (the "License"); 5*f585d8a3SJacky Wang * you may not use this file except in compliance with the License. 6*f585d8a3SJacky Wang * You may obtain a copy of the License at 7*f585d8a3SJacky Wang * 8*f585d8a3SJacky Wang * http://www.apache.org/licenses/LICENSE-2.0 9*f585d8a3SJacky Wang * 10*f585d8a3SJacky Wang * Unless required by applicable law or agreed to in writing, software 11*f585d8a3SJacky Wang * distributed under the License is distributed on an "AS IS" BASIS, 12*f585d8a3SJacky Wang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*f585d8a3SJacky Wang * See the License for the specific language governing permissions and 14*f585d8a3SJacky Wang * limitations under the License. 15*f585d8a3SJacky Wang */ 16*f585d8a3SJacky Wang 17*f585d8a3SJacky Wang package dagger.internal.codegen; 18*f585d8a3SJacky Wang 19*f585d8a3SJacky Wang import static com.google.testing.compile.CompilationSubject.assertThat; 20*f585d8a3SJacky Wang import static dagger.internal.codegen.Compilers.compilerWithOptions; 21*f585d8a3SJacky Wang import static dagger.internal.codegen.TestUtils.endsWithMessage; 22*f585d8a3SJacky Wang 23*f585d8a3SJacky Wang import androidx.room.compiler.processing.util.Source; 24*f585d8a3SJacky Wang import com.google.common.collect.ImmutableList; 25*f585d8a3SJacky Wang import com.google.testing.compile.Compilation; 26*f585d8a3SJacky Wang import dagger.testing.compile.CompilerTests; 27*f585d8a3SJacky Wang import java.util.regex.Pattern; 28*f585d8a3SJacky Wang import org.junit.Test; 29*f585d8a3SJacky Wang import org.junit.runner.RunWith; 30*f585d8a3SJacky Wang import org.junit.runners.Parameterized; 31*f585d8a3SJacky Wang import org.junit.runners.Parameterized.Parameters; 32*f585d8a3SJacky Wang 33*f585d8a3SJacky Wang @RunWith(Parameterized.class) 34*f585d8a3SJacky Wang public class DependencyCycleValidationTest { 35*f585d8a3SJacky Wang @Parameters(name = "{0}") parameters()36*f585d8a3SJacky Wang public static ImmutableList<Object[]> parameters() { 37*f585d8a3SJacky Wang return CompilerMode.TEST_PARAMETERS; 38*f585d8a3SJacky Wang } 39*f585d8a3SJacky Wang 40*f585d8a3SJacky Wang private final CompilerMode compilerMode; 41*f585d8a3SJacky Wang DependencyCycleValidationTest(CompilerMode compilerMode)42*f585d8a3SJacky Wang public DependencyCycleValidationTest(CompilerMode compilerMode) { 43*f585d8a3SJacky Wang this.compilerMode = compilerMode; 44*f585d8a3SJacky Wang } 45*f585d8a3SJacky Wang 46*f585d8a3SJacky Wang private static final Source SIMPLE_CYCLIC_DEPENDENCY = 47*f585d8a3SJacky Wang CompilerTests.javaSource( 48*f585d8a3SJacky Wang "test.Outer", 49*f585d8a3SJacky Wang "package test;", 50*f585d8a3SJacky Wang "", 51*f585d8a3SJacky Wang "import dagger.Binds;", 52*f585d8a3SJacky Wang "import dagger.Component;", 53*f585d8a3SJacky Wang "import dagger.Module;", 54*f585d8a3SJacky Wang "import dagger.Provides;", 55*f585d8a3SJacky Wang "import javax.inject.Inject;", 56*f585d8a3SJacky Wang "", 57*f585d8a3SJacky Wang "final class Outer {", 58*f585d8a3SJacky Wang " static class A {", 59*f585d8a3SJacky Wang " @Inject A(C cParam) {}", 60*f585d8a3SJacky Wang " }", 61*f585d8a3SJacky Wang "", 62*f585d8a3SJacky Wang " static class B {", 63*f585d8a3SJacky Wang " @Inject B(A aParam) {}", 64*f585d8a3SJacky Wang " }", 65*f585d8a3SJacky Wang "", 66*f585d8a3SJacky Wang " static class C {", 67*f585d8a3SJacky Wang " @Inject C(B bParam) {}", 68*f585d8a3SJacky Wang " }", 69*f585d8a3SJacky Wang "", 70*f585d8a3SJacky Wang " @Module", 71*f585d8a3SJacky Wang " interface MModule {", 72*f585d8a3SJacky Wang " @Binds Object object(C c);", 73*f585d8a3SJacky Wang " }", 74*f585d8a3SJacky Wang "", 75*f585d8a3SJacky Wang " @Component", 76*f585d8a3SJacky Wang " interface CComponent {", 77*f585d8a3SJacky Wang " C getC();", 78*f585d8a3SJacky Wang " }", 79*f585d8a3SJacky Wang "}"); 80*f585d8a3SJacky Wang 81*f585d8a3SJacky Wang @Test cyclicDependency()82*f585d8a3SJacky Wang public void cyclicDependency() { 83*f585d8a3SJacky Wang CompilerTests.daggerCompiler(SIMPLE_CYCLIC_DEPENDENCY) 84*f585d8a3SJacky Wang .withProcessingOptions(compilerMode.processorOptions()) 85*f585d8a3SJacky Wang .compile( 86*f585d8a3SJacky Wang subject -> { 87*f585d8a3SJacky Wang subject.hasErrorCount(1); 88*f585d8a3SJacky Wang subject.hasErrorContaining( 89*f585d8a3SJacky Wang String.join( 90*f585d8a3SJacky Wang "\n", 91*f585d8a3SJacky Wang "Found a dependency cycle:", 92*f585d8a3SJacky Wang " Outer.C is injected at", 93*f585d8a3SJacky Wang " Outer.A(cParam)", 94*f585d8a3SJacky Wang " Outer.A is injected at", 95*f585d8a3SJacky Wang " Outer.B(aParam)", 96*f585d8a3SJacky Wang " Outer.B is injected at", 97*f585d8a3SJacky Wang " Outer.C(bParam)", 98*f585d8a3SJacky Wang " Outer.C is injected at", 99*f585d8a3SJacky Wang " Outer.A(cParam)", 100*f585d8a3SJacky Wang " ...", 101*f585d8a3SJacky Wang "", 102*f585d8a3SJacky Wang "The cycle is requested via:", 103*f585d8a3SJacky Wang " Outer.C is requested at", 104*f585d8a3SJacky Wang " Outer.CComponent.getC()")) 105*f585d8a3SJacky Wang .onSource(SIMPLE_CYCLIC_DEPENDENCY) 106*f585d8a3SJacky Wang .onLineContaining("interface CComponent"); 107*f585d8a3SJacky Wang }); 108*f585d8a3SJacky Wang } 109*f585d8a3SJacky Wang 110*f585d8a3SJacky Wang // TODO(b/243720787): Requires CompilationResultSubject#hasErrorContainingMatch() 111*f585d8a3SJacky Wang @Test cyclicDependencyWithModuleBindingValidation()112*f585d8a3SJacky Wang public void cyclicDependencyWithModuleBindingValidation() { 113*f585d8a3SJacky Wang // Cycle errors should not show a dependency trace to an entry point when doing full binding 114*f585d8a3SJacky Wang // graph validation. So ensure that the message doesn't end with "test.Outer.C is requested at 115*f585d8a3SJacky Wang // test.Outer.CComponent.getC()", as the previous test's message does. 116*f585d8a3SJacky Wang Pattern moduleBindingValidationError = 117*f585d8a3SJacky Wang endsWithMessage( 118*f585d8a3SJacky Wang "Found a dependency cycle:", 119*f585d8a3SJacky Wang " Outer.C is injected at", 120*f585d8a3SJacky Wang " Outer.A(cParam)", 121*f585d8a3SJacky Wang " Outer.A is injected at", 122*f585d8a3SJacky Wang " Outer.B(aParam)", 123*f585d8a3SJacky Wang " Outer.B is injected at", 124*f585d8a3SJacky Wang " Outer.C(bParam)", 125*f585d8a3SJacky Wang " Outer.C is injected at", 126*f585d8a3SJacky Wang " Outer.A(cParam)", 127*f585d8a3SJacky Wang " ...", 128*f585d8a3SJacky Wang "", 129*f585d8a3SJacky Wang "======================", 130*f585d8a3SJacky Wang "Full classname legend:", 131*f585d8a3SJacky Wang "======================", 132*f585d8a3SJacky Wang "Outer: test.Outer", 133*f585d8a3SJacky Wang "========================", 134*f585d8a3SJacky Wang "End of classname legend:", 135*f585d8a3SJacky Wang "========================"); 136*f585d8a3SJacky Wang 137*f585d8a3SJacky Wang Compilation compilation = 138*f585d8a3SJacky Wang compilerWithOptions("-Adagger.fullBindingGraphValidation=ERROR") 139*f585d8a3SJacky Wang .compile(SIMPLE_CYCLIC_DEPENDENCY.toJFO()); 140*f585d8a3SJacky Wang assertThat(compilation).failed(); 141*f585d8a3SJacky Wang 142*f585d8a3SJacky Wang assertThat(compilation) 143*f585d8a3SJacky Wang .hadErrorContainingMatch(moduleBindingValidationError) 144*f585d8a3SJacky Wang .inFile(SIMPLE_CYCLIC_DEPENDENCY.toJFO()) 145*f585d8a3SJacky Wang .onLineContaining("interface MModule"); 146*f585d8a3SJacky Wang 147*f585d8a3SJacky Wang assertThat(compilation) 148*f585d8a3SJacky Wang .hadErrorContainingMatch(moduleBindingValidationError) 149*f585d8a3SJacky Wang .inFile(SIMPLE_CYCLIC_DEPENDENCY.toJFO()) 150*f585d8a3SJacky Wang .onLineContaining("interface CComponent"); 151*f585d8a3SJacky Wang 152*f585d8a3SJacky Wang assertThat(compilation).hadErrorCount(2); 153*f585d8a3SJacky Wang } 154*f585d8a3SJacky Wang cyclicDependencyNotIncludingEntryPoint()155*f585d8a3SJacky Wang @Test public void cyclicDependencyNotIncludingEntryPoint() { 156*f585d8a3SJacky Wang Source component = 157*f585d8a3SJacky Wang CompilerTests.javaSource( 158*f585d8a3SJacky Wang "test.Outer", 159*f585d8a3SJacky Wang "package test;", 160*f585d8a3SJacky Wang "", 161*f585d8a3SJacky Wang "import dagger.Component;", 162*f585d8a3SJacky Wang "import dagger.Module;", 163*f585d8a3SJacky Wang "import dagger.Provides;", 164*f585d8a3SJacky Wang "import javax.inject.Inject;", 165*f585d8a3SJacky Wang "", 166*f585d8a3SJacky Wang "final class Outer {", 167*f585d8a3SJacky Wang " static class A {", 168*f585d8a3SJacky Wang " @Inject A(C cParam) {}", 169*f585d8a3SJacky Wang " }", 170*f585d8a3SJacky Wang "", 171*f585d8a3SJacky Wang " static class B {", 172*f585d8a3SJacky Wang " @Inject B(A aParam) {}", 173*f585d8a3SJacky Wang " }", 174*f585d8a3SJacky Wang "", 175*f585d8a3SJacky Wang " static class C {", 176*f585d8a3SJacky Wang " @Inject C(B bParam) {}", 177*f585d8a3SJacky Wang " }", 178*f585d8a3SJacky Wang "", 179*f585d8a3SJacky Wang " static class D {", 180*f585d8a3SJacky Wang " @Inject D(C cParam) {}", 181*f585d8a3SJacky Wang " }", 182*f585d8a3SJacky Wang "", 183*f585d8a3SJacky Wang " @Component", 184*f585d8a3SJacky Wang " interface DComponent {", 185*f585d8a3SJacky Wang " D getD();", 186*f585d8a3SJacky Wang " }", 187*f585d8a3SJacky Wang "}"); 188*f585d8a3SJacky Wang 189*f585d8a3SJacky Wang CompilerTests.daggerCompiler(component) 190*f585d8a3SJacky Wang .withProcessingOptions(compilerMode.processorOptions()) 191*f585d8a3SJacky Wang .compile( 192*f585d8a3SJacky Wang subject -> { 193*f585d8a3SJacky Wang subject.hasErrorCount(1); 194*f585d8a3SJacky Wang subject.hasErrorContaining( 195*f585d8a3SJacky Wang String.join( 196*f585d8a3SJacky Wang "\n", 197*f585d8a3SJacky Wang "Found a dependency cycle:", 198*f585d8a3SJacky Wang " Outer.C is injected at", 199*f585d8a3SJacky Wang " Outer.A(cParam)", 200*f585d8a3SJacky Wang " Outer.A is injected at", 201*f585d8a3SJacky Wang " Outer.B(aParam)", 202*f585d8a3SJacky Wang " Outer.B is injected at", 203*f585d8a3SJacky Wang " Outer.C(bParam)", 204*f585d8a3SJacky Wang " Outer.C is injected at", 205*f585d8a3SJacky Wang " Outer.A(cParam)", 206*f585d8a3SJacky Wang " ...", 207*f585d8a3SJacky Wang "", 208*f585d8a3SJacky Wang "The cycle is requested via:", 209*f585d8a3SJacky Wang " Outer.C is injected at", 210*f585d8a3SJacky Wang " Outer.D(cParam)", 211*f585d8a3SJacky Wang " Outer.D is requested at", 212*f585d8a3SJacky Wang " Outer.DComponent.getD()")) 213*f585d8a3SJacky Wang .onSource(component) 214*f585d8a3SJacky Wang .onLineContaining("interface DComponent"); 215*f585d8a3SJacky Wang }); 216*f585d8a3SJacky Wang } 217*f585d8a3SJacky Wang 218*f585d8a3SJacky Wang @Test cyclicDependencyNotBrokenByMapBinding()219*f585d8a3SJacky Wang public void cyclicDependencyNotBrokenByMapBinding() { 220*f585d8a3SJacky Wang Source component = 221*f585d8a3SJacky Wang CompilerTests.javaSource( 222*f585d8a3SJacky Wang "test.Outer", 223*f585d8a3SJacky Wang "package test;", 224*f585d8a3SJacky Wang "", 225*f585d8a3SJacky Wang "import dagger.Component;", 226*f585d8a3SJacky Wang "import dagger.Module;", 227*f585d8a3SJacky Wang "import dagger.Provides;", 228*f585d8a3SJacky Wang "import dagger.multibindings.IntoMap;", 229*f585d8a3SJacky Wang "import dagger.multibindings.StringKey;", 230*f585d8a3SJacky Wang "import java.util.Map;", 231*f585d8a3SJacky Wang "import javax.inject.Inject;", 232*f585d8a3SJacky Wang "", 233*f585d8a3SJacky Wang "final class Outer {", 234*f585d8a3SJacky Wang " static class A {", 235*f585d8a3SJacky Wang " @Inject A(Map<String, C> cMap) {}", 236*f585d8a3SJacky Wang " }", 237*f585d8a3SJacky Wang "", 238*f585d8a3SJacky Wang " static class B {", 239*f585d8a3SJacky Wang " @Inject B(A aParam) {}", 240*f585d8a3SJacky Wang " }", 241*f585d8a3SJacky Wang "", 242*f585d8a3SJacky Wang " static class C {", 243*f585d8a3SJacky Wang " @Inject C(B bParam) {}", 244*f585d8a3SJacky Wang " }", 245*f585d8a3SJacky Wang "", 246*f585d8a3SJacky Wang " @Component(modules = CModule.class)", 247*f585d8a3SJacky Wang " interface CComponent {", 248*f585d8a3SJacky Wang " C getC();", 249*f585d8a3SJacky Wang " }", 250*f585d8a3SJacky Wang "", 251*f585d8a3SJacky Wang " @Module", 252*f585d8a3SJacky Wang " static class CModule {", 253*f585d8a3SJacky Wang " @Provides @IntoMap", 254*f585d8a3SJacky Wang " @StringKey(\"C\")", 255*f585d8a3SJacky Wang " static C c(C c) {", 256*f585d8a3SJacky Wang " return c;", 257*f585d8a3SJacky Wang " }", 258*f585d8a3SJacky Wang " }", 259*f585d8a3SJacky Wang "}"); 260*f585d8a3SJacky Wang 261*f585d8a3SJacky Wang CompilerTests.daggerCompiler(component) 262*f585d8a3SJacky Wang .withProcessingOptions(compilerMode.processorOptions()) 263*f585d8a3SJacky Wang .compile( 264*f585d8a3SJacky Wang subject -> { 265*f585d8a3SJacky Wang subject.hasErrorCount(1); 266*f585d8a3SJacky Wang subject.hasErrorContaining( 267*f585d8a3SJacky Wang String.join( 268*f585d8a3SJacky Wang "\n", 269*f585d8a3SJacky Wang "Found a dependency cycle:", 270*f585d8a3SJacky Wang " Outer.C is injected at", 271*f585d8a3SJacky Wang " Outer.CModule.c(c)", 272*f585d8a3SJacky Wang " Map<String,Outer.C> is injected at", 273*f585d8a3SJacky Wang " Outer.A(cMap)", 274*f585d8a3SJacky Wang " Outer.A is injected at", 275*f585d8a3SJacky Wang " Outer.B(aParam)", 276*f585d8a3SJacky Wang " Outer.B is injected at", 277*f585d8a3SJacky Wang " Outer.C(bParam)", 278*f585d8a3SJacky Wang " Outer.C is injected at", 279*f585d8a3SJacky Wang " Outer.CModule.c(c)", 280*f585d8a3SJacky Wang " ...", 281*f585d8a3SJacky Wang "", 282*f585d8a3SJacky Wang "The cycle is requested via:", 283*f585d8a3SJacky Wang " Outer.C is requested at", 284*f585d8a3SJacky Wang " Outer.CComponent.getC()")) 285*f585d8a3SJacky Wang .onSource(component) 286*f585d8a3SJacky Wang .onLineContaining("interface CComponent"); 287*f585d8a3SJacky Wang }); 288*f585d8a3SJacky Wang } 289*f585d8a3SJacky Wang 290*f585d8a3SJacky Wang @Test cyclicDependencyWithSetBinding()291*f585d8a3SJacky Wang public void cyclicDependencyWithSetBinding() { 292*f585d8a3SJacky Wang Source component = 293*f585d8a3SJacky Wang CompilerTests.javaSource( 294*f585d8a3SJacky Wang "test.Outer", 295*f585d8a3SJacky Wang "package test;", 296*f585d8a3SJacky Wang "", 297*f585d8a3SJacky Wang "import dagger.Component;", 298*f585d8a3SJacky Wang "import dagger.Module;", 299*f585d8a3SJacky Wang "import dagger.Provides;", 300*f585d8a3SJacky Wang "import dagger.multibindings.IntoSet;", 301*f585d8a3SJacky Wang "import java.util.Set;", 302*f585d8a3SJacky Wang "import javax.inject.Inject;", 303*f585d8a3SJacky Wang "", 304*f585d8a3SJacky Wang "final class Outer {", 305*f585d8a3SJacky Wang " static class A {", 306*f585d8a3SJacky Wang " @Inject A(Set<C> cSet) {}", 307*f585d8a3SJacky Wang " }", 308*f585d8a3SJacky Wang "", 309*f585d8a3SJacky Wang " static class B {", 310*f585d8a3SJacky Wang " @Inject B(A aParam) {}", 311*f585d8a3SJacky Wang " }", 312*f585d8a3SJacky Wang "", 313*f585d8a3SJacky Wang " static class C {", 314*f585d8a3SJacky Wang " @Inject C(B bParam) {}", 315*f585d8a3SJacky Wang " }", 316*f585d8a3SJacky Wang "", 317*f585d8a3SJacky Wang " @Component(modules = CModule.class)", 318*f585d8a3SJacky Wang " interface CComponent {", 319*f585d8a3SJacky Wang " C getC();", 320*f585d8a3SJacky Wang " }", 321*f585d8a3SJacky Wang "", 322*f585d8a3SJacky Wang " @Module", 323*f585d8a3SJacky Wang " static class CModule {", 324*f585d8a3SJacky Wang " @Provides @IntoSet", 325*f585d8a3SJacky Wang " static C c(C c) {", 326*f585d8a3SJacky Wang " return c;", 327*f585d8a3SJacky Wang " }", 328*f585d8a3SJacky Wang " }", 329*f585d8a3SJacky Wang "}"); 330*f585d8a3SJacky Wang 331*f585d8a3SJacky Wang CompilerTests.daggerCompiler(component) 332*f585d8a3SJacky Wang .withProcessingOptions(compilerMode.processorOptions()) 333*f585d8a3SJacky Wang .compile( 334*f585d8a3SJacky Wang subject -> { 335*f585d8a3SJacky Wang subject.hasErrorCount(1); 336*f585d8a3SJacky Wang subject.hasErrorContaining( 337*f585d8a3SJacky Wang String.join( 338*f585d8a3SJacky Wang "\n", 339*f585d8a3SJacky Wang "Found a dependency cycle:", 340*f585d8a3SJacky Wang " Outer.C is injected at", 341*f585d8a3SJacky Wang " Outer.CModule.c(c)", 342*f585d8a3SJacky Wang " Set<Outer.C> is injected at", 343*f585d8a3SJacky Wang " Outer.A(cSet)", 344*f585d8a3SJacky Wang " Outer.A is injected at", 345*f585d8a3SJacky Wang " Outer.B(aParam)", 346*f585d8a3SJacky Wang " Outer.B is injected at", 347*f585d8a3SJacky Wang " Outer.C(bParam)", 348*f585d8a3SJacky Wang " Outer.C is injected at", 349*f585d8a3SJacky Wang " Outer.CModule.c(c)", 350*f585d8a3SJacky Wang " ...", 351*f585d8a3SJacky Wang "", 352*f585d8a3SJacky Wang "The cycle is requested via:", 353*f585d8a3SJacky Wang " Outer.C is requested at", 354*f585d8a3SJacky Wang " Outer.CComponent.getC()")) 355*f585d8a3SJacky Wang .onSource(component) 356*f585d8a3SJacky Wang .onLineContaining("interface CComponent"); 357*f585d8a3SJacky Wang }); 358*f585d8a3SJacky Wang } 359*f585d8a3SJacky Wang 360*f585d8a3SJacky Wang @Test falsePositiveCyclicDependencyIndirectionDetected()361*f585d8a3SJacky Wang public void falsePositiveCyclicDependencyIndirectionDetected() { 362*f585d8a3SJacky Wang Source component = 363*f585d8a3SJacky Wang CompilerTests.javaSource( 364*f585d8a3SJacky Wang "test.Outer", 365*f585d8a3SJacky Wang "package test;", 366*f585d8a3SJacky Wang "", 367*f585d8a3SJacky Wang "import dagger.Component;", 368*f585d8a3SJacky Wang "import dagger.Module;", 369*f585d8a3SJacky Wang "import dagger.Provides;", 370*f585d8a3SJacky Wang "import javax.inject.Inject;", 371*f585d8a3SJacky Wang "import javax.inject.Provider;", 372*f585d8a3SJacky Wang "", 373*f585d8a3SJacky Wang "final class Outer {", 374*f585d8a3SJacky Wang " static class A {", 375*f585d8a3SJacky Wang " @Inject A(C cParam) {}", 376*f585d8a3SJacky Wang " }", 377*f585d8a3SJacky Wang "", 378*f585d8a3SJacky Wang " static class B {", 379*f585d8a3SJacky Wang " @Inject B(A aParam) {}", 380*f585d8a3SJacky Wang " }", 381*f585d8a3SJacky Wang "", 382*f585d8a3SJacky Wang " static class C {", 383*f585d8a3SJacky Wang " @Inject C(B bParam) {}", 384*f585d8a3SJacky Wang " }", 385*f585d8a3SJacky Wang "", 386*f585d8a3SJacky Wang " static class D {", 387*f585d8a3SJacky Wang " @Inject D(Provider<C> cParam) {}", 388*f585d8a3SJacky Wang " }", 389*f585d8a3SJacky Wang "", 390*f585d8a3SJacky Wang " @Component", 391*f585d8a3SJacky Wang " interface DComponent {", 392*f585d8a3SJacky Wang " D getD();", 393*f585d8a3SJacky Wang " }", 394*f585d8a3SJacky Wang "}"); 395*f585d8a3SJacky Wang 396*f585d8a3SJacky Wang CompilerTests.daggerCompiler(component) 397*f585d8a3SJacky Wang .withProcessingOptions(compilerMode.processorOptions()) 398*f585d8a3SJacky Wang .compile( 399*f585d8a3SJacky Wang subject -> { 400*f585d8a3SJacky Wang subject.hasErrorCount(1); 401*f585d8a3SJacky Wang subject.hasErrorContaining( 402*f585d8a3SJacky Wang String.join( 403*f585d8a3SJacky Wang "\n", 404*f585d8a3SJacky Wang "Found a dependency cycle:", 405*f585d8a3SJacky Wang " Outer.C is injected at", 406*f585d8a3SJacky Wang " Outer.A(cParam)", 407*f585d8a3SJacky Wang " Outer.A is injected at", 408*f585d8a3SJacky Wang " Outer.B(aParam)", 409*f585d8a3SJacky Wang " Outer.B is injected at", 410*f585d8a3SJacky Wang " Outer.C(bParam)", 411*f585d8a3SJacky Wang " Outer.C is injected at", 412*f585d8a3SJacky Wang " Outer.A(cParam)", 413*f585d8a3SJacky Wang " ...", 414*f585d8a3SJacky Wang "", 415*f585d8a3SJacky Wang "The cycle is requested via:", 416*f585d8a3SJacky Wang " Provider<Outer.C> is injected at", 417*f585d8a3SJacky Wang " Outer.D(cParam)", 418*f585d8a3SJacky Wang " Outer.D is requested at", 419*f585d8a3SJacky Wang " Outer.DComponent.getD()")) 420*f585d8a3SJacky Wang .onSource(component) 421*f585d8a3SJacky Wang .onLineContaining("interface DComponent"); 422*f585d8a3SJacky Wang }); 423*f585d8a3SJacky Wang } 424*f585d8a3SJacky Wang 425*f585d8a3SJacky Wang @Test cyclicDependencyInSubcomponents()426*f585d8a3SJacky Wang public void cyclicDependencyInSubcomponents() { 427*f585d8a3SJacky Wang Source parent = 428*f585d8a3SJacky Wang CompilerTests.javaSource( 429*f585d8a3SJacky Wang "test.Parent", 430*f585d8a3SJacky Wang "package test;", 431*f585d8a3SJacky Wang "", 432*f585d8a3SJacky Wang "import dagger.Component;", 433*f585d8a3SJacky Wang "", 434*f585d8a3SJacky Wang "@Component", 435*f585d8a3SJacky Wang "interface Parent {", 436*f585d8a3SJacky Wang " Child.Builder child();", 437*f585d8a3SJacky Wang "}"); 438*f585d8a3SJacky Wang Source child = 439*f585d8a3SJacky Wang CompilerTests.javaSource( 440*f585d8a3SJacky Wang "test.Child", 441*f585d8a3SJacky Wang "package test;", 442*f585d8a3SJacky Wang "", 443*f585d8a3SJacky Wang "import dagger.Subcomponent;", 444*f585d8a3SJacky Wang "", 445*f585d8a3SJacky Wang "@Subcomponent(modules = CycleModule.class)", 446*f585d8a3SJacky Wang "interface Child {", 447*f585d8a3SJacky Wang " Grandchild.Builder grandchild();", 448*f585d8a3SJacky Wang "", 449*f585d8a3SJacky Wang " @Subcomponent.Builder", 450*f585d8a3SJacky Wang " interface Builder {", 451*f585d8a3SJacky Wang " Child build();", 452*f585d8a3SJacky Wang " }", 453*f585d8a3SJacky Wang "}"); 454*f585d8a3SJacky Wang Source grandchild = 455*f585d8a3SJacky Wang CompilerTests.javaSource( 456*f585d8a3SJacky Wang "test.Grandchild", 457*f585d8a3SJacky Wang "package test;", 458*f585d8a3SJacky Wang "", 459*f585d8a3SJacky Wang "import dagger.Subcomponent;", 460*f585d8a3SJacky Wang "", 461*f585d8a3SJacky Wang "@Subcomponent", 462*f585d8a3SJacky Wang "interface Grandchild {", 463*f585d8a3SJacky Wang " String entry();", 464*f585d8a3SJacky Wang "", 465*f585d8a3SJacky Wang " @Subcomponent.Builder", 466*f585d8a3SJacky Wang " interface Builder {", 467*f585d8a3SJacky Wang " Grandchild build();", 468*f585d8a3SJacky Wang " }", 469*f585d8a3SJacky Wang "}"); 470*f585d8a3SJacky Wang Source cycleModule = 471*f585d8a3SJacky Wang CompilerTests.javaSource( 472*f585d8a3SJacky Wang "test.CycleModule", 473*f585d8a3SJacky Wang "package test;", 474*f585d8a3SJacky Wang "", 475*f585d8a3SJacky Wang "import dagger.Module;", 476*f585d8a3SJacky Wang "import dagger.Provides;", 477*f585d8a3SJacky Wang "", 478*f585d8a3SJacky Wang "@Module", 479*f585d8a3SJacky Wang "abstract class CycleModule {", 480*f585d8a3SJacky Wang " @Provides static Object object(String string) {", 481*f585d8a3SJacky Wang " return string;", 482*f585d8a3SJacky Wang " }", 483*f585d8a3SJacky Wang "", 484*f585d8a3SJacky Wang " @Provides static String string(Object object) {", 485*f585d8a3SJacky Wang " return object.toString();", 486*f585d8a3SJacky Wang " }", 487*f585d8a3SJacky Wang "}"); 488*f585d8a3SJacky Wang 489*f585d8a3SJacky Wang CompilerTests.daggerCompiler(parent, child, grandchild, cycleModule) 490*f585d8a3SJacky Wang .withProcessingOptions(compilerMode.processorOptions()) 491*f585d8a3SJacky Wang .compile( 492*f585d8a3SJacky Wang subject -> { 493*f585d8a3SJacky Wang subject.hasErrorCount(1); 494*f585d8a3SJacky Wang subject.hasErrorContaining( 495*f585d8a3SJacky Wang String.join( 496*f585d8a3SJacky Wang "\n", 497*f585d8a3SJacky Wang "Found a dependency cycle:", 498*f585d8a3SJacky Wang " String is injected at", 499*f585d8a3SJacky Wang " CycleModule.object(string)", 500*f585d8a3SJacky Wang " Object is injected at", 501*f585d8a3SJacky Wang " CycleModule.string(object)", 502*f585d8a3SJacky Wang " String is injected at", 503*f585d8a3SJacky Wang " CycleModule.object(string)", 504*f585d8a3SJacky Wang " ...", 505*f585d8a3SJacky Wang "", 506*f585d8a3SJacky Wang "The cycle is requested via:", 507*f585d8a3SJacky Wang " String is requested at", 508*f585d8a3SJacky Wang " Grandchild.entry()")) 509*f585d8a3SJacky Wang .onSource(parent) 510*f585d8a3SJacky Wang .onLineContaining("interface Parent"); 511*f585d8a3SJacky Wang }); 512*f585d8a3SJacky Wang } 513*f585d8a3SJacky Wang 514*f585d8a3SJacky Wang @Test cyclicDependencyInSubcomponentsWithChildren()515*f585d8a3SJacky Wang public void cyclicDependencyInSubcomponentsWithChildren() { 516*f585d8a3SJacky Wang Source parent = 517*f585d8a3SJacky Wang CompilerTests.javaSource( 518*f585d8a3SJacky Wang "test.Parent", 519*f585d8a3SJacky Wang "package test;", 520*f585d8a3SJacky Wang "", 521*f585d8a3SJacky Wang "import dagger.Component;", 522*f585d8a3SJacky Wang "", 523*f585d8a3SJacky Wang "@Component", 524*f585d8a3SJacky Wang "interface Parent {", 525*f585d8a3SJacky Wang " Child.Builder child();", 526*f585d8a3SJacky Wang "}"); 527*f585d8a3SJacky Wang Source child = 528*f585d8a3SJacky Wang CompilerTests.javaSource( 529*f585d8a3SJacky Wang "test.Child", 530*f585d8a3SJacky Wang "package test;", 531*f585d8a3SJacky Wang "", 532*f585d8a3SJacky Wang "import dagger.Subcomponent;", 533*f585d8a3SJacky Wang "", 534*f585d8a3SJacky Wang "@Subcomponent(modules = CycleModule.class)", 535*f585d8a3SJacky Wang "interface Child {", 536*f585d8a3SJacky Wang " String entry();", 537*f585d8a3SJacky Wang "", 538*f585d8a3SJacky Wang " Grandchild.Builder grandchild();", 539*f585d8a3SJacky Wang "", 540*f585d8a3SJacky Wang " @Subcomponent.Builder", 541*f585d8a3SJacky Wang " interface Builder {", 542*f585d8a3SJacky Wang " Child build();", 543*f585d8a3SJacky Wang " }", 544*f585d8a3SJacky Wang "}"); 545*f585d8a3SJacky Wang // Grandchild has no entry point that depends on the cycle. http://b/111317986 546*f585d8a3SJacky Wang Source grandchild = 547*f585d8a3SJacky Wang CompilerTests.javaSource( 548*f585d8a3SJacky Wang "test.Grandchild", 549*f585d8a3SJacky Wang "package test;", 550*f585d8a3SJacky Wang "", 551*f585d8a3SJacky Wang "import dagger.Subcomponent;", 552*f585d8a3SJacky Wang "", 553*f585d8a3SJacky Wang "@Subcomponent", 554*f585d8a3SJacky Wang "interface Grandchild {", 555*f585d8a3SJacky Wang "", 556*f585d8a3SJacky Wang " @Subcomponent.Builder", 557*f585d8a3SJacky Wang " interface Builder {", 558*f585d8a3SJacky Wang " Grandchild build();", 559*f585d8a3SJacky Wang " }", 560*f585d8a3SJacky Wang "}"); 561*f585d8a3SJacky Wang Source cycleModule = 562*f585d8a3SJacky Wang CompilerTests.javaSource( 563*f585d8a3SJacky Wang "test.CycleModule", 564*f585d8a3SJacky Wang "package test;", 565*f585d8a3SJacky Wang "", 566*f585d8a3SJacky Wang "import dagger.Module;", 567*f585d8a3SJacky Wang "import dagger.Provides;", 568*f585d8a3SJacky Wang "", 569*f585d8a3SJacky Wang "@Module", 570*f585d8a3SJacky Wang "abstract class CycleModule {", 571*f585d8a3SJacky Wang " @Provides static Object object(String string) {", 572*f585d8a3SJacky Wang " return string;", 573*f585d8a3SJacky Wang " }", 574*f585d8a3SJacky Wang "", 575*f585d8a3SJacky Wang " @Provides static String string(Object object) {", 576*f585d8a3SJacky Wang " return object.toString();", 577*f585d8a3SJacky Wang " }", 578*f585d8a3SJacky Wang "}"); 579*f585d8a3SJacky Wang 580*f585d8a3SJacky Wang CompilerTests.daggerCompiler(parent, child, grandchild, cycleModule) 581*f585d8a3SJacky Wang .withProcessingOptions(compilerMode.processorOptions()) 582*f585d8a3SJacky Wang .compile( 583*f585d8a3SJacky Wang subject -> { 584*f585d8a3SJacky Wang subject.hasErrorCount(1); 585*f585d8a3SJacky Wang subject.hasErrorContaining( 586*f585d8a3SJacky Wang String.join( 587*f585d8a3SJacky Wang "\n", 588*f585d8a3SJacky Wang "Found a dependency cycle:", 589*f585d8a3SJacky Wang " String is injected at", 590*f585d8a3SJacky Wang " CycleModule.object(string)", 591*f585d8a3SJacky Wang " Object is injected at", 592*f585d8a3SJacky Wang " CycleModule.string(object)", 593*f585d8a3SJacky Wang " String is injected at", 594*f585d8a3SJacky Wang " CycleModule.object(string)", 595*f585d8a3SJacky Wang " ...", 596*f585d8a3SJacky Wang "", 597*f585d8a3SJacky Wang "The cycle is requested via:", 598*f585d8a3SJacky Wang " String is requested at", 599*f585d8a3SJacky Wang " Child.entry() [Parent → Child]")) 600*f585d8a3SJacky Wang .onSource(parent) 601*f585d8a3SJacky Wang .onLineContaining("interface Parent"); 602*f585d8a3SJacky Wang }); 603*f585d8a3SJacky Wang } 604*f585d8a3SJacky Wang 605*f585d8a3SJacky Wang @Test circularBindsMethods()606*f585d8a3SJacky Wang public void circularBindsMethods() { 607*f585d8a3SJacky Wang Source qualifier = 608*f585d8a3SJacky Wang CompilerTests.javaSource( 609*f585d8a3SJacky Wang "test.SomeQualifier", 610*f585d8a3SJacky Wang "package test;", 611*f585d8a3SJacky Wang "", 612*f585d8a3SJacky Wang "import javax.inject.Qualifier;", 613*f585d8a3SJacky Wang "", 614*f585d8a3SJacky Wang "@Qualifier @interface SomeQualifier {}"); 615*f585d8a3SJacky Wang Source module = 616*f585d8a3SJacky Wang CompilerTests.javaSource( 617*f585d8a3SJacky Wang "test.TestModule", 618*f585d8a3SJacky Wang "package test;", 619*f585d8a3SJacky Wang "", 620*f585d8a3SJacky Wang "import dagger.Binds;", 621*f585d8a3SJacky Wang "import dagger.Module;", 622*f585d8a3SJacky Wang "", 623*f585d8a3SJacky Wang "@Module", 624*f585d8a3SJacky Wang "abstract class TestModule {", 625*f585d8a3SJacky Wang " @Binds abstract Object bindUnqualified(@SomeQualifier Object qualified);", 626*f585d8a3SJacky Wang " @Binds @SomeQualifier abstract Object bindQualified(Object unqualified);", 627*f585d8a3SJacky Wang "}"); 628*f585d8a3SJacky Wang Source component = 629*f585d8a3SJacky Wang CompilerTests.javaSource( 630*f585d8a3SJacky Wang "test.TestComponent", 631*f585d8a3SJacky Wang "package test;", 632*f585d8a3SJacky Wang "", 633*f585d8a3SJacky Wang "import dagger.Component;", 634*f585d8a3SJacky Wang "", 635*f585d8a3SJacky Wang "@Component(modules = TestModule.class)", 636*f585d8a3SJacky Wang "interface TestComponent {", 637*f585d8a3SJacky Wang " Object unqualified();", 638*f585d8a3SJacky Wang "}"); 639*f585d8a3SJacky Wang 640*f585d8a3SJacky Wang CompilerTests.daggerCompiler(qualifier, module, component) 641*f585d8a3SJacky Wang .withProcessingOptions(compilerMode.processorOptions()) 642*f585d8a3SJacky Wang .compile( 643*f585d8a3SJacky Wang subject -> { 644*f585d8a3SJacky Wang subject.hasErrorCount(1); 645*f585d8a3SJacky Wang subject.hasErrorContaining( 646*f585d8a3SJacky Wang String.join( 647*f585d8a3SJacky Wang "\n", 648*f585d8a3SJacky Wang "Found a dependency cycle:", 649*f585d8a3SJacky Wang " Object is injected at", 650*f585d8a3SJacky Wang " TestModule.bindQualified(unqualified)", 651*f585d8a3SJacky Wang " @SomeQualifier Object is injected at", 652*f585d8a3SJacky Wang " TestModule.bindUnqualified(qualified)", 653*f585d8a3SJacky Wang " Object is injected at", 654*f585d8a3SJacky Wang " TestModule.bindQualified(unqualified)", 655*f585d8a3SJacky Wang " ...", 656*f585d8a3SJacky Wang "", 657*f585d8a3SJacky Wang "The cycle is requested via:", 658*f585d8a3SJacky Wang " Object is requested at", 659*f585d8a3SJacky Wang " TestComponent.unqualified()")) 660*f585d8a3SJacky Wang .onSource(component) 661*f585d8a3SJacky Wang .onLineContaining("interface TestComponent"); 662*f585d8a3SJacky Wang }); 663*f585d8a3SJacky Wang } 664*f585d8a3SJacky Wang 665*f585d8a3SJacky Wang @Test selfReferentialBinds()666*f585d8a3SJacky Wang public void selfReferentialBinds() { 667*f585d8a3SJacky Wang Source module = 668*f585d8a3SJacky Wang CompilerTests.javaSource( 669*f585d8a3SJacky Wang "test.TestModule", 670*f585d8a3SJacky Wang "package test;", 671*f585d8a3SJacky Wang "", 672*f585d8a3SJacky Wang "import dagger.Binds;", 673*f585d8a3SJacky Wang "import dagger.Module;", 674*f585d8a3SJacky Wang "", 675*f585d8a3SJacky Wang "@Module", 676*f585d8a3SJacky Wang "abstract class TestModule {", 677*f585d8a3SJacky Wang " @Binds abstract Object bindToSelf(Object sameKey);", 678*f585d8a3SJacky Wang "}"); 679*f585d8a3SJacky Wang Source component = 680*f585d8a3SJacky Wang CompilerTests.javaSource( 681*f585d8a3SJacky Wang "test.TestComponent", 682*f585d8a3SJacky Wang "package test;", 683*f585d8a3SJacky Wang "", 684*f585d8a3SJacky Wang "import dagger.Component;", 685*f585d8a3SJacky Wang "", 686*f585d8a3SJacky Wang "@Component(modules = TestModule.class)", 687*f585d8a3SJacky Wang "interface TestComponent {", 688*f585d8a3SJacky Wang " Object selfReferential();", 689*f585d8a3SJacky Wang "}"); 690*f585d8a3SJacky Wang 691*f585d8a3SJacky Wang CompilerTests.daggerCompiler(module, component) 692*f585d8a3SJacky Wang .withProcessingOptions(compilerMode.processorOptions()) 693*f585d8a3SJacky Wang .compile( 694*f585d8a3SJacky Wang subject -> { 695*f585d8a3SJacky Wang subject.hasErrorCount(1); 696*f585d8a3SJacky Wang subject.hasErrorContaining( 697*f585d8a3SJacky Wang String.join( 698*f585d8a3SJacky Wang "\n", 699*f585d8a3SJacky Wang "Found a dependency cycle:", 700*f585d8a3SJacky Wang " Object is injected at", 701*f585d8a3SJacky Wang " TestModule.bindToSelf(sameKey)", 702*f585d8a3SJacky Wang " Object is injected at", 703*f585d8a3SJacky Wang " TestModule.bindToSelf(sameKey)", 704*f585d8a3SJacky Wang " ...", 705*f585d8a3SJacky Wang "", 706*f585d8a3SJacky Wang "The cycle is requested via:", 707*f585d8a3SJacky Wang " Object is requested at", 708*f585d8a3SJacky Wang " TestComponent.selfReferential()")) 709*f585d8a3SJacky Wang .onSource(component) 710*f585d8a3SJacky Wang .onLineContaining("interface TestComponent"); 711*f585d8a3SJacky Wang }); 712*f585d8a3SJacky Wang } 713*f585d8a3SJacky Wang 714*f585d8a3SJacky Wang @Test cycleFromMembersInjectionMethod_WithSameKeyAsMembersInjectionMethod()715*f585d8a3SJacky Wang public void cycleFromMembersInjectionMethod_WithSameKeyAsMembersInjectionMethod() { 716*f585d8a3SJacky Wang Source a = 717*f585d8a3SJacky Wang CompilerTests.javaSource( 718*f585d8a3SJacky Wang "test.A", 719*f585d8a3SJacky Wang "package test;", 720*f585d8a3SJacky Wang "", 721*f585d8a3SJacky Wang "import javax.inject.Inject;", 722*f585d8a3SJacky Wang "", 723*f585d8a3SJacky Wang "class A {", 724*f585d8a3SJacky Wang " @Inject A() {}", 725*f585d8a3SJacky Wang " @Inject B b;", 726*f585d8a3SJacky Wang "}"); 727*f585d8a3SJacky Wang Source b = 728*f585d8a3SJacky Wang CompilerTests.javaSource( 729*f585d8a3SJacky Wang "test.B", 730*f585d8a3SJacky Wang "package test;", 731*f585d8a3SJacky Wang "", 732*f585d8a3SJacky Wang "import javax.inject.Inject;", 733*f585d8a3SJacky Wang "", 734*f585d8a3SJacky Wang "class B {", 735*f585d8a3SJacky Wang " @Inject B() {}", 736*f585d8a3SJacky Wang " @Inject A a;", 737*f585d8a3SJacky Wang "}"); 738*f585d8a3SJacky Wang Source component = 739*f585d8a3SJacky Wang CompilerTests.javaSource( 740*f585d8a3SJacky Wang "test.CycleComponent", 741*f585d8a3SJacky Wang "package test;", 742*f585d8a3SJacky Wang "", 743*f585d8a3SJacky Wang "import dagger.Component;", 744*f585d8a3SJacky Wang "", 745*f585d8a3SJacky Wang "@Component", 746*f585d8a3SJacky Wang "interface CycleComponent {", 747*f585d8a3SJacky Wang " void inject(A a);", 748*f585d8a3SJacky Wang "}"); 749*f585d8a3SJacky Wang 750*f585d8a3SJacky Wang CompilerTests.daggerCompiler(a, b, component) 751*f585d8a3SJacky Wang .withProcessingOptions(compilerMode.processorOptions()) 752*f585d8a3SJacky Wang .compile( 753*f585d8a3SJacky Wang subject -> { 754*f585d8a3SJacky Wang subject.hasErrorCount(1); 755*f585d8a3SJacky Wang subject.hasErrorContaining( 756*f585d8a3SJacky Wang String.join( 757*f585d8a3SJacky Wang "\n", 758*f585d8a3SJacky Wang "Found a dependency cycle:", 759*f585d8a3SJacky Wang " test.B is injected at", 760*f585d8a3SJacky Wang " test.A.b", 761*f585d8a3SJacky Wang " test.A is injected at", 762*f585d8a3SJacky Wang " test.B.a", 763*f585d8a3SJacky Wang " test.B is injected at", 764*f585d8a3SJacky Wang " test.A.b", 765*f585d8a3SJacky Wang " ...", 766*f585d8a3SJacky Wang "", 767*f585d8a3SJacky Wang "The cycle is requested via:", 768*f585d8a3SJacky Wang " test.B is injected at", 769*f585d8a3SJacky Wang " test.A.b", 770*f585d8a3SJacky Wang " test.A is injected at", 771*f585d8a3SJacky Wang " CycleComponent.inject(test.A)")) 772*f585d8a3SJacky Wang .onSource(component) 773*f585d8a3SJacky Wang .onLineContaining("interface CycleComponent"); 774*f585d8a3SJacky Wang }); 775*f585d8a3SJacky Wang } 776*f585d8a3SJacky Wang 777*f585d8a3SJacky Wang @Test longCycleMaskedByShortBrokenCycles()778*f585d8a3SJacky Wang public void longCycleMaskedByShortBrokenCycles() { 779*f585d8a3SJacky Wang Source cycles = 780*f585d8a3SJacky Wang CompilerTests.javaSource( 781*f585d8a3SJacky Wang "test.Cycles", 782*f585d8a3SJacky Wang "package test;", 783*f585d8a3SJacky Wang "", 784*f585d8a3SJacky Wang "import javax.inject.Inject;", 785*f585d8a3SJacky Wang "import javax.inject.Provider;", 786*f585d8a3SJacky Wang "import dagger.Component;", 787*f585d8a3SJacky Wang "", 788*f585d8a3SJacky Wang "final class Cycles {", 789*f585d8a3SJacky Wang " static class A {", 790*f585d8a3SJacky Wang " @Inject A(Provider<A> aProvider, B b) {}", 791*f585d8a3SJacky Wang " }", 792*f585d8a3SJacky Wang "", 793*f585d8a3SJacky Wang " static class B {", 794*f585d8a3SJacky Wang " @Inject B(Provider<B> bProvider, A a) {}", 795*f585d8a3SJacky Wang " }", 796*f585d8a3SJacky Wang "", 797*f585d8a3SJacky Wang " @Component", 798*f585d8a3SJacky Wang " interface C {", 799*f585d8a3SJacky Wang " A a();", 800*f585d8a3SJacky Wang " }", 801*f585d8a3SJacky Wang "}"); 802*f585d8a3SJacky Wang CompilerTests.daggerCompiler(cycles) 803*f585d8a3SJacky Wang .withProcessingOptions(compilerMode.processorOptions()) 804*f585d8a3SJacky Wang .compile( 805*f585d8a3SJacky Wang subject -> { 806*f585d8a3SJacky Wang subject.hasErrorCount(1); 807*f585d8a3SJacky Wang subject.hasErrorContaining("Found a dependency cycle:") 808*f585d8a3SJacky Wang .onSource(cycles) 809*f585d8a3SJacky Wang .onLineContaining("interface C"); 810*f585d8a3SJacky Wang }); 811*f585d8a3SJacky Wang } 812*f585d8a3SJacky Wang } 813