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