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