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