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