• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.Compilers.daggerCompiler;
22 import static dagger.internal.codegen.TestUtils.endsWithMessage;
23 
24 import com.google.testing.compile.Compilation;
25 import com.google.testing.compile.JavaFileObjects;
26 import java.util.regex.Pattern;
27 import javax.tools.JavaFileObject;
28 import org.junit.Test;
29 import org.junit.runner.RunWith;
30 import org.junit.runners.JUnit4;
31 
32 @RunWith(JUnit4.class)
33 public final class FullBindingGraphValidationTest {
34   private static final JavaFileObject MODULE_WITH_ERRORS =
35       JavaFileObjects.forSourceLines(
36           "test.ModuleWithErrors",
37           "package test;",
38           "",
39           "import dagger.Binds;",
40           "import dagger.Module;",
41           "",
42           "@Module",
43           "interface ModuleWithErrors {",
44           "  @Binds Object object1(String string);",
45           "  @Binds Object object2(Long l);",
46           "  @Binds Number missingDependency(Integer i);",
47           "}");
48 
49   // Make sure the error doesn't show other bindings or a dependency trace afterwards.
50   private static final Pattern MODULE_WITH_ERRORS_MESSAGE =
51       endsWithMessage(
52           "\033[1;31m[Dagger/DuplicateBindings]\033[0m Object is bound multiple times:",
53           "    @Binds Object ModuleWithErrors.object1(String)",
54           "    @Binds Object ModuleWithErrors.object2(Long)",
55           "    in component: [ModuleWithErrors]",
56           "",
57           "======================",
58           "Full classname legend:",
59           "======================",
60           "ModuleWithErrors: test.ModuleWithErrors",
61           "========================",
62           "End of classname legend:",
63           "========================");
64 
65   private static final Pattern INCLUDES_MODULE_WITH_ERRORS_MESSAGE =
66       endsWithMessage(
67           "\033[1;31m[Dagger/DuplicateBindings]\033[0m Object is bound multiple times:",
68           "    @Binds Object ModuleWithErrors.object1(String)",
69           "    @Binds Object ModuleWithErrors.object2(Long)",
70           "    in component: [IncludesModuleWithErrors]",
71           "",
72           "======================",
73           "Full classname legend:",
74           "======================",
75           "IncludesModuleWithErrors: test.IncludesModuleWithErrors",
76           "ModuleWithErrors:         test.ModuleWithErrors",
77           "========================",
78           "End of classname legend:",
79           "========================");
80 
81 
82   @Test
moduleWithErrors_validationTypeNone()83   public void moduleWithErrors_validationTypeNone() {
84     Compilation compilation = daggerCompiler().compile(MODULE_WITH_ERRORS);
85     assertThat(compilation).succeededWithoutWarnings();
86   }
87 
88   @Test
moduleWithErrors_validationTypeError()89   public void moduleWithErrors_validationTypeError() {
90     Compilation compilation =
91         compilerWithOptions("-Adagger.fullBindingGraphValidation=ERROR")
92             .compile(MODULE_WITH_ERRORS);
93 
94     assertThat(compilation).failed();
95 
96     assertThat(compilation)
97         .hadErrorContainingMatch(MODULE_WITH_ERRORS_MESSAGE)
98         .inFile(MODULE_WITH_ERRORS)
99         .onLineContaining("interface ModuleWithErrors");
100 
101     assertThat(compilation).hadErrorCount(1);
102   }
103 
104   @Test
moduleWithErrors_validationTypeWarning()105   public void moduleWithErrors_validationTypeWarning() {
106     Compilation compilation =
107         compilerWithOptions("-Adagger.fullBindingGraphValidation=WARNING")
108             .compile(MODULE_WITH_ERRORS);
109 
110     assertThat(compilation).succeeded();
111 
112     assertThat(compilation)
113         .hadWarningContainingMatch(MODULE_WITH_ERRORS_MESSAGE)
114         .inFile(MODULE_WITH_ERRORS)
115         .onLineContaining("interface ModuleWithErrors");
116 
117     assertThat(compilation).hadWarningCount(1);
118   }
119 
120   private static final JavaFileObject INCLUDES_MODULE_WITH_ERRORS =
121       JavaFileObjects.forSourceLines(
122           "test.IncludesModuleWithErrors",
123           "package test;",
124           "",
125           "import dagger.Binds;",
126           "import dagger.Module;",
127           "",
128           "@Module(includes = ModuleWithErrors.class)",
129           "interface IncludesModuleWithErrors {}");
130 
131   @Test
includesModuleWithErrors_validationTypeNone()132   public void includesModuleWithErrors_validationTypeNone() {
133     Compilation compilation =
134         daggerCompiler().compile(MODULE_WITH_ERRORS, INCLUDES_MODULE_WITH_ERRORS);
135     assertThat(compilation).succeededWithoutWarnings();
136   }
137 
138   @Test
includesModuleWithErrors_validationTypeError()139   public void includesModuleWithErrors_validationTypeError() {
140     Compilation compilation =
141         compilerWithOptions("-Adagger.fullBindingGraphValidation=ERROR")
142             .compile(MODULE_WITH_ERRORS, INCLUDES_MODULE_WITH_ERRORS);
143 
144     assertThat(compilation).failed();
145 
146     assertThat(compilation)
147         .hadErrorContainingMatch(MODULE_WITH_ERRORS_MESSAGE)
148         .inFile(MODULE_WITH_ERRORS)
149         .onLineContaining("interface ModuleWithErrors");
150 
151     assertThat(compilation)
152         .hadErrorContainingMatch("ModuleWithErrors has errors")
153         .inFile(INCLUDES_MODULE_WITH_ERRORS)
154         .onLineContaining("ModuleWithErrors.class");
155 
156     assertThat(compilation).hadErrorCount(2);
157   }
158 
159   @Test
includesModuleWithErrors_validationTypeWarning()160   public void includesModuleWithErrors_validationTypeWarning() {
161     Compilation compilation =
162         compilerWithOptions("-Adagger.fullBindingGraphValidation=WARNING")
163             .compile(MODULE_WITH_ERRORS, INCLUDES_MODULE_WITH_ERRORS);
164 
165     assertThat(compilation).succeeded();
166 
167     assertThat(compilation)
168         .hadWarningContainingMatch(MODULE_WITH_ERRORS_MESSAGE)
169         .inFile(MODULE_WITH_ERRORS)
170         .onLineContaining("interface ModuleWithErrors");
171 
172     // TODO(b/130284666)
173     assertThat(compilation)
174         .hadWarningContainingMatch(INCLUDES_MODULE_WITH_ERRORS_MESSAGE)
175         .inFile(INCLUDES_MODULE_WITH_ERRORS)
176         .onLineContaining("interface IncludesModuleWithErrors");
177 
178     assertThat(compilation).hadWarningCount(2);
179   }
180 
181   private static final JavaFileObject A_MODULE =
182       JavaFileObjects.forSourceLines(
183           "test.AModule",
184           "package test;",
185           "",
186           "import dagger.Binds;",
187           "import dagger.Module;",
188           "",
189           "@Module",
190           "interface AModule {",
191           "  @Binds Object object(String string);",
192           "}");
193 
194   private static final JavaFileObject COMBINED_WITH_A_MODULE_HAS_ERRORS =
195       JavaFileObjects.forSourceLines(
196           "test.CombinedWithAModuleHasErrors",
197           "package test;",
198           "",
199           "import dagger.Binds;",
200           "import dagger.Module;",
201           "",
202           "@Module(includes = AModule.class)",
203           "interface CombinedWithAModuleHasErrors {",
204           "  @Binds Object object(Long l);",
205           "}");
206 
207   // Make sure the error doesn't show other bindings or a dependency trace afterwards.
208   private static final Pattern COMBINED_WITH_A_MODULE_HAS_ERRORS_MESSAGE =
209       endsWithMessage(
210           "\033[1;31m[Dagger/DuplicateBindings]\033[0m Object is bound multiple times:",
211           "    @Binds Object AModule.object(String)",
212           "    @Binds Object CombinedWithAModuleHasErrors.object(Long)",
213           "    in component: [CombinedWithAModuleHasErrors]",
214           "",
215           "======================",
216           "Full classname legend:",
217           "======================",
218           "AModule:                      test.AModule",
219           "CombinedWithAModuleHasErrors: test.CombinedWithAModuleHasErrors",
220           "========================",
221           "End of classname legend:",
222           "========================");
223 
224   @Test
moduleIncludingModuleWithCombinedErrors_validationTypeNone()225   public void moduleIncludingModuleWithCombinedErrors_validationTypeNone() {
226     Compilation compilation = daggerCompiler().compile(A_MODULE, COMBINED_WITH_A_MODULE_HAS_ERRORS);
227 
228     assertThat(compilation).succeededWithoutWarnings();
229   }
230 
231   @Test
moduleIncludingModuleWithCombinedErrors_validationTypeError()232   public void moduleIncludingModuleWithCombinedErrors_validationTypeError() {
233     Compilation compilation =
234         compilerWithOptions("-Adagger.fullBindingGraphValidation=ERROR")
235             .compile(A_MODULE, COMBINED_WITH_A_MODULE_HAS_ERRORS);
236 
237     assertThat(compilation).failed();
238 
239     assertThat(compilation)
240         .hadErrorContainingMatch(COMBINED_WITH_A_MODULE_HAS_ERRORS_MESSAGE)
241         .inFile(COMBINED_WITH_A_MODULE_HAS_ERRORS)
242         .onLineContaining("interface CombinedWithAModuleHasErrors");
243 
244     assertThat(compilation).hadErrorCount(1);
245   }
246 
247   @Test
moduleIncludingModuleWithCombinedErrors_validationTypeWarning()248   public void moduleIncludingModuleWithCombinedErrors_validationTypeWarning() {
249     Compilation compilation =
250         compilerWithOptions("-Adagger.fullBindingGraphValidation=WARNING")
251             .compile(A_MODULE, COMBINED_WITH_A_MODULE_HAS_ERRORS);
252 
253     assertThat(compilation).succeeded();
254 
255     assertThat(compilation)
256         .hadWarningContainingMatch(COMBINED_WITH_A_MODULE_HAS_ERRORS_MESSAGE)
257         .inFile(COMBINED_WITH_A_MODULE_HAS_ERRORS)
258         .onLineContaining("interface CombinedWithAModuleHasErrors");
259 
260     assertThat(compilation).hadWarningCount(1);
261   }
262 
263   private static final JavaFileObject SUBCOMPONENT_WITH_ERRORS =
264       JavaFileObjects.forSourceLines(
265           "test.SubcomponentWithErrors",
266           "package test;",
267           "",
268           "import dagger.BindsInstance;",
269           "import dagger.Subcomponent;",
270           "",
271           "@Subcomponent(modules = AModule.class)",
272           "interface SubcomponentWithErrors {",
273           "  @Subcomponent.Builder",
274           "  interface Builder {",
275           "    @BindsInstance Builder object(Object object);",
276           "    SubcomponentWithErrors build();",
277           "  }",
278           "}");
279 
280   // Make sure the error doesn't show other bindings or a dependency trace afterwards.
281   private static final Pattern SUBCOMPONENT_WITH_ERRORS_MESSAGE =
282       endsWithMessage(
283           "\033[1;31m[Dagger/DuplicateBindings]\033[0m Object is bound multiple times:",
284           "    @Binds Object AModule.object(String)",
285           "    @BindsInstance SubcomponentWithErrors.Builder"
286               + " SubcomponentWithErrors.Builder.object(Object)",
287           "    in component: [SubcomponentWithErrors]",
288           "",
289           "======================",
290           "Full classname legend:",
291           "======================",
292           "AModule:                test.AModule",
293           "SubcomponentWithErrors: test.SubcomponentWithErrors",
294           "========================",
295           "End of classname legend:",
296           "========================");
297 
298   private static final Pattern MODULE_WITH_SUBCOMPONENT_WITH_ERRORS_MESSAGE =
299       endsWithMessage(
300           "\033[1;31m[Dagger/DuplicateBindings]\033[0m Object is bound multiple times:",
301           "    @Binds Object AModule.object(String)",
302           "    @BindsInstance SubcomponentWithErrors.Builder"
303               + " SubcomponentWithErrors.Builder.object(Object)",
304           "    in component: [ModuleWithSubcomponentWithErrors → SubcomponentWithErrors]",
305           "",
306           "======================",
307           "Full classname legend:",
308           "======================",
309           "AModule:                          test.AModule",
310           "ModuleWithSubcomponentWithErrors: test.ModuleWithSubcomponentWithErrors",
311           "SubcomponentWithErrors:           test.SubcomponentWithErrors",
312           "========================",
313           "End of classname legend:",
314           "========================");
315 
316   @Test
subcomponentWithErrors_validationTypeNone()317   public void subcomponentWithErrors_validationTypeNone() {
318     Compilation compilation = daggerCompiler().compile(SUBCOMPONENT_WITH_ERRORS, A_MODULE);
319 
320     assertThat(compilation).succeededWithoutWarnings();
321   }
322 
323   @Test
subcomponentWithErrors_validationTypeError()324   public void subcomponentWithErrors_validationTypeError() {
325     Compilation compilation =
326         compilerWithOptions("-Adagger.fullBindingGraphValidation=ERROR")
327             .compile(SUBCOMPONENT_WITH_ERRORS, A_MODULE);
328 
329     assertThat(compilation).failed();
330 
331     assertThat(compilation)
332         .hadErrorContainingMatch(SUBCOMPONENT_WITH_ERRORS_MESSAGE)
333         .inFile(SUBCOMPONENT_WITH_ERRORS)
334         .onLineContaining("interface SubcomponentWithErrors");
335 
336     assertThat(compilation).hadErrorCount(1);
337   }
338 
339   @Test
subcomponentWithErrors_validationTypeWarning()340   public void subcomponentWithErrors_validationTypeWarning() {
341     Compilation compilation =
342         compilerWithOptions("-Adagger.fullBindingGraphValidation=WARNING")
343             .compile(SUBCOMPONENT_WITH_ERRORS, A_MODULE);
344 
345     assertThat(compilation).succeeded();
346 
347     assertThat(compilation)
348         .hadWarningContainingMatch(SUBCOMPONENT_WITH_ERRORS_MESSAGE)
349         .inFile(SUBCOMPONENT_WITH_ERRORS)
350         .onLineContaining("interface SubcomponentWithErrors");
351 
352     assertThat(compilation).hadWarningCount(1);
353   }
354 
355   private static final JavaFileObject MODULE_WITH_SUBCOMPONENT_WITH_ERRORS =
356       JavaFileObjects.forSourceLines(
357           "test.ModuleWithSubcomponentWithErrors",
358           "package test;",
359           "",
360           "import dagger.Binds;",
361           "import dagger.Module;",
362           "",
363           "@Module(subcomponents = SubcomponentWithErrors.class)",
364           "interface ModuleWithSubcomponentWithErrors {}");
365 
366   @Test
moduleWithSubcomponentWithErrors_validationTypeNone()367   public void moduleWithSubcomponentWithErrors_validationTypeNone() {
368     Compilation compilation =
369         daggerCompiler()
370             .compile(MODULE_WITH_SUBCOMPONENT_WITH_ERRORS, SUBCOMPONENT_WITH_ERRORS, A_MODULE);
371 
372     assertThat(compilation).succeededWithoutWarnings();
373   }
374 
375   @Test
moduleWithSubcomponentWithErrors_validationTypeError()376   public void moduleWithSubcomponentWithErrors_validationTypeError() {
377     Compilation compilation =
378         compilerWithOptions("-Adagger.fullBindingGraphValidation=ERROR")
379             .compile(MODULE_WITH_SUBCOMPONENT_WITH_ERRORS, SUBCOMPONENT_WITH_ERRORS, A_MODULE);
380 
381     assertThat(compilation).failed();
382 
383     assertThat(compilation)
384         .hadErrorContainingMatch(MODULE_WITH_SUBCOMPONENT_WITH_ERRORS_MESSAGE)
385         .inFile(MODULE_WITH_SUBCOMPONENT_WITH_ERRORS)
386         .onLineContaining("interface ModuleWithSubcomponentWithErrors");
387 
388     // TODO(b/130283677)
389     assertThat(compilation)
390         .hadErrorContainingMatch(SUBCOMPONENT_WITH_ERRORS_MESSAGE)
391         .inFile(SUBCOMPONENT_WITH_ERRORS)
392         .onLineContaining("interface SubcomponentWithErrors");
393 
394     assertThat(compilation).hadErrorCount(2);
395   }
396 
397   @Test
moduleWithSubcomponentWithErrors_validationTypeWarning()398   public void moduleWithSubcomponentWithErrors_validationTypeWarning() {
399     Compilation compilation =
400         compilerWithOptions("-Adagger.fullBindingGraphValidation=WARNING")
401             .compile(MODULE_WITH_SUBCOMPONENT_WITH_ERRORS, SUBCOMPONENT_WITH_ERRORS, A_MODULE);
402 
403     assertThat(compilation).succeeded();
404 
405     assertThat(compilation)
406         .hadWarningContainingMatch(MODULE_WITH_SUBCOMPONENT_WITH_ERRORS_MESSAGE)
407         .inFile(MODULE_WITH_SUBCOMPONENT_WITH_ERRORS)
408         .onLineContaining("interface ModuleWithSubcomponentWithErrors");
409 
410     // TODO(b/130283677)
411     assertThat(compilation)
412         .hadWarningContainingMatch(SUBCOMPONENT_WITH_ERRORS_MESSAGE)
413         .inFile(SUBCOMPONENT_WITH_ERRORS)
414         .onLineContaining("interface SubcomponentWithErrors");
415 
416     assertThat(compilation).hadWarningCount(2);
417   }
418 
419   private static final JavaFileObject A_SUBCOMPONENT =
420       JavaFileObjects.forSourceLines(
421           "test.ASubcomponent",
422           "package test;",
423           "",
424           "import dagger.BindsInstance;",
425           "import dagger.Subcomponent;",
426           "",
427           "@Subcomponent(modules = AModule.class)",
428           "interface ASubcomponent {",
429           "  @Subcomponent.Builder",
430           "  interface Builder {",
431           "    ASubcomponent build();",
432           "  }",
433           "}");
434 
435   private static final JavaFileObject COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS =
436       JavaFileObjects.forSourceLines(
437           "test.CombinedWithASubcomponentHasErrors",
438           "package test;",
439           "",
440           "import dagger.Binds;",
441           "import dagger.Module;",
442           "",
443           "@Module(subcomponents = ASubcomponent.class)",
444           "interface CombinedWithASubcomponentHasErrors {",
445           "  @Binds Object object(Number number);",
446           "}");
447 
448   // Make sure the error doesn't show other bindings or a dependency trace afterwards.
449   private static final Pattern COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS_MESSAGE =
450       endsWithMessage(
451           "\033[1;31m[Dagger/DuplicateBindings]\033[0m Object is bound multiple times:",
452           "    @Binds Object AModule.object(String)",
453           "    @Binds Object CombinedWithASubcomponentHasErrors.object(Number)",
454           "    in component: [CombinedWithASubcomponentHasErrors → ASubcomponent]",
455           "",
456           "======================",
457           "Full classname legend:",
458           "======================",
459           "AModule:                            test.AModule",
460           "ASubcomponent:                      test.ASubcomponent",
461           "CombinedWithASubcomponentHasErrors: test.CombinedWithASubcomponentHasErrors",
462           "========================",
463           "End of classname legend:",
464           "========================");
465 
466   @Test
moduleWithSubcomponentWithCombinedErrors_validationTypeNone()467   public void moduleWithSubcomponentWithCombinedErrors_validationTypeNone() {
468     Compilation compilation =
469         daggerCompiler().compile(COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS, A_SUBCOMPONENT, A_MODULE);
470 
471     assertThat(compilation).succeededWithoutWarnings();
472   }
473 
474   @Test
moduleWithSubcomponentWithCombinedErrors_validationTypeError()475   public void moduleWithSubcomponentWithCombinedErrors_validationTypeError() {
476     Compilation compilation =
477         compilerWithOptions("-Adagger.fullBindingGraphValidation=ERROR")
478             .compile(COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS, A_SUBCOMPONENT, A_MODULE);
479 
480     assertThat(compilation).failed();
481 
482     assertThat(compilation)
483         .hadErrorContainingMatch(COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS_MESSAGE)
484         .inFile(COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS)
485         .onLineContaining("interface CombinedWithASubcomponentHasErrors");
486 
487     assertThat(compilation).hadErrorCount(1);
488   }
489 
490   @Test
moduleWithSubcomponentWithCombinedErrors_validationTypeWarning()491   public void moduleWithSubcomponentWithCombinedErrors_validationTypeWarning() {
492     Compilation compilation =
493         compilerWithOptions("-Adagger.fullBindingGraphValidation=WARNING")
494             .compile(COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS, A_SUBCOMPONENT, A_MODULE);
495 
496     assertThat(compilation).succeeded();
497 
498     assertThat(compilation)
499         .hadWarningContainingMatch(COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS_MESSAGE)
500         .inFile(COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS)
501         .onLineContaining("interface CombinedWithASubcomponentHasErrors");
502 
503     assertThat(compilation).hadWarningCount(1);
504   }
505 
506   @Test
bothAliasesDifferentValues()507   public void bothAliasesDifferentValues() {
508     Compilation compilation =
509         compilerWithOptions(
510                 "-Adagger.moduleBindingValidation=NONE",
511                 "-Adagger.fullBindingGraphValidation=ERROR")
512             .compile(MODULE_WITH_ERRORS);
513 
514     assertThat(compilation).failed();
515 
516     assertThat(compilation)
517         .hadErrorContaining(
518             "Only one of the equivalent options "
519                 + "(-Adagger.fullBindingGraphValidation, -Adagger.moduleBindingValidation)"
520                 + " should be used; prefer -Adagger.fullBindingGraphValidation");
521 
522     assertThat(compilation).hadErrorCount(1);
523   }
524 
525   @Test
bothAliasesSameValue()526   public void bothAliasesSameValue() {
527     Compilation compilation =
528         compilerWithOptions(
529                 "-Adagger.moduleBindingValidation=NONE", "-Adagger.fullBindingGraphValidation=NONE")
530             .compile(MODULE_WITH_ERRORS);
531 
532     assertThat(compilation).succeeded();
533 
534     assertThat(compilation)
535         .hadWarningContaining(
536             "Only one of the equivalent options "
537                 + "(-Adagger.fullBindingGraphValidation, -Adagger.moduleBindingValidation)"
538                 + " should be used; prefer -Adagger.fullBindingGraphValidation");
539   }
540 }
541