1 /*
2  * Copyright (C) 2017 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.Compilers.compilerWithOptions;
21 import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
22 
23 import com.google.testing.compile.Compilation;
24 import com.google.testing.compile.JavaFileObjects;
25 import java.util.Collection;
26 import javax.tools.JavaFileObject;
27 import org.junit.Test;
28 import org.junit.runner.RunWith;
29 import org.junit.runners.Parameterized;
30 import org.junit.runners.Parameterized.Parameters;
31 
32 @RunWith(Parameterized.class)
33 public class ComponentRequirementFieldTest {
34   @Parameters(name = "{0}")
parameters()35   public static Collection<Object[]> parameters() {
36     return CompilerMode.TEST_PARAMETERS;
37   }
38 
39   private final CompilerMode compilerMode;
40 
ComponentRequirementFieldTest(CompilerMode compilerMode)41   public ComponentRequirementFieldTest(CompilerMode compilerMode) {
42     this.compilerMode = compilerMode;
43   }
44 
45   @Test
bindsInstance()46   public void bindsInstance() {
47     JavaFileObject component =
48         JavaFileObjects.forSourceLines(
49             "test.TestComponent",
50             "package test;",
51             "",
52             "import dagger.BindsInstance;",
53             "import dagger.Component;",
54             "import java.util.List;",
55             "",
56             "@Component",
57             "interface TestComponent {",
58             "  int i();",
59             "  List<String> list();",
60             "",
61             "  @Component.Builder",
62             "  interface Builder {",
63             "    @BindsInstance Builder i(int i);",
64             "    @BindsInstance Builder list(List<String> list);",
65             "    TestComponent build();",
66             "  }",
67             "}");
68     Compilation compilation =
69         compilerWithOptions(compilerMode.javacopts()).compile(component);
70     assertThat(compilation).succeeded();
71     assertThat(compilation)
72         .generatedSourceFile("test.DaggerTestComponent")
73         .containsElementsIn(
74             JavaFileObjects.forSourceLines(
75                 "test.DaggerTestComponent",
76                 "package test;",
77                 "",
78                 GENERATED_CODE_ANNOTATIONS,
79                 "final class DaggerTestComponent implements TestComponent {",
80                 "  private final Integer i;",
81                 "  private final List<String> list;",
82                 "",
83                 "  private DaggerTestComponent(Integer iParam, List<String> listParam) {",
84                 "    this.i = iParam;",
85                 "    this.list = listParam;",
86                 "  }",
87                 "",
88                 "  @Override",
89                 "  public int i() {",
90                 "    return i;",
91                 "  }",
92                 "",
93                 "  @Override",
94                 "  public List<String> list() {",
95                 "    return list;",
96                 "  }",
97                 "",
98                 "  private static final class Builder implements TestComponent.Builder {",
99                 "    private Integer i;",
100                 "    private List<String> list;",
101                 "",
102                 "    @Override",
103                 "    public Builder i(int i) {",
104                 "      this.i = Preconditions.checkNotNull(i);",
105                 "      return this;",
106                 "    }",
107                 "",
108                 "    @Override",
109                 "    public Builder list(List<String> list) {",
110                 "      this.list = Preconditions.checkNotNull(list);",
111                 "      return this;",
112                 "    }",
113                 "",
114                 "    @Override",
115                 "    public TestComponent build() {",
116                 "      Preconditions.checkBuilderRequirement(i, Integer.class);",
117                 "      Preconditions.checkBuilderRequirement(list, List.class);",
118                 "      return new DaggerTestComponent(i, list);",
119                 "    }",
120                 "  }",
121                 "}"));
122   }
123 
124   @Test
instanceModuleMethod()125   public void instanceModuleMethod() {
126     JavaFileObject module =
127         JavaFileObjects.forSourceLines(
128             "test.ParentModule",
129             "package test;",
130             "",
131             "import dagger.Module;",
132             "import dagger.Provides;",
133             "",
134             "@Module",
135             "class ParentModule {",
136             "  @Provides int i() { return 0; }",
137             "}");
138     JavaFileObject otherPackageModule =
139         JavaFileObjects.forSourceLines(
140             "other.OtherPackageModule",
141             "package other;",
142             "",
143             "import dagger.Module;",
144             "import dagger.Provides;",
145             "",
146             "@Module",
147             "public class OtherPackageModule {",
148             "  @Provides long l() { return 0L; }",
149             "}");
150     JavaFileObject component =
151         JavaFileObjects.forSourceLines(
152             "test.TestComponent",
153             "package test;",
154             "",
155             "import dagger.Component;",
156             "import other.OtherPackageModule;",
157             "",
158             "@Component(modules = {ParentModule.class, OtherPackageModule.class})",
159             "interface TestComponent {",
160             "  int i();",
161             "  long l();",
162             "}");
163     Compilation compilation =
164         compilerWithOptions(compilerMode.javacopts())
165             .compile(module, otherPackageModule, component);
166     assertThat(compilation).succeeded();
167     JavaFileObject generatedComponent =
168         JavaFileObjects.forSourceLines(
169             "test.DaggerTestComponent",
170             "package test;",
171             "",
172             "import other.OtherPackageModule;",
173             "import other.OtherPackageModule_LFactory;",
174             "",
175             GENERATED_CODE_ANNOTATIONS,
176             "final class DaggerTestComponent implements TestComponent {",
177             "  private final ParentModule parentModule;",
178             "  private final OtherPackageModule otherPackageModule;",
179             "",
180             "  @Override",
181             "  public int i() {",
182             "    return parentModule.i();",
183             "  }",
184             "",
185             "  @Override",
186             "  public long l() {",
187             "    return OtherPackageModule_LFactory.l(otherPackageModule);",
188             "  }",
189             "}");
190     assertThat(compilation)
191         .generatedSourceFile("test.DaggerTestComponent")
192         .containsElementsIn(generatedComponent);
193   }
194 
195   @Test
componentInstances()196   public void componentInstances() {
197     JavaFileObject dependency =
198         JavaFileObjects.forSourceLines(
199             "test.Dep",
200             "package test;",
201             "",
202             "interface Dep {",
203             "  String string();",
204             "  Object object();",
205             "}");
206 
207     JavaFileObject component =
208         JavaFileObjects.forSourceLines(
209             "test.TestComponent",
210             "package test;",
211             "",
212             "import dagger.Component;",
213             "",
214             "@Component(dependencies = Dep.class)",
215             "interface TestComponent {",
216             "  TestComponent self();",
217             "  TestSubcomponent subcomponent();",
218             "",
219             "  Dep dep();",
220             "  String methodOnDep();",
221             "  Object otherMethodOnDep();",
222             "}");
223     JavaFileObject subcomponent =
224         JavaFileObjects.forSourceLines(
225             "test.TestComponent",
226             "package test;",
227             "",
228             "import dagger.Subcomponent;",
229             "",
230             "@Subcomponent",
231             "interface TestSubcomponent {",
232             "  TestComponent parent();",
233             "  Dep depFromSubcomponent();",
234             "}");
235 
236     Compilation compilation =
237         compilerWithOptions(compilerMode.javacopts())
238             .compile(dependency, component, subcomponent);
239     assertThat(compilation).succeeded();
240     assertThat(compilation)
241         .generatedSourceFile("test.DaggerTestComponent")
242         .containsElementsIn(
243             JavaFileObjects.forSourceLines(
244                 "test.DaggerTestComponent",
245                 "package test;",
246                 "",
247                 GENERATED_CODE_ANNOTATIONS,
248                 "final class DaggerTestComponent implements TestComponent {",
249                 "  private final Dep dep;",
250                 "",
251                 "  private DaggerTestComponent(Dep depParam) {",
252                 "    this.dep = depParam;",
253                 "  }",
254                 "",
255                 "  @Override",
256                 "  public TestComponent self() {",
257                 "    return this;",
258                 "  }",
259                 "",
260                 "  @Override",
261                 "  public Dep dep() {",
262                 "    return dep;",
263                 "  }",
264                 "",
265                 "  @Override",
266                 "  public String methodOnDep() {",
267                 "    return Preconditions.checkNotNullFromComponent(",
268                 "        dep.string());",
269                 "  }",
270                 "",
271                 "  @Override",
272                 "  public Object otherMethodOnDep() {",
273                 "    return Preconditions.checkNotNullFromComponent(dep.object());",
274                 "  }",
275                 "",
276                 "  private final class TestSubcomponentImpl implements TestSubcomponent {",
277                 "    @Override",
278                 "    public TestComponent parent() {",
279                 "      return DaggerTestComponent.this;",
280                 "    }",
281                 "",
282                 "    @Override",
283                 "    public Dep depFromSubcomponent() {",
284                 "      return DaggerTestComponent.this.dep;",
285                 "    }",
286                 "  }",
287                 "}"));
288   }
289 
290   @Test
componentRequirementNeededInFactoryCreationOfSubcomponent()291   public void componentRequirementNeededInFactoryCreationOfSubcomponent() {
292     JavaFileObject parentModule =
293         JavaFileObjects.forSourceLines(
294             "test.ParentModule",
295             "package test;",
296             "",
297             "import dagger.Module;",
298             "import dagger.multibindings.IntoSet;",
299             "import dagger.Provides;",
300             "import java.util.Set;",
301             "",
302             "@Module",
303             "class ParentModule {",
304             "  @Provides",
305             // intentionally non-static. this needs to require the module when the subcompnent
306             // adds to the Set binding
307             "  Object reliesOnMultibinding(Set<Object> set) { return set; }",
308             "",
309             "  @Provides @IntoSet static Object contribution() { return new Object(); }",
310             "}");
311 
312     JavaFileObject childModule =
313         JavaFileObjects.forSourceLines(
314             "test.ChildModule",
315             "package test;",
316             "",
317             "import dagger.Module;",
318             "import dagger.multibindings.IntoSet;",
319             "import dagger.Provides;",
320             "",
321             "@Module",
322             "class ChildModule {",
323             "  @Provides @IntoSet static Object contribution() { return new Object(); }",
324             "}");
325 
326     JavaFileObject component =
327         JavaFileObjects.forSourceLines(
328             "test.TestComponent",
329             "package test;",
330             "",
331             "import dagger.Component;",
332             "import javax.inject.Provider;",
333             "",
334             "@Component(modules = ParentModule.class)",
335             "interface TestComponent {",
336             "  Provider<Object> dependsOnMultibinding();",
337             "  TestSubcomponent subcomponent();",
338             "}");
339 
340     JavaFileObject subcomponent =
341         JavaFileObjects.forSourceLines(
342             "test.TestSubcomponent",
343             "package test;",
344             "",
345             "import dagger.Subcomponent;",
346             "import javax.inject.Provider;",
347             "",
348             "@Subcomponent(modules = ChildModule.class)",
349             "interface TestSubcomponent {",
350             "  Provider<Object> dependsOnMultibinding();",
351             "}");
352     JavaFileObject generatedComponent;
353     switch (compilerMode) {
354       case FAST_INIT_MODE:
355         generatedComponent =
356             JavaFileObjects.forSourceLines(
357                 "test.DaggerTestComponent",
358                 "package test;",
359                 "",
360                 GENERATED_CODE_ANNOTATIONS,
361                 "final class DaggerTestComponent implements TestComponent {",
362                 "  private final ParentModule parentModule;",
363                 "",
364                 "  private DaggerTestComponent(ParentModule parentModuleParam) {",
365                 "    this.parentModule = parentModuleParam;",
366                 "  }",
367                 "",
368                 "  private final class TestSubcomponentImpl implements TestSubcomponent {",
369                 "    private Set<Object> setOfObject() {",
370                 "      return ImmutableSet.<Object>of(",
371                 "          ParentModule_ContributionFactory.contribution(),",
372                 "          ChildModule_ContributionFactory.contribution());",
373                 "    }",
374                 "",
375                 "    private Object object() {",
376                 "      return ParentModule_ReliesOnMultibindingFactory.reliesOnMultibinding(",
377                 "          DaggerTestComponent.this.parentModule, setOfObject());",
378                 "    }",
379                 "  }",
380                 "}");
381         break;
382       default:
383         generatedComponent =
384             JavaFileObjects.forSourceLines(
385                 "test.DaggerTestComponent",
386                 "package test;",
387                 "",
388                 GENERATED_CODE_ANNOTATIONS,
389                 "final class DaggerTestComponent implements TestComponent {",
390                 "  private final ParentModule parentModule;",
391                 "",
392                 "  private DaggerTestComponent(ParentModule parentModuleParam) {",
393                 "    this.parentModule = parentModuleParam;",
394                 "    initialize(parentModuleParam);",
395                 "  }",
396                 "",
397                 "  @SuppressWarnings(\"unchecked\")",
398                 "  private void initialize(final ParentModule parentModuleParam) {",
399                 "    this.setOfObjectProvider =",
400                 "        SetFactory.<Object>builder(1, 0)",
401                 "            .addProvider(ParentModule_ContributionFactory.create())",
402                 "            .build();",
403                 "    this.reliesOnMultibindingProvider =",
404                 "        ParentModule_ReliesOnMultibindingFactory.create(",
405                 "            parentModuleParam, setOfObjectProvider);",
406                 "  }",
407                 "",
408                 "  private final class TestSubcomponentImpl implements TestSubcomponent {",
409                 "    @SuppressWarnings(\"unchecked\")",
410                 "    private void initialize() {",
411                 "      this.setOfObjectProvider =",
412                 "          SetFactory.<Object>builder(2, 0)",
413                 "              .addProvider(ParentModule_ContributionFactory.create())",
414                 "              .addProvider(ChildModule_ContributionFactory.create())",
415                 "              .build();",
416                 "      this.reliesOnMultibindingProvider =",
417                 "          ParentModule_ReliesOnMultibindingFactory.create(",
418                 "              DaggerTestComponent.this.parentModule, setOfObjectProvider);",
419                 "    }",
420                 "  }",
421                 "}");
422     }
423     Compilation compilation =
424         compilerWithOptions(compilerMode.javacopts())
425             .compile(parentModule, childModule, component, subcomponent);
426     assertThat(compilation).succeeded();
427     assertThat(compilation)
428         .generatedSourceFile("test.DaggerTestComponent")
429         .containsElementsIn(generatedComponent);
430   }
431 }
432