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