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