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