• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 Google, Inc.
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 package dagger.internal.codegen;
17 
18 import com.google.common.collect.ImmutableList;
19 import com.google.testing.compile.JavaFileObjects;
20 import javax.tools.JavaFileObject;
21 import org.junit.Test;
22 import org.junit.runner.RunWith;
23 import org.junit.runners.JUnit4;
24 
25 import static com.google.common.truth.Truth.assertAbout;
26 import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
27 
28 @RunWith(JUnit4.class)
29 public final class SubcomponentValidationTest {
factoryMethod_missingModulesWithParameters()30   @Test public void factoryMethod_missingModulesWithParameters() {
31     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
32         "package test;",
33         "",
34         "import dagger.Component;",
35         "",
36         "@Component",
37         "interface TestComponent {",
38         "  ChildComponent newChildComponent();",
39         "}");
40     JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
41         "package test;",
42         "",
43         "import dagger.Subcomponent;",
44         "",
45         "@Subcomponent(modules = ModuleWithParameters.class)",
46         "interface ChildComponent {",
47         "  Object object();",
48         "}");
49     JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.ModuleWithParameters",
50         "package test;",
51         "",
52         "import dagger.Module;",
53         "import dagger.Provides;",
54         "",
55         "@Module",
56         "final class ModuleWithParameters {",
57         "  private final Object object;",
58         "",
59         "  ModuleWithParameters(Object object) {",
60         "    this.object = object;",
61         "  }",
62         "",
63         "  @Provides Object object() {",
64         "    return object;",
65         "  }",
66         "}");
67     assertAbout(javaSources()).that(ImmutableList.of(componentFile, childComponentFile, moduleFile))
68         .processedWith(new ComponentProcessor())
69         .failsToCompile()
70         .withErrorContaining(
71             "test.ChildComponent requires modules which have no visible default constructors. "
72                 + "Add the following modules as parameters to this method: "
73                 + "test.ModuleWithParameters")
74         .in(componentFile).onLine(7);
75   }
76 
factoryMethod_nonModuleParameter()77   @Test public void factoryMethod_nonModuleParameter() {
78     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
79         "package test;",
80         "",
81         "import dagger.Component;",
82         "",
83         "@Component",
84         "interface TestComponent {",
85         "  ChildComponent newChildComponent(String someRandomString);",
86         "}");
87     JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
88         "package test;",
89         "",
90         "import dagger.Subcomponent;",
91         "",
92         "@Subcomponent",
93         "interface ChildComponent {}");
94     assertAbout(javaSources()).that(ImmutableList.of(componentFile, childComponentFile))
95         .processedWith(new ComponentProcessor())
96         .failsToCompile()
97         .withErrorContaining(
98             "Subcomponent factory methods may only accept modules, but java.lang.String is not.")
99         .in(componentFile).onLine(7).atColumn(43);
100   }
101 
factoryMethod_duplicateParameter()102   @Test public void factoryMethod_duplicateParameter() {
103     JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
104         "package test;",
105         "",
106         "import dagger.Module;",
107         "",
108         "@Module",
109         "final class TestModule {}");
110     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
111         "package test;",
112         "",
113         "import dagger.Component;",
114         "",
115         "@Component",
116         "interface TestComponent {",
117         "  ChildComponent newChildComponent(TestModule testModule1, TestModule testModule2);",
118         "}");
119     JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
120         "package test;",
121         "",
122         "import dagger.Subcomponent;",
123         "",
124         "@Subcomponent(modules = TestModule.class)",
125         "interface ChildComponent {}");
126     assertAbout(javaSources()).that(ImmutableList.of(moduleFile, componentFile, childComponentFile))
127         .processedWith(new ComponentProcessor())
128         .failsToCompile()
129         .withErrorContaining(
130             "A module may only occur once an an argument in a Subcomponent factory method, "
131                 + "but test.TestModule was already passed.")
132         .in(componentFile).onLine(7).atColumn(71);
133   }
134 
factoryMethod_superflouousModule()135   @Test public void factoryMethod_superflouousModule() {
136     JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
137         "package test;",
138         "",
139         "import dagger.Module;",
140         "",
141         "@Module",
142         "final class TestModule {}");
143     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
144         "package test;",
145         "",
146         "import dagger.Component;",
147         "",
148         "@Component",
149         "interface TestComponent {",
150         "  ChildComponent newChildComponent(TestModule testModule);",
151         "}");
152     JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
153         "package test;",
154         "",
155         "import dagger.Subcomponent;",
156         "",
157         "@Subcomponent",
158         "interface ChildComponent {}");
159     assertAbout(javaSources()).that(ImmutableList.of(moduleFile, componentFile, childComponentFile))
160     .processedWith(new ComponentProcessor())
161     .failsToCompile()
162     .withErrorContaining(
163         "test.TestModule is present as an argument to the test.ChildComponent factory method, but "
164             + "is not one of the modules used to implement the subcomponent.")
165                 .in(componentFile).onLine(7);
166   }
167 
missingBinding()168   @Test public void missingBinding() {
169     JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
170         "package test;",
171         "",
172         "import dagger.Module;",
173         "import dagger.Provides;",
174         "",
175         "@Module",
176         "final class TestModule {",
177         "  @Provides String provideString(int i) {",
178         "    return Integer.toString(i);",
179         "  }",
180         "}");
181     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
182         "package test;",
183         "",
184         "import dagger.Component;",
185         "",
186         "@Component",
187         "interface TestComponent {",
188         "  ChildComponent newChildComponent();",
189         "}");
190     JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
191         "package test;",
192         "",
193         "import dagger.Subcomponent;",
194         "",
195         "@Subcomponent(modules = TestModule.class)",
196         "interface ChildComponent {",
197         "  String getString();",
198         "}");
199     assertAbout(javaSources()).that(ImmutableList.of(moduleFile, componentFile, childComponentFile))
200         .processedWith(new ComponentProcessor())
201         .failsToCompile()
202         .withErrorContaining(
203             "java.lang.Integer cannot be provided without an @Inject constructor or from an "
204                 + "@Provides-annotated method");
205   }
206 
subcomponentOnConcreteType()207   @Test public void subcomponentOnConcreteType() {
208     JavaFileObject subcomponentFile = JavaFileObjects.forSourceLines("test.NotASubcomponent",
209         "package test;",
210         "",
211         "import dagger.Subcomponent;",
212         "",
213         "@Subcomponent",
214         "final class NotASubcomponent {}");
215     assertAbout(javaSources()).that(ImmutableList.of(subcomponentFile))
216         .processedWith(new ComponentProcessor())
217         .failsToCompile()
218         .withErrorContaining("interface");
219   }
220 
scopeMismatch()221   @Test public void scopeMismatch() {
222     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.ParentComponent",
223         "package test;",
224         "",
225         "import dagger.Component;",
226         "import javax.inject.Singleton;",
227         "",
228         "@Component",
229         "@Singleton",
230         "interface ParentComponent {",
231         "  ChildComponent childComponent();",
232         "}");
233     JavaFileObject subcomponentFile = JavaFileObjects.forSourceLines("test.ChildComponent",
234         "package test;",
235         "",
236         "import dagger.Subcomponent;",
237         "",
238         "@Subcomponent(modules = ChildModule.class)",
239         "interface ChildComponent {",
240         "  Object getObject();",
241         "}");
242     JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.ChildModule",
243         "package test;",
244         "",
245         "import dagger.Module;",
246         "import dagger.Provides;",
247         "import javax.inject.Singleton;",
248         "",
249         "@Module",
250         "final class ChildModule {",
251         "  @Provides @Singleton Object provideObject() { return null; }",
252         "}");
253     assertAbout(javaSources()).that(ImmutableList.of(componentFile, subcomponentFile, moduleFile))
254         .processedWith(new ComponentProcessor())
255         .failsToCompile()
256         .withErrorContaining("@Singleton");
257   }
258 
259   @Test
delegateFactoryNotCreatedForSubcomponentWhenProviderExistsInParent()260   public void delegateFactoryNotCreatedForSubcomponentWhenProviderExistsInParent() {
261     JavaFileObject parentComponentFile =
262         JavaFileObjects.forSourceLines(
263             "test.ParentComponent",
264             "package test;",
265             "",
266             "import dagger.Component;",
267             "",
268             "@Component",
269             "interface ParentComponent {",
270             "  ChildComponent childComponent();",
271             "  Dep1 getDep1();",
272             "  Dep2 getDep2();",
273             "}");
274     JavaFileObject childComponentFile =
275         JavaFileObjects.forSourceLines(
276             "test.ChildComponent",
277             "package test;",
278             "",
279             "import dagger.Subcomponent;",
280             "",
281             "@Subcomponent(modules = ChildModule.class)",
282             "interface ChildComponent {",
283             "  Object getObject();",
284             "}");
285     JavaFileObject childModuleFile =
286         JavaFileObjects.forSourceLines(
287             "test.ChildModule",
288             "package test;",
289             "",
290             "import dagger.Module;",
291             "import dagger.Provides;",
292             "",
293             "@Module",
294             "final class ChildModule {",
295             "  @Provides Object provideObject(A a) { return null; }",
296             "}");
297     JavaFileObject aFile =
298         JavaFileObjects.forSourceLines(
299             "test.A",
300             "package test;",
301             "",
302             "import javax.inject.Inject;",
303             "",
304             "final class A {",
305             "  @Inject public A(NeedsDep1 a, Dep1 b, Dep2 c) { }",
306             "  @Inject public void methodA() { }",
307             "}");
308     JavaFileObject needsDep1File =
309         JavaFileObjects.forSourceLines(
310             "test.NeedsDep1",
311             "package test;",
312             "",
313             "import javax.inject.Inject;",
314             "",
315             "final class NeedsDep1 {",
316             "  @Inject public NeedsDep1(Dep1 d) { }",
317             "}");
318     JavaFileObject dep1File =
319         JavaFileObjects.forSourceLines(
320             "test.Dep1",
321             "package test;",
322             "",
323             "import javax.inject.Inject;",
324             "",
325             "final class Dep1 {",
326             "  @Inject public Dep1() { }",
327             "  @Inject public void dep1Method() { }",
328             "}");
329     JavaFileObject dep2File =
330         JavaFileObjects.forSourceLines(
331             "test.Dep2",
332             "package test;",
333             "",
334             "import javax.inject.Inject;",
335             "",
336             "final class Dep2 {",
337             "  @Inject public Dep2() { }",
338             "  @Inject public void dep2Method() { }",
339             "}");
340 
341     JavaFileObject componentGeneratedFile =
342         JavaFileObjects.forSourceLines(
343             "DaggerParentComponent",
344             "package test;",
345             "",
346             "import dagger.MembersInjector;",
347             "import javax.annotation.Generated;",
348             "import javax.inject.Provider;",
349             "",
350             "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
351             "public final class DaggerParentComponent implements ParentComponent {",
352             "  private MembersInjector<Dep1> dep1MembersInjector;",
353             "  private Provider<Dep1> dep1Provider;",
354             "  private MembersInjector<Dep2> dep2MembersInjector;",
355             "  private Provider<Dep2> dep2Provider;",
356             "",
357             "  private DaggerParentComponent(Builder builder) {  ",
358             "    assert builder != null;",
359             "    initialize(builder);",
360             "  }",
361             "",
362             "  public static Builder builder() {  ",
363             "    return new Builder();",
364             "  }",
365             "",
366             "  public static ParentComponent create() {  ",
367             "    return builder().build();",
368             "  }",
369             "",
370             "  @SuppressWarnings(\"unchecked\")",
371             "  private void initialize(final Builder builder) {  ",
372             "    this.dep1MembersInjector = Dep1_MembersInjector.create();",
373             "    this.dep1Provider = Dep1_Factory.create(dep1MembersInjector);",
374             "    this.dep2MembersInjector = Dep2_MembersInjector.create();",
375             "    this.dep2Provider = Dep2_Factory.create(dep2MembersInjector);",
376             "  }",
377             "",
378             "  @Override",
379             "  public Dep1 getDep1() {  ",
380             "    return dep1Provider.get();",
381             "  }",
382             "",
383             "  @Override",
384             "  public Dep2 getDep2() {  ",
385             "    return dep2Provider.get();",
386             "  }",
387             "",
388             "  @Override",
389             "  public ChildComponent childComponent() {  ",
390             "    return new ChildComponentImpl();",
391             "  }",
392             "",
393             "  public static final class Builder {",
394             "    private Builder() {  ",
395             "    }",
396             "  ",
397             "    public ParentComponent build() {  ",
398             "      return new DaggerParentComponent(this);",
399             "    }",
400             "  }",
401             "",
402             "  private final class ChildComponentImpl implements ChildComponent {",
403             "    private final ChildModule childModule;",
404             "    private MembersInjector<A> aMembersInjector;",
405             "    private Provider<NeedsDep1> needsDep1Provider;",
406             "    private Provider<A> aProvider;",
407             "    private Provider<Object> provideObjectProvider;",
408             "  ",
409             "    private ChildComponentImpl() {  ",
410             "      this.childModule = new ChildModule();",
411             "      initialize();",
412             "    }",
413             "",
414             "    @SuppressWarnings(\"unchecked\")",
415             "    private void initialize() {  ",
416             "      this.aMembersInjector = A_MembersInjector.create();",
417             "      this.needsDep1Provider = NeedsDep1_Factory.create(",
418             "          DaggerParentComponent.this.dep1Provider);",
419             "      this.aProvider = A_Factory.create(",
420             "          aMembersInjector,",
421             "          needsDep1Provider,",
422             "          DaggerParentComponent.this.dep1Provider,",
423             "          DaggerParentComponent.this.dep2Provider);",
424             "      this.provideObjectProvider = ChildModule_ProvideObjectFactory.create(",
425             "          childModule, aProvider);",
426             "    }",
427             "  ",
428             "    @Override",
429             "    public Object getObject() {  ",
430             "      return provideObjectProvider.get();",
431             "    }",
432             "  }",
433             "}");
434     assertAbout(javaSources())
435         .that(
436             ImmutableList.of(
437                 parentComponentFile,
438                 childComponentFile,
439                 childModuleFile,
440                 aFile,
441                 needsDep1File,
442                 dep1File,
443                 dep2File))
444         .processedWith(new ComponentProcessor())
445         .compilesWithoutError()
446         .and()
447         .generatesSources(componentGeneratedFile);
448   }
449 }
450