• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.daggerCompiler;
21 import static dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatModuleMethod;
22 
23 import com.google.testing.compile.Compilation;
24 import com.google.testing.compile.JavaFileObjects;
25 import dagger.Module;
26 import dagger.producers.ProducerModule;
27 import java.lang.annotation.Annotation;
28 import java.util.Arrays;
29 import java.util.Collection;
30 import javax.tools.JavaFileObject;
31 import org.junit.Test;
32 import org.junit.runner.RunWith;
33 import org.junit.runners.Parameterized;
34 
35 @RunWith(Parameterized.class)
36 public final class ModuleValidationTest {
37 
38   @Parameterized.Parameters(name = "{0}")
parameters()39   public static Collection<Object[]> parameters() {
40     return Arrays.asList(new Object[][] {{ModuleType.MODULE}, {ModuleType.PRODUCER_MODULE}});
41   }
42 
43   private enum ModuleType {
44     MODULE(Module.class),
45     PRODUCER_MODULE(ProducerModule.class),
46     ;
47 
48     private final Class<? extends Annotation> annotation;
49 
ModuleType(Class<? extends Annotation> annotation)50     ModuleType(Class<? extends Annotation> annotation) {
51       this.annotation = annotation;
52     }
53 
annotationWithSubcomponent(String subcomponent)54     String annotationWithSubcomponent(String subcomponent) {
55       return String.format("@%s(subcomponents = %s)", annotation.getSimpleName(), subcomponent);
56     }
57 
importStatement()58     String importStatement() {
59       return String.format("import %s;", annotation.getName());
60     }
61 
simpleName()62     String simpleName() {
63       return annotation.getSimpleName();
64     }
65   }
66 
67   private final ModuleType moduleType;
68 
ModuleValidationTest(ModuleType moduleType)69   public ModuleValidationTest(ModuleType moduleType) {
70     this.moduleType = moduleType;
71   }
72 
73   @Test
moduleSubcomponents_notASubcomponent()74   public void moduleSubcomponents_notASubcomponent() {
75     JavaFileObject module =
76         JavaFileObjects.forSourceLines(
77             "test.TestModule",
78             "package test;",
79             "",
80             moduleType.importStatement(),
81             "",
82             moduleType.annotationWithSubcomponent("NotASubcomponent.class"),
83             "class TestModule {}");
84     JavaFileObject notASubcomponent =
85         JavaFileObjects.forSourceLines(
86             "test.NotASubcomponent", "package test;", "", "class NotASubcomponent {}");
87     Compilation compilation = daggerCompiler().compile(module, notASubcomponent);
88     assertThat(compilation).failed();
89     assertThat(compilation)
90         .hadErrorContaining(
91             "test.NotASubcomponent is not a @Subcomponent or @ProductionSubcomponent")
92         .inFile(module)
93         .onLine(5);
94   }
95 
96   @Test
moduleSubcomponents_listsSubcomponentBuilder()97   public void moduleSubcomponents_listsSubcomponentBuilder() {
98     JavaFileObject module =
99         JavaFileObjects.forSourceLines(
100             "test.TestModule",
101             "package test;",
102             "",
103             moduleType.importStatement(),
104             "",
105             moduleType.annotationWithSubcomponent("Sub.Builder.class"),
106             "class TestModule {}");
107     JavaFileObject subcomponent =
108         JavaFileObjects.forSourceLines(
109             "test.Sub",
110             "package test;",
111             "",
112             "import dagger.Subcomponent;",
113             "",
114             "@Subcomponent",
115             "interface Sub {",
116             "  @Subcomponent.Builder",
117             "  interface Builder {",
118             "    Sub build();",
119             "  }",
120             "}");
121     Compilation compilation = daggerCompiler().compile(module, subcomponent);
122     assertThat(compilation).failed();
123     assertThat(compilation)
124         .hadErrorContaining(
125             "test.Sub.Builder is a @Subcomponent.Builder. Did you mean to use test.Sub?")
126         .inFile(module)
127         .onLine(5);
128   }
129 
130   @Test
moduleSubcomponents_listsSubcomponentFactory()131   public void moduleSubcomponents_listsSubcomponentFactory() {
132     JavaFileObject module =
133         JavaFileObjects.forSourceLines(
134             "test.TestModule",
135             "package test;",
136             "",
137             moduleType.importStatement(),
138             "",
139             moduleType.annotationWithSubcomponent("Sub.Factory.class"),
140             "class TestModule {}");
141     JavaFileObject subcomponent =
142         JavaFileObjects.forSourceLines(
143             "test.Sub",
144             "package test;",
145             "",
146             "import dagger.Subcomponent;",
147             "",
148             "@Subcomponent",
149             "interface Sub {",
150             "  @Subcomponent.Factory",
151             "  interface Factory {",
152             "    Sub creator();",
153             "  }",
154             "}");
155     Compilation compilation = daggerCompiler().compile(module, subcomponent);
156     assertThat(compilation).failed();
157     assertThat(compilation)
158         .hadErrorContaining(
159             "test.Sub.Factory is a @Subcomponent.Factory. Did you mean to use test.Sub?")
160         .inFile(module)
161         .onLine(5);
162   }
163 
164   @Test
moduleSubcomponents_listsProductionSubcomponentBuilder()165   public void moduleSubcomponents_listsProductionSubcomponentBuilder() {
166     JavaFileObject module =
167         JavaFileObjects.forSourceLines(
168             "test.TestModule",
169             "package test;",
170             "",
171             moduleType.importStatement(),
172             "",
173             moduleType.annotationWithSubcomponent("Sub.Builder.class"),
174             "class TestModule {}");
175     JavaFileObject subcomponent =
176         JavaFileObjects.forSourceLines(
177             "test.Sub",
178             "package test;",
179             "",
180             "import dagger.producers.ProductionSubcomponent;",
181             "",
182             "@ProductionSubcomponent",
183             "interface Sub {",
184             "  @ProductionSubcomponent.Builder",
185             "  interface Builder {",
186             "    Sub build();",
187             "  }",
188             "}");
189     Compilation compilation = daggerCompiler().compile(module, subcomponent);
190     assertThat(compilation).failed();
191     assertThat(compilation)
192         .hadErrorContaining(
193             "test.Sub.Builder is a @ProductionSubcomponent.Builder. Did you mean to use test.Sub?")
194         .inFile(module)
195         .onLine(5);
196   }
197 
198   @Test
moduleSubcomponents_listsProductionSubcomponentFactory()199   public void moduleSubcomponents_listsProductionSubcomponentFactory() {
200     JavaFileObject module =
201         JavaFileObjects.forSourceLines(
202             "test.TestModule",
203             "package test;",
204             "",
205             moduleType.importStatement(),
206             "",
207             moduleType.annotationWithSubcomponent("Sub.Factory.class"),
208             "class TestModule {}");
209     JavaFileObject subcomponent =
210         JavaFileObjects.forSourceLines(
211             "test.Sub",
212             "package test;",
213             "",
214             "import dagger.producers.ProductionSubcomponent;",
215             "",
216             "@ProductionSubcomponent",
217             "interface Sub {",
218             "  @ProductionSubcomponent.Factory",
219             "  interface Factory {",
220             "    Sub create();",
221             "  }",
222             "}");
223     Compilation compilation = daggerCompiler().compile(module, subcomponent);
224     assertThat(compilation).failed();
225     assertThat(compilation)
226         .hadErrorContaining(
227             "test.Sub.Factory is a @ProductionSubcomponent.Factory. Did you mean to use test.Sub?")
228         .inFile(module)
229         .onLine(5);
230   }
231 
232   @Test
moduleSubcomponents_noSubcomponentCreator()233   public void moduleSubcomponents_noSubcomponentCreator() {
234     JavaFileObject module =
235         JavaFileObjects.forSourceLines(
236             "test.TestModule",
237             "package test;",
238             "",
239             moduleType.importStatement(),
240             "",
241             moduleType.annotationWithSubcomponent("NoBuilder.class"),
242             "class TestModule {}");
243     JavaFileObject subcomponent =
244         JavaFileObjects.forSourceLines(
245             "test.NoBuilder",
246             "package test;",
247             "",
248             "import dagger.Subcomponent;",
249             "",
250             "@Subcomponent",
251             "interface NoBuilder {}");
252     Compilation compilation = daggerCompiler().compile(module, subcomponent);
253     assertThat(compilation).failed();
254     assertThat(compilation)
255         .hadErrorContaining(
256             "test.NoBuilder doesn't have a @Subcomponent.Builder or @Subcomponent.Factory, which "
257                 + "is required when used with @"
258                 + moduleType.simpleName()
259                 + ".subcomponents")
260         .inFile(module)
261         .onLine(5);
262   }
263 
264   @Test
moduleSubcomponents_noProductionSubcomponentCreator()265   public void moduleSubcomponents_noProductionSubcomponentCreator() {
266     JavaFileObject module =
267         JavaFileObjects.forSourceLines(
268             "test.TestModule",
269             "package test;",
270             "",
271             moduleType.importStatement(),
272             "",
273             moduleType.annotationWithSubcomponent("NoBuilder.class"),
274             "class TestModule {}");
275     JavaFileObject subcomponent =
276         JavaFileObjects.forSourceLines(
277             "test.NoBuilder",
278             "package test;",
279             "",
280             "import dagger.producers.ProductionSubcomponent;",
281             "",
282             "@ProductionSubcomponent",
283             "interface NoBuilder {}");
284     Compilation compilation = daggerCompiler().compile(module, subcomponent);
285     assertThat(compilation).failed();
286     assertThat(compilation)
287         .hadErrorContaining(
288             "test.NoBuilder doesn't have a @ProductionSubcomponent.Builder or "
289                 + "@ProductionSubcomponent.Factory, which is required when used with @"
290                 + moduleType.simpleName()
291                 + ".subcomponents")
292         .inFile(module)
293         .onLine(5);
294   }
295 
296   @Test
moduleSubcomponentsAreTypes()297   public void moduleSubcomponentsAreTypes() {
298     JavaFileObject module =
299         JavaFileObjects.forSourceLines(
300             "test.TestModule",
301             "package test;",
302             "",
303             "import dagger.Module;",
304             "",
305             "@Module(subcomponents = int.class)",
306             "class TestModule {}");
307     Compilation compilation = daggerCompiler().compile(module);
308     assertThat(compilation).failed();
309     assertThat(compilation)
310         .hadErrorContaining("int is not a valid subcomponent type")
311         .inFile(module)
312         .onLine(5);
313   }
314 
315   @Test
tooManyAnnotations()316   public void tooManyAnnotations() {
317     assertThatModuleMethod(
318             "@BindsOptionalOf @Multibinds abstract Set<Object> tooManyAnnotations();")
319         .hasError("is annotated with more than one of");
320   }
321 
322   @Test
invalidIncludedModule()323   public void invalidIncludedModule() {
324     JavaFileObject badModule =
325         JavaFileObjects.forSourceLines(
326             "test.BadModule",
327             "package test;",
328             "",
329             "import dagger.Binds;",
330             "import dagger.Module;",
331             "",
332             "@Module",
333             "abstract class BadModule {",
334             "  @Binds abstract Object noParameters();",
335             "}");
336     JavaFileObject module =
337         JavaFileObjects.forSourceLines(
338             "test.IncludesBadModule",
339             "package test;",
340             "",
341             "import dagger.Module;",
342             "",
343             "@Module(includes = BadModule.class)",
344             "abstract class IncludesBadModule {}");
345     Compilation compilation = daggerCompiler().compile(badModule, module);
346     assertThat(compilation).hadErrorCount(2);
347     assertThat(compilation)
348         .hadErrorContaining("test.BadModule has errors")
349         .inFile(module)
350         .onLine(5);
351     assertThat(compilation)
352         .hadErrorContaining(
353             "@Binds methods must have exactly one parameter, whose type is assignable to the "
354                 + "return type")
355         .inFile(badModule)
356         .onLine(8);
357   }
358 
359   @Test
scopeOnModule()360   public void scopeOnModule() {
361     JavaFileObject badModule =
362         JavaFileObjects.forSourceLines(
363             "test.BadModule",
364             "package test;",
365             "",
366             "import dagger.Module;",
367             "import javax.inject.Singleton;",
368             "",
369             "@Singleton",
370             "@Module",
371             "interface BadModule {}");
372     Compilation compilation = daggerCompiler().compile(badModule);
373     assertThat(compilation).failed();
374     assertThat(compilation)
375         .hadErrorContaining("@Modules cannot be scoped")
376         .inFile(badModule)
377         .onLineContaining("@Singleton");
378   }
379 
380   @Test
moduleIncludesSelfCycle()381   public void moduleIncludesSelfCycle() {
382     JavaFileObject module =
383         JavaFileObjects.forSourceLines(
384             "test.TestModule",
385             "package test;",
386             "",
387             moduleType.importStatement(),
388             "import dagger.Provides;",
389             "",
390             String.format("@%s(", moduleType.simpleName()),
391             "  includes = {",
392             "      TestModule.class, // first",
393             "      OtherModule.class,",
394             "      TestModule.class, // second",
395             "  }",
396             ")",
397             "class TestModule {",
398             "  @Provides int i() { return 0; }",
399             "}");
400 
401     JavaFileObject otherModule =
402         JavaFileObjects.forSourceLines(
403             "test.OtherModule",
404             "package test;",
405             "",
406             "import dagger.Module;",
407             "",
408             "@Module",
409             "class OtherModule {}");
410 
411     Compilation compilation = daggerCompiler().compile(module, otherModule);
412     assertThat(compilation).failed();
413     String error = String.format("@%s cannot include themselves", moduleType.simpleName());
414     assertThat(compilation).hadErrorContaining(error).inFile(module).onLineContaining("Module(");
415   }
416 }
417