1 /* 2 * Copyright (C) 2021 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.common.truth.Truth.assertThat; 20 import static org.junit.Assert.assertThrows; 21 22 import androidx.room.compiler.processing.XConstructorElement; 23 import androidx.room.compiler.processing.XElement; 24 import androidx.room.compiler.processing.XMethodElement; 25 import androidx.room.compiler.processing.XProcessingEnv; 26 import androidx.room.compiler.processing.XProcessingStep; 27 import androidx.room.compiler.processing.XTypeElement; 28 import androidx.room.compiler.processing.XVariableElement; 29 import androidx.room.compiler.processing.util.Source; 30 import com.google.common.base.Joiner; 31 import com.google.common.collect.ImmutableList; 32 import com.google.common.collect.ImmutableSet; 33 import dagger.BindsInstance; 34 import dagger.Component; 35 import dagger.internal.codegen.base.DaggerSuperficialValidation; 36 import dagger.internal.codegen.base.DaggerSuperficialValidation.ValidationException; 37 import dagger.testing.compile.CompilerTests; 38 import java.util.Map; 39 import java.util.Set; 40 import javax.inject.Singleton; 41 import org.junit.Test; 42 import org.junit.runner.RunWith; 43 import org.junit.runners.Parameterized; 44 import org.junit.runners.Parameterized.Parameters; 45 46 @RunWith(Parameterized.class) 47 public class DaggerSuperficialValidationTest { 48 enum SourceKind { 49 JAVA, 50 KOTLIN 51 } 52 53 @Parameters(name = "sourceKind={0}") parameters()54 public static ImmutableList<Object[]> parameters() { 55 return ImmutableList.of(new Object[] {SourceKind.JAVA}, new Object[] {SourceKind.KOTLIN}); 56 } 57 58 private final SourceKind sourceKind; 59 DaggerSuperficialValidationTest(SourceKind sourceKind)60 public DaggerSuperficialValidationTest(SourceKind sourceKind) { 61 this.sourceKind = sourceKind; 62 } 63 64 private static final Joiner NEW_LINES = Joiner.on("\n "); 65 66 @Test missingReturnType()67 public void missingReturnType() { 68 runTest( 69 CompilerTests.javaSource( 70 "test.TestClass", 71 "package test;", 72 "", 73 "abstract class TestClass {", 74 " abstract MissingType blah();", 75 "}"), 76 CompilerTests.kotlinSource( 77 "test.TestClass.kt", 78 "package test", 79 "", 80 "abstract class TestClass {", 81 " abstract fun blah(): MissingType", 82 "}"), 83 (processingEnv, superficialValidation) -> { 84 XTypeElement testClassElement = processingEnv.findTypeElement("test.TestClass"); 85 ValidationException exception = 86 assertThrows( 87 ValidationException.KnownErrorType.class, 88 () -> superficialValidation.validateElement(testClassElement)); 89 // TODO(b/248552462): Javac and KSP should match once this bug is fixed. 90 boolean isJavac = processingEnv.getBackend() == XProcessingEnv.Backend.JAVAC; 91 assertThat(exception) 92 .hasMessageThat() 93 .contains( 94 String.format( 95 NEW_LINES.join( 96 "Validation trace:", 97 " => element (CLASS): test.TestClass", 98 " => element (METHOD): blah()", 99 " => type (ERROR return type): %1$s"), 100 isJavac ? "MissingType" : "error.NonExistentClass")); 101 }); 102 } 103 104 @Test missingGenericReturnType()105 public void missingGenericReturnType() { 106 runTest( 107 CompilerTests.javaSource( 108 "test.TestClass", 109 "package test;", 110 "", 111 "abstract class TestClass {", 112 " abstract MissingType<?> blah();", 113 "}"), 114 CompilerTests.kotlinSource( 115 "test.TestClass.kt", 116 "package test", 117 "", 118 "abstract class TestClass {", 119 " abstract fun blah(): MissingType<*>", 120 "}"), 121 (processingEnv, superficialValidation) -> { 122 XTypeElement testClassElement = processingEnv.findTypeElement("test.TestClass"); 123 ValidationException exception = 124 assertThrows( 125 ValidationException.KnownErrorType.class, 126 () -> superficialValidation.validateElement(testClassElement)); 127 // TODO(b/248552462): Javac and KSP should match once this bug is fixed. 128 boolean isJavac = processingEnv.getBackend() == XProcessingEnv.Backend.JAVAC; 129 assertThat(exception) 130 .hasMessageThat() 131 .contains( 132 String.format( 133 NEW_LINES.join( 134 "Validation trace:", 135 " => element (CLASS): test.TestClass", 136 " => element (METHOD): blah()", 137 " => type (ERROR return type): %1$s"), 138 isJavac ? "<any>" : "error.NonExistentClass")); 139 }); 140 } 141 142 @Test missingReturnTypeTypeParameter()143 public void missingReturnTypeTypeParameter() { 144 runTest( 145 CompilerTests.javaSource( 146 "test.TestClass", 147 "package test;", 148 "", 149 "import java.util.Map;", 150 "import java.util.Set;", 151 "", 152 "abstract class TestClass {", 153 " abstract Map<Set<?>, MissingType<?>> blah();", 154 "}"), 155 CompilerTests.kotlinSource( 156 "test.TestClass.kt", 157 "package test", 158 "", 159 "abstract class TestClass {", 160 " abstract fun blah(): Map<Set<*>, MissingType<*>>", 161 "}"), 162 (processingEnv, superficialValidation) -> { 163 XTypeElement testClassElement = processingEnv.findTypeElement("test.TestClass"); 164 ValidationException exception = 165 assertThrows( 166 ValidationException.KnownErrorType.class, 167 () -> superficialValidation.validateElement(testClassElement)); 168 // TODO(b/248552462): Javac and KSP should match once this bug is fixed. 169 boolean isJavac = processingEnv.getBackend() == XProcessingEnv.Backend.JAVAC; 170 assertThat(exception) 171 .hasMessageThat() 172 .contains( 173 String.format( 174 NEW_LINES.join( 175 "Validation trace:", 176 " => element (CLASS): test.TestClass", 177 " => element (METHOD): blah()", 178 " => type (DECLARED return type): " 179 + "java.util.Map<java.util.Set<?>,%1$s>", 180 " => type (ERROR type argument): %1$s"), 181 isJavac ? "<any>" : "error.NonExistentClass")); 182 }); 183 } 184 185 @Test missingTypeParameter()186 public void missingTypeParameter() { 187 runTest( 188 CompilerTests.javaSource( 189 "test.TestClass", // 190 "package test;", 191 "", 192 "class TestClass<T extends MissingType> {}"), 193 CompilerTests.kotlinSource( 194 "test.TestClass.kt", // 195 "package test", 196 "", 197 "class TestClass<T : MissingType>"), 198 (processingEnv, superficialValidation) -> { 199 if (isKAPT(processingEnv)) { 200 // TODO(b/268536260): Figure out why XProcessing Testing infra fails when using KAPT. 201 return; 202 } 203 XTypeElement testClassElement = processingEnv.findTypeElement("test.TestClass"); 204 ValidationException exception = 205 assertThrows( 206 ValidationException.KnownErrorType.class, 207 () -> superficialValidation.validateElement(testClassElement)); 208 // TODO(b/248552462): Javac and KSP should match once this bug is fixed. 209 boolean isJavac = processingEnv.getBackend() == XProcessingEnv.Backend.JAVAC; 210 assertThat(exception) 211 .hasMessageThat() 212 .contains( 213 String.format( 214 NEW_LINES.join( 215 "Validation trace:", 216 " => element (CLASS): test.TestClass", 217 " => element (TYPE_PARAMETER): T", 218 " => type (ERROR bound type): %s"), 219 isJavac ? "MissingType" : "error.NonExistentClass")); 220 }); 221 } 222 223 @Test missingParameterType()224 public void missingParameterType() { 225 runTest( 226 CompilerTests.javaSource( 227 "test.TestClass", 228 "package test;", 229 "", 230 "abstract class TestClass {", 231 " abstract void foo(MissingType param);", 232 "}"), 233 CompilerTests.kotlinSource( 234 "test.TestClass.kt", 235 "package test", 236 "", 237 "abstract class TestClass {", 238 " abstract fun foo(param: MissingType);", 239 "}"), 240 (processingEnv, superficialValidation) -> { 241 XTypeElement testClassElement = processingEnv.findTypeElement("test.TestClass"); 242 ValidationException exception = 243 assertThrows( 244 ValidationException.KnownErrorType.class, 245 () -> superficialValidation.validateElement(testClassElement)); 246 // TODO(b/248552462): Javac and KSP should match once this bug is fixed. 247 boolean isJavac = processingEnv.getBackend() == XProcessingEnv.Backend.JAVAC; 248 assertThat(exception) 249 .hasMessageThat() 250 .contains( 251 String.format( 252 NEW_LINES.join( 253 "Validation trace:", 254 " => element (CLASS): test.TestClass", 255 " => element (METHOD): foo(%1$s)", 256 " => element (PARAMETER): param", 257 " => type (ERROR parameter type): %1$s"), 258 isJavac ? "MissingType" : "error.NonExistentClass")); 259 }); 260 } 261 262 @Test missingAnnotation()263 public void missingAnnotation() { 264 runTest( 265 CompilerTests.javaSource( 266 "test.TestClass", // 267 "package test;", 268 "", 269 "@MissingAnnotation", 270 "class TestClass {}"), 271 CompilerTests.kotlinSource( 272 "test.TestClass.kt", // 273 "package test", 274 "", 275 "@MissingAnnotation", 276 "class TestClass"), 277 (processingEnv, superficialValidation) -> { 278 XTypeElement testClassElement = processingEnv.findTypeElement("test.TestClass"); 279 ValidationException exception = 280 assertThrows( 281 ValidationException.KnownErrorType.class, 282 () -> superficialValidation.validateElement(testClassElement)); 283 // TODO(b/248552462): Javac and KSP should match once this bug is fixed. 284 boolean isJavac = processingEnv.getBackend() == XProcessingEnv.Backend.JAVAC; 285 assertThat(exception) 286 .hasMessageThat() 287 .contains( 288 String.format( 289 NEW_LINES.join( 290 "Validation trace:", 291 " => element (CLASS): test.TestClass", 292 " => annotation type: MissingAnnotation", 293 " => type (ERROR annotation type): %s"), 294 isJavac ? "MissingAnnotation" : "error.NonExistentClass")); 295 }); 296 } 297 298 @Test handlesRecursiveTypeParams()299 public void handlesRecursiveTypeParams() { 300 runSuccessfulTest( 301 CompilerTests.javaSource( 302 "test.TestClass", // 303 "package test;", 304 "", 305 "class TestClass<T extends Comparable<T>> {}"), 306 CompilerTests.kotlinSource( 307 "test.TestClass.kt", // 308 "package test", 309 "", 310 "class TestClass<T : Comparable<T>>"), 311 (processingEnv, superficialValidation) -> 312 superficialValidation.validateElement(processingEnv.findTypeElement("test.TestClass"))); 313 } 314 315 @Test handlesRecursiveType()316 public void handlesRecursiveType() { 317 runSuccessfulTest( 318 CompilerTests.javaSource( 319 "test.TestClass", 320 "package test;", 321 "", 322 "abstract class TestClass {", 323 " abstract TestClass foo(TestClass x);", 324 "}"), 325 CompilerTests.kotlinSource( 326 "test.TestClass.kt", 327 "package test", 328 "", 329 "abstract class TestClass {", 330 " abstract fun foo(x: TestClass): TestClass", 331 "}"), 332 (processingEnv, superficialValidation) -> 333 superficialValidation.validateElement(processingEnv.findTypeElement("test.TestClass"))); 334 } 335 336 @Test missingWildcardBound()337 public void missingWildcardBound() { 338 runTest( 339 CompilerTests.javaSource( 340 "test.TestClass", 341 "package test;", 342 "", 343 "import java.util.Set;", 344 "", 345 "class TestClass {", 346 " static final class Foo<T> {}", 347 "", 348 " Foo<? extends MissingType> extendsTest() {", 349 " return null;", 350 " }", 351 "", 352 " Foo<? super MissingType> superTest() {", 353 " return null;", 354 " }", 355 "}"), 356 CompilerTests.kotlinSource( 357 "test.TestClass.kt", 358 "package test", 359 "", 360 "class TestClass {", 361 " class Foo<T>", 362 "", 363 " fun extendsTest(): Foo<out MissingType> = TODO()", 364 "", 365 " fun superTest(): Foo<in MissingType> = TODO()", 366 "}"), 367 (processingEnv, superficialValidation) -> { 368 XTypeElement testClassElement = processingEnv.findTypeElement("test.TestClass"); 369 ValidationException exception = 370 assertThrows( 371 ValidationException.KnownErrorType.class, 372 () -> superficialValidation.validateElement(testClassElement)); 373 // TODO(b/248552462): Javac and KSP should match once this bug is fixed. 374 boolean isJavac = processingEnv.getBackend() == XProcessingEnv.Backend.JAVAC; 375 assertThat(exception) 376 .hasMessageThat() 377 .contains( 378 String.format( 379 NEW_LINES.join( 380 "Validation trace:", 381 " => element (CLASS): test.TestClass", 382 " => element (METHOD): extendsTest()", 383 " => type (DECLARED return type): test.TestClass.Foo<? extends %1$s>", 384 " => type (WILDCARD type argument): ? extends %1$s", 385 " => type (ERROR extends bound type): %1$s"), 386 isJavac ? "MissingType" : "error.NonExistentClass")); 387 }); 388 } 389 390 @Test missingIntersection()391 public void missingIntersection() { 392 runTest( 393 CompilerTests.javaSource( 394 "test.TestClass", 395 "package test;", 396 "", 397 "class TestClass<T extends Number & Missing> {}"), 398 CompilerTests.kotlinSource( 399 "test.TestClass.kt", 400 "package test", 401 "", 402 "class TestClass<T> where T: Number, T: Missing"), 403 (processingEnv, superficialValidation) -> { 404 if (isKAPT(processingEnv)) { 405 // TODO(b/268536260): Figure out why XProcessing Testing infra fails when using KAPT. 406 return; 407 } 408 XTypeElement testClassElement = processingEnv.findTypeElement("test.TestClass"); 409 ValidationException exception = 410 assertThrows( 411 ValidationException.KnownErrorType.class, 412 () -> superficialValidation.validateElement(testClassElement)); 413 // TODO(b/248552462): Javac and KSP should match once this bug is fixed. 414 boolean isJavac = processingEnv.getBackend() == XProcessingEnv.Backend.JAVAC; 415 assertThat(exception) 416 .hasMessageThat() 417 .contains( 418 String.format( 419 NEW_LINES.join( 420 "Validation trace:", 421 " => element (CLASS): test.TestClass", 422 " => element (TYPE_PARAMETER): T", 423 " => type (ERROR bound type): %s"), 424 isJavac ? "Missing" : "error.NonExistentClass")); 425 }); 426 } 427 428 @Test invalidAnnotationValue()429 public void invalidAnnotationValue() { 430 runTest( 431 CompilerTests.javaSource( 432 "test.Outer", 433 "package test;", 434 "", 435 "final class Outer {", 436 " @interface TestAnnotation {", 437 " Class[] classes();", 438 " }", 439 "", 440 " @TestAnnotation(classes = MissingType.class)", 441 " static class TestClass {}", 442 "}"), 443 CompilerTests.kotlinSource( 444 "test.Outer.kt", 445 "package test", 446 "", 447 "class Outer {", 448 " annotation class TestAnnotation(", 449 " val classes: Array<kotlin.reflect.KClass<*>>", 450 " )", 451 "", 452 " @TestAnnotation(classes = [MissingType::class])", 453 " class TestClass {}", 454 "}"), 455 (processingEnv, superficialValidation) -> { 456 XTypeElement testClassElement = processingEnv.findTypeElement("test.Outer.TestClass"); 457 if (processingEnv.getBackend() == XProcessingEnv.Backend.KSP 458 && sourceKind == SourceKind.KOTLIN) { 459 // TODO(b/269364338): When using kotlin source with KSP the MissingType annotation value 460 // appears to be missing so validating this element does not cause the expected failure. 461 superficialValidation.validateElement(testClassElement); 462 return; 463 } 464 ValidationException exception = 465 assertThrows( 466 ValidationException.KnownErrorType.class, 467 () -> superficialValidation.validateElement(testClassElement)); 468 // TODO(b/248552462): Javac and KSP should match once this bug is fixed. 469 boolean isJavac = processingEnv.getBackend() == XProcessingEnv.Backend.JAVAC; 470 assertThat(exception) 471 .hasMessageThat() 472 .contains( 473 String.format( 474 NEW_LINES.join( 475 "Validation trace:", 476 " => element (CLASS): test.Outer.TestClass", 477 " => annotation type: test.Outer.TestAnnotation", 478 " => annotation: @test.Outer.TestAnnotation(classes={<%1$s>})", 479 " => annotation value (TYPE_ARRAY): classes={<%1$s>}", 480 " => annotation value (TYPE): classes=<%1$s>"), 481 isJavac ? "error" : "Error")); 482 }); 483 } 484 485 @Test invalidAnnotationValueOnParameter()486 public void invalidAnnotationValueOnParameter() { 487 runTest( 488 CompilerTests.javaSource( 489 "test.Outer", 490 "package test;", 491 "", 492 "final class Outer {", 493 " @interface TestAnnotation {", 494 " Class[] classes();", 495 " }", 496 "", 497 " static class TestClass {", 498 " TestClass(@TestAnnotation(classes = MissingType.class) String strParam) {}", 499 " }", 500 "}"), 501 CompilerTests.kotlinSource( 502 "test.Outer.kt", 503 "package test", 504 "", 505 "class Outer {", 506 " annotation class TestAnnotation(", 507 " val classes: Array<kotlin.reflect.KClass<*>>", 508 " )", 509 "", 510 " class TestClass(", 511 " @TestAnnotation(classes = [MissingType::class]) strParam: String", 512 " )", 513 "}"), 514 (processingEnv, superficialValidation) -> { 515 if (sourceKind == SourceKind.KOTLIN) { 516 // TODO(b/268536260): Figure out why XProcessing Testing infra fails when using KAPT. 517 // TODO(b/269364338): When using kotlin source the MissingType annotation value appears 518 // to be missing so validating this element does not cause the expected failure. 519 return; 520 } 521 XTypeElement testClassElement = processingEnv.findTypeElement("test.Outer.TestClass"); 522 XConstructorElement constructor = testClassElement.getConstructors().get(0); 523 XVariableElement parameter = constructor.getParameters().get(0); 524 ValidationException exception = 525 assertThrows( 526 ValidationException.KnownErrorType.class, 527 () -> superficialValidation.validateElement(parameter)); 528 // TODO(b/248552462): Javac and KSP should match once this bug is fixed. 529 boolean isJavac = processingEnv.getBackend() == XProcessingEnv.Backend.JAVAC; 530 assertThat(exception) 531 .hasMessageThat() 532 .contains( 533 String.format( 534 NEW_LINES.join( 535 "Validation trace:", 536 " => element (CLASS): test.Outer.TestClass", 537 " => element (CONSTRUCTOR): TestClass(java.lang.String)", 538 " => element (PARAMETER): strParam", 539 " => annotation type: test.Outer.TestAnnotation", 540 " => annotation: @test.Outer.TestAnnotation(classes={<%1$s>})", 541 " => annotation value (TYPE_ARRAY): classes={<%1$s>}", 542 " => annotation value (TYPE): classes=<%1$s>"), 543 isJavac ? "error" : "Error")); 544 }); 545 } 546 547 @Test invalidSuperclassInTypeHierarchy()548 public void invalidSuperclassInTypeHierarchy() { 549 runTest( 550 CompilerTests.javaSource( 551 "test.Outer", 552 "package test;", 553 "", 554 "final class Outer {", 555 " Child<Long> getChild() { return null; }", 556 " static class Child<T> extends Parent<T> {}", 557 " static class Parent<T> extends MissingType<T> {}", 558 "}"), 559 CompilerTests.kotlinSource( 560 "test.Outer.kt", 561 "package test", 562 "", 563 "class Outer {", 564 " fun getChild(): Child<Long> = TODO()", 565 " class Child<T> : Parent<T>", 566 " open class Parent<T> : MissingType<T>", 567 "}"), 568 (processingEnv, superficialValidation) -> { 569 XTypeElement outerElement = processingEnv.findTypeElement("test.Outer"); 570 XMethodElement getChildMethod = outerElement.getDeclaredMethods().get(0); 571 ValidationException exception = 572 assertThrows( 573 ValidationException.KnownErrorType.class, 574 () -> 575 superficialValidation.validateTypeHierarchyOf( 576 "return type", getChildMethod, getChildMethod.getReturnType())); 577 // TODO(b/248552462): Javac and KSP should match once this bug is fixed. 578 boolean isJavac = processingEnv.getBackend() == XProcessingEnv.Backend.JAVAC; 579 assertThat(exception) 580 .hasMessageThat() 581 .contains( 582 String.format( 583 NEW_LINES.join( 584 "Validation trace:", 585 " => element (CLASS): test.Outer", 586 " => element (METHOD): getChild()", 587 " => type (DECLARED return type): test.Outer.Child<java.lang.Long>", 588 " => type (DECLARED supertype): test.Outer.Parent<java.lang.Long>", 589 " => type (ERROR supertype): %s"), 590 isJavac ? "MissingType<T>" : "error.NonExistentClass")); 591 }); 592 } 593 594 @Test invalidSuperclassTypeParameterInTypeHierarchy()595 public void invalidSuperclassTypeParameterInTypeHierarchy() { 596 runTest( 597 CompilerTests.javaSource( 598 "test.Outer", 599 "package test;", 600 "", 601 "final class Outer {", 602 " Child getChild() { return null; }", 603 " static class Child extends Parent<MissingType> {}", 604 " static class Parent<T> {}", 605 "}"), 606 CompilerTests.kotlinSource( 607 "test.Outer.kt", 608 "package test", 609 "", 610 "class Outer {", 611 " fun getChild(): Child = TODO()", 612 " class Child : Parent<MissingType>()", 613 " open class Parent<T>", 614 "}"), 615 (processingEnv, superficialValidation) -> { 616 XTypeElement outerElement = processingEnv.findTypeElement("test.Outer"); 617 XMethodElement getChildMethod = outerElement.getDeclaredMethods().get(0); 618 if (isKAPT(processingEnv)) { 619 // https://youtrack.jetbrains.com/issue/KT-34193/Kapt-CorrectErrorTypes-doesnt-work-for-generics 620 // There's no way to work around this bug in KAPT so validation doesn't catch this case. 621 superficialValidation.validateTypeHierarchyOf( 622 "return type", getChildMethod, getChildMethod.getReturnType()); 623 return; 624 } 625 ValidationException exception = 626 assertThrows( 627 ValidationException.KnownErrorType.class, 628 () -> 629 superficialValidation.validateTypeHierarchyOf( 630 "return type", getChildMethod, getChildMethod.getReturnType())); 631 // TODO(b/248552462): Javac and KSP should match once this bug is fixed. 632 boolean isJavac = processingEnv.getBackend() == XProcessingEnv.Backend.JAVAC; 633 assertThat(exception) 634 .hasMessageThat() 635 .contains( 636 String.format( 637 NEW_LINES.join( 638 "Validation trace:", 639 " => element (CLASS): test.Outer", 640 " => element (METHOD): getChild()", 641 " => type (DECLARED return type): test.Outer.Child", 642 " => type (DECLARED supertype): test.Outer.Parent<%1$s>", 643 " => type (ERROR type argument): %1$s"), 644 isJavac ? "MissingType" : "error.NonExistentClass")); 645 }); 646 } 647 runTest( Source.JavaSource javaSource, Source.KotlinSource kotlinSource, AssertionHandler assertionHandler)648 private void runTest( 649 Source.JavaSource javaSource, 650 Source.KotlinSource kotlinSource, 651 AssertionHandler assertionHandler) { 652 CompilerTests.daggerCompiler(sourceKind == SourceKind.JAVA ? javaSource : kotlinSource) 653 .withProcessingSteps(() -> new AssertingStep(assertionHandler)) 654 // We're expecting compiler errors that we assert on in the assertionHandler. 655 .compile(subject -> subject.hasError()); 656 } 657 runSuccessfulTest( Source.JavaSource javaSource, Source.KotlinSource kotlinSource, AssertionHandler assertionHandler)658 private void runSuccessfulTest( 659 Source.JavaSource javaSource, 660 Source.KotlinSource kotlinSource, 661 AssertionHandler assertionHandler) { 662 CompilerTests.daggerCompiler(sourceKind == SourceKind.JAVA ? javaSource : kotlinSource) 663 .withProcessingSteps(() -> new AssertingStep(assertionHandler)) 664 .compile(subject -> subject.hasErrorCount(0)); 665 } 666 isKAPT(XProcessingEnv processingEnv)667 private boolean isKAPT(XProcessingEnv processingEnv) { 668 return processingEnv.getBackend() == XProcessingEnv.Backend.JAVAC 669 && sourceKind == SourceKind.KOTLIN; 670 } 671 672 private interface AssertionHandler { runAssertions( XProcessingEnv processingEnv, DaggerSuperficialValidation superficialValidation)673 void runAssertions( 674 XProcessingEnv processingEnv, DaggerSuperficialValidation superficialValidation); 675 } 676 677 private static final class AssertingStep implements XProcessingStep { 678 private final AssertionHandler assertionHandler; 679 private boolean processed = false; 680 AssertingStep(AssertionHandler assertionHandler)681 AssertingStep(AssertionHandler assertionHandler) { 682 this.assertionHandler = assertionHandler; 683 } 684 685 @Override annotations()686 public final ImmutableSet<String> annotations() { 687 return ImmutableSet.of("*"); 688 } 689 690 @Override process( XProcessingEnv env, Map<String, ? extends Set<? extends XElement>> elementsByAnnotation)691 public ImmutableSet<XElement> process( 692 XProcessingEnv env, Map<String, ? extends Set<? extends XElement>> elementsByAnnotation) { 693 if (!processed) { 694 processed = true; // only process once. 695 TestComponent component = 696 DaggerDaggerSuperficialValidationTest_TestComponent.factory().create(env); 697 assertionHandler.runAssertions(env, component.superficialValidation()); 698 } 699 return ImmutableSet.of(); 700 } 701 702 @Override processOver( XProcessingEnv env, Map<String, ? extends Set<? extends XElement>> elementsByAnnotation)703 public void processOver( 704 XProcessingEnv env, Map<String, ? extends Set<? extends XElement>> elementsByAnnotation) {} 705 } 706 707 @Singleton 708 @Component(modules = ProcessingEnvironmentModule.class) 709 interface TestComponent { superficialValidation()710 DaggerSuperficialValidation superficialValidation(); 711 712 @Component.Factory 713 interface Factory { create(@indsInstance XProcessingEnv processingEnv)714 TestComponent create(@BindsInstance XProcessingEnv processingEnv); 715 } 716 } 717 } 718