• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
20 import static dagger.internal.codegen.base.ComponentCreatorAnnotation.SUBCOMPONENT_BUILDER;
21 import static dagger.internal.codegen.base.ComponentCreatorAnnotation.SUBCOMPONENT_FACTORY;
22 import static dagger.internal.codegen.base.ComponentCreatorKind.BUILDER;
23 import static dagger.internal.codegen.base.ComponentCreatorKind.FACTORY;
24 import static dagger.internal.codegen.base.ComponentKind.SUBCOMPONENT;
25 import static dagger.internal.codegen.binding.ErrorMessages.ComponentCreatorMessages.moreThanOneRefToSubcomponent;
26 import static dagger.internal.codegen.binding.ErrorMessages.componentMessagesFor;
27 
28 import androidx.room.compiler.processing.util.Source;
29 import com.google.common.collect.ImmutableList;
30 import dagger.internal.codegen.base.ComponentCreatorAnnotation;
31 import dagger.testing.compile.CompilerTests;
32 import java.util.Collection;
33 import org.junit.Test;
34 import org.junit.runner.RunWith;
35 import org.junit.runners.Parameterized;
36 import org.junit.runners.Parameterized.Parameters;
37 
38 /** Tests for {@link dagger.Subcomponent.Builder} validation. */
39 @RunWith(Parameterized.class)
40 public class SubcomponentCreatorValidationTest extends ComponentCreatorTestHelper {
41   @Parameters(name = "creatorKind={0}")
parameters()42   public static Collection<Object[]> parameters() {
43     return ImmutableList.copyOf(new Object[][] {{SUBCOMPONENT_BUILDER}, {SUBCOMPONENT_FACTORY}});
44   }
45 
SubcomponentCreatorValidationTest(ComponentCreatorAnnotation componentCreatorAnnotation)46   public SubcomponentCreatorValidationTest(ComponentCreatorAnnotation componentCreatorAnnotation) {
47     super(DEFAULT_MODE, componentCreatorAnnotation);
48   }
49 
50   @Test
testRefSubcomponentAndSubCreatorFails()51   public void testRefSubcomponentAndSubCreatorFails() {
52     Source componentFile =
53         preprocessedJavaSource(
54             "test.ParentComponent",
55             "package test;",
56             "",
57             "import dagger.Component;",
58             "import javax.inject.Provider;",
59             "",
60             "@Component",
61             "interface ParentComponent {",
62             "  ChildComponent child();",
63             "  ChildComponent.Builder childComponentBuilder();",
64             "}");
65     Source childComponentFile = preprocessedJavaSource("test.ChildComponent",
66         "package test;",
67         "",
68         "import dagger.Subcomponent;",
69         "",
70         "@Subcomponent",
71         "interface ChildComponent {",
72         "  @Subcomponent.Builder",
73         "  static interface Builder {",
74         "    ChildComponent build();",
75         "  }",
76         "}");
77     CompilerTests.daggerCompiler(componentFile, childComponentFile)
78         .compile(
79             subject -> {
80               subject.hasErrorCount(1);
81               subject.hasErrorContaining(
82                       String.format(
83                           moreThanOneRefToSubcomponent(),
84                           "test.ChildComponent",
85                           process("["
86                               + "test.ParentComponent.child(), "
87                               + "test.ParentComponent.childComponentBuilder()"
88                               + "]")))
89                   .onSource(componentFile);
90             });
91   }
92 
93   @Test
testRefSubCreatorTwiceFails()94   public void testRefSubCreatorTwiceFails() {
95     Source componentFile = preprocessedJavaSource("test.ParentComponent",
96         "package test;",
97         "",
98         "import dagger.Component;",
99         "import javax.inject.Provider;",
100         "",
101         "@Component",
102         "interface ParentComponent {",
103         "  ChildComponent.Builder builder1();",
104         "  ChildComponent.Builder builder2();",
105         "}");
106     Source childComponentFile = preprocessedJavaSource("test.ChildComponent",
107         "package test;",
108         "",
109         "import dagger.Subcomponent;",
110         "",
111         "@Subcomponent",
112         "interface ChildComponent {",
113         "  @Subcomponent.Builder",
114         "  static interface Builder {",
115         "    ChildComponent build();",
116         "  }",
117         "}");
118     CompilerTests.daggerCompiler(componentFile, childComponentFile)
119         .compile(
120             subject -> {
121               subject.hasErrorCount(1);
122               subject.hasErrorContaining(
123                       String.format(
124                           moreThanOneRefToSubcomponent(),
125                           "test.ChildComponent",
126                           process("["
127                               + "test.ParentComponent.builder1(), "
128                               + "test.ParentComponent.builder2()"
129                               + "]")))
130                   .onSource(componentFile);
131             });
132   }
133 
134   @Test
testMoreThanOneCreatorFails()135   public void testMoreThanOneCreatorFails() {
136     Source componentFile = preprocessedJavaSource("test.ParentComponent",
137         "package test;",
138         "",
139         "import dagger.Component;",
140         "import javax.inject.Provider;",
141         "",
142         "@Component",
143         "interface ParentComponent {",
144         "  ChildComponent.Builder1 build();",
145         "}");
146     Source childComponentFile = preprocessedJavaSource("test.ChildComponent",
147         "package test;",
148         "",
149         "import dagger.Subcomponent;",
150         "",
151         "@Subcomponent",
152         "interface ChildComponent {",
153         "  @Subcomponent.Builder",
154         "  static interface Builder1 {",
155         "    ChildComponent build();",
156         "  }",
157         "",
158         "  @Subcomponent.Builder",
159         "  static interface Builder2 {",
160         "    ChildComponent build();",
161         "  }",
162         "}");
163     CompilerTests.daggerCompiler(componentFile, childComponentFile)
164         .compile(
165             subject -> {
166               subject.hasErrorCount(1);
167               subject.hasErrorContaining(
168                       String.format(
169                           componentMessagesFor(SUBCOMPONENT).moreThanOne(),
170                           process("[test.ChildComponent.Builder1, test.ChildComponent.Builder2]")))
171                   .onSource(childComponentFile);
172             });
173   }
174 
175   @Test
testMoreThanOneCreatorFails_differentTypes()176   public void testMoreThanOneCreatorFails_differentTypes() {
177     Source componentFile = preprocessedJavaSource("test.ParentComponent",
178         "package test;",
179         "",
180         "import dagger.Component;",
181         "import javax.inject.Provider;",
182         "",
183         "@Component",
184         "interface ParentComponent {",
185         "  ChildComponent.Builder build();",
186         "}");
187     Source childComponentFile =
188         CompilerTests.javaSource("test.ChildComponent",
189         "package test;",
190         "",
191         "import dagger.Subcomponent;",
192         "",
193         "@Subcomponent",
194         "interface ChildComponent {",
195         "  @Subcomponent.Builder",
196         "  static interface Builder {",
197         "    ChildComponent build();",
198         "  }",
199         "",
200         "  @Subcomponent.Factory",
201         "  static interface Factory {",
202         "    ChildComponent create();",
203         "  }",
204         "}");
205     CompilerTests.daggerCompiler(componentFile, childComponentFile)
206         .compile(
207             subject -> {
208               subject.hasErrorCount(1);
209               subject.hasErrorContaining(
210                       String.format(
211                           componentMessagesFor(SUBCOMPONENT).moreThanOne(),
212                           "[test.ChildComponent.Builder, test.ChildComponent.Factory]"))
213                   .onSource(childComponentFile);
214             });
215   }
216 
217   @Test
testCreatorGenericsFails()218   public void testCreatorGenericsFails() {
219     Source componentFile = preprocessedJavaSource("test.ParentComponent",
220         "package test;",
221         "",
222         "import dagger.Component;",
223         "import javax.inject.Provider;",
224         "",
225         "@Component",
226         "interface ParentComponent {",
227         "  ChildComponent.Builder build();",
228         "}");
229     Source childComponentFile = preprocessedJavaSource("test.ChildComponent",
230         "package test;",
231         "",
232         "import dagger.Subcomponent;",
233         "",
234         "@Subcomponent",
235         "interface ChildComponent {",
236         "  @Subcomponent.Builder",
237         "  interface Builder<T> {",
238         "     ChildComponent build();",
239         "  }",
240         "}");
241     CompilerTests.daggerCompiler(componentFile, childComponentFile)
242         .compile(
243             subject -> {
244               subject.hasErrorCount(1);
245               subject.hasErrorContaining(messages.generics()).onSource(childComponentFile);
246             });
247   }
248 
249   @Test
testCreatorNotInComponentFails()250   public void testCreatorNotInComponentFails() {
251     Source builder = preprocessedJavaSource("test.Builder",
252         "package test;",
253         "",
254         "import dagger.Subcomponent;",
255         "",
256         "@Subcomponent.Builder",
257         "interface Builder {}");
258     CompilerTests.daggerCompiler(builder)
259         .compile(
260             subject -> {
261               subject.hasErrorCount(1);
262               subject.hasErrorContaining(messages.mustBeInComponent()).onSource(builder);
263             });
264   }
265 
266   @Test
testCreatorMissingFactoryMethodFails()267   public void testCreatorMissingFactoryMethodFails() {
268     Source componentFile =
269         preprocessedJavaSource(
270             "test.ParentComponent",
271             "package test;",
272             "",
273             "import dagger.Component;",
274             "import javax.inject.Provider;",
275             "",
276             "@Component",
277             "interface ParentComponent {",
278             "  ChildComponent.Builder childComponentBuilder();",
279             "}");
280     Source childComponentFile = preprocessedJavaSource("test.ChildComponent",
281         "package test;",
282         "",
283         "import dagger.Subcomponent;",
284         "",
285         "@Subcomponent",
286         "interface ChildComponent {",
287         "  @Subcomponent.Builder",
288         "  interface Builder {}",
289         "}");
290     CompilerTests.daggerCompiler(componentFile, childComponentFile)
291         .compile(
292             subject -> {
293               subject.hasErrorCount(1);
294               subject.hasErrorContaining(messages.missingFactoryMethod())
295                   .onSource(childComponentFile);
296             });
297   }
298 
299   @Test
testPrivateCreatorFails()300   public void testPrivateCreatorFails() {
301     Source childComponentFile = preprocessedJavaSource("test.ChildComponent",
302         "package test;",
303         "",
304         "import dagger.Subcomponent;",
305         "",
306         "@Subcomponent",
307         "abstract class ChildComponent {",
308         "  @Subcomponent.Builder",
309         "  private interface Builder {}",
310         "}");
311     CompilerTests.daggerCompiler(childComponentFile)
312         .compile(
313             subject -> {
314               subject.hasErrorCount(1);
315               subject.hasErrorContaining(messages.isPrivate()).onSource(childComponentFile);
316             });
317   }
318 
319   @Test
testNonStaticCreatorFails()320   public void testNonStaticCreatorFails() {
321     Source childComponentFile = preprocessedJavaSource("test.ChildComponent",
322         "package test;",
323         "",
324         "import dagger.Subcomponent;",
325         "",
326         "@Subcomponent",
327         "abstract class ChildComponent {",
328         "  @Subcomponent.Builder",
329         "  abstract class Builder {}",
330         "}");
331     CompilerTests.daggerCompiler(childComponentFile)
332         .compile(
333             subject -> {
334               subject.hasErrorCount(1);
335               subject.hasErrorContaining(messages.mustBeStatic()).onSource(childComponentFile);
336             });
337   }
338 
339   @Test
testNonAbstractCreatorFails()340   public void testNonAbstractCreatorFails() {
341     Source childComponentFile = preprocessedJavaSource("test.ChildComponent",
342         "package test;",
343         "",
344         "import dagger.Subcomponent;",
345         "",
346         "@Subcomponent",
347         "abstract class ChildComponent {",
348         "  @Subcomponent.Builder",
349         "  static class Builder {}",
350         "}");
351     CompilerTests.daggerCompiler(childComponentFile)
352         .compile(
353             subject -> {
354               subject.hasErrorCount(1);
355               subject.hasErrorContaining(messages.mustBeAbstract())
356                   .onSource(childComponentFile);
357             });
358   }
359 
360   @Test
testCreatorOneConstructorWithArgsFails()361   public void testCreatorOneConstructorWithArgsFails() {
362     Source childComponentFile = preprocessedJavaSource("test.ChildComponent",
363         "package test;",
364         "",
365         "import dagger.Subcomponent;",
366         "",
367         "@Subcomponent",
368         "abstract class ChildComponent {",
369         "  @Subcomponent.Builder",
370         "  static abstract class Builder {",
371         "    Builder(String unused) {}",
372         "    abstract ChildComponent build();",
373         "  }",
374         "}");
375     CompilerTests.daggerCompiler(childComponentFile)
376         .compile(
377             subject -> {
378               subject.hasErrorCount(1);
379               subject.hasErrorContaining(messages.invalidConstructor())
380                   .onSource(childComponentFile);
381             });
382   }
383 
384   @Test
testCreatorMoreThanOneConstructorFails()385   public void testCreatorMoreThanOneConstructorFails() {
386     Source childComponentFile = preprocessedJavaSource("test.ChildComponent",
387         "package test;",
388         "",
389         "import dagger.Subcomponent;",
390         "",
391         "@Subcomponent",
392         "abstract class ChildComponent {",
393         "  @Subcomponent.Builder",
394         "  static abstract class Builder {",
395         "    Builder() {}",
396         "    Builder(String unused) {}",
397         "    abstract ChildComponent build();",
398         "  }",
399         "}");
400     CompilerTests.daggerCompiler(childComponentFile)
401         .compile(
402             subject -> {
403               subject.hasErrorCount(1);
404               subject.hasErrorContaining(messages.invalidConstructor())
405                   .onSource(childComponentFile);
406             });
407   }
408 
409   @Test
testCreatorEnumFails()410   public void testCreatorEnumFails() {
411     Source childComponentFile = preprocessedJavaSource("test.ChildComponent",
412         "package test;",
413         "",
414         "import dagger.Subcomponent;",
415         "",
416         "@Subcomponent",
417         "abstract class ChildComponent {",
418         "  @Subcomponent.Builder",
419         "  enum Builder {}",
420         "}");
421     CompilerTests.daggerCompiler(childComponentFile)
422         .compile(
423             subject -> {
424               subject.hasErrorCount(1);
425               subject.hasErrorContaining(messages.mustBeClassOrInterface())
426                   .onSource(childComponentFile);
427             });
428   }
429 
430   @Test
testCreatorFactoryMethodReturnsWrongTypeFails()431   public void testCreatorFactoryMethodReturnsWrongTypeFails() {
432     Source childComponentFile = preprocessedJavaSource("test.ChildComponent",
433         "package test;",
434         "",
435         "import dagger.Subcomponent;",
436         "",
437         "@Subcomponent",
438         "abstract class ChildComponent {",
439         "  @Subcomponent.Builder",
440         "  interface Builder {",
441         "    String build();",
442         "  }",
443         "}");
444     CompilerTests.daggerCompiler(childComponentFile)
445         .compile(
446             subject -> {
447               subject.hasErrorCount(1);
448               subject.hasErrorContaining(messages.factoryMethodMustReturnComponentType())
449                   .onSource(childComponentFile)
450                   .onLine(9);
451             });
452   }
453 
454   @Test
testInheritedCreatorFactoryMethodReturnsWrongTypeFails()455   public void testInheritedCreatorFactoryMethodReturnsWrongTypeFails() {
456     Source childComponentFile = preprocessedJavaSource("test.ChildComponent",
457         "package test;",
458         "",
459         "import dagger.Subcomponent;",
460         "",
461         "@Subcomponent",
462         "abstract class ChildComponent {",
463         "  interface Parent {",
464         "    String build();",
465         "  }",
466         "",
467         "  @Subcomponent.Builder",
468         "  interface Builder extends Parent {}",
469         "}");
470 
471     CompilerTests.daggerCompiler(childComponentFile)
472         .compile(
473             subject -> {
474               subject.hasErrorCount(1);
475               subject.hasErrorContaining(
476                       String.format(
477                           messages.inheritedFactoryMethodMustReturnComponentType(),
478                           process("String test.ChildComponent.Parent.build()")))
479                   .onSource(childComponentFile)
480                   .onLine(12);
481             });
482   }
483 
484   @Test
testTwoFactoryMethodsFails()485   public void testTwoFactoryMethodsFails() {
486     Source childComponentFile = preprocessedJavaSource("test.ChildComponent",
487         "package test;",
488         "",
489         "import dagger.Subcomponent;",
490         "",
491         "@Subcomponent",
492         "abstract class ChildComponent {",
493         "  @Subcomponent.Builder",
494         "  interface Builder {",
495         "    ChildComponent build();",
496         "    ChildComponent build1();",
497         "  }",
498         "}");
499 
500     CompilerTests.daggerCompiler(childComponentFile)
501         .compile(
502             subject -> {
503               subject.hasErrorCount(1);
504               subject.hasErrorContaining(
505                       String.format(
506                           messages.twoFactoryMethods(),
507                           process("test.ChildComponent test.ChildComponent.Builder.build()")))
508                   .onSource(childComponentFile)
509                   .onLine(10);
510             });
511   }
512 
513   @Test
testInheritedTwoFactoryMethodsFails()514   public void testInheritedTwoFactoryMethodsFails() {
515     Source childComponentFile = preprocessedJavaSource("test.ChildComponent",
516         "package test;",
517         "",
518         "import dagger.Subcomponent;",
519         "",
520         "@Subcomponent",
521         "abstract class ChildComponent {",
522         "  interface Parent {",
523         "    ChildComponent build();",
524         "    ChildComponent build1();",
525         "  }",
526         "",
527         "  @Subcomponent.Builder",
528         "  interface Builder extends Parent {}",
529         "}");
530 
531     CompilerTests.daggerCompiler(childComponentFile)
532         .compile(
533             subject -> {
534               subject.hasErrorCount(1);
535               subject.hasErrorContaining(
536                       String.format(
537                           messages.inheritedTwoFactoryMethods(),
538                           process("test.ChildComponent test.ChildComponent.Parent.build()"),
539                           process("test.ChildComponent test.ChildComponent.Parent.build1()")))
540                   .onSource(childComponentFile)
541                   .onLine(13);
542             });
543   }
544 
545   @Test
testMultipleSettersPerTypeFails()546   public void testMultipleSettersPerTypeFails() {
547     Source moduleFile =
548         CompilerTests.javaSource(
549             "test.TestModule",
550             "package test;",
551             "",
552             "import dagger.Module;",
553             "import dagger.Provides;",
554             "",
555             "@Module",
556             "final class TestModule {",
557             "  @Provides String s() { return \"\"; }",
558             "}");
559     Source componentFile =
560         preprocessedJavaSource(
561             "test.ParentComponent",
562             "package test;",
563             "",
564             "import dagger.Component;",
565             "",
566             "@Component",
567             "interface ParentComponent {",
568             "  ChildComponent.Builder childComponentBuilder();",
569             "}");
570     Source childComponentFile =
571         javaFileBuilder("test.ChildComponent")
572             .addLines(
573                 "package test;",
574                 "",
575                 "import dagger.Subcomponent;",
576                 "import javax.inject.Provider;",
577                 "",
578                 "@Subcomponent(modules = TestModule.class)",
579                 "abstract class ChildComponent {",
580                 "  abstract String s();",
581                 "")
582             .addLinesIf(
583                 BUILDER,
584                 "  @Subcomponent.Builder",
585                 "  interface Builder {",
586                 "    ChildComponent build();",
587                 "    void set1(TestModule s);",
588                 "    void set2(TestModule s);",
589                 "  }")
590             .addLinesIf(
591                 FACTORY,
592                 "  @Subcomponent.Factory",
593                 "  interface Factory {",
594                 "    ChildComponent create(TestModule m1, TestModule m2);",
595                 "  }")
596             .addLines( //
597                 "}")
598             .buildSource();
599 
600     String elements =
601         creatorKind.equals(BUILDER)
602             ? "[void test.ChildComponent.Builder.set1(test.TestModule), "
603                 + "void test.ChildComponent.Builder.set2(test.TestModule)]"
604             : "[test.TestModule m1, test.TestModule m2]";
605     CompilerTests.daggerCompiler(moduleFile, componentFile, childComponentFile)
606         .compile(
607             subject -> {
608               subject.hasErrorCount(1);
609               subject.hasErrorContaining(
610                   String.format(
611                       messages.multipleSettersForModuleOrDependencyType(),
612                       "test.TestModule",
613                       elements))
614                   .onSource(childComponentFile)
615                   .onLine(11);
616             });
617   }
618 
619   @Test
testMultipleSettersPerTypeIncludingResolvedGenericsFails()620   public void testMultipleSettersPerTypeIncludingResolvedGenericsFails() {
621     Source moduleFile =
622         CompilerTests.javaSource(
623             "test.TestModule",
624             "package test;",
625             "",
626             "import dagger.Module;",
627             "import dagger.Provides;",
628             "",
629             "@Module",
630             "final class TestModule {",
631             "  @Provides String s() { return \"\"; }",
632             "}");
633     Source componentFile =
634         preprocessedJavaSource(
635             "test.ParentComponent",
636             "package test;",
637             "",
638             "import dagger.Component;",
639             "",
640             "@Component",
641             "interface ParentComponent {",
642             "  ChildComponent.Builder childComponentBuilder();",
643             "}");
644     Source childComponentFile =
645         javaFileBuilder("test.ChildComponent")
646             .addLines(
647                 "package test;",
648                 "",
649                 "import dagger.Subcomponent;",
650                 "import javax.inject.Provider;",
651                 "",
652                 "@Subcomponent(modules = TestModule.class)",
653                 "abstract class ChildComponent {",
654                 "  abstract String s();",
655                 "")
656             .addLinesIf(
657                 BUILDER,
658                 "  interface Parent<T> {",
659                 "    void set1(T t);",
660                 "  }",
661                 "",
662                 "  @Subcomponent.Builder",
663                 "  interface Builder extends Parent<TestModule> {",
664                 "    ChildComponent build();",
665                 "    void set2(TestModule s);",
666                 "  }")
667             .addLinesIf(
668                 FACTORY,
669                 "  interface Parent<C, T> {",
670                 "    C create(TestModule m1, T t);",
671                 "  }",
672                 "",
673                 "  @Subcomponent.Factory",
674                 "  interface Factory extends Parent<ChildComponent, TestModule> {}")
675             .addLines( //
676                 "}")
677             .buildSource();
678 
679     String elements =
680         creatorKind.equals(BUILDER)
681             ? "[void test.ChildComponent.Builder.set1(test.TestModule), "
682                 + "void test.ChildComponent.Builder.set2(test.TestModule)]"
683             : "[test.TestModule m1, test.TestModule t]";
684     CompilerTests.daggerCompiler(moduleFile, componentFile, childComponentFile)
685         .compile(
686             subject -> {
687               subject.hasErrorCount(1);
688               subject.hasErrorContaining(
689                   String.format(
690                       messages.multipleSettersForModuleOrDependencyType(),
691                       "test.TestModule",
692                       elements))
693                   .onSource(childComponentFile)
694                   .onLine(15);
695             });
696   }
697 
698   @Test
testMultipleSettersPerBoundInstanceTypeFails()699   public void testMultipleSettersPerBoundInstanceTypeFails() {
700     Source componentFile =
701         preprocessedJavaSource(
702             "test.ParentComponent",
703             "package test;",
704             "",
705             "import dagger.Component;",
706             "",
707             "@Component",
708             "interface ParentComponent {",
709             "  ChildComponent.Builder childComponentBuilder();",
710             "}");
711     Source childComponentFile =
712         javaFileBuilder("test.ChildComponent")
713             .addLines(
714                 "package test;",
715                 "",
716                 "import dagger.BindsInstance;",
717                 "import dagger.Subcomponent;",
718                 "",
719                 "@Subcomponent",
720                 "abstract class ChildComponent {",
721                 "  abstract String s();",
722                 "")
723             .addLinesIf(
724                 BUILDER,
725                 "  @Subcomponent.Builder",
726                 "  interface Builder {",
727                 "    ChildComponent build();",
728                 "    @BindsInstance void set1(String s);",
729                 "    @BindsInstance void set2(String s);",
730                 "  }")
731             .addLinesIf(
732                 FACTORY,
733                 "  @Subcomponent.Factory",
734                 "  interface Factory {",
735                 "    ChildComponent create(",
736                 "        @BindsInstance String s1, @BindsInstance String s2);",
737                 "  }")
738             .addLines( //
739                 "}")
740             .buildSource();
741 
742     String firstBinding = creatorKind.equals(FACTORY)
743         ? "ChildComponent.Factory.create(s1, …)"
744         : "@BindsInstance void ChildComponent.Builder.set1(String)";
745     String secondBinding = creatorKind.equals(FACTORY)
746         ? "ChildComponent.Factory.create(…, s2)"
747         : "@BindsInstance void ChildComponent.Builder.set2(String)";
748     CompilerTests.daggerCompiler(componentFile, childComponentFile)
749         .compile(
750             subject -> {
751               subject.hasErrorCount(1);
752               subject.hasErrorContaining("String is bound multiple times:");
753               subject.hasErrorContaining("    " + firstBinding);
754               subject.hasErrorContaining("    " + secondBinding);
755               subject.hasErrorContaining("    in component: [ParentComponent → ChildComponent]")
756                   .onSource(componentFile)
757                   .onLineContaining("interface ParentComponent {");
758             });
759   }
760 
761   @Test
testExtraSettersFails()762   public void testExtraSettersFails() {
763     Source componentFile = preprocessedJavaSource("test.ParentComponent",
764         "package test;",
765         "",
766         "import dagger.Component;",
767         "import javax.inject.Provider;",
768         "",
769         "@Component",
770         "interface ParentComponent {",
771         "  ChildComponent.Builder build();",
772         "}");
773     Source childComponentFile =
774         javaFileBuilder("test.ChildComponent")
775             .addLines(
776                 "package test;",
777                 "",
778                 "import dagger.Subcomponent;",
779                 "import javax.inject.Provider;",
780                 "",
781                 "@Subcomponent",
782                 "abstract class ChildComponent {")
783             .addLinesIf(
784                 BUILDER,
785                 "  @Subcomponent.Builder",
786                 "  interface Builder {",
787                 "    ChildComponent build();",
788                 "    void set1(String s);",
789                 "    void set2(Integer s);",
790                 "  }")
791             .addLinesIf(
792                 FACTORY,
793                 "  @Subcomponent.Factory",
794                 "  interface Factory {",
795                 "    ChildComponent create(String s, Integer i);",
796                 "  }")
797             .addLines("}")
798             .buildSource();
799 
800     String elements =
801         creatorKind.equals(FACTORY)
802             ? "[String s, Integer i]"
803             : "[void test.ChildComponent.Builder.set1(String),"
804                 + " void test.ChildComponent.Builder.set2(Integer)]";
805     CompilerTests.daggerCompiler(componentFile, childComponentFile)
806         .compile(
807             subject -> {
808               subject.hasErrorCount(1);
809               subject.hasErrorContaining(String.format(messages.extraSetters(), elements))
810                   .onSource(childComponentFile)
811                   .onLine(9);
812             });
813   }
814 
815   @Test
testMissingSettersFail()816   public void testMissingSettersFail() {
817     Source moduleFile =
818         CompilerTests.javaSource(
819             "test.TestModule",
820             "package test;",
821             "",
822             "import dagger.Module;",
823             "import dagger.Provides;",
824             "",
825             "@Module",
826             "final class TestModule {",
827             "  TestModule(String unused) {}",
828             "  @Provides String s() { return null; }",
829             "}");
830     Source module2File =
831         CompilerTests.javaSource(
832             "test.Test2Module",
833             "package test;",
834             "",
835             "import dagger.Module;",
836             "import dagger.Provides;",
837             "",
838             "@Module",
839             "final class Test2Module {",
840             "  @Provides Integer i() { return null; }",
841             "}");
842     Source module3File =
843         CompilerTests.javaSource(
844             "test.Test3Module",
845             "package test;",
846             "",
847             "import dagger.Module;",
848             "import dagger.Provides;",
849             "",
850             "@Module",
851             "final class Test3Module {",
852             "  Test3Module(String unused) {}",
853             "  @Provides Double d() { return null; }",
854             "}");
855     Source componentFile =
856         preprocessedJavaSource(
857             "test.ParentComponent",
858             "package test;",
859             "",
860             "import dagger.Component;",
861             "import javax.inject.Provider;",
862             "",
863             "@Component",
864             "interface ParentComponent {",
865             "  ChildComponent.Builder build();",
866             "}");
867     Source childComponentFile =
868         preprocessedJavaSource(
869             "test.ChildComponent",
870             "package test;",
871             "",
872             "import dagger.Subcomponent;",
873             "",
874             "@Subcomponent(modules = {TestModule.class, Test2Module.class, Test3Module.class})",
875             "interface ChildComponent {",
876             "  String string();",
877             "  Integer integer();",
878             "",
879             "  @Subcomponent.Builder",
880             "  interface Builder {",
881             "    ChildComponent build();",
882             "  }",
883             "}");
884 
885     CompilerTests.daggerCompiler(
886             moduleFile, module2File, module3File, componentFile, childComponentFile)
887         .compile(
888             subject -> {
889               subject.hasErrorCount(1);
890               subject.hasErrorContaining(
891                       // Ignores Test2Module because we can construct it ourselves.
892                       // TODO(sameb): Ignore Test3Module because it's not used within transitive
893                       // dependencies.
894                       String.format(
895                           messages.missingSetters(),
896                           "[test.TestModule, test.Test3Module]"))
897                   .onSource(childComponentFile)
898                   .onLine(11);
899             });
900   }
901 
902   @Test
covariantFactoryMethodReturnType()903   public void covariantFactoryMethodReturnType() {
904     Source foo =
905         CompilerTests.javaSource(
906             "test.Foo",
907             "package test;",
908             "",
909             "import javax.inject.Inject;",
910             "",
911             "class Foo {",
912             "  @Inject Foo() {}",
913             "}");
914     Source supertype =
915         CompilerTests.javaSource(
916             "test.Supertype",
917             "package test;",
918             "",
919             "interface Supertype {",
920             "  Foo foo();",
921             "}");
922 
923     Source subcomponent =
924         preprocessedJavaSource(
925             "test.HasSupertype",
926             "package test;",
927             "",
928             "import dagger.Subcomponent;",
929             "",
930             "@Subcomponent",
931             "interface HasSupertype extends Supertype {",
932             "  @Subcomponent.Builder",
933             "  interface Builder {",
934             "    Supertype build();",
935             "  }",
936             "}");
937 
938     CompilerTests.daggerCompiler(foo, supertype, subcomponent)
939         .compile(
940             subject -> {
941               subject.hasErrorCount(0);
942               subject.hasWarningCount(0);
943             });
944   }
945 
946   @Test
covariantFactoryMethodReturnType_hasNewMethod()947   public void covariantFactoryMethodReturnType_hasNewMethod() {
948     Source foo =
949         CompilerTests.javaSource(
950             "test.Foo",
951             "package test;",
952             "",
953             "import javax.inject.Inject;",
954             "",
955             "class Foo {",
956             "  @Inject Foo() {}",
957             "}");
958     Source bar =
959         CompilerTests.javaSource(
960             "test.Bar",
961             "package test;",
962             "",
963             "import javax.inject.Inject;",
964             "",
965             "class Bar {",
966             "  @Inject Bar() {}",
967             "}");
968     Source supertype =
969         CompilerTests.javaSource(
970             "test.Supertype",
971             "package test;",
972             "",
973             "interface Supertype {",
974             "  Foo foo();",
975             "}");
976 
977     Source subcomponent =
978         preprocessedJavaSource(
979             "test.HasSupertype",
980             "package test;",
981             "",
982             "import dagger.Subcomponent;",
983             "",
984             "@Subcomponent",
985             "interface HasSupertype extends Supertype {",
986             "  Bar bar();",
987             "",
988             "  @Subcomponent.Builder",
989             "  interface Builder {",
990             "    Supertype build();",
991             "  }",
992             "}");
993 
994     CompilerTests.daggerCompiler(foo, bar, supertype, subcomponent)
995         .compile(
996             subject -> {
997               subject.hasErrorCount(0);
998               subject.hasWarningCount(1);
999               subject.hasWarningContaining(
1000                       process(
1001                           "test.HasSupertype.Builder.build() returns test.Supertype, but "
1002                               + "test.HasSupertype declares additional component method(s): bar(). "
1003                               + "In order to provide type-safe access to these methods, override "
1004                               + "build() to return test.HasSupertype"))
1005                   .onSource(subcomponent)
1006                   .onLine(11);
1007             });
1008   }
1009 
1010   @Test
covariantFactoryMethodReturnType_hasNewMethod_buildMethodInherited()1011   public void covariantFactoryMethodReturnType_hasNewMethod_buildMethodInherited() {
1012     Source foo =
1013         CompilerTests.javaSource(
1014             "test.Foo",
1015             "package test;",
1016             "",
1017             "import javax.inject.Inject;",
1018             "",
1019             "class Foo {",
1020             "  @Inject Foo() {}",
1021             "}");
1022     Source bar =
1023         CompilerTests.javaSource(
1024             "test.Bar",
1025             "package test;",
1026             "",
1027             "import javax.inject.Inject;",
1028             "",
1029             "class Bar {",
1030             "  @Inject Bar() {}",
1031             "}");
1032     Source supertype =
1033         CompilerTests.javaSource(
1034             "test.Supertype",
1035             "package test;",
1036             "",
1037             "interface Supertype {",
1038             "  Foo foo();",
1039             "}");
1040 
1041     Source creatorSupertype =
1042         preprocessedJavaSource(
1043             "test.CreatorSupertype",
1044             "package test;",
1045             "",
1046             "interface CreatorSupertype {",
1047             "  Supertype build();",
1048             "}");
1049 
1050     Source subcomponent =
1051         preprocessedJavaSource(
1052             "test.HasSupertype",
1053             "package test;",
1054             "",
1055             "import dagger.Subcomponent;",
1056             "",
1057             "@Subcomponent",
1058             "interface HasSupertype extends Supertype {",
1059             "  Bar bar();",
1060             "",
1061             "  @Subcomponent.Builder",
1062             "  interface Builder extends CreatorSupertype {}",
1063             "}");
1064 
1065     CompilerTests.daggerCompiler(foo, bar, supertype, creatorSupertype, subcomponent)
1066         .compile(
1067             subject -> {
1068               subject.hasErrorCount(0);
1069               subject.hasWarningCount(1);
1070               subject.hasWarningContaining(
1071                   process(
1072                       "[test.CreatorSupertype.build()] test.HasSupertype.Builder.build() returns "
1073                           + "test.Supertype, but test.HasSupertype declares additional component "
1074                           + "method(s): bar(). In order to provide type-safe access to these "
1075                           + "methods, override build() to return test.HasSupertype"));
1076             });
1077   }
1078 }
1079