• 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.common.collect.Sets.immutableEnumSet;
20 import static com.google.testing.compile.CompilationSubject.assertThat;
21 import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
22 import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
23 import static dagger.internal.codegen.Compilers.daggerCompiler;
24 import static dagger.internal.codegen.ComponentCreatorAnnotation.COMPONENT_BUILDER;
25 import static dagger.internal.codegen.ComponentCreatorAnnotation.COMPONENT_FACTORY;
26 import static dagger.internal.codegen.ComponentCreatorKind.BUILDER;
27 import static dagger.internal.codegen.ComponentCreatorKind.FACTORY;
28 import static dagger.internal.codegen.ComponentKind.COMPONENT;
29 import static dagger.internal.codegen.ErrorMessages.componentMessagesFor;
30 import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
31 import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
32 
33 import com.google.common.collect.ImmutableList;
34 import com.google.common.collect.Iterables;
35 import com.google.common.collect.Sets;
36 import com.google.testing.compile.Compilation;
37 import com.google.testing.compile.JavaFileObjects;
38 import java.util.Collection;
39 import java.util.List;
40 import java.util.Set;
41 import javax.tools.JavaFileObject;
42 import org.junit.Test;
43 import org.junit.runner.RunWith;
44 import org.junit.runners.Parameterized;
45 import org.junit.runners.Parameterized.Parameters;
46 
47 /** Tests for properties of component creators shared by both builders and factories. */
48 @RunWith(Parameterized.class)
49 public class ComponentCreatorTest extends ComponentCreatorTestHelper {
50   @Parameters(name = "compilerMode={0}, creatorKind={1}")
parameters()51   public static Collection<Object[]> parameters() {
52     Set<List<Object>> params =
53         Sets.<Object>cartesianProduct(
54             immutableEnumSet(DEFAULT_MODE, FAST_INIT_MODE),
55             immutableEnumSet(COMPONENT_BUILDER, COMPONENT_FACTORY));
56     return ImmutableList.copyOf(Iterables.transform(params, Collection::toArray));
57   }
58 
ComponentCreatorTest( CompilerMode compilerMode, ComponentCreatorAnnotation componentCreatorAnnotation)59   public ComponentCreatorTest(
60       CompilerMode compilerMode, ComponentCreatorAnnotation componentCreatorAnnotation) {
61     super(compilerMode, componentCreatorAnnotation);
62   }
63 
64   @Test
testEmptyCreator()65   public void testEmptyCreator() {
66     JavaFileObject injectableTypeFile =
67         JavaFileObjects.forSourceLines(
68             "test.SomeInjectableType",
69             "package test;",
70             "",
71             "import javax.inject.Inject;",
72             "",
73             "final class SomeInjectableType {",
74             "  @Inject SomeInjectableType() {}",
75             "}");
76     JavaFileObject componentFile =
77         preprocessedJavaFile(
78             "test.SimpleComponent",
79             "package test;",
80             "",
81             "import dagger.Component;",
82             "import javax.inject.Provider;",
83             "",
84             "@Component",
85             "interface SimpleComponent {",
86             "  SomeInjectableType someInjectableType();",
87             "",
88             "  @Component.Builder",
89             "  static interface Builder {",
90             "     SimpleComponent build();",
91             "  }",
92             "}");
93     JavaFileObject generatedComponent =
94         preprocessedJavaFile(
95             "test.DaggerSimpleComponent",
96             "package test;",
97             "",
98             GENERATED_ANNOTATION,
99             "final class DaggerSimpleComponent implements SimpleComponent {",
100             "  private static final class Builder implements SimpleComponent.Builder {",
101             "    @Override",
102             "    public SimpleComponent build() {",
103             "      return new DaggerSimpleComponent();",
104             "    }",
105             "  }",
106             "}");
107     Compilation compilation = compile(injectableTypeFile, componentFile);
108     assertThat(compilation).succeeded();
109     assertThat(compilation)
110         .generatedSourceFile("test.DaggerSimpleComponent")
111         .containsElementsIn(generatedComponent);
112   }
113 
114   @Test
testCanInstantiateModulesUserCannotSet()115   public void testCanInstantiateModulesUserCannotSet() {
116     JavaFileObject module =
117         JavaFileObjects.forSourceLines(
118             "test.TestModule",
119             "package test;",
120             "",
121             "import dagger.Module;",
122             "import dagger.Provides;",
123             "",
124             "@Module",
125             "final class TestModule {",
126             "  @Provides String string() { return null; }",
127             "}");
128 
129     JavaFileObject componentFile =
130         preprocessedJavaFile(
131             "test.TestComponent",
132             "package test;",
133             "",
134             "import dagger.Component;",
135             "",
136             "@Component(modules = TestModule.class)",
137             "interface TestComponent {",
138             "  String string();",
139             "",
140             "  @Component.Builder",
141             "  interface Builder {",
142             "    TestComponent build();",
143             "  }",
144             "}");
145     JavaFileObject generatedComponent =
146         preprocessedJavaFile(
147             "test.DaggerTestComponent",
148             "package test;",
149             "",
150             IMPORT_GENERATED_ANNOTATION,
151             "",
152             GENERATED_ANNOTATION,
153             "final class DaggerTestComponent implements TestComponent {",
154             "  private final TestModule testModule;",
155             "",
156             "  private DaggerTestComponent(TestModule testModuleParam) {",
157             "    this.testModule = testModuleParam;",
158             "  }",
159             "",
160             "  public static TestComponent.Builder builder() {",
161             "    return new Builder();",
162             "  }",
163             "",
164             "  public static TestComponent create() {",
165             "    return new Builder().build();",
166             "  }",
167             "",
168             "  @Override",
169             "  public String string() {",
170             "    return TestModule_StringFactory.string(testModule);",
171             "  }",
172             "",
173             "  private static final class Builder implements TestComponent.Builder {",
174             "    @Override",
175             "    public TestComponent build() {",
176             "      return new DaggerTestComponent(new TestModule());",
177             "    }",
178             "  }",
179             "}");
180     Compilation compilation = compile(module, componentFile);
181     assertThat(compilation).succeeded();
182     assertThat(compilation)
183         .generatedSourceFile("test.DaggerTestComponent")
184         .hasSourceEquivalentTo(generatedComponent);
185   }
186 
187   @Test
testMoreThanOneCreatorOfSameTypeFails()188   public void testMoreThanOneCreatorOfSameTypeFails() {
189     JavaFileObject componentFile =
190         preprocessedJavaFile(
191             "test.SimpleComponent",
192             "package test;",
193             "",
194             "import dagger.Component;",
195             "import javax.inject.Provider;",
196             "",
197             "@Component",
198             "interface SimpleComponent {",
199             "  @Component.Builder",
200             "  static interface Builder {",
201             "     SimpleComponent build();",
202             "  }",
203             "",
204             "  @Component.Builder",
205             "  interface Builder2 {",
206             "     SimpleComponent build();",
207             "  }",
208             "}");
209     Compilation compilation = compile(componentFile);
210     assertThat(compilation).failed();
211     assertThat(compilation)
212         .hadErrorContaining(
213             String.format(
214                 componentMessagesFor(COMPONENT).moreThanOne(),
215                 process("[test.SimpleComponent.Builder, test.SimpleComponent.Builder2]")))
216         .inFile(componentFile);
217   }
218 
219   @Test
testBothBuilderAndFactoryFails()220   public void testBothBuilderAndFactoryFails() {
221     JavaFileObject componentFile =
222         JavaFileObjects.forSourceLines(
223             "test.SimpleComponent",
224             "package test;",
225             "",
226             "import dagger.Component;",
227             "import javax.inject.Provider;",
228             "",
229             "@Component",
230             "interface SimpleComponent {",
231             "  @Component.Builder",
232             "  static interface Builder {",
233             "     SimpleComponent build();",
234             "  }",
235             "",
236             "  @Component.Factory",
237             "  interface Factory {",
238             "     SimpleComponent create();",
239             "  }",
240             "}");
241     Compilation compilation = compile(componentFile);
242     assertThat(compilation).failed();
243     assertThat(compilation)
244         .hadErrorContaining(
245             String.format(
246                 componentMessagesFor(COMPONENT).moreThanOne(),
247                 "[test.SimpleComponent.Builder, test.SimpleComponent.Factory]"))
248         .inFile(componentFile);
249   }
250 
251   @Test
testGenericCreatorTypeFails()252   public void testGenericCreatorTypeFails() {
253     JavaFileObject componentFile =
254         preprocessedJavaFile(
255             "test.SimpleComponent",
256             "package test;",
257             "",
258             "import dagger.Component;",
259             "import javax.inject.Provider;",
260             "",
261             "@Component",
262             "interface SimpleComponent {",
263             "  @Component.Builder",
264             "  interface Builder<T> {",
265             "     SimpleComponent build();",
266             "  }",
267             "}");
268     Compilation compilation = compile(componentFile);
269     assertThat(compilation).failed();
270     assertThat(compilation).hadErrorContaining(messages.generics()).inFile(componentFile);
271   }
272 
273   @Test
testCreatorNotInComponentFails()274   public void testCreatorNotInComponentFails() {
275     JavaFileObject builder =
276         preprocessedJavaFile(
277             "test.Builder",
278             "package test;",
279             "",
280             "import dagger.Component;",
281             "",
282             "@Component.Builder",
283             "interface Builder {}");
284     Compilation compilation = compile(builder);
285     assertThat(compilation).failed();
286     assertThat(compilation).hadErrorContaining(messages.mustBeInComponent()).inFile(builder);
287   }
288 
289   @Test
testCreatorMissingFactoryMethodFails()290   public void testCreatorMissingFactoryMethodFails() {
291     JavaFileObject componentFile =
292         preprocessedJavaFile(
293             "test.SimpleComponent",
294             "package test;",
295             "",
296             "import dagger.Component;",
297             "import javax.inject.Provider;",
298             "",
299             "@Component",
300             "interface SimpleComponent {",
301             "  @Component.Builder",
302             "  interface Builder {}",
303             "}");
304     Compilation compilation = compile(componentFile);
305     assertThat(compilation).failed();
306     assertThat(compilation)
307         .hadErrorContaining(messages.missingFactoryMethod())
308         .inFile(componentFile);
309   }
310 
311   @Test
testCreatorWithBindsInstanceNoStaticCreateGenerated()312   public void testCreatorWithBindsInstanceNoStaticCreateGenerated() {
313     JavaFileObject componentFile =
314         javaFileBuilder("test.SimpleComponent")
315             .addLines(
316                 "package test;",
317                 "",
318                 "import dagger.BindsInstance;",
319                 "import dagger.Component;",
320                 "import javax.inject.Provider;",
321                 "",
322                 "@Component",
323                 "interface SimpleComponent {",
324                 "  Object object();",
325                 "")
326             .addLinesIf(
327                 BUILDER,
328                 "  @Component.Builder",
329                 "  interface Builder {",
330                 "    @BindsInstance Builder object(Object object);",
331                 "    SimpleComponent build();",
332                 "  }")
333             .addLinesIf(
334                 FACTORY,
335                 "  @Component.Factory",
336                 "  interface Factory {",
337                 "    SimpleComponent create(@BindsInstance Object object);",
338                 "  }")
339             .addLines("}")
340             .build();
341 
342     JavaFileObject generatedComponent =
343         javaFileBuilder("test.DaggerSimpleComponent")
344             .addLines(
345                 "package test;",
346                 "",
347                 "import dagger.internal.Preconditions;",
348                 IMPORT_GENERATED_ANNOTATION,
349                 "",
350                 GENERATED_ANNOTATION,
351                 "final class DaggerSimpleComponent implements SimpleComponent {",
352                 "  private final Object object;",
353                 "",
354                 "  private DaggerSimpleComponent(Object objectParam) {",
355                 "    this.object = objectParam;",
356                 "  }",
357                 "")
358             .addLinesIf(
359                 BUILDER,
360                 "  public static SimpleComponent.Builder builder() {",
361                 "    return new Builder();",
362                 "  }")
363             .addLinesIf(
364                 FACTORY,
365                 "  public static SimpleComponent.Factory factory() {",
366                 "    return new Factory();",
367                 "  }")
368             .addLines(
369                 "", //
370                 "  @Override",
371                 "  public Object object() {",
372                 "    return object;",
373                 "  }",
374                 "")
375             .addLinesIf(
376                 BUILDER,
377                 "  private static final class Builder implements SimpleComponent.Builder {",
378                 "    private Object object;",
379                 "",
380                 "    @Override",
381                 "    public Builder object(Object object) {",
382                 "      this.object = Preconditions.checkNotNull(object);",
383                 "      return this;",
384                 "    }",
385                 "",
386                 "    @Override",
387                 "    public SimpleComponent build() {",
388                 "      Preconditions.checkBuilderRequirement(object, Object.class);",
389                 "      return new DaggerSimpleComponent(object);",
390                 "    }",
391                 "  }")
392             .addLinesIf(
393                 FACTORY,
394                 "  private static final class Factory implements SimpleComponent.Factory {",
395                 "    @Override",
396                 "    public SimpleComponent create(Object object) {",
397                 "      Preconditions.checkNotNull(object);",
398                 "      return new DaggerSimpleComponent(object);",
399                 "    }",
400                 "  }")
401             .addLines("}")
402             .build();
403 
404     Compilation compilation = compile(componentFile);
405     assertThat(compilation).succeededWithoutWarnings();
406     assertThat(compilation)
407         .generatedSourceFile("test.DaggerSimpleComponent")
408         .hasSourceEquivalentTo(generatedComponent);
409   }
410 
411   @Test
testCreatorWithPrimitiveBindsInstance()412   public void testCreatorWithPrimitiveBindsInstance() {
413     JavaFileObject componentFile =
414         javaFileBuilder("test.SimpleComponent")
415             .addLines(
416                 "package test;",
417                 "",
418                 "import dagger.BindsInstance;",
419                 "import dagger.Component;",
420                 "import javax.inject.Provider;",
421                 "",
422                 "@Component",
423                 "interface SimpleComponent {",
424                 "  int anInt();",
425                 "")
426             .addLinesIf(
427                 BUILDER,
428                 "  @Component.Builder",
429                 "  interface Builder {",
430                 "    @BindsInstance Builder i(int i);",
431                 "    SimpleComponent build();",
432                 "  }")
433             .addLinesIf(
434                 FACTORY,
435                 "  @Component.Factory",
436                 "  interface Factory {",
437                 "    SimpleComponent create(@BindsInstance int i);",
438                 "  }")
439             .addLines(
440                 "}")
441             .build();
442 
443     JavaFileObject generatedComponent =
444         javaFileBuilder("test.DaggerSimpleComponent")
445             .addLines(
446                 "package test;",
447                 "",
448                 "import dagger.internal.Preconditions;",
449                 IMPORT_GENERATED_ANNOTATION,
450                 "",
451                 GENERATED_ANNOTATION,
452                 "final class DaggerSimpleComponent implements SimpleComponent {",
453                 "  private final Integer i;",
454                 "",
455                 "  private DaggerSimpleComponent(Integer iParam) {",
456                 "    this.i = iParam;",
457                 "  }",
458                 "",
459                 "  @Override",
460                 "  public int anInt() {",
461                 "    return i;",
462                 "  }",
463                 "")
464             .addLinesIf(
465                 BUILDER,
466                 "  private static final class Builder implements SimpleComponent.Builder {",
467                 "    private Integer i;",
468                 "",
469                 "    @Override",
470                 "    public Builder i(int i) {",
471                 "      this.i = Preconditions.checkNotNull(i);",
472                 "      return this;",
473                 "    }",
474                 "",
475                 "    @Override",
476                 "    public SimpleComponent build() {",
477                 "      Preconditions.checkBuilderRequirement(i, Integer.class);",
478                 "      return new DaggerSimpleComponent(i);",
479                 "    }",
480                 "  }")
481             .addLinesIf(
482                 FACTORY,
483                 "  private static final class Factory implements SimpleComponent.Factory {",
484                 "    @Override",
485                 "    public SimpleComponent create(int i) {",
486                 "      Preconditions.checkNotNull(i);",
487                 "      return new DaggerSimpleComponent(i);",
488                 "    }",
489                 "  }")
490             .addLines(
491                 "}")
492             .build();
493 
494     Compilation compilation = compile(componentFile);
495     assertThat(compilation).succeededWithoutWarnings();
496     assertThat(compilation)
497         .generatedSourceFile("test.DaggerSimpleComponent")
498         .containsElementsIn(generatedComponent);
499   }
500 
501   @Test
testPrivateCreatorFails()502   public void testPrivateCreatorFails() {
503     JavaFileObject componentFile =
504         preprocessedJavaFile(
505             "test.SimpleComponent",
506             "package test;",
507             "",
508             "import dagger.Component;",
509             "import javax.inject.Provider;",
510             "",
511             "@Component",
512             "abstract class SimpleComponent {",
513             "  @Component.Builder",
514             "  private interface Builder {}",
515             "}");
516     Compilation compilation = compile(componentFile);
517     assertThat(compilation).failed();
518     assertThat(compilation).hadErrorContaining(messages.isPrivate()).inFile(componentFile);
519   }
520 
521   @Test
testNonStaticCreatorFails()522   public void testNonStaticCreatorFails() {
523     JavaFileObject componentFile =
524         preprocessedJavaFile(
525             "test.SimpleComponent",
526             "package test;",
527             "",
528             "import dagger.Component;",
529             "import javax.inject.Provider;",
530             "",
531             "@Component",
532             "abstract class SimpleComponent {",
533             "  @Component.Builder",
534             "  abstract class Builder {}",
535             "}");
536     Compilation compilation = compile(componentFile);
537     assertThat(compilation).failed();
538     assertThat(compilation).hadErrorContaining(messages.mustBeStatic()).inFile(componentFile);
539   }
540 
541   @Test
testNonAbstractCreatorFails()542   public void testNonAbstractCreatorFails() {
543     JavaFileObject componentFile =
544         preprocessedJavaFile(
545             "test.SimpleComponent",
546             "package test;",
547             "",
548             "import dagger.Component;",
549             "import javax.inject.Provider;",
550             "",
551             "@Component",
552             "abstract class SimpleComponent {",
553             "  @Component.Builder",
554             "  static class Builder {}",
555             "}");
556     Compilation compilation = compile(componentFile);
557     assertThat(compilation).failed();
558     assertThat(compilation).hadErrorContaining(messages.mustBeAbstract()).inFile(componentFile);
559   }
560 
561   @Test
testCreatorOneConstructorWithArgsFails()562   public void testCreatorOneConstructorWithArgsFails() {
563     JavaFileObject componentFile =
564         preprocessedJavaFile(
565             "test.SimpleComponent",
566             "package test;",
567             "",
568             "import dagger.Component;",
569             "import javax.inject.Provider;",
570             "",
571             "@Component",
572             "abstract class SimpleComponent {",
573             "  @Component.Builder",
574             "  static abstract class Builder {",
575             "    Builder(String unused) {}",
576             "  }",
577             "}");
578     Compilation compilation = compile(componentFile);
579     assertThat(compilation).failed();
580     assertThat(compilation)
581         .hadErrorContaining(messages.invalidConstructor())
582         .inFile(componentFile);
583   }
584 
585   @Test
testCreatorMoreThanOneConstructorFails()586   public void testCreatorMoreThanOneConstructorFails() {
587     JavaFileObject componentFile =
588         preprocessedJavaFile(
589             "test.SimpleComponent",
590             "package test;",
591             "",
592             "import dagger.Component;",
593             "import javax.inject.Provider;",
594             "",
595             "@Component",
596             "abstract class SimpleComponent {",
597             "  @Component.Builder",
598             "  static abstract class Builder {",
599             "    Builder() {}",
600             "    Builder(String unused) {}",
601             "  }",
602             "}");
603     Compilation compilation = compile(componentFile);
604     assertThat(compilation).failed();
605     assertThat(compilation)
606         .hadErrorContaining(messages.invalidConstructor())
607         .inFile(componentFile);
608   }
609 
610   @Test
testCreatorEnumFails()611   public void testCreatorEnumFails() {
612     JavaFileObject componentFile =
613         preprocessedJavaFile(
614             "test.SimpleComponent",
615             "package test;",
616             "",
617             "import dagger.Component;",
618             "import javax.inject.Provider;",
619             "",
620             "@Component",
621             "abstract class SimpleComponent {",
622             "  @Component.Builder",
623             "  enum Builder {}",
624             "}");
625     Compilation compilation = compile(componentFile);
626     assertThat(compilation).failed();
627     assertThat(compilation)
628         .hadErrorContaining(messages.mustBeClassOrInterface())
629         .inFile(componentFile);
630   }
631 
632   @Test
testCreatorFactoryMethodReturnsWrongTypeFails()633   public void testCreatorFactoryMethodReturnsWrongTypeFails() {
634     JavaFileObject componentFile =
635         preprocessedJavaFile(
636             "test.SimpleComponent",
637             "package test;",
638             "",
639             "import dagger.Component;",
640             "import javax.inject.Provider;",
641             "",
642             "@Component",
643             "abstract class SimpleComponent {",
644             "  @Component.Builder",
645             "  interface Builder {",
646             "    String build();",
647             "  }",
648             "}");
649     Compilation compilation = compile(componentFile);
650     assertThat(compilation).failed();
651     assertThat(compilation)
652         .hadErrorContaining(messages.factoryMethodMustReturnComponentType())
653         .inFile(componentFile)
654         .onLineContaining(process("String build();"));
655   }
656 
657   @Test
testCreatorSetterForNonBindsInstancePrimitiveFails()658   public void testCreatorSetterForNonBindsInstancePrimitiveFails() {
659     JavaFileObject component =
660         javaFileBuilder("test.TestComponent")
661             .addLines(
662                 "package test;",
663                 "",
664                 "import dagger.Component;",
665                 "",
666                 "@Component",
667                 "interface TestComponent {",
668                 "  Object object();",
669                 "")
670             .addLinesIf(
671                 BUILDER,
672                 "  @Component.Builder",
673                 "  interface Builder {",
674                 "    Builder primitive(long l);",
675                 "    TestComponent build();",
676                 "  }")
677             .addLinesIf(
678                 FACTORY,
679                 "  @Component.Factory",
680                 "  interface Factory {",
681                 "    TestComponent create(long l);",
682                 "  }")
683             .addLines( //
684                 "}")
685             .build();
686     Compilation compilation = compile(component);
687     assertThat(compilation).failed();
688 
689     assertThat(compilation)
690         .hadErrorContaining(messages.nonBindsInstanceParametersMayNotBePrimitives())
691         .inFile(component)
692         .onLineContaining("(long l)");
693   }
694 
695   @Test
testInheritedBuilderBuildReturnsWrongTypeFails()696   public void testInheritedBuilderBuildReturnsWrongTypeFails() {
697     JavaFileObject componentFile =
698         preprocessedJavaFile(
699             "test.SimpleComponent",
700             "package test;",
701             "",
702             "import dagger.Component;",
703             "import javax.inject.Provider;",
704             "",
705             "@Component",
706             "abstract class SimpleComponent {",
707             "  interface Parent {",
708             "    String build();",
709             "  }",
710             "",
711             "  @Component.Builder",
712             "  interface Builder extends Parent {}",
713             "}");
714     Compilation compilation = compile(componentFile);
715     assertThat(compilation).failed();
716     assertThat(compilation)
717         .hadErrorContaining(
718             String.format(
719                 messages.inheritedFactoryMethodMustReturnComponentType(), process("build")))
720         .inFile(componentFile)
721         .onLineContaining(process("interface Builder"));
722   }
723 
724   @Test
testTwoFactoryMethodsFails()725   public void testTwoFactoryMethodsFails() {
726     JavaFileObject componentFile =
727         preprocessedJavaFile(
728             "test.SimpleComponent",
729             "package test;",
730             "",
731             "import dagger.Component;",
732             "import javax.inject.Provider;",
733             "",
734             "@Component",
735             "abstract class SimpleComponent {",
736             "  @Component.Builder",
737             "  interface Builder {",
738             "    SimpleComponent build();",
739             "    SimpleComponent newSimpleComponent();",
740             "  }",
741             "}");
742     Compilation compilation = compile(componentFile);
743     assertThat(compilation).failed();
744     assertThat(compilation)
745         .hadErrorContaining(String.format(messages.twoFactoryMethods(), process("build")))
746         .inFile(componentFile)
747         .onLineContaining("SimpleComponent newSimpleComponent();");
748   }
749 
750   @Test
testInheritedTwoFactoryMethodsFails()751   public void testInheritedTwoFactoryMethodsFails() {
752     JavaFileObject componentFile =
753         preprocessedJavaFile(
754             "test.SimpleComponent",
755             "package test;",
756             "",
757             "import dagger.Component;",
758             "import javax.inject.Provider;",
759             "",
760             "@Component",
761             "abstract class SimpleComponent {",
762             "  interface Parent {",
763             "    SimpleComponent build();",
764             "    SimpleComponent newSimpleComponent();",
765             "  }",
766             "",
767             "  @Component.Builder",
768             "  interface Builder extends Parent {}",
769             "}");
770     Compilation compilation = compile(componentFile);
771     assertThat(compilation).failed();
772     assertThat(compilation)
773         .hadErrorContaining(
774             String.format(
775                 messages.inheritedTwoFactoryMethods(), process("build()"), "newSimpleComponent()"))
776         .inFile(componentFile)
777         .onLineContaining(process("interface Builder"));
778   }
779 
780   @Test
testMultipleSettersPerTypeFails()781   public void testMultipleSettersPerTypeFails() {
782     JavaFileObject moduleFile =
783         JavaFileObjects.forSourceLines(
784             "test.TestModule",
785             "package test;",
786             "",
787             "import dagger.Module;",
788             "import dagger.Provides;",
789             "",
790             "@Module",
791             "final class TestModule {",
792             "  @Provides String s() { return \"\"; }",
793             "}");
794     JavaFileObject componentFile =
795         javaFileBuilder("test.SimpleComponent")
796             .addLines(
797                 "package test;",
798                 "",
799                 "import dagger.Component;",
800                 "import javax.inject.Provider;",
801                 "",
802                 "@Component(modules = TestModule.class)",
803                 "abstract class SimpleComponent {",
804                 "  abstract String s();",
805                 "")
806             .addLinesIf(
807                 BUILDER,
808                 "  @Component.Builder",
809                 "  interface Builder {",
810                 "    SimpleComponent build();",
811                 "    void set1(TestModule s);",
812                 "    void set2(TestModule s);",
813                 "  }")
814             .addLinesIf(
815                 FACTORY,
816                 "  @Component.Factory",
817                 "  interface Factory {",
818                 "    SimpleComponent create(TestModule m1, TestModule m2);",
819                 "  }")
820             .addLines( //
821                 "}")
822             .build();
823     Compilation compilation = compile(moduleFile, componentFile);
824     assertThat(compilation).failed();
825     String elements =
826         creatorKind.equals(BUILDER)
827             ? "[void test.SimpleComponent.Builder.set1(test.TestModule), "
828                 + "void test.SimpleComponent.Builder.set2(test.TestModule)]"
829             : "[test.TestModule m1, test.TestModule m2]";
830     assertThat(compilation)
831         .hadErrorContaining(
832             String.format(
833                 messages.multipleSettersForModuleOrDependencyType(), "test.TestModule", elements))
834         .inFile(componentFile)
835         .onLineContaining(process("interface Builder"));
836   }
837 
838   @Test
testMultipleSettersPerTypeIncludingResolvedGenericsFails()839   public void testMultipleSettersPerTypeIncludingResolvedGenericsFails() {
840     JavaFileObject moduleFile =
841         JavaFileObjects.forSourceLines(
842             "test.TestModule",
843             "package test;",
844             "",
845             "import dagger.Module;",
846             "import dagger.Provides;",
847             "",
848             "@Module",
849             "final class TestModule {",
850             "  @Provides String s() { return \"\"; }",
851             "}");
852     JavaFileObject componentFile =
853         javaFileBuilder("test.SimpleComponent")
854             .addLines(
855                 "package test;",
856                 "",
857                 "import dagger.Component;",
858                 "import javax.inject.Provider;",
859                 "",
860                 "@Component(modules = TestModule.class)",
861                 "abstract class SimpleComponent {",
862                 "  abstract String s();",
863                 "")
864             .addLinesIf(
865                 BUILDER,
866                 "  interface Parent<T> {",
867                 "    void set1(T t);",
868                 "  }",
869                 "",
870                 "  @Component.Builder",
871                 "  interface Builder extends Parent<TestModule> {",
872                 "    SimpleComponent build();",
873                 "    void set2(TestModule s);",
874                 "  }")
875             .addLinesIf(
876                 FACTORY,
877                 "  interface Parent<C, T> {",
878                 "    C create(TestModule m1, T t);",
879                 "  }",
880                 "",
881                 "  @Component.Factory",
882                 "  interface Factory extends Parent<SimpleComponent, TestModule> {}")
883             .addLines( //
884                 "}")
885             .build();
886     Compilation compilation = compile(moduleFile, componentFile);
887     assertThat(compilation).failed();
888     String elements =
889         creatorKind.equals(BUILDER)
890             ? "[void test.SimpleComponent.Builder.set1(test.TestModule), "
891                 + "void test.SimpleComponent.Builder.set2(test.TestModule)]"
892             : "[test.TestModule m1, test.TestModule t]";
893     assertThat(compilation)
894         .hadErrorContaining(
895             String.format(
896                 messages.multipleSettersForModuleOrDependencyType(), "test.TestModule", elements))
897         .inFile(componentFile)
898         .onLineContaining(process("interface Builder"));
899   }
900 
901   @Test
testExtraSettersFails()902   public void testExtraSettersFails() {
903     JavaFileObject componentFile =
904         javaFileBuilder("test.SimpleComponent")
905             .addLines(
906                 "package test;",
907                 "",
908                 "import dagger.Component;",
909                 "import javax.inject.Provider;",
910                 "",
911                 "@Component(modules = AbstractModule.class)",
912                 "abstract class SimpleComponent {")
913             .addLinesIf(
914                 BUILDER,
915                 "  @Component.Builder",
916                 "  interface Builder {",
917                 "    SimpleComponent build();",
918                 "    void abstractModule(AbstractModule abstractModule);",
919                 "    void other(String s);",
920                 "  }")
921             .addLinesIf(
922                 FACTORY,
923                 "  @Component.Factory",
924                 "  interface Factory {",
925                 "    SimpleComponent create(AbstractModule abstractModule, String s);",
926                 "  }")
927             .addLines("}")
928             .build();
929     JavaFileObject abstractModule =
930         JavaFileObjects.forSourceLines(
931             "test.AbstractModule",
932             "package test;",
933             "",
934             "import dagger.Module;",
935             "",
936             "@Module",
937             "abstract class AbstractModule {}");
938     Compilation compilation = compile(componentFile, abstractModule);
939     assertThat(compilation).failed();
940     String elements =
941         creatorKind.equals(BUILDER)
942             ? "[void test.SimpleComponent.Builder.abstractModule(test.AbstractModule), "
943                 + "void test.SimpleComponent.Builder.other(String)]"
944             : "[test.AbstractModule abstractModule, String s]";
945     assertThat(compilation)
946         .hadErrorContaining(String.format(messages.extraSetters(), elements))
947         .inFile(componentFile)
948         .onLineContaining(process("interface Builder"));
949   }
950 
951   @Test
testMissingSettersFail()952   public void testMissingSettersFail() {
953     JavaFileObject moduleFile =
954         JavaFileObjects.forSourceLines(
955             "test.TestModule",
956             "package test;",
957             "",
958             "import dagger.Module;",
959             "import dagger.Provides;",
960             "",
961             "@Module",
962             "final class TestModule {",
963             "  TestModule(String unused) {}",
964             "  @Provides String s() { return null; }",
965             "}");
966     JavaFileObject module2File =
967         JavaFileObjects.forSourceLines(
968             "test.Test2Module",
969             "package test;",
970             "",
971             "import dagger.Module;",
972             "import dagger.Provides;",
973             "",
974             "@Module",
975             "final class Test2Module {",
976             "  @Provides Integer i() { return null; }",
977             "}");
978     JavaFileObject module3File =
979         JavaFileObjects.forSourceLines(
980             "test.Test3Module",
981             "package test;",
982             "",
983             "import dagger.Module;",
984             "import dagger.Provides;",
985             "",
986             "@Module",
987             "final class Test3Module {",
988             "  Test3Module(String unused) {}",
989             "  @Provides Double d() { return null; }",
990             "}");
991     JavaFileObject componentFile =
992         preprocessedJavaFile(
993             "test.TestComponent",
994             "package test;",
995             "",
996             "import dagger.Component;",
997             "",
998             "@Component(modules = {TestModule.class, Test2Module.class, Test3Module.class},",
999             "           dependencies = OtherComponent.class)",
1000             "interface TestComponent {",
1001             "  String string();",
1002             "  Integer integer();",
1003             "",
1004             "  @Component.Builder",
1005             "  interface Builder {",
1006             "    TestComponent create();",
1007             "  }",
1008             "}");
1009     JavaFileObject otherComponent =
1010         JavaFileObjects.forSourceLines(
1011             "test.OtherComponent",
1012             "package test;",
1013             "",
1014             "import dagger.Component;",
1015             "",
1016             "@Component",
1017             "interface OtherComponent {}");
1018     Compilation compilation =
1019         daggerCompiler()
1020             .compile(moduleFile, module2File, module3File, componentFile, otherComponent);
1021     assertThat(compilation).failed();
1022     assertThat(compilation)
1023         .hadErrorContaining(
1024             // Ignores Test2Module because we can construct it ourselves.
1025             // TODO(sameb): Ignore Test3Module because it's not used within transitive dependencies.
1026             String.format(
1027                 messages.missingSetters(),
1028                 "[test.TestModule, test.Test3Module, test.OtherComponent]"))
1029         .inFile(componentFile)
1030         .onLineContaining(process("interface Builder"));
1031   }
1032 
1033   @Test
covariantFactoryMethodReturnType()1034   public void covariantFactoryMethodReturnType() {
1035     JavaFileObject foo =
1036         JavaFileObjects.forSourceLines(
1037             "test.Foo",
1038             "package test;",
1039             "",
1040             "import javax.inject.Inject;",
1041             "",
1042             "class Foo {",
1043             "  @Inject Foo() {}",
1044             "}");
1045     JavaFileObject supertype =
1046         JavaFileObjects.forSourceLines(
1047             "test.Supertype",
1048             "package test;",
1049             "",
1050             "interface Supertype {",
1051             "  Foo foo();",
1052             "}");
1053 
1054     JavaFileObject component =
1055         preprocessedJavaFile(
1056             "test.HasSupertype",
1057             "package test;",
1058             "",
1059             "import dagger.Component;",
1060             "",
1061             "@Component",
1062             "interface HasSupertype extends Supertype {",
1063             "  @Component.Builder",
1064             "  interface Builder {",
1065             "    Supertype build();",
1066             "  }",
1067             "}");
1068 
1069     Compilation compilation = compile(foo, supertype, component);
1070     assertThat(compilation).succeededWithoutWarnings();
1071   }
1072 
1073   @Test
covariantFactoryMethodReturnType_hasNewMethod()1074   public void covariantFactoryMethodReturnType_hasNewMethod() {
1075     JavaFileObject foo =
1076         JavaFileObjects.forSourceLines(
1077             "test.Foo",
1078             "package test;",
1079             "",
1080             "import javax.inject.Inject;",
1081             "",
1082             "class Foo {",
1083             "  @Inject Foo() {}",
1084             "}");
1085     JavaFileObject bar =
1086         JavaFileObjects.forSourceLines(
1087             "test.Bar",
1088             "package test;",
1089             "",
1090             "import javax.inject.Inject;",
1091             "",
1092             "class Bar {",
1093             "  @Inject Bar() {}",
1094             "}");
1095     JavaFileObject supertype =
1096         JavaFileObjects.forSourceLines(
1097             "test.Supertype",
1098             "package test;",
1099             "",
1100             "interface Supertype {",
1101             "  Foo foo();",
1102             "}");
1103 
1104     JavaFileObject component =
1105         preprocessedJavaFile(
1106             "test.HasSupertype",
1107             "package test;",
1108             "",
1109             "import dagger.Component;",
1110             "",
1111             "@Component",
1112             "interface HasSupertype extends Supertype {",
1113             "  Bar bar();",
1114             "",
1115             "  @Component.Builder",
1116             "  interface Builder {",
1117             "    Supertype build();",
1118             "  }",
1119             "}");
1120 
1121     Compilation compilation = compile(foo, bar, supertype, component);
1122     assertThat(compilation).succeeded();
1123     assertThat(compilation)
1124         .hadWarningContaining(
1125             process(
1126                 "test.HasSupertype.Builder.build() returns test.Supertype, but test.HasSupertype "
1127                     + "declares additional component method(s): bar(). In order to provide "
1128                     + "type-safe access to these methods, override build() to return "
1129                     + "test.HasSupertype"))
1130         .inFile(component)
1131         .onLine(11);
1132   }
1133 
1134   @Test
covariantFactoryMethodReturnType_hasNewMethod_factoryMethodInherited()1135   public void covariantFactoryMethodReturnType_hasNewMethod_factoryMethodInherited() {
1136     JavaFileObject foo =
1137         JavaFileObjects.forSourceLines(
1138             "test.Foo",
1139             "package test;",
1140             "",
1141             "import javax.inject.Inject;",
1142             "",
1143             "class Foo {",
1144             "  @Inject Foo() {}",
1145             "}");
1146     JavaFileObject bar =
1147         JavaFileObjects.forSourceLines(
1148             "test.Bar",
1149             "package test;",
1150             "",
1151             "import javax.inject.Inject;",
1152             "",
1153             "class Bar {",
1154             "  @Inject Bar() {}",
1155             "}");
1156     JavaFileObject supertype =
1157         JavaFileObjects.forSourceLines(
1158             "test.Supertype",
1159             "package test;",
1160             "",
1161             "interface Supertype {",
1162             "  Foo foo();",
1163             "}");
1164 
1165     JavaFileObject creatorSupertype =
1166         preprocessedJavaFile(
1167             "test.CreatorSupertype",
1168             "package test;",
1169             "",
1170             "interface CreatorSupertype {",
1171             "  Supertype build();",
1172             "}");
1173 
1174     JavaFileObject component =
1175         preprocessedJavaFile(
1176             "test.HasSupertype",
1177             "package test;",
1178             "",
1179             "import dagger.Component;",
1180             "",
1181             "@Component",
1182             "interface HasSupertype extends Supertype {",
1183             "  Bar bar();",
1184             "",
1185             "  @Component.Builder",
1186             "  interface Builder extends CreatorSupertype {}",
1187             "}");
1188 
1189     Compilation compilation = compile(foo, bar, supertype, creatorSupertype, component);
1190     assertThat(compilation).succeeded();
1191     assertThat(compilation)
1192         .hadWarningContaining(
1193             process(
1194                 "test.HasSupertype.Builder.build() returns test.Supertype, but test.HasSupertype "
1195                     + "declares additional component method(s): bar(). In order to provide "
1196                     + "type-safe access to these methods, override build() to return "
1197                     + "test.HasSupertype"));
1198   }
1199 
1200   @Test
testGenericsOnFactoryMethodFails()1201   public void testGenericsOnFactoryMethodFails() {
1202     JavaFileObject componentFile =
1203         preprocessedJavaFile(
1204             "test.SimpleComponent",
1205             "package test;",
1206             "",
1207             "import dagger.Component;",
1208             "import javax.inject.Provider;",
1209             "",
1210             "@Component",
1211             "abstract class SimpleComponent {",
1212             "  @Component.Builder",
1213             "  interface Builder {",
1214             "    <T> SimpleComponent build();",
1215             "  }",
1216             "}");
1217     Compilation compilation = compile(componentFile);
1218     assertThat(compilation).failed();
1219     assertThat(compilation)
1220         .hadErrorContaining(messages.methodsMayNotHaveTypeParameters())
1221         .inFile(componentFile)
1222         .onLineContaining(process("<T> SimpleComponent build();"));
1223   }
1224 
1225   @Test
testGenericsOnInheritedFactoryMethodFails()1226   public void testGenericsOnInheritedFactoryMethodFails() {
1227     JavaFileObject componentFile =
1228         preprocessedJavaFile(
1229             "test.SimpleComponent",
1230             "package test;",
1231             "",
1232             "import dagger.Component;",
1233             "import javax.inject.Provider;",
1234             "",
1235             "@Component",
1236             "abstract class SimpleComponent {",
1237             "  interface Parent {",
1238             "    <T> SimpleComponent build();",
1239             "  }",
1240             "",
1241             "  @Component.Builder",
1242             "  interface Builder extends Parent {}",
1243             "}");
1244     Compilation compilation = compile(componentFile);
1245     assertThat(compilation).failed();
1246     assertThat(compilation)
1247         .hadErrorContaining(
1248             String.format(
1249                 messages.inheritedMethodsMayNotHaveTypeParameters(), process("<T>build()")))
1250         .inFile(componentFile)
1251         .onLineContaining(process("interface Builder"));
1252   }
1253 }
1254