• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Dagger Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dagger.internal.codegen;
18 
19 import static com.google.testing.compile.CompilationSubject.assertThat;
20 import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
21 import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
22 import static dagger.internal.codegen.Compilers.compilerWithOptions;
23 import static dagger.internal.codegen.Compilers.daggerCompiler;
24 
25 import com.google.testing.compile.Compilation;
26 import com.google.testing.compile.JavaFileObjects;
27 import java.util.Collection;
28 import javax.tools.JavaFileObject;
29 import org.junit.Test;
30 import org.junit.runner.RunWith;
31 import org.junit.runners.Parameterized;
32 import org.junit.runners.Parameterized.Parameters;
33 
34 @RunWith(Parameterized.class)
35 public class SubcomponentValidationTest {
36   @Parameters(name = "{0}")
parameters()37   public static Collection<Object[]> parameters() {
38     return CompilerMode.TEST_PARAMETERS;
39   }
40 
41   private final CompilerMode compilerMode;
42 
SubcomponentValidationTest(CompilerMode compilerMode)43   public SubcomponentValidationTest(CompilerMode compilerMode) {
44     this.compilerMode = compilerMode;
45   }
46 
factoryMethod_missingModulesWithParameters()47   @Test public void factoryMethod_missingModulesWithParameters() {
48     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
49         "package test;",
50         "",
51         "import dagger.Component;",
52         "",
53         "@Component",
54         "interface TestComponent {",
55         "  ChildComponent newChildComponent();",
56         "}");
57     JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
58         "package test;",
59         "",
60         "import dagger.Subcomponent;",
61         "",
62         "@Subcomponent(modules = ModuleWithParameters.class)",
63         "interface ChildComponent {",
64         "  Object object();",
65         "}");
66     JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.ModuleWithParameters",
67         "package test;",
68         "",
69         "import dagger.Module;",
70         "import dagger.Provides;",
71         "",
72         "@Module",
73         "final class ModuleWithParameters {",
74         "  private final Object object;",
75         "",
76         "  ModuleWithParameters(Object object) {",
77         "    this.object = object;",
78         "  }",
79         "",
80         "  @Provides Object object() {",
81         "    return object;",
82         "  }",
83         "}");
84     Compilation compilation =
85         compilerWithOptions(compilerMode.javacopts())
86             .compile(componentFile, childComponentFile, moduleFile);
87     assertThat(compilation).failed();
88     assertThat(compilation)
89         .hadErrorContaining(
90             "test.ChildComponent requires modules which have no visible default constructors. "
91                 + "Add the following modules as parameters to this method: "
92                 + "test.ModuleWithParameters")
93         .inFile(componentFile)
94         .onLineContaining("ChildComponent newChildComponent();");
95   }
96 
97   @Test
factoryMethod_grandchild()98   public void factoryMethod_grandchild() {
99     JavaFileObject component =
100         JavaFileObjects.forSourceLines(
101             "test.TestComponent",
102             "package test;",
103             "",
104             "import dagger.Component;",
105             "",
106             "@Component",
107             "interface TestComponent {",
108             "  ChildComponent newChildComponent();",
109             "}");
110     JavaFileObject childComponent =
111         JavaFileObjects.forSourceLines(
112             "test.ChildComponent",
113             "package test;",
114             "",
115             "import dagger.Subcomponent;",
116             "",
117             "@Subcomponent",
118             "interface ChildComponent {",
119             "  GrandchildComponent newGrandchildComponent();",
120             "}");
121     JavaFileObject grandchildComponent =
122         JavaFileObjects.forSourceLines(
123             "test.GrandchildComponent",
124             "package test;",
125             "",
126             "import dagger.Subcomponent;",
127             "",
128             "@Subcomponent(modules = GrandchildModule.class)",
129             "interface GrandchildComponent {",
130             "  Object object();",
131             "}");
132     JavaFileObject grandchildModule =
133         JavaFileObjects.forSourceLines(
134             "test.GrandchildModule",
135             "package test;",
136             "",
137             "import dagger.Module;",
138             "import dagger.Provides;",
139             "",
140             "@Module",
141             "final class GrandchildModule {",
142             "  private final Object object;",
143             "",
144             "  GrandchildModule(Object object) {",
145             "    this.object = object;",
146             "  }",
147             "",
148             "  @Provides Object object() {",
149             "    return object;",
150             "  }",
151             "}");
152     Compilation compilation =
153         compilerWithOptions(compilerMode.javacopts())
154             .compile(component, childComponent, grandchildComponent, grandchildModule);
155     assertThat(compilation).failed();
156     assertThat(compilation)
157         .hadErrorContaining(
158             "[ChildComponent.newGrandchildComponent()] "
159                 + "GrandchildComponent requires modules which have no visible default "
160                 + "constructors. Add the following modules as parameters to this method: "
161                 + "GrandchildModule")
162         .inFile(component)
163         .onLineContaining("interface TestComponent");
164   }
165 
factoryMethod_nonModuleParameter()166   @Test public void factoryMethod_nonModuleParameter() {
167     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
168         "package test;",
169         "",
170         "import dagger.Component;",
171         "",
172         "@Component",
173         "interface TestComponent {",
174         "  ChildComponent newChildComponent(String someRandomString);",
175         "}");
176     JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
177         "package test;",
178         "",
179         "import dagger.Subcomponent;",
180         "",
181         "@Subcomponent",
182         "interface ChildComponent {}");
183     Compilation compilation =
184         compilerWithOptions(compilerMode.javacopts())
185             .compile(componentFile, childComponentFile);
186     assertThat(compilation).failed();
187     assertThat(compilation)
188         .hadErrorContaining(
189             "Subcomponent factory methods may only accept modules, but java.lang.String is not.")
190         .inFile(componentFile)
191         .onLine(7)
192         .atColumn(43);
193   }
194 
factoryMethod_duplicateParameter()195   @Test public void factoryMethod_duplicateParameter() {
196     JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
197         "package test;",
198         "",
199         "import dagger.Module;",
200         "",
201         "@Module",
202         "final class TestModule {}");
203     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
204         "package test;",
205         "",
206         "import dagger.Component;",
207         "",
208         "@Component",
209         "interface TestComponent {",
210         "  ChildComponent newChildComponent(TestModule testModule1, TestModule testModule2);",
211         "}");
212     JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
213         "package test;",
214         "",
215         "import dagger.Subcomponent;",
216         "",
217         "@Subcomponent(modules = TestModule.class)",
218         "interface ChildComponent {}");
219     Compilation compilation =
220         compilerWithOptions(compilerMode.javacopts())
221             .compile(moduleFile, componentFile, childComponentFile);
222     assertThat(compilation).failed();
223     assertThat(compilation)
224         .hadErrorContaining(
225             "A module may only occur once as an argument in a Subcomponent factory method, "
226                 + "but test.TestModule was already passed.")
227         .inFile(componentFile)
228         .onLine(7)
229         .atColumn(71);
230   }
231 
factoryMethod_superflouousModule()232   @Test public void factoryMethod_superflouousModule() {
233     JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
234         "package test;",
235         "",
236         "import dagger.Module;",
237         "",
238         "@Module",
239         "final class TestModule {}");
240     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
241         "package test;",
242         "",
243         "import dagger.Component;",
244         "",
245         "@Component",
246         "interface TestComponent {",
247         "  ChildComponent newChildComponent(TestModule testModule);",
248         "}");
249     JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
250         "package test;",
251         "",
252         "import dagger.Subcomponent;",
253         "",
254         "@Subcomponent",
255         "interface ChildComponent {}");
256     Compilation compilation =
257         compilerWithOptions(compilerMode.javacopts())
258             .compile(moduleFile, componentFile, childComponentFile);
259     assertThat(compilation).failed();
260     assertThat(compilation)
261         .hadErrorContaining(
262             "test.TestModule is present as an argument to the test.ChildComponent factory method, "
263                 + "but is not one of the modules used to implement the subcomponent.")
264         .inFile(componentFile)
265         .onLine(7);
266   }
267 
missingBinding()268   @Test public void missingBinding() {
269     JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
270         "package test;",
271         "",
272         "import dagger.Module;",
273         "import dagger.Provides;",
274         "",
275         "@Module",
276         "final class TestModule {",
277         "  @Provides String provideString(int i) {",
278         "    return Integer.toString(i);",
279         "  }",
280         "}");
281     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
282         "package test;",
283         "",
284         "import dagger.Component;",
285         "",
286         "@Component",
287         "interface TestComponent {",
288         "  ChildComponent newChildComponent();",
289         "}");
290     JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
291         "package test;",
292         "",
293         "import dagger.Subcomponent;",
294         "",
295         "@Subcomponent(modules = TestModule.class)",
296         "interface ChildComponent {",
297         "  String string();",
298         "}");
299     Compilation compilation =
300         compilerWithOptions(compilerMode.javacopts())
301             .compile(moduleFile, componentFile, childComponentFile);
302     assertThat(compilation).failed();
303     assertThat(compilation)
304         .hadErrorContaining(
305             "Integer cannot be provided without an @Inject constructor or an "
306                 + "@Provides-annotated method")
307         .inFile(componentFile)
308         .onLineContaining("interface TestComponent");
309   }
310 
subcomponentOnConcreteType()311   @Test public void subcomponentOnConcreteType() {
312     JavaFileObject subcomponentFile = JavaFileObjects.forSourceLines("test.NotASubcomponent",
313         "package test;",
314         "",
315         "import dagger.Subcomponent;",
316         "",
317         "@Subcomponent",
318         "final class NotASubcomponent {}");
319     Compilation compilation =
320         compilerWithOptions(compilerMode.javacopts()).compile(subcomponentFile);
321     assertThat(compilation).failed();
322     assertThat(compilation).hadErrorContaining("interface");
323   }
324 
scopeMismatch()325   @Test public void scopeMismatch() {
326     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.ParentComponent",
327         "package test;",
328         "",
329         "import dagger.Component;",
330         "import javax.inject.Singleton;",
331         "",
332         "@Component",
333         "@Singleton",
334         "interface ParentComponent {",
335         "  ChildComponent childComponent();",
336         "}");
337     JavaFileObject subcomponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
338         "package test;",
339         "",
340         "import dagger.Subcomponent;",
341         "",
342         "@Subcomponent(modules = ChildModule.class)",
343         "interface ChildComponent {",
344         "  Object object();",
345         "}");
346     JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.ChildModule",
347         "package test;",
348         "",
349         "import dagger.Module;",
350         "import dagger.Provides;",
351         "import javax.inject.Singleton;",
352         "",
353         "@Module",
354         "final class ChildModule {",
355         "  @Provides @Singleton Object provideObject() { return null; }",
356         "}");
357     Compilation compilation =
358         compilerWithOptions(compilerMode.javacopts())
359             .compile(componentFile, subcomponentFile, moduleFile);
360     assertThat(compilation).failed();
361     assertThat(compilation).hadErrorContaining("@Singleton");
362   }
363 
364   @Test
delegateFactoryNotCreatedForSubcomponentWhenProviderExistsInParent()365   public void delegateFactoryNotCreatedForSubcomponentWhenProviderExistsInParent() {
366     JavaFileObject parentComponentFile =
367         JavaFileObjects.forSourceLines(
368             "test.ParentComponent",
369             "package test;",
370             "",
371             "import dagger.Component;",
372             "import javax.inject.Singleton;",
373             "",
374             "@Singleton",
375             "@Component",
376             "interface ParentComponent {",
377             "  ChildComponent childComponent();",
378             "  Dep1 dep1();",
379             "  Dep2 dep2();",
380             "}");
381     JavaFileObject childComponentFile =
382         JavaFileObjects.forSourceLines(
383             "test.ChildComponent",
384             "package test;",
385             "",
386             "import dagger.Subcomponent;",
387             "",
388             "@Subcomponent(modules = ChildModule.class)",
389             "interface ChildComponent {",
390             "  Object object();",
391             "}");
392     JavaFileObject childModuleFile =
393         JavaFileObjects.forSourceLines(
394             "test.ChildModule",
395             "package test;",
396             "",
397             "import dagger.Module;",
398             "import dagger.Provides;",
399             "",
400             "@Module",
401             "final class ChildModule {",
402             "  @Provides Object provideObject(A a) { return null; }",
403             "}");
404     JavaFileObject aFile =
405         JavaFileObjects.forSourceLines(
406             "test.A",
407             "package test;",
408             "",
409             "import javax.inject.Inject;",
410             "",
411             "final class A {",
412             "  @Inject public A(NeedsDep1 a, Dep1 b, Dep2 c) { }",
413             "  @Inject public void methodA() { }",
414             "}");
415     JavaFileObject needsDep1File =
416         JavaFileObjects.forSourceLines(
417             "test.NeedsDep1",
418             "package test;",
419             "",
420             "import javax.inject.Inject;",
421             "",
422             "final class NeedsDep1 {",
423             "  @Inject public NeedsDep1(Dep1 d) { }",
424             "}");
425     JavaFileObject dep1File =
426         JavaFileObjects.forSourceLines(
427             "test.Dep1",
428             "package test;",
429             "",
430             "import javax.inject.Inject;",
431             "import javax.inject.Singleton;",
432             "",
433             "@Singleton",
434             "final class Dep1 {",
435             "  @Inject public Dep1() { }",
436             "  @Inject public void dep1Method() { }",
437             "}");
438     JavaFileObject dep2File =
439         JavaFileObjects.forSourceLines(
440             "test.Dep2",
441             "package test;",
442             "",
443             "import javax.inject.Inject;",
444             "import javax.inject.Singleton;",
445             "",
446             "@Singleton",
447             "final class Dep2 {",
448             "  @Inject public Dep2() { }",
449             "  @Inject public void dep2Method() { }",
450             "}");
451 
452     JavaFileObject generatedComponent =
453         compilerMode
454             .javaFileBuilder("test.DaggerParentComponent")
455             .addLines(
456                 "package test;",
457                 "",
458                 GeneratedLines.generatedAnnotations(),
459                 "final class DaggerParentComponent implements ParentComponent {",
460                 "  private Provider<Dep1> dep1Provider;",
461                 "  private Provider<Dep2> dep2Provider;")
462             .addLinesIn(
463                 DEFAULT_MODE,
464                 "  @SuppressWarnings(\"unchecked\")",
465                 "  private void initialize() {",
466                 "    this.dep1Provider = DoubleCheck.provider(Dep1_Factory.create());",
467                 "    this.dep2Provider = DoubleCheck.provider(Dep2_Factory.create());",
468                 "  }",
469                 "")
470             .addLinesIn(
471                 FAST_INIT_MODE,
472                 "  @SuppressWarnings(\"unchecked\")",
473                 "  private void initialize() {",
474                 "    this.dep1Provider = DoubleCheck.provider(",
475                 "        new SwitchingProvider<Dep1>(parentComponent, 0));",
476                 "    this.dep2Provider = DoubleCheck.provider(",
477                 "        new SwitchingProvider<Dep2>(parentComponent, 1));",
478                 "  }")
479             .addLines(
480                 "  @Override",
481                 "  public Dep1 dep1() {",
482                 "    return dep1Provider.get();",
483                 "  }",
484                 "",
485                 "  @Override",
486                 "  public Dep2 dep2() {",
487                 "    return dep2Provider.get();",
488                 "  }",
489                 "",
490                 "  @Override",
491                 "  public ChildComponent childComponent() {",
492                 "    return new ChildComponentImpl(parentComponent);",
493                 "  }")
494             .addLinesIn(
495                 FAST_INIT_MODE,
496                 "  @CanIgnoreReturnValue",
497                 "  private Dep1 injectDep1(Dep1 instance) {",
498                 "    Dep1_MembersInjector.injectDep1Method(instance);",
499                 "    return instance;",
500                 "  }",
501                 "",
502                 "  @CanIgnoreReturnValue",
503                 "  private Dep2 injectDep2(Dep2 instance) {",
504                 "    Dep2_MembersInjector.injectDep2Method(instance);",
505                 "    return instance;",
506                 "  }")
507             .addLines(
508                 "  private static final class ChildComponentImpl implements ChildComponent {",
509                 "    private NeedsDep1 needsDep1() {",
510                 "      return new NeedsDep1(parentComponent.dep1Provider.get());",
511                 "    }",
512                 "",
513                 "    private A a() {",
514                 "      return injectA(",
515                 "          A_Factory.newInstance(",
516                 "          needsDep1(),",
517                 "          parentComponent.dep1Provider.get(),",
518                 "          parentComponent.dep2Provider.get()));",
519                 "    }",
520                 "",
521                 "    @Override",
522                 "    public Object object() {",
523                 "      return ChildModule_ProvideObjectFactory.provideObject(",
524                 "          childModule, a());",
525                 "    }",
526                 "",
527                 "    @CanIgnoreReturnValue",
528                 "    private A injectA(A instance) {",
529                 "      A_MembersInjector.injectMethodA(instance);",
530                 "      return instance;",
531                 "    }",
532                 "  }")
533             .addLinesIn(
534                 FAST_INIT_MODE,
535                 "  private static final class SwitchingProvider<T> implements Provider<T> {",
536                 "    @SuppressWarnings(\"unchecked\")",
537                 "    @Override",
538                 "    public T get() {",
539                 "      switch (id) {",
540                 "        case 0:",
541                 "          return (T) parentComponent.injectDep1(Dep1_Factory.newInstance());",
542                 "        case 1:",
543                 "          return (T) parentComponent.injectDep2(Dep2_Factory.newInstance());",
544                 "        default: throw new AssertionError(id);",
545                 "      }",
546                 "    }",
547                 "  }",
548                 "}")
549             .build();
550 
551     Compilation compilation =
552         compilerWithOptions(compilerMode.javacopts())
553             .compile(
554                 parentComponentFile,
555                 childComponentFile,
556                 childModuleFile,
557                 aFile,
558                 needsDep1File,
559                 dep1File,
560                 dep2File);
561     assertThat(compilation).succeeded();
562     assertThat(compilation)
563         .generatedSourceFile("test.DaggerParentComponent")
564         .containsElementsIn(generatedComponent);
565   }
566 
567   @Test
multipleSubcomponentsWithSameSimpleNamesCanExistInSameComponent()568   public void multipleSubcomponentsWithSameSimpleNamesCanExistInSameComponent() {
569     JavaFileObject parent =
570         JavaFileObjects.forSourceLines(
571             "test.ParentComponent",
572             "package test;",
573             "",
574             "import dagger.Component;",
575             "",
576             "@Component",
577             "interface ParentComponent {",
578             "  Foo.Sub newInstanceSubcomponent();",
579             "  NoConflict newNoConflictSubcomponent();",
580             "}");
581     JavaFileObject foo =
582         JavaFileObjects.forSourceLines(
583             "test.Foo",
584             "package test;",
585             "",
586             "import dagger.Subcomponent;",
587             "",
588             "interface Foo {",
589             "  @Subcomponent interface Sub {",
590             "    Bar.Sub newBarSubcomponent();",
591             "  }",
592             "}");
593     JavaFileObject bar =
594         JavaFileObjects.forSourceLines(
595             "test.Bar",
596             "package test;",
597             "",
598             "import dagger.Subcomponent;",
599             "",
600             "interface Bar {",
601             "  @Subcomponent interface Sub {",
602             "    test.subpackage.Sub newSubcomponentInSubpackage();",
603             "  }",
604             "}");
605     JavaFileObject baz =
606         JavaFileObjects.forSourceLines(
607             "test.subpackage.Sub",
608             "package test.subpackage;",
609             "",
610             "import dagger.Subcomponent;",
611             "",
612             "@Subcomponent public interface Sub {}");
613     JavaFileObject noConflict =
614         JavaFileObjects.forSourceLines(
615             "test.NoConflict",
616             "package test;",
617             "",
618             "import dagger.Subcomponent;",
619             "",
620             "@Subcomponent interface NoConflict {}");
621 
622     JavaFileObject componentGeneratedFile =
623         JavaFileObjects.forSourceLines(
624             "test.DaggerParentComponent",
625             "package test;",
626             "",
627             "import test.subpackage.Sub;",
628             "",
629             GeneratedLines.generatedAnnotations(),
630             "final class DaggerParentComponent implements ParentComponent {",
631             "  @Override",
632             "  public Foo.Sub newInstanceSubcomponent() {",
633             "    return new F_SubImpl(parentComponent);",
634             "  }",
635             "",
636             "  @Override",
637             "  public NoConflict newNoConflictSubcomponent() {",
638             "    return new NoConflictImpl(parentComponent);",
639             "  }",
640             "",
641             "  static final class Builder {",
642             "    public ParentComponent build() {",
643             "      return new DaggerParentComponent();",
644             "    }",
645             "  }",
646             "",
647             "  private static final class ts_SubImpl implements Sub {}",
648             "",
649             "  private static final class B_SubImpl implements Bar.Sub {}",
650             "",
651             "  private static final class F_SubImpl implements Foo.Sub {}",
652             "",
653             "  private static final class NoConflictImpl implements NoConflict {}",
654             "}");
655     Compilation compilation =
656         compilerWithOptions(compilerMode.javacopts())
657             .compile(parent, foo, bar, baz, noConflict);
658     assertThat(compilation).succeeded();
659     assertThat(compilation)
660         .generatedSourceFile("test.DaggerParentComponent")
661         .containsElementsIn(componentGeneratedFile);
662   }
663 
664   @Test
subcomponentSimpleNamesDisambiguated()665   public void subcomponentSimpleNamesDisambiguated() {
666     JavaFileObject parent =
667         JavaFileObjects.forSourceLines(
668             "test.ParentComponent",
669             "package test;",
670             "",
671             "import dagger.Component;",
672             "",
673             "@Component",
674             "interface ParentComponent {",
675             "  Sub newSubcomponent();",
676             "}");
677     JavaFileObject sub =
678         JavaFileObjects.forSourceLines(
679             "test.Sub",
680             "package test;",
681             "",
682             "import dagger.Subcomponent;",
683             "",
684             "@Subcomponent interface Sub {",
685             "  test.deep.many.levels.that.match.test.Sub newDeepSubcomponent();",
686             "}");
687     JavaFileObject deepSub =
688         JavaFileObjects.forSourceLines(
689             "test.deep.many.levels.that.match.test.Sub",
690             "package test.deep.many.levels.that.match.test;",
691             "",
692             "import dagger.Subcomponent;",
693             "",
694             "@Subcomponent public interface Sub {}");
695 
696     JavaFileObject componentGeneratedFile =
697         JavaFileObjects.forSourceLines(
698             "test.DaggerParentComponent",
699             "package test;",
700             "",
701             GeneratedLines.generatedAnnotations(),
702             "final class DaggerParentComponent implements ParentComponent {",
703             "  @Override",
704             "  public Sub newSubcomponent() {",
705             "    return new t_SubImpl(parentComponent);",
706             "  }",
707             "",
708             "  static final class Builder {",
709             "    public ParentComponent build() {",
710             "      return new DaggerParentComponent();",
711             "    }",
712             "  }",
713             "",
714             "  private static final class tdmltmt_SubImpl",
715             "      implements test.deep.many.levels.that.match.test.Sub {}",
716             "",
717             "  private static final class t_SubImpl implements Sub {}",
718             "}");
719     Compilation compilation =
720         compilerWithOptions(compilerMode.javacopts()).compile(parent, sub, deepSub);
721     assertThat(compilation).succeeded();
722     assertThat(compilation)
723         .generatedSourceFile("test.DaggerParentComponent")
724         .containsElementsIn(componentGeneratedFile);
725   }
726 
727   @Test
subcomponentSimpleNamesDisambiguatedInRoot()728   public void subcomponentSimpleNamesDisambiguatedInRoot() {
729     JavaFileObject parent =
730         JavaFileObjects.forSourceLines(
731             "ParentComponent",
732             "import dagger.Component;",
733             "",
734             "@Component",
735             "interface ParentComponent {",
736             "  Sub newSubcomponent();",
737             "}");
738     JavaFileObject sub =
739         JavaFileObjects.forSourceLines(
740             "Sub",
741             "import dagger.Subcomponent;",
742             "",
743             "@Subcomponent interface Sub {",
744             "  test.deep.many.levels.that.match.test.Sub newDeepSubcomponent();",
745             "}");
746     JavaFileObject deepSub =
747         JavaFileObjects.forSourceLines(
748             "test.deep.many.levels.that.match.test.Sub",
749             "package test.deep.many.levels.that.match.test;",
750             "",
751             "import dagger.Subcomponent;",
752             "",
753             "@Subcomponent public interface Sub {}");
754 
755     JavaFileObject componentGeneratedFile =
756         JavaFileObjects.forSourceLines(
757             "DaggerParentComponent",
758             "",
759             GeneratedLines.generatedAnnotations(),
760             "final class DaggerParentComponent implements ParentComponent {",
761             "  @Override",
762             "  public Sub newSubcomponent() {",
763             "    return new $_SubImpl(parentComponent);",
764             "  }",
765             "",
766             "  private static final class tdmltmt_SubImpl",
767             "      implements test.deep.many.levels.that.match.test.Sub {}",
768             "",
769             "  private static final class $_SubImpl implements Sub {}",
770             "}",
771             "");
772 
773     Compilation compilation =
774         compilerWithOptions(compilerMode.javacopts()).compile(parent, sub, deepSub);
775     assertThat(compilation).succeeded();
776     assertThat(compilation)
777         .generatedSourceFile("DaggerParentComponent")
778         .containsElementsIn(componentGeneratedFile);
779   }
780 
781   @Test
subcomponentImplNameUsesFullyQualifiedClassNameIfNecessary()782   public void subcomponentImplNameUsesFullyQualifiedClassNameIfNecessary() {
783     JavaFileObject parent =
784         JavaFileObjects.forSourceLines(
785             "test.ParentComponent",
786             "package test;",
787             "",
788             "import dagger.Component;",
789             "",
790             "@Component",
791             "interface ParentComponent {",
792             "  top1.a.b.c.d.E.F.Sub top1();",
793             "  top2.a.b.c.d.E.F.Sub top2();",
794             "}");
795     JavaFileObject top1 =
796         JavaFileObjects.forSourceLines(
797             "top1.a.b.c.d.E",
798             "package top1.a.b.c.d;",
799             "",
800             "import dagger.Subcomponent;",
801             "",
802             "public interface E {",
803             "  interface F {",
804             "    @Subcomponent interface Sub {}",
805             "  }",
806             "}");
807     JavaFileObject top2 =
808         JavaFileObjects.forSourceLines(
809             "top2.a.b.c.d.E",
810             "package top2.a.b.c.d;",
811             "",
812             "import dagger.Subcomponent;",
813             "",
814             "public interface E {",
815             "  interface F {",
816             "    @Subcomponent interface Sub {}",
817             "  }",
818             "}");
819 
820     JavaFileObject componentGeneratedFile =
821         JavaFileObjects.forSourceLines(
822             "test.DaggerParentComponent",
823             "package test;",
824             "",
825             "import top1.a.b.c.d.E;",
826             "",
827             GeneratedLines.generatedAnnotations(),
828             "final class DaggerParentComponent implements ParentComponent {",
829             "  @Override",
830             "  public E.F.Sub top1() {",
831             "    return new F_SubImpl(parentComponent);",
832             "  }",
833             "",
834             "  @Override",
835             "  public top2.a.b.c.d.E.F.Sub top2() {",
836             "    return new F2_SubImpl(parentComponent);",
837             "  }",
838             "",
839             "  private static final class F_SubImpl implements E.F.Sub {",
840             "    private F_SubImpl(DaggerParentComponent parentComponent) {",
841             "      this.parentComponent = parentComponent;",
842             "    }",
843             "  }",
844             "  private static final class F2_SubImpl implements top2.a.b.c.d.E.F.Sub {",
845             "    private F2_SubImpl(DaggerParentComponent parentComponent) {",
846             "      this.parentComponent = parentComponent;",
847             "    }",
848             "  }",
849             "}");
850 
851     Compilation compilation =
852         compilerWithOptions(compilerMode.javacopts()).compile(parent, top1, top2);
853     assertThat(compilation).succeeded();
854     assertThat(compilation)
855         .generatedSourceFile("test.DaggerParentComponent")
856         .containsElementsIn(componentGeneratedFile);
857   }
858 
859   @Test
parentComponentNameShouldNotBeDisambiguatedWhenItConflictsWithASubcomponent()860   public void parentComponentNameShouldNotBeDisambiguatedWhenItConflictsWithASubcomponent() {
861     JavaFileObject parent =
862         JavaFileObjects.forSourceLines(
863             "test.C",
864             "package test;",
865             "",
866             "import dagger.Component;",
867             "",
868             "@Component",
869             "interface C {",
870             "  test.Foo.C newInstanceC();",
871             "}");
872     JavaFileObject subcomponentWithSameSimpleNameAsParent =
873         JavaFileObjects.forSourceLines(
874             "test.Foo",
875             "package test;",
876             "",
877             "import dagger.Subcomponent;",
878             "",
879             "interface Foo {",
880             "  @Subcomponent interface C {}",
881             "}");
882 
883     JavaFileObject componentGeneratedFile =
884         JavaFileObjects.forSourceLines(
885             "test.DaggerC",
886             "package test;",
887             "",
888             GeneratedLines.generatedAnnotations(),
889             "final class DaggerC implements C {",
890             "  @Override",
891             "  public Foo.C newInstanceC() {",
892             "    return new F_CImpl(c);",
893             "  }",
894             "",
895             "  private static final class F_CImpl implements Foo.C {}",
896             "}");
897 
898     Compilation compilation =
899         compilerWithOptions(compilerMode.javacopts())
900             .compile(parent, subcomponentWithSameSimpleNameAsParent);
901     assertThat(compilation).succeeded();
902     assertThat(compilation)
903         .generatedSourceFile("test.DaggerC")
904         .containsElementsIn(componentGeneratedFile);
905   }
906 
907   @Test
subcomponentBuilderNamesShouldNotConflict()908   public void subcomponentBuilderNamesShouldNotConflict() {
909     JavaFileObject parent =
910         JavaFileObjects.forSourceLines(
911             "test.C",
912             "package test;",
913             "",
914             "import dagger.Component;",
915             "import dagger.Subcomponent;",
916             "",
917             "@Component",
918             "interface C {",
919             "  Foo.Sub.Builder fooBuilder();",
920             "  Bar.Sub.Builder barBuilder();",
921             "",
922             "  interface Foo {",
923             "    @Subcomponent",
924             "    interface Sub {",
925             "      @Subcomponent.Builder",
926             "      interface Builder {",
927             "        Sub build();",
928             "      }",
929             "    }",
930             "  }",
931             "",
932             "  interface Bar {",
933             "    @Subcomponent",
934             "    interface Sub {",
935             "      @Subcomponent.Builder",
936             "      interface Builder {",
937             "        Sub build();",
938             "      }",
939             "    }",
940             "  }",
941             "}");
942     JavaFileObject componentGeneratedFile =
943         JavaFileObjects.forSourceLines(
944             "test.DaggerC",
945             "package test;",
946             "",
947             GeneratedLines.generatedImports(),
948             "",
949             GeneratedLines.generatedAnnotations(),
950             "final class DaggerC implements C {",
951             "  @Override",
952             "  public C.Foo.Sub.Builder fooBuilder() {",
953             "    return new F_SubBuilder(c);",
954             "  }",
955             "",
956             "  @Override",
957             "  public C.Bar.Sub.Builder barBuilder() {",
958             "    return new B_SubBuilder(c);",
959             "  }",
960             "",
961             // TODO(bcorso): Reverse the order of subcomponent and builder so that subcomponent
962             // comes first.
963             "  private static final class F_SubBuilder implements C.Foo.Sub.Builder {",
964             "    @Override",
965             "    public C.Foo.Sub build() {",
966             "      return new F_SubImpl(c);",
967             "    }",
968             "  }",
969             "",
970             "  private static final class B_SubBuilder implements C.Bar.Sub.Builder {",
971             "    @Override",
972             "    public C.Bar.Sub build() {",
973             "      return new B_SubImpl(c);",
974             "    }",
975             "  }",
976             "",
977             "  private static final class F_SubImpl implements C.Foo.Sub {",
978             "    private F_SubImpl(DaggerC c) {",
979             "      this.c = c;",
980             "    }",
981             "  }",
982             "",
983             "  private static final class B_SubImpl implements C.Bar.Sub {",
984             "    private B_SubImpl(DaggerC c) {",
985             "      this.c = c;",
986             "    }",
987             "  }",
988             "}");
989     Compilation compilation =
990         compilerWithOptions(compilerMode.javacopts()).compile(parent);
991     assertThat(compilation).succeeded();
992     assertThat(compilation)
993         .generatedSourceFile("test.DaggerC")
994         .containsElementsIn(componentGeneratedFile);
995   }
996 
997   @Test
duplicateBindingWithSubcomponentDeclaration()998   public void duplicateBindingWithSubcomponentDeclaration() {
999     JavaFileObject module =
1000         JavaFileObjects.forSourceLines(
1001             "test.TestModule",
1002             "package test;",
1003             "",
1004             "import dagger.Module;",
1005             "import dagger.Provides;",
1006             "",
1007             "@Module(subcomponents = Sub.class)",
1008             "class TestModule {",
1009             "  @Provides Sub.Builder providesConflictsWithModuleSubcomponents() { return null; }",
1010             "  @Provides Object usesSubcomponentBuilder(Sub.Builder builder) {",
1011             "    return new Builder().toString();",
1012             "  }",
1013             "}");
1014 
1015     JavaFileObject subcomponent =
1016         JavaFileObjects.forSourceLines(
1017             "test.Sub",
1018             "package test;",
1019             "",
1020             "import dagger.Subcomponent;",
1021             "",
1022             "@Subcomponent",
1023             "interface Sub {",
1024             "  @Subcomponent.Builder",
1025             "  interface Builder {",
1026             "    Sub build();",
1027             "  }",
1028             "}");
1029 
1030     JavaFileObject component =
1031         JavaFileObjects.forSourceLines(
1032             "test.Sub",
1033             "package test;",
1034             "",
1035             "import dagger.Component;",
1036             "",
1037             "@Component(modules = TestModule.class)",
1038             "interface C {",
1039             "  Object dependsOnBuilder();",
1040             "}");
1041 
1042     Compilation compilation =
1043         compilerWithOptions(compilerMode.javacopts())
1044             .compile(module, component, subcomponent);
1045     assertThat(compilation).failed();
1046     assertThat(compilation).hadErrorContaining("Sub.Builder is bound multiple times:");
1047     assertThat(compilation)
1048         .hadErrorContaining(
1049             "@Provides Sub.Builder "
1050                 + "TestModule.providesConflictsWithModuleSubcomponents()");
1051     assertThat(compilation)
1052         .hadErrorContaining("@Module(subcomponents = Sub.class) for TestModule");
1053   }
1054 
1055   @Test
subcomponentDependsOnGeneratedType()1056   public void subcomponentDependsOnGeneratedType() {
1057     JavaFileObject parent =
1058         JavaFileObjects.forSourceLines(
1059             "test.Parent",
1060             "package test;",
1061             "",
1062             "import dagger.Component;",
1063             "",
1064             "@Component",
1065             "interface Parent {",
1066             "  Child.Builder childBuilder();",
1067             "}");
1068 
1069     JavaFileObject child =
1070         JavaFileObjects.forSourceLines(
1071             "test.Child",
1072             "package test;",
1073             "",
1074             "import dagger.Subcomponent;",
1075             "",
1076             "@Subcomponent",
1077             "interface Child extends ChildSupertype {",
1078             "  @Subcomponent.Builder",
1079             "  interface Builder {",
1080             "    Child build();",
1081             "  }",
1082             "}");
1083 
1084     JavaFileObject childSupertype =
1085         JavaFileObjects.forSourceLines(
1086             "test.ChildSupertype",
1087             "package test;",
1088             "",
1089             "interface ChildSupertype {",
1090             "  GeneratedType generatedType();",
1091             "}");
1092 
1093     Compilation compilation =
1094         daggerCompiler(
1095                 new GeneratingProcessor(
1096                     "test.GeneratedType",
1097                     "package test;",
1098                     "",
1099                     "import javax.inject.Inject;",
1100                     "",
1101                     "final class GeneratedType {",
1102                     "  @Inject GeneratedType() {}",
1103                     "}"))
1104             .compile(parent, child, childSupertype);
1105     assertThat(compilation).succeededWithoutWarnings();
1106   }
1107 }
1108