xref: /aosp_15_r20/external/dagger2/javatests/dagger/internal/codegen/DaggerSuperficialValidationTest.java (revision f585d8a307d0621d6060bd7e80091fdcbf94fe27)
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