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