• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 static com.google.testing.compile.CompilationSubject.assertThat;
20 import static dagger.internal.codegen.Compilers.compilerWithOptions;
21 import static dagger.internal.codegen.Compilers.daggerCompiler;
22 import static dagger.internal.codegen.TestUtils.message;
23 
24 import com.google.testing.compile.Compilation;
25 import com.google.testing.compile.JavaFileObjects;
26 import javax.tools.JavaFileObject;
27 import org.junit.Test;
28 import org.junit.runner.RunWith;
29 import org.junit.runners.JUnit4;
30 
31 @RunWith(JUnit4.class)
32 public class ScopingValidationTest {
33   @Test
componentWithoutScopeIncludesScopedBindings_Fail()34   public void componentWithoutScopeIncludesScopedBindings_Fail() {
35     JavaFileObject componentFile =
36         JavaFileObjects.forSourceLines(
37             "test.MyComponent",
38             "package test;",
39             "",
40             "import dagger.Component;",
41             "import javax.inject.Singleton;",
42             "",
43             "@Component(modules = ScopedModule.class)",
44             "interface MyComponent {",
45             "  ScopedType string();",
46             "}");
47     JavaFileObject typeFile =
48         JavaFileObjects.forSourceLines(
49             "test.ScopedType",
50             "package test;",
51             "",
52             "import javax.inject.Inject;",
53             "import javax.inject.Singleton;",
54             "",
55             "@Singleton",
56             "class ScopedType {",
57             "  @Inject ScopedType(String s, long l, float f) {}",
58             "}");
59     JavaFileObject moduleFile =
60         JavaFileObjects.forSourceLines(
61             "test.ScopedModule",
62             "package test;",
63             "",
64             "import dagger.Module;",
65             "import dagger.Provides;",
66             "import javax.inject.Singleton;",
67             "",
68             "@Module",
69             "class ScopedModule {",
70             "  @Provides @Singleton String string() { return \"a string\"; }",
71             "  @Provides long integer() { return 0L; }",
72             "  @Provides float floatingPoint() { return 0.0f; }",
73             "}");
74 
75     Compilation compilation = daggerCompiler().compile(componentFile, typeFile, moduleFile);
76     assertThat(compilation).failed();
77     assertThat(compilation)
78         .hadErrorContaining(
79             message(
80                 "MyComponent (unscoped) may not reference scoped bindings:",
81                 "    @Singleton class ScopedType",
82                 "    @Provides @Singleton String ScopedModule.string()"));
83   }
84 
85   @Test // b/79859714
bindsWithChildScope_inParentModule_notAllowed()86   public void bindsWithChildScope_inParentModule_notAllowed() {
87     JavaFileObject childScope =
88         JavaFileObjects.forSourceLines(
89             "test.ChildScope",
90             "package test;",
91             "",
92             "import javax.inject.Scope;",
93             "",
94             "@Scope",
95             "@interface ChildScope {}");
96 
97     JavaFileObject foo =
98         JavaFileObjects.forSourceLines(
99             "test.Foo",
100             "package test;",
101             "", //
102             "interface Foo {}");
103 
104     JavaFileObject fooImpl =
105         JavaFileObjects.forSourceLines(
106             "test.ChildModule",
107             "package test;",
108             "",
109             "import javax.inject.Inject;",
110             "",
111             "class FooImpl implements Foo {",
112             "  @Inject FooImpl() {}",
113             "}");
114 
115     JavaFileObject parentModule =
116         JavaFileObjects.forSourceLines(
117             "test.ParentModule",
118             "package test;",
119             "",
120             "import dagger.Binds;",
121             "import dagger.Module;",
122             "",
123             "@Module",
124             "interface ParentModule {",
125             "  @Binds @ChildScope Foo bind(FooImpl fooImpl);",
126             "}");
127 
128     JavaFileObject parent =
129         JavaFileObjects.forSourceLines(
130             "test.ParentComponent",
131             "package test;",
132             "",
133             "import dagger.Component;",
134             "import javax.inject.Singleton;",
135             "",
136             "@Singleton",
137             "@Component(modules = ParentModule.class)",
138             "interface Parent {",
139             "  Child child();",
140             "}");
141 
142     JavaFileObject child =
143         JavaFileObjects.forSourceLines(
144             "test.Child",
145             "package test;",
146             "",
147             "import dagger.Subcomponent;",
148             "",
149             "@ChildScope",
150             "@Subcomponent",
151             "interface Child {",
152             "  Foo foo();",
153             "}");
154 
155     Compilation compilation =
156         daggerCompiler().compile(childScope, foo, fooImpl, parentModule, parent, child);
157     assertThat(compilation).failed();
158     assertThat(compilation)
159         .hadErrorContaining(
160             message(
161                 "Parent scoped with @Singleton may not reference bindings with different "
162                     + "scopes:",
163                 "    @Binds @ChildScope Foo ParentModule.bind(FooImpl)"));
164   }
165 
166   @Test
componentWithScopeIncludesIncompatiblyScopedBindings_Fail()167   public void componentWithScopeIncludesIncompatiblyScopedBindings_Fail() {
168     JavaFileObject componentFile =
169         JavaFileObjects.forSourceLines(
170             "test.MyComponent",
171             "package test;",
172             "",
173             "import dagger.Component;",
174             "import javax.inject.Singleton;",
175             "",
176             "@Singleton",
177             "@Component(modules = ScopedModule.class)",
178             "interface MyComponent {",
179             "  ScopedType string();",
180             "}");
181     JavaFileObject scopeFile =
182         JavaFileObjects.forSourceLines(
183             "test.PerTest",
184             "package test;",
185             "",
186             "import javax.inject.Scope;",
187             "",
188             "@Scope",
189             "@interface PerTest {}");
190     JavaFileObject scopeWithAttribute =
191         JavaFileObjects.forSourceLines(
192             "test.Per",
193             "package test;",
194             "",
195             "import javax.inject.Scope;",
196             "",
197             "@Scope",
198             "@interface Per {",
199             "  Class<?> value();",
200             "}");
201     JavaFileObject typeFile =
202         JavaFileObjects.forSourceLines(
203             "test.ScopedType",
204             "package test;",
205             "",
206             "import javax.inject.Inject;",
207             "",
208             "@PerTest", // incompatible scope
209             "class ScopedType {",
210             "  @Inject ScopedType(String s, long l, float f, boolean b) {}",
211             "}");
212     JavaFileObject moduleFile =
213         JavaFileObjects.forSourceLines(
214             "test.ScopedModule",
215             "package test;",
216             "",
217             "import dagger.Module;",
218             "import dagger.Provides;",
219             "import javax.inject.Singleton;",
220             "",
221             "@Module",
222             "class ScopedModule {",
223             "  @Provides @PerTest String string() { return \"a string\"; }", // incompatible scope
224             "  @Provides long integer() { return 0L; }", // unscoped - valid
225             "  @Provides @Singleton float floatingPoint() { return 0.0f; }", // same scope - valid
226             "  @Provides @Per(MyComponent.class) boolean bool() { return false; }", // incompatible
227             "}");
228 
229     Compilation compilation =
230         daggerCompiler()
231             .compile(componentFile, scopeFile, scopeWithAttribute, typeFile, moduleFile);
232     assertThat(compilation).failed();
233     assertThat(compilation)
234         .hadErrorContaining(
235             message(
236                 "MyComponent scoped with @Singleton "
237                     + "may not reference bindings with different scopes:",
238                 "    @PerTest class ScopedType",
239                 "    @Provides @PerTest String ScopedModule.string()",
240                 "    @Provides @Per(MyComponent.class) boolean "
241                     + "ScopedModule.bool()"))
242         .inFile(componentFile)
243         .onLineContaining("interface MyComponent");
244 
245     compilation =
246         compilerWithOptions("-Adagger.fullBindingGraphValidation=ERROR")
247             .compile(componentFile, scopeFile, scopeWithAttribute, typeFile, moduleFile);
248     // The @Inject binding for ScopedType should not appear here, but the @Singleton binding should.
249     assertThat(compilation)
250         .hadErrorContaining(
251             message(
252                 "ScopedModule contains bindings with different scopes:",
253                 "    @Provides @PerTest String ScopedModule.string()",
254                 "    @Provides @Singleton float ScopedModule.floatingPoint()",
255                 "    @Provides @Per(MyComponent.class) boolean "
256                     + "ScopedModule.bool()"))
257         .inFile(moduleFile)
258         .onLineContaining("class ScopedModule");
259   }
260 
261   @Test
fullBindingGraphValidationDoesNotReportForOneScope()262   public void fullBindingGraphValidationDoesNotReportForOneScope() {
263     Compilation compilation =
264         compilerWithOptions(
265                 "-Adagger.fullBindingGraphValidation=ERROR",
266                 "-Adagger.moduleHasDifferentScopesValidation=ERROR")
267             .compile(
268                 JavaFileObjects.forSourceLines(
269                     "test.TestModule",
270                     "package test;",
271                     "",
272                     "import dagger.Module;",
273                     "import dagger.Provides;",
274                     "import javax.inject.Singleton;",
275                     "",
276                     "@Module",
277                     "interface TestModule {",
278                     "  @Provides @Singleton static Object object() { return \"object\"; }",
279                     "  @Provides @Singleton static String string() { return \"string\"; }",
280                     "  @Provides static int integer() { return 4; }",
281                     "}"));
282     assertThat(compilation).succeededWithoutWarnings();
283   }
284 
285   @Test
fullBindingGraphValidationDoesNotReportInjectBindings()286   public void fullBindingGraphValidationDoesNotReportInjectBindings() {
287     Compilation compilation =
288         compilerWithOptions(
289                 "-Adagger.fullBindingGraphValidation=ERROR",
290                 "-Adagger.moduleHasDifferentScopesValidation=ERROR")
291             .compile(
292                 JavaFileObjects.forSourceLines(
293                     "test.UsedInRootRedScoped",
294                     "package test;",
295                     "",
296                     "import javax.inject.Inject;",
297                     "",
298                     "@RedScope",
299                     "final class UsedInRootRedScoped {",
300                     "  @Inject UsedInRootRedScoped() {}",
301                     "}"),
302                 JavaFileObjects.forSourceLines(
303                     "test.UsedInRootBlueScoped",
304                     "package test;",
305                     "",
306                     "import javax.inject.Inject;",
307                     "",
308                     "@BlueScope",
309                     "final class UsedInRootBlueScoped {",
310                     "  @Inject UsedInRootBlueScoped() {}",
311                     "}"),
312                 JavaFileObjects.forSourceLines(
313                     "test.RedScope",
314                     "package test;",
315                     "",
316                     "import javax.inject.Scope;",
317                     "",
318                     "@Scope",
319                     "@interface RedScope {}"),
320                 JavaFileObjects.forSourceLines(
321                     "test.BlueScope",
322                     "package test;",
323                     "",
324                     "import javax.inject.Scope;",
325                     "",
326                     "@Scope",
327                     "@interface BlueScope {}"),
328                 JavaFileObjects.forSourceLines(
329                     "test.TestModule",
330                     "package test;",
331                     "",
332                     "import dagger.Module;",
333                     "import dagger.Provides;",
334                     "import javax.inject.Singleton;",
335                     "",
336                     "@Module(subcomponents = Child.class)",
337                     "interface TestModule {",
338                     "  @Provides @Singleton",
339                     "  static Object object(",
340                     "      UsedInRootRedScoped usedInRootRedScoped,",
341                     "      UsedInRootBlueScoped usedInRootBlueScoped) {",
342                     "    return \"object\";",
343                     "  }",
344                     "}"),
345                 JavaFileObjects.forSourceLines(
346                     "test.Child",
347                     "package test;",
348                     "",
349                     "import dagger.Subcomponent;",
350                     "",
351                     "@Subcomponent",
352                     "interface Child {",
353                     "  UsedInChildRedScoped usedInChildRedScoped();",
354                     "  UsedInChildBlueScoped usedInChildBlueScoped();",
355                     "",
356                     "  @Subcomponent.Builder",
357                     "  interface Builder {",
358                     "    Child child();",
359                     "  }",
360                     "}"),
361                 JavaFileObjects.forSourceLines(
362                     "test.UsedInChildRedScoped",
363                     "package test;",
364                     "",
365                     "import javax.inject.Inject;",
366                     "",
367                     "@RedScope",
368                     "final class UsedInChildRedScoped {",
369                     "  @Inject UsedInChildRedScoped() {}",
370                     "}"),
371                 JavaFileObjects.forSourceLines(
372                     "test.UsedInChildBlueScoped",
373                     "package test;",
374                     "",
375                     "import javax.inject.Inject;",
376                     "",
377                     "@BlueScope",
378                     "final class UsedInChildBlueScoped {",
379                     "  @Inject UsedInChildBlueScoped() {}",
380                     "}"));
381     assertThat(compilation).succeededWithoutWarnings();
382   }
383 
384   @Test
componentWithScopeCanDependOnMultipleScopedComponents()385   public void componentWithScopeCanDependOnMultipleScopedComponents() {
386     // If a scoped component will have dependencies, they can include multiple scoped component
387     JavaFileObject type =
388         JavaFileObjects.forSourceLines(
389             "test.SimpleType",
390             "package test;",
391             "",
392             "import javax.inject.Inject;",
393             "",
394             "class SimpleType {",
395             "  @Inject SimpleType() {}",
396             "  static class A { @Inject A() {} }",
397             "  static class B { @Inject B() {} }",
398             "}");
399     JavaFileObject simpleScope =
400         JavaFileObjects.forSourceLines(
401             "test.SimpleScope",
402             "package test;",
403             "",
404             "import javax.inject.Scope;",
405             "",
406             "@Scope @interface SimpleScope {}");
407     JavaFileObject singletonScopedA =
408         JavaFileObjects.forSourceLines(
409             "test.SingletonComponentA",
410             "package test;",
411             "",
412             "import dagger.Component;",
413             "import javax.inject.Singleton;",
414             "",
415             "@Singleton",
416             "@Component",
417             "interface SingletonComponentA {",
418             "  SimpleType.A type();",
419             "}");
420     JavaFileObject singletonScopedB =
421         JavaFileObjects.forSourceLines(
422             "test.SingletonComponentB",
423             "package test;",
424             "",
425             "import dagger.Component;",
426             "import javax.inject.Singleton;",
427             "",
428             "@Singleton",
429             "@Component",
430             "interface SingletonComponentB {",
431             "  SimpleType.B type();",
432             "}");
433     JavaFileObject scopeless =
434         JavaFileObjects.forSourceLines(
435             "test.ScopelessComponent",
436             "package test;",
437             "",
438             "import dagger.Component;",
439             "",
440             "@Component",
441             "interface ScopelessComponent {",
442             "  SimpleType type();",
443             "}");
444     JavaFileObject simpleScoped =
445         JavaFileObjects.forSourceLines(
446             "test.SimpleScopedComponent",
447             "package test;",
448             "",
449             "import dagger.Component;",
450             "",
451             "@SimpleScope",
452             "@Component(dependencies = {SingletonComponentA.class, SingletonComponentB.class})",
453             "interface SimpleScopedComponent {",
454             "  SimpleType.A type();",
455             "}");
456 
457     Compilation compilation =
458         daggerCompiler()
459             .compile(
460                 type, simpleScope, simpleScoped, singletonScopedA, singletonScopedB, scopeless);
461     assertThat(compilation).succeededWithoutWarnings();
462   }
463 
464 
465 
466   // Tests the following component hierarchy:
467   //
468   //        @ScopeA
469   //        ComponentA
470   //        [SimpleType getSimpleType()]
471   //        /        \
472   //       /          \
473   //   @ScopeB         @ScopeB
474   //   ComponentB1     ComponentB2
475   //      \            [SimpleType getSimpleType()]
476   //       \          /
477   //        \        /
478   //         @ScopeC
479   //         ComponentC
480   //         [SimpleType getSimpleType()]
481   @Test
componentWithScopeCanDependOnMultipleScopedComponentsEvenDoingADiamond()482   public void componentWithScopeCanDependOnMultipleScopedComponentsEvenDoingADiamond() {
483     JavaFileObject type =
484         JavaFileObjects.forSourceLines(
485             "test.SimpleType",
486             "package test;",
487             "",
488             "import javax.inject.Inject;",
489             "",
490             "class SimpleType {",
491             "  @Inject SimpleType() {}",
492             "}");
493     JavaFileObject simpleScope =
494         JavaFileObjects.forSourceLines(
495             "test.SimpleScope",
496             "package test;",
497             "",
498             "import javax.inject.Scope;",
499             "",
500             "@Scope @interface SimpleScope {}");
501     JavaFileObject scopeA =
502         JavaFileObjects.forSourceLines(
503             "test.ScopeA",
504             "package test;",
505             "",
506             "import javax.inject.Scope;",
507             "",
508             "@Scope @interface ScopeA {}");
509     JavaFileObject scopeB =
510         JavaFileObjects.forSourceLines(
511             "test.ScopeB",
512             "package test;",
513             "",
514             "import javax.inject.Scope;",
515             "",
516             "@Scope @interface ScopeB {}");
517     JavaFileObject componentA =
518         JavaFileObjects.forSourceLines(
519             "test.ComponentA",
520             "package test;",
521             "",
522             "import dagger.Component;",
523             "",
524             "@ScopeA",
525             "@Component",
526             "interface ComponentA {",
527             "  SimpleType type();",
528             "}");
529     JavaFileObject componentB1 =
530         JavaFileObjects.forSourceLines(
531             "test.ComponentB1",
532             "package test;",
533             "",
534             "import dagger.Component;",
535             "",
536             "@ScopeB",
537             "@Component(dependencies = ComponentA.class)",
538             "interface ComponentB1 {",
539             "  SimpleType type();",
540             "}");
541     JavaFileObject componentB2 =
542         JavaFileObjects.forSourceLines(
543             "test.ComponentB2",
544             "package test;",
545             "",
546             "import dagger.Component;",
547             "",
548             "@ScopeB",
549             "@Component(dependencies = ComponentA.class)",
550             "interface ComponentB2 {",
551             "}");
552     JavaFileObject componentC =
553         JavaFileObjects.forSourceLines(
554             "test.ComponentC",
555             "package test;",
556             "",
557             "import dagger.Component;",
558             "",
559             "@SimpleScope",
560             "@Component(dependencies = {ComponentB1.class, ComponentB2.class})",
561             "interface ComponentC {",
562             "  SimpleType type();",
563             "}");
564 
565     Compilation compilation =
566         daggerCompiler()
567             .compile(
568                 type, simpleScope, scopeA, scopeB,
569                 componentA, componentB1, componentB2, componentC);
570     assertThat(compilation).succeededWithoutWarnings();
571   }
572 
573   @Test
componentWithoutScopeCannotDependOnScopedComponent()574   public void componentWithoutScopeCannotDependOnScopedComponent() {
575     JavaFileObject type =
576         JavaFileObjects.forSourceLines(
577             "test.SimpleType",
578             "package test;",
579             "",
580             "import javax.inject.Inject;",
581             "",
582             "class SimpleType {",
583             "  @Inject SimpleType() {}",
584             "}");
585     JavaFileObject scopedComponent =
586         JavaFileObjects.forSourceLines(
587             "test.ScopedComponent",
588             "package test;",
589             "",
590             "import dagger.Component;",
591             "import javax.inject.Singleton;",
592             "",
593             "@Singleton",
594             "@Component",
595             "interface ScopedComponent {",
596             "  SimpleType type();",
597             "}");
598     JavaFileObject unscopedComponent =
599         JavaFileObjects.forSourceLines(
600             "test.UnscopedComponent",
601             "package test;",
602             "",
603             "import dagger.Component;",
604             "import javax.inject.Singleton;",
605             "",
606             "@Component(dependencies = ScopedComponent.class)",
607             "interface UnscopedComponent {",
608             "  SimpleType type();",
609             "}");
610 
611     Compilation compilation = daggerCompiler().compile(type, scopedComponent, unscopedComponent);
612     assertThat(compilation).failed();
613     assertThat(compilation)
614         .hadErrorContaining(
615             message(
616                 "test.UnscopedComponent (unscoped) cannot depend on scoped components:",
617                 "    @Singleton test.ScopedComponent"));
618   }
619 
620   @Test
componentWithSingletonScopeMayNotDependOnOtherScope()621   public void componentWithSingletonScopeMayNotDependOnOtherScope() {
622     // Singleton must be the widest lifetime of present scopes.
623     JavaFileObject type =
624         JavaFileObjects.forSourceLines(
625             "test.SimpleType",
626             "package test;",
627             "",
628             "import javax.inject.Inject;",
629             "",
630             "class SimpleType {",
631             "  @Inject SimpleType() {}",
632             "}");
633     JavaFileObject simpleScope =
634         JavaFileObjects.forSourceLines(
635             "test.SimpleScope",
636             "package test;",
637             "",
638             "import javax.inject.Scope;",
639             "",
640             "@Scope @interface SimpleScope {}");
641     JavaFileObject simpleScoped =
642         JavaFileObjects.forSourceLines(
643             "test.SimpleScopedComponent",
644             "package test;",
645             "",
646             "import dagger.Component;",
647             "",
648             "@SimpleScope",
649             "@Component",
650             "interface SimpleScopedComponent {",
651             "  SimpleType type();",
652             "}");
653     JavaFileObject singletonScoped =
654         JavaFileObjects.forSourceLines(
655             "test.SingletonComponent",
656             "package test;",
657             "",
658             "import dagger.Component;",
659             "import javax.inject.Singleton;",
660             "",
661             "@Singleton",
662             "@Component(dependencies = SimpleScopedComponent.class)",
663             "interface SingletonComponent {",
664             "  SimpleType type();",
665             "}");
666 
667     Compilation compilation =
668         daggerCompiler().compile(type, simpleScope, simpleScoped, singletonScoped);
669     assertThat(compilation).failed();
670     assertThat(compilation)
671         .hadErrorContaining(
672             message(
673                 "This @Singleton component cannot depend on scoped components:",
674                 "    @test.SimpleScope test.SimpleScopedComponent"));
675   }
676 
677   @Test
componentScopeWithMultipleScopedDependenciesMustNotCycle()678   public void componentScopeWithMultipleScopedDependenciesMustNotCycle() {
679     JavaFileObject type =
680         JavaFileObjects.forSourceLines(
681             "test.SimpleType",
682             "package test;",
683             "",
684             "import javax.inject.Inject;",
685             "",
686             "class SimpleType {",
687             "  @Inject SimpleType() {}",
688             "}");
689     JavaFileObject scopeA =
690         JavaFileObjects.forSourceLines(
691             "test.ScopeA",
692             "package test;",
693             "",
694             "import javax.inject.Scope;",
695             "",
696             "@Scope @interface ScopeA {}");
697     JavaFileObject scopeB =
698         JavaFileObjects.forSourceLines(
699             "test.ScopeB",
700             "package test;",
701             "",
702             "import javax.inject.Scope;",
703             "",
704             "@Scope @interface ScopeB {}");
705     JavaFileObject longLifetime =
706         JavaFileObjects.forSourceLines(
707             "test.ComponentLong",
708             "package test;",
709             "",
710             "import dagger.Component;",
711             "",
712             "@ScopeA",
713             "@Component",
714             "interface ComponentLong {",
715             "  SimpleType type();",
716             "}");
717     JavaFileObject mediumLifetime1 =
718         JavaFileObjects.forSourceLines(
719             "test.ComponentMedium1",
720             "package test;",
721             "",
722             "import dagger.Component;",
723             "",
724             "@ScopeB",
725             "@Component(dependencies = ComponentLong.class)",
726             "interface ComponentMedium1 {",
727             "  SimpleType type();",
728             "}");
729     JavaFileObject mediumLifetime2 =
730         JavaFileObjects.forSourceLines(
731             "test.ComponentMedium2",
732             "package test;",
733             "",
734             "import dagger.Component;",
735             "",
736             "@ScopeB",
737             "@Component",
738             "interface ComponentMedium2 {",
739             "}");
740     JavaFileObject shortLifetime =
741         JavaFileObjects.forSourceLines(
742             "test.ComponentShort",
743             "package test;",
744             "",
745             "import dagger.Component;",
746             "",
747             "@ScopeA",
748             "@Component(dependencies = {ComponentMedium1.class, ComponentMedium2.class})",
749             "interface ComponentShort {",
750             "  SimpleType type();",
751             "}");
752 
753     Compilation compilation =
754         daggerCompiler()
755             .compile(
756                 type, scopeA, scopeB,
757                 longLifetime, mediumLifetime1, mediumLifetime2, shortLifetime);
758     assertThat(compilation).failed();
759     assertThat(compilation)
760         .hadErrorContaining(
761             message(
762                 "test.ComponentShort depends on scoped components in a non-hierarchical scope "
763                     + "ordering:",
764                 "    @test.ScopeA test.ComponentLong",
765                 "    @test.ScopeB test.ComponentMedium1",
766                 "    @test.ScopeA test.ComponentShort"));
767   }
768 
769   @Test
componentScopeAncestryMustNotCycle()770   public void componentScopeAncestryMustNotCycle() {
771     // The dependency relationship of components is necessarily from shorter lifetimes to
772     // longer lifetimes.  The scoping annotations must reflect this, and so one cannot declare
773     // scopes on components such that they cycle.
774     JavaFileObject type =
775         JavaFileObjects.forSourceLines(
776             "test.SimpleType",
777             "package test;",
778             "",
779             "import javax.inject.Inject;",
780             "",
781             "class SimpleType {",
782             "  @Inject SimpleType() {}",
783             "}");
784     JavaFileObject scopeA =
785         JavaFileObjects.forSourceLines(
786             "test.ScopeA",
787             "package test;",
788             "",
789             "import javax.inject.Scope;",
790             "",
791             "@Scope @interface ScopeA {}");
792     JavaFileObject scopeB =
793         JavaFileObjects.forSourceLines(
794             "test.ScopeB",
795             "package test;",
796             "",
797             "import javax.inject.Scope;",
798             "",
799             "@Scope @interface ScopeB {}");
800     JavaFileObject longLifetime =
801         JavaFileObjects.forSourceLines(
802             "test.ComponentLong",
803             "package test;",
804             "",
805             "import dagger.Component;",
806             "",
807             "@ScopeA",
808             "@Component",
809             "interface ComponentLong {",
810             "  SimpleType type();",
811             "}");
812     JavaFileObject mediumLifetime =
813         JavaFileObjects.forSourceLines(
814             "test.ComponentMedium",
815             "package test;",
816             "",
817             "import dagger.Component;",
818             "",
819             "@ScopeB",
820             "@Component(dependencies = ComponentLong.class)",
821             "interface ComponentMedium {",
822             "  SimpleType type();",
823             "}");
824     JavaFileObject shortLifetime =
825         JavaFileObjects.forSourceLines(
826             "test.ComponentShort",
827             "package test;",
828             "",
829             "import dagger.Component;",
830             "",
831             "@ScopeA",
832             "@Component(dependencies = ComponentMedium.class)",
833             "interface ComponentShort {",
834             "  SimpleType type();",
835             "}");
836 
837     Compilation compilation =
838         daggerCompiler().compile(type, scopeA, scopeB, longLifetime, mediumLifetime, shortLifetime);
839     assertThat(compilation).failed();
840     assertThat(compilation)
841         .hadErrorContaining(
842             message(
843                 "test.ComponentShort depends on scoped components in a non-hierarchical scope "
844                     + "ordering:",
845                 "    @test.ScopeA test.ComponentLong",
846                 "    @test.ScopeB test.ComponentMedium",
847                 "    @test.ScopeA test.ComponentShort"));
848 
849 
850     // Test that compilation succeeds when transitive validation is disabled because the scope cycle
851     // cannot be detected.
852     compilation =
853         compilerWithOptions("-Adagger.validateTransitiveComponentDependencies=DISABLED")
854             .compile(type, scopeA, scopeB, longLifetime, mediumLifetime, shortLifetime);
855     assertThat(compilation).succeeded();
856   }
857 
858   @Test
reusableNotAllowedOnComponent()859   public void reusableNotAllowedOnComponent() {
860     JavaFileObject someComponent =
861         JavaFileObjects.forSourceLines(
862             "test.SomeComponent",
863             "package test;",
864             "",
865             "import dagger.Component;",
866             "import dagger.Reusable;",
867             "",
868             "@Reusable",
869             "@Component",
870             "interface SomeComponent {}");
871     Compilation compilation = daggerCompiler().compile(someComponent);
872     assertThat(compilation).failed();
873     assertThat(compilation)
874         .hadErrorContaining("@Reusable cannot be applied to components or subcomponents")
875         .inFile(someComponent)
876         .onLine(6);
877   }
878 
879   @Test
reusableNotAllowedOnSubcomponent()880   public void reusableNotAllowedOnSubcomponent() {
881     JavaFileObject someSubcomponent =
882         JavaFileObjects.forSourceLines(
883             "test.SomeComponent",
884             "package test;",
885             "",
886             "import dagger.Reusable;",
887             "import dagger.Subcomponent;",
888             "",
889             "@Reusable",
890             "@Subcomponent",
891             "interface SomeSubcomponent {}");
892     Compilation compilation = daggerCompiler().compile(someSubcomponent);
893     assertThat(compilation).failed();
894     assertThat(compilation)
895         .hadErrorContaining("@Reusable cannot be applied to components or subcomponents")
896         .inFile(someSubcomponent)
897         .onLine(6);
898   }
899 }
900