Merge changes Ic3a572e4,I56e090c2,I0463b1bd into androidx-master-dev

* changes:
  Run TypeAdapterStore tests with KSP
  Enable as many tests as possible with KSP
  New processing test APIs
diff --git a/room/compiler-processing-testing/build.gradle b/room/compiler-processing-testing/build.gradle
index 0edce59..0ffb5fd 100644
--- a/room/compiler-processing-testing/build.gradle
+++ b/room/compiler-processing-testing/build.gradle
@@ -26,10 +26,10 @@
 
 dependencies {
     implementation("androidx.annotation:annotation:1.1.0")
-    implementation(project(":room:room-compiler-processing"))
+    api(project(":room:room-compiler-processing"))
     implementation(KOTLIN_STDLIB)
     implementation(KOTLIN_KSP_API)
-    testImplementation(KOTLIN_KSP)
+    implementation(KOTLIN_KSP)
     implementation(GOOGLE_COMPILE_TESTING)
     implementation(KOTLIN_COMPILE_TESTING_KSP)
 }
diff --git a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/SyntheticJavacProcessor.kt b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/SyntheticJavacProcessor.kt
index 0ac5928..54fc124 100644
--- a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/SyntheticJavacProcessor.kt
+++ b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/SyntheticJavacProcessor.kt
@@ -16,21 +16,27 @@
 
 package androidx.room.compiler.processing
 
+import androidx.room.compiler.processing.util.RecordingXMessager
 import androidx.room.compiler.processing.util.XTestInvocation
-import java.lang.AssertionError
 import javax.lang.model.SourceVersion
 
 class SyntheticJavacProcessor(
-    val handler: (XTestInvocation) -> Unit
-) : JavacTestProcessor() {
+    val handler: (XTestInvocation) -> Unit,
+) : JavacTestProcessor(), SyntheticProcessor {
+    override val invocationInstances = mutableListOf<XTestInvocation>()
     private var result: Result<Unit>? = null
+    override val messageWatcher = RecordingXMessager()
 
     override fun doProcess(annotations: Set<XTypeElement>, roundEnv: XRoundEnv): Boolean {
+        val xEnv = XProcessingEnv.create(processingEnv)
+        xEnv.messager.addMessageWatcher(messageWatcher)
         result = kotlin.runCatching {
             handler(
                 XTestInvocation(
-                    processingEnv = XProcessingEnv.create(processingEnv)
-                )
+                    processingEnv = xEnv
+                ).also {
+                    invocationInstances.add(it)
+                }
             )
         }
         return true
@@ -42,7 +48,7 @@
 
     override fun getSupportedAnnotationTypes() = setOf("*")
 
-    fun throwIfFailed() {
+    override fun throwIfFailed() {
         val result = checkNotNull(result) {
             "did not compile"
         }
diff --git a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/SyntheticKspProcessor.kt b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/SyntheticKspProcessor.kt
index 106d6b1..68ec813 100644
--- a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/SyntheticKspProcessor.kt
+++ b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/SyntheticKspProcessor.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.compiler.processing
 
+import androidx.room.compiler.processing.util.RecordingXMessager
 import androidx.room.compiler.processing.util.XTestInvocation
 import com.google.devtools.ksp.processing.CodeGenerator
 import com.google.devtools.ksp.processing.KSPLogger
@@ -24,11 +25,14 @@
 
 class SyntheticKspProcessor(
     private val handler: (XTestInvocation) -> Unit
-) : SymbolProcessor {
+) : SymbolProcessor, SyntheticProcessor {
+    override val invocationInstances = mutableListOf<XTestInvocation>()
     private var result: Result<Unit>? = null
     private lateinit var options: Map<String, String>
     private lateinit var codeGenerator: CodeGenerator
     private lateinit var logger: KSPLogger
+    override val messageWatcher = RecordingXMessager()
+
     override fun finish() {
     }
 
@@ -44,21 +48,25 @@
     }
 
     override fun process(resolver: Resolver) {
+        val xEnv = XProcessingEnv.create(
+            options,
+            resolver,
+            codeGenerator,
+            logger
+        )
+        xEnv.messager.addMessageWatcher(messageWatcher)
         result = kotlin.runCatching {
             handler(
                 XTestInvocation(
-                    processingEnv = XProcessingEnv.create(
-                        options,
-                        resolver,
-                        codeGenerator,
-                        logger
-                    )
-                )
+                    processingEnv = xEnv
+                ).also {
+                    invocationInstances.add(it)
+                }
             )
         }
     }
 
-    fun throwIfFailed() {
+    override fun throwIfFailed() {
         val result = checkNotNull(result) {
             "did not compile"
         }
diff --git a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/SyntheticProcessor.kt b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/SyntheticProcessor.kt
new file mode 100644
index 0000000..05cc826
--- /dev/null
+++ b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/SyntheticProcessor.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing
+
+import androidx.room.compiler.processing.util.RecordingXMessager
+import androidx.room.compiler.processing.util.XTestInvocation
+
+/**
+ * Common interface for SyntheticProcessors that we create for testing.
+ */
+internal interface SyntheticProcessor {
+    /**
+     * List of invocations that was sent to the test code.
+     *
+     * The test code can register assertions on the compilation result, which is why we need this
+     * list (to run assertions after compilation).
+     */
+    val invocationInstances: List<XTestInvocation>
+
+    /**
+     * The recorder for messages where we'll grab the diagnostics.
+     */
+    val messageWatcher: RecordingXMessager
+
+    /**
+     * Should throw if processor did throw an exception.
+     * When assertions fail, we don't fail the compilation to keep the stack trace, instead,
+     * dispatch them afterwards.
+     */
+    fun throwIfFailed()
+}
diff --git a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/CompilationResultSubject.kt b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/CompilationResultSubject.kt
new file mode 100644
index 0000000..3dbbff7
--- /dev/null
+++ b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/CompilationResultSubject.kt
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing.util
+
+import androidx.room.compiler.processing.SyntheticJavacProcessor
+import androidx.room.compiler.processing.SyntheticProcessor
+import androidx.room.compiler.processing.util.runner.CompilationTestRunner
+import com.google.common.truth.Fact.simpleFact
+import com.google.common.truth.FailureMetadata
+import com.google.common.truth.Subject
+import com.google.common.truth.Subject.Factory
+import com.google.common.truth.Truth
+import com.google.testing.compile.Compilation
+import com.google.testing.compile.CompileTester
+import com.tschuchort.compiletesting.KotlinCompilation
+import javax.tools.Diagnostic
+
+/**
+ * Holds the information about a test compilation result.
+ */
+abstract class CompilationResult internal constructor(
+    /**
+     * The test infra which run this test
+     */
+    internal val testRunnerName: String,
+    /**
+     * The [SyntheticProcessor] used in this compilation.
+     */
+    internal val processor: SyntheticProcessor,
+    /**
+     * True if compilation result was success.
+     */
+    internal val successfulCompilation: Boolean,
+) {
+    private val diagnostics = processor.messageWatcher.diagnostics()
+
+    fun diagnosticsOfKind(kind: Diagnostic.Kind) = diagnostics[kind].orEmpty()
+
+    override fun toString(): String {
+        return buildString {
+            appendLine("CompilationResult (with $testRunnerName)")
+            Diagnostic.Kind.values().forEach { kind ->
+                val messages = diagnosticsOfKind(kind)
+                appendLine("${kind.name}: ${messages.size}")
+                messages.forEach {
+                    appendLine(it)
+                }
+                appendLine()
+            }
+        }
+    }
+}
+
+/**
+ * Truth subject that can run assertions on the [CompilationResult].
+ * see: [XTestInvocation.assertCompilationResult]
+ */
+class CompilationResultSubject(
+    failureMetadata: FailureMetadata,
+    val compilationResult: CompilationResult,
+) : Subject<CompilationResultSubject, CompilationResult>(
+    failureMetadata, compilationResult
+) {
+    /**
+     * set to true if any assertion on the subject requires it to fail (e.g. looking for errors)
+     */
+    internal var shouldSucceed: Boolean = true
+
+    /**
+     * Asserts that compilation did fail. This covers the cases where the processor won't print
+     * any diagnostics but compilation will still fail (e.g. bad generated code).
+     *
+     * @see hasError
+     */
+    fun compilationDidFail() = chain {
+        shouldSucceed = false
+    }
+
+    /**
+     * Asserts that compilation has a warning with the given text.
+     *
+     * @see hasError
+     */
+    fun hasWarning(expected: String) = chain {
+        hasDiagnosticWithMessage(
+            kind = Diagnostic.Kind.WARNING,
+            expected = expected
+        ) {
+            "expected warning: $expected"
+        }
+    }
+
+    /**
+     * Asserts that compilation has an error with the given text.
+     *
+     * @see hasWarning
+     */
+    fun hasError(expected: String) = chain {
+        shouldSucceed = false
+        hasDiagnosticWithMessage(
+            kind = Diagnostic.Kind.ERROR,
+            expected = expected
+        ) {
+            "expected error: $expected"
+        }
+    }
+
+    /**
+     * Asserts that compilation has at least one diagnostics message with kind error.
+     *
+     * @see compilationDidFail
+     * @see hasWarning
+     */
+    fun hasError() = chain {
+        shouldSucceed = false
+        if (actual().diagnosticsOfKind(Diagnostic.Kind.ERROR).isEmpty()) {
+            failWithActual(
+                simpleFact("expected at least one failure message")
+            )
+        }
+    }
+
+    /**
+     * Called after handler is invoked to check its compilation failure assertion against the
+     * compilation result.
+     */
+    internal fun assertCompilationResult() {
+        if (compilationResult.successfulCompilation != shouldSucceed) {
+            failWithActual(
+                simpleFact(
+                    "expected compilation result to be: $shouldSucceed but was " +
+                        "${compilationResult.successfulCompilation}"
+                )
+            )
+        }
+    }
+
+    private fun hasDiagnosticWithMessage(
+        kind: Diagnostic.Kind,
+        expected: String,
+        buildErrorMessage: () -> String
+    ) {
+        val diagnostics = compilationResult.diagnosticsOfKind(kind)
+        if (diagnostics.any { it.msg == expected }) {
+            return
+        }
+        failWithActual(simpleFact(buildErrorMessage()))
+    }
+
+    private fun chain(
+        block: () -> Unit
+    ): CompileTester.ChainingClause<CompilationResultSubject> {
+        block()
+        return CompileTester.ChainingClause<CompilationResultSubject> {
+            this
+        }
+    }
+
+    companion object {
+        private val FACTORY =
+            Factory<CompilationResultSubject, CompilationResult> { metadata, actual ->
+                CompilationResultSubject(metadata, actual)
+            }
+
+        fun assertThat(
+            compilationResult: CompilationResult
+        ): CompilationResultSubject {
+            return Truth.assertAbout(FACTORY).that(
+                compilationResult
+            )
+        }
+    }
+}
+
+internal class JavaCompileTestingCompilationResult(
+    testRunner: CompilationTestRunner,
+    @Suppress("unused")
+    private val delegate: Compilation,
+    processor: SyntheticJavacProcessor
+) : CompilationResult(
+    testRunnerName = testRunner.name,
+    processor = processor,
+    successfulCompilation = delegate.status() == Compilation.Status.SUCCESS
+)
+
+internal class KotlinCompileTestingCompilationResult(
+    testRunner: CompilationTestRunner,
+    @Suppress("unused")
+    private val delegate: KotlinCompilation.Result,
+    processor: SyntheticProcessor,
+    successfulCompilation: Boolean
+) : CompilationResult(
+    testRunnerName = testRunner.name,
+    processor = processor,
+    successfulCompilation = successfulCompilation
+)
\ No newline at end of file
diff --git a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/DiagnosticMessage.kt b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/DiagnosticMessage.kt
new file mode 100644
index 0000000..34ef8e3
--- /dev/null
+++ b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/DiagnosticMessage.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing.util
+
+import androidx.room.compiler.processing.XElement
+
+/**
+ * Holder for diagnostics messages
+ */
+data class DiagnosticMessage(
+    val msg: String,
+    val element: XElement?
+)
\ No newline at end of file
diff --git a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/ProcessorTestExt.kt b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/ProcessorTestExt.kt
index 076b531..bbf2888 100644
--- a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/ProcessorTestExt.kt
+++ b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/ProcessorTestExt.kt
@@ -16,275 +16,147 @@
 
 package androidx.room.compiler.processing.util
 
-import androidx.room.compiler.processing.SyntheticJavacProcessor
-import androidx.room.compiler.processing.SyntheticKspProcessor
-import com.google.common.truth.Truth
+import androidx.room.compiler.processing.util.runner.CompilationTestRunner
+import androidx.room.compiler.processing.util.runner.JavacCompilationTestRunner
+import androidx.room.compiler.processing.util.runner.KaptCompilationTestRunner
+import androidx.room.compiler.processing.util.runner.KspCompilationTestRunner
+import androidx.room.compiler.processing.util.runner.TestCompilationParameters
 import com.google.common.truth.Truth.assertThat
-import com.google.testing.compile.CompileTester
-import com.google.testing.compile.JavaSourcesSubjectFactory
+import com.google.common.truth.Truth.assertWithMessage
 import com.tschuchort.compiletesting.KotlinCompilation
-import com.tschuchort.compiletesting.SourceFile
-import com.tschuchort.compiletesting.kspSourcesDir
-import com.tschuchort.compiletesting.symbolProcessors
 import java.io.File
 
-// TODO get rid of these once kotlin compile testing supports two step compilation for KSP.
-//  https://github.com/tschuchortdev/kotlin-compile-testing/issues/72
-private val KotlinCompilation.kspJavaSourceDir: File
-    get() = kspSourcesDir.resolve("java")
+private fun runTests(
+    params: TestCompilationParameters,
+    vararg runners: CompilationTestRunner
+) {
+    val runCount = runners.count { runner ->
+        if (runner.canRun(params)) {
+            val compilationResult = runner.compile(params)
+            val subject = CompilationResultSubject.assertThat(compilationResult)
+            // if any assertion failed, throw first those.
+            compilationResult.processor.throwIfFailed()
 
-private val KotlinCompilation.kspKotlinSourceDir: File
-    get() = kspSourcesDir.resolve("kotlin")
+            compilationResult.processor.invocationInstances.forEach {
+                it.runPostCompilationChecks(subject)
+            }
+            assertWithMessage(
+                "compilation should've run the processor callback at least once"
+            ).that(
+                compilationResult.processor.invocationInstances
+            ).isNotEmpty()
 
-private fun compileSources(
-    sources: List<Source>,
-    classpath: List<File>,
+            subject.assertCompilationResult()
+            true
+        } else {
+            false
+        }
+    }
+    // make sure some tests did run
+    assertThat(runCount).isGreaterThan(0)
+}
+
+fun runProcessorTestWithoutKsp(
+    sources: List<Source> = emptyList(),
+    classpath: List<File> = emptyList(),
     handler: (XTestInvocation) -> Unit
-): Pair<SyntheticJavacProcessor, CompileTester> {
-    val syntheticJavacProcessor = SyntheticJavacProcessor(handler)
-    return syntheticJavacProcessor to Truth.assertAbout(
-        JavaSourcesSubjectFactory.javaSources()
-    ).that(
-        sources.map {
-            it.toJFO()
-        }
-    ).apply {
-        if (classpath.isNotEmpty()) {
-            withClasspath(classpath)
-        }
-    }.processedWith(
-        syntheticJavacProcessor
+) {
+    runTests(
+        params = TestCompilationParameters(
+            sources = sources,
+            classpath = classpath,
+            handler = handler
+        ),
+        JavacCompilationTestRunner,
+        KaptCompilationTestRunner
     )
 }
 
-private fun compileWithKapt(
-    sources: List<Source>,
-    classpath: List<File>,
-    handler: (XTestInvocation) -> Unit
-): Pair<SyntheticJavacProcessor, KotlinCompilation> {
-    val syntheticJavacProcessor = SyntheticJavacProcessor(handler)
-    val compilation = KotlinCompilation()
-    sources.forEach {
-        compilation.workingDir.resolve("sources")
-            .resolve(it.relativePath())
-            .parentFile
-            .mkdirs()
-    }
-    compilation.sources = sources.map {
-        it.toKotlinSourceFile()
-    }
-    compilation.annotationProcessors = listOf(syntheticJavacProcessor)
-    compilation.inheritClassPath = true
-    compilation.verbose = false
-    compilation.classpaths += classpath
-
-    return syntheticJavacProcessor to compilation
-}
-
-private fun compileWithKsp(
-    sources: List<Source>,
-    classpath: List<File>,
-    handler: (XTestInvocation) -> Unit
-): Pair<SyntheticKspProcessor, KotlinCompilation.Result> {
-    @Suppress("NAME_SHADOWING")
-    val sources = if (sources.none { it is Source.KotlinSource }) {
-        // looks like this requires a kotlin source file
-        // see: https://github.com/tschuchortdev/kotlin-compile-testing/issues/57
-        sources + Source.kotlin("placeholder.kt", "")
-    } else {
-        sources
-    }
-    val syntheticKspProcessor = SyntheticKspProcessor(handler)
-    fun prepareCompilation(): KotlinCompilation {
-        val compilation = KotlinCompilation()
-        sources.forEach {
-            compilation.workingDir.resolve("sources")
-                .resolve(it.relativePath())
-                .parentFile
-                .mkdirs()
-        }
-        compilation.sources = sources.map {
-            it.toKotlinSourceFile()
-        }
-        compilation.jvmDefault = "enable"
-        compilation.jvmTarget = "1.8"
-        compilation.inheritClassPath = true
-        compilation.verbose = false
-        compilation.classpaths += classpath
-        return compilation
-    }
-
-    val kspCompilation = prepareCompilation()
-    kspCompilation.symbolProcessors = listOf(syntheticKspProcessor)
-    kspCompilation.compile()
-    // ignore KSP result for now because KSP stops compilation, which might create false negatives
-    // when java code accesses kotlin code.
-    // TODO:  fix once https://github.com/tschuchortdev/kotlin-compile-testing/issues/72 is fixed
-
-    // after ksp, compile without ksp with KSP's output as input
-    val finalCompilation = prepareCompilation()
-    // build source files from generated code
-    finalCompilation.sources += kspCompilation.kspJavaSourceDir.collectSourceFiles() +
-        kspCompilation.kspKotlinSourceDir.collectSourceFiles()
-    return syntheticKspProcessor to finalCompilation.compile()
-}
-
-private fun File.collectSourceFiles(): List<SourceFile> {
-    return walkTopDown().filter {
-        it.isFile
-    }.map { file ->
-        SourceFile.fromPath(file)
-    }.toList()
-}
-
+/**
+ * Runs the compilation test with all 3 backends (javac, kapt, ksp) if possible (e.g. javac
+ * cannot test kotlin sources).
+ *
+ * The [handler] will be invoked for each compilation hence it should be repeatable.
+ *
+ * To assert on the compilation results, [handler] can call
+ * [XTestInvocation.assertCompilationResult] where it will receive a subject for post compilation
+ * assertions.
+ *
+ * By default, the compilation is expected to succeed. If it should fail, there must be an
+ * assertion on [XTestInvocation.assertCompilationResult] which expects a failure (e.g. checking
+ * errors).
+ */
 fun runProcessorTest(
     sources: List<Source> = emptyList(),
     classpath: List<File> = emptyList(),
     handler: (XTestInvocation) -> Unit
 ) {
-    @Suppress("NAME_SHADOWING")
-    val sources = if (sources.isEmpty()) {
-        // synthesize a source to trigger compilation
-        listOf(
-            Source.java(
-                "foo.bar.SyntheticSource",
-                """
-            package foo.bar;
-            public class SyntheticSource {}
-                """.trimIndent()
-            )
-        )
-    } else {
-        sources
-    }
-    // we can compile w/ javac only if all code is in java
-    if (sources.canCompileWithJava()) {
-        runJavaProcessorTest(
+    runTests(
+        params = TestCompilationParameters(
             sources = sources,
             classpath = classpath,
-            handler = handler,
-            succeed = true
-        )
-    }
-    runKaptTest(
-        sources = sources,
-        classpath = classpath,
-        handler = handler,
-        succeed = true
+            handler = handler
+        ),
+        JavacCompilationTestRunner,
+        KaptCompilationTestRunner,
+        KspCompilationTestRunner
     )
 }
 
 /**
- * This method is oddly named instead of being an overload on runProcessorTest to easily track
- * which tests started to support KSP.
+ * Runs the test only with javac compilation backend.
  *
- * Eventually, it will be merged with runProcessorTest when all tests pass with KSP.
+ * @see runProcessorTest
  */
-fun runProcessorTestIncludingKsp(
-    sources: List<Source> = emptyList(),
-    classpath: List<File> = emptyList(),
-    handler: (XTestInvocation) -> Unit
-) {
-    runProcessorTest(
-        sources = sources,
-        classpath = classpath,
-        handler = handler
-    )
-    runKspTest(
-        sources = sources,
-        classpath = classpath,
-        succeed = true,
-        handler = handler
-    )
-}
-
-fun runProcessorTestForFailedCompilation(
-    sources: List<Source>,
-    classpath: List<File> = emptyList(),
-    handler: (XTestInvocation) -> Unit
-) {
-    if (sources.canCompileWithJava()) {
-        // run with java processor
-        runJavaProcessorTest(
-            sources = sources,
-            classpath = classpath,
-            handler = handler,
-            succeed = false
-        )
-    }
-    // now run with kapt
-    runKaptTest(
-        sources = sources,
-        classpath = classpath,
-        handler = handler,
-        succeed = false
-    )
-}
-
-fun runProcessorTestForFailedCompilationIncludingKsp(
-    sources: List<Source>,
-    classpath: List<File>,
-    handler: (XTestInvocation) -> Unit
-) {
-    runProcessorTestForFailedCompilation(
-        sources = sources,
-        classpath = classpath,
-        handler = handler
-    )
-    // now run with ksp
-    runKspTest(
-        sources = sources,
-        classpath = classpath,
-        handler = handler,
-        succeed = false
-    )
-}
-
 fun runJavaProcessorTest(
     sources: List<Source>,
     classpath: List<File>,
-    succeed: Boolean,
     handler: (XTestInvocation) -> Unit
 ) {
-    val (syntheticJavacProcessor, compileTester) = compileSources(sources, classpath, handler)
-    if (succeed) {
-        compileTester.compilesWithoutError()
-    } else {
-        compileTester.failsToCompile()
-    }
-    syntheticJavacProcessor.throwIfFailed()
+    runTests(
+        params = TestCompilationParameters(
+            sources = sources,
+            classpath = classpath,
+            handler = handler
+        ),
+        JavacCompilationTestRunner
+    )
 }
 
+/**
+ * Runs the test only with kapt compilation backend
+ */
 fun runKaptTest(
     sources: List<Source>,
     classpath: List<File> = emptyList(),
-    succeed: Boolean = true,
     handler: (XTestInvocation) -> Unit
 ) {
-    // now run with kapt
-    val (kaptProcessor, kotlinCompilation) = compileWithKapt(sources, classpath, handler)
-    val compilationResult = kotlinCompilation.compile()
-    if (succeed) {
-        assertThat(compilationResult.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
-    } else {
-        assertThat(compilationResult.exitCode).isNotEqualTo(KotlinCompilation.ExitCode.OK)
-    }
-    kaptProcessor.throwIfFailed()
+    runTests(
+        params = TestCompilationParameters(
+            sources = sources,
+            classpath = classpath,
+            handler = handler
+        ),
+        KaptCompilationTestRunner
+    )
 }
 
+/**
+ * Runs the test only with ksp compilation backend
+ */
 fun runKspTest(
     sources: List<Source>,
     classpath: List<File> = emptyList(),
-    succeed: Boolean = true,
     handler: (XTestInvocation) -> Unit
 ) {
-    val (kspProcessor, compilationResult) = compileWithKsp(sources, classpath, handler)
-    if (succeed) {
-        assertThat(compilationResult.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
-    } else {
-        assertThat(compilationResult.exitCode).isNotEqualTo(KotlinCompilation.ExitCode.OK)
-    }
-    kspProcessor.throwIfFailed()
+    runTests(
+        params = TestCompilationParameters(
+            sources = sources,
+            classpath = classpath,
+            handler = handler
+        ),
+        KspCompilationTestRunner
+    )
 }
 
 /**
@@ -314,5 +186,3 @@
     }
     return compilation.classesDir
 }
-
-private fun List<Source>.canCompileWithJava() = all { it is Source.JavaSource }
diff --git a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/RecordingXMessager.kt b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/RecordingXMessager.kt
new file mode 100644
index 0000000..c2aa5dc
--- /dev/null
+++ b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/RecordingXMessager.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing.util
+
+import androidx.room.compiler.processing.XElement
+import androidx.room.compiler.processing.XMessager
+import javax.tools.Diagnostic
+
+/**
+ * An XMessager implementation that holds onto dispatched diagnostics.
+ */
+class RecordingXMessager : XMessager() {
+    private val diagnostics = mutableMapOf<Diagnostic.Kind, MutableList<DiagnosticMessage>>()
+
+    fun diagnostics(): Map<Diagnostic.Kind, List<DiagnosticMessage>> = diagnostics
+
+    override fun onPrintMessage(kind: Diagnostic.Kind, msg: String, element: XElement?) {
+        diagnostics.getOrPut(
+            kind
+        ) {
+            mutableListOf()
+        }.add(
+            DiagnosticMessage(
+                msg = msg,
+                element = element
+            )
+        )
+    }
+}
\ No newline at end of file
diff --git a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/XTestInvocation.kt b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/XTestInvocation.kt
index 267c140..d7d0e75 100644
--- a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/XTestInvocation.kt
+++ b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/XTestInvocation.kt
@@ -17,6 +17,7 @@
 package androidx.room.compiler.processing.util
 
 import androidx.room.compiler.processing.XProcessingEnv
+import kotlin.reflect.KClass
 
 /**
  * Data holder for XProcessing tests to access the processing environment.
@@ -24,6 +25,48 @@
 class XTestInvocation(
     val processingEnv: XProcessingEnv,
 ) {
+    /**
+     * Extension mechanism to allow putting objects into invocation that can be retrieved later.
+     */
+    private val userData = mutableMapOf<KClass<*>, Any>()
+
+    private val postCompilationAssertions = mutableListOf<CompilationResultSubject.() -> Unit>()
     val isKsp: Boolean
         get() = processingEnv.backend == XProcessingEnv.Backend.KSP
+
+    /**
+     * Registers a block that will be called with a [CompilationResultSubject] when compilation
+     * finishes.
+     *
+     * Note that it is not safe to access the environment in this block.
+     */
+    fun assertCompilationResult(block: CompilationResultSubject.() -> Unit) {
+        postCompilationAssertions.add(block)
+    }
+
+    internal fun runPostCompilationChecks(
+        compilationResultSubject: CompilationResultSubject
+    ) {
+        postCompilationAssertions.forEach {
+            it(compilationResultSubject)
+        }
+    }
+
+    fun <T : Any> getUserData(key: KClass<T>): T? {
+        @Suppress("UNCHECKED_CAST")
+        return userData[key] as T?
+    }
+
+    fun <T : Any> putUserData(key: KClass<T>, value: T) {
+        userData[key] = value
+    }
+
+    fun <T : Any> getOrPutUserData(key: KClass<T>, create: () -> T): T {
+        getUserData(key)?.let {
+            return it
+        }
+        return create().also {
+            putUserData(key, it)
+        }
+    }
 }
\ No newline at end of file
diff --git a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/runner/CompilationTestRunner.kt b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/runner/CompilationTestRunner.kt
new file mode 100644
index 0000000..257e58a
--- /dev/null
+++ b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/runner/CompilationTestRunner.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing.util.runner
+
+import androidx.room.compiler.processing.util.CompilationResult
+import androidx.room.compiler.processing.util.Source
+import androidx.room.compiler.processing.util.XTestInvocation
+import java.io.File
+
+/**
+ * Common interface for compilation tests
+ */
+internal interface CompilationTestRunner {
+    // user visible name that we can print in assertions
+    val name: String
+
+    fun canRun(params: TestCompilationParameters): Boolean
+
+    fun compile(params: TestCompilationParameters): CompilationResult
+}
+
+internal data class TestCompilationParameters(
+    val sources: List<Source> = emptyList(),
+    val classpath: List<File> = emptyList(),
+    val handler: (XTestInvocation) -> Unit
+)
diff --git a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/runner/JavacCompilationTestRunner.kt b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/runner/JavacCompilationTestRunner.kt
new file mode 100644
index 0000000..5fdba61
--- /dev/null
+++ b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/runner/JavacCompilationTestRunner.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing.util.runner
+
+import androidx.room.compiler.processing.SyntheticJavacProcessor
+import androidx.room.compiler.processing.util.CompilationResult
+import androidx.room.compiler.processing.util.JavaCompileTestingCompilationResult
+import androidx.room.compiler.processing.util.Source
+import com.google.testing.compile.Compiler
+
+internal object JavacCompilationTestRunner : CompilationTestRunner {
+
+    override val name: String = "javac"
+
+    override fun canRun(params: TestCompilationParameters): Boolean {
+        return params.sources.all { it is Source.JavaSource }
+    }
+
+    override fun compile(params: TestCompilationParameters): CompilationResult {
+        val syntheticJavacProcessor = SyntheticJavacProcessor(params.handler)
+        val sources = if (params.sources.isEmpty()) {
+            // synthesize a source to trigger compilation
+            listOf(
+                Source.java(
+                    qName = "foo.bar.SyntheticSource",
+                    code = """
+                    package foo.bar;
+                    public class SyntheticSource {}
+                    """.trimIndent()
+                )
+            )
+        } else {
+            params.sources
+        }
+        val compiler = Compiler
+            .javac()
+            .withProcessors(syntheticJavacProcessor)
+            .withOptions("-Xlint")
+            .let {
+                if (params.classpath.isNotEmpty()) {
+                    it.withClasspath(params.classpath)
+                } else {
+                    it
+                }
+            }
+        val javaFileObjects = sources.map {
+            it.toJFO()
+        }
+        val compilation = compiler.compile(javaFileObjects)
+        return JavaCompileTestingCompilationResult(
+            testRunner = this,
+            delegate = compilation,
+            processor = syntheticJavacProcessor
+        )
+    }
+}
\ No newline at end of file
diff --git a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/runner/KaptCompilationTestRunner.kt b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/runner/KaptCompilationTestRunner.kt
new file mode 100644
index 0000000..e6cb5ee
--- /dev/null
+++ b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/runner/KaptCompilationTestRunner.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing.util.runner
+
+import androidx.room.compiler.processing.SyntheticJavacProcessor
+import androidx.room.compiler.processing.util.CompilationResult
+import androidx.room.compiler.processing.util.KotlinCompileTestingCompilationResult
+import com.tschuchort.compiletesting.KotlinCompilation
+
+internal object KaptCompilationTestRunner : CompilationTestRunner {
+
+    override val name: String = "kapt"
+
+    override fun canRun(params: TestCompilationParameters): Boolean {
+        return true
+    }
+
+    override fun compile(params: TestCompilationParameters): CompilationResult {
+        val syntheticJavacProcessor = SyntheticJavacProcessor(params.handler)
+        val compilation = KotlinCompilation()
+        params.sources.forEach {
+            compilation.workingDir.resolve("sources")
+                .resolve(it.relativePath())
+                .parentFile
+                .mkdirs()
+        }
+        compilation.sources = params.sources.map {
+            it.toKotlinSourceFile()
+        }
+        compilation.annotationProcessors = listOf(syntheticJavacProcessor)
+        compilation.inheritClassPath = true
+        compilation.verbose = false
+        compilation.classpaths += params.classpath
+
+        val result = compilation.compile()
+        return KotlinCompileTestingCompilationResult(
+            testRunner = this,
+            delegate = result,
+            processor = syntheticJavacProcessor,
+            successfulCompilation = result.exitCode == KotlinCompilation.ExitCode.OK
+        )
+    }
+}
\ No newline at end of file
diff --git a/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/runner/KspCompilationTestRunner.kt b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/runner/KspCompilationTestRunner.kt
new file mode 100644
index 0000000..8ac5ed3
--- /dev/null
+++ b/room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/runner/KspCompilationTestRunner.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing.util.runner
+
+import androidx.room.compiler.processing.SyntheticKspProcessor
+import androidx.room.compiler.processing.util.CompilationResult
+import androidx.room.compiler.processing.util.KotlinCompileTestingCompilationResult
+import androidx.room.compiler.processing.util.Source
+import com.tschuchort.compiletesting.KotlinCompilation
+import com.tschuchort.compiletesting.SourceFile
+import com.tschuchort.compiletesting.kspSourcesDir
+import com.tschuchort.compiletesting.symbolProcessors
+import java.io.File
+import javax.tools.Diagnostic
+
+internal object KspCompilationTestRunner : CompilationTestRunner {
+
+    override val name: String = "ksp"
+
+    override fun canRun(params: TestCompilationParameters): Boolean {
+        return true
+    }
+
+    override fun compile(params: TestCompilationParameters): CompilationResult {
+        @Suppress("NAME_SHADOWING")
+        val sources = if (params.sources.none { it is Source.KotlinSource }) {
+            // looks like this requires a kotlin source file
+            // see: https://github.com/tschuchortdev/kotlin-compile-testing/issues/57
+            params.sources + Source.kotlin("placeholder.kt", "")
+        } else {
+            params.sources
+        }
+        val syntheticKspProcessor = SyntheticKspProcessor(params.handler)
+        fun prepareCompilation(): KotlinCompilation {
+            val compilation = KotlinCompilation()
+            sources.forEach {
+                compilation.workingDir.resolve("sources")
+                    .resolve(it.relativePath())
+                    .parentFile
+                    .mkdirs()
+            }
+            compilation.sources = sources.map {
+                it.toKotlinSourceFile()
+            }
+            compilation.jvmDefault = "enable"
+            compilation.jvmTarget = "1.8"
+            compilation.inheritClassPath = true
+            compilation.verbose = false
+            compilation.classpaths += params.classpath
+            return compilation
+        }
+
+        val kspCompilation = prepareCompilation()
+        kspCompilation.symbolProcessors = listOf(syntheticKspProcessor)
+        kspCompilation.compile()
+        // ignore KSP result for now because KSP stops compilation, which might create false
+        // negatives when java code accesses kotlin code.
+        // TODO:  fix once https://github.com/tschuchortdev/kotlin-compile-testing/issues/72 is
+        //  fixed
+
+        // after ksp, compile without ksp with KSP's output as input
+        val finalCompilation = prepareCompilation()
+        // build source files from generated code
+        finalCompilation.sources += kspCompilation.kspJavaSourceDir.collectSourceFiles() +
+            kspCompilation.kspKotlinSourceDir.collectSourceFiles()
+        val result = finalCompilation.compile()
+        // workaround for: https://github.com/google/ksp/issues/122
+        // KSP does not fail compilation for error diagnostics hence we do it here.
+        val hasErrorDiagnostics = syntheticKspProcessor.messageWatcher
+            .diagnostics()[Diagnostic.Kind.ERROR].orEmpty().isNotEmpty()
+        return KotlinCompileTestingCompilationResult(
+            testRunner = this,
+            delegate = result,
+            processor = syntheticKspProcessor,
+            successfulCompilation = result.exitCode == KotlinCompilation.ExitCode.OK &&
+                !hasErrorDiagnostics
+
+        )
+    }
+
+    // TODO get rid of these once kotlin compile testing supports two step compilation for KSP.
+    //  https://github.com/tschuchortdev/kotlin-compile-testing/issues/72
+    private val KotlinCompilation.kspJavaSourceDir: File
+        get() = kspSourcesDir.resolve("java")
+
+    private val KotlinCompilation.kspKotlinSourceDir: File
+        get() = kspSourcesDir.resolve("kotlin")
+
+    private fun File.collectSourceFiles(): List<SourceFile> {
+        return walkTopDown().filter {
+            it.isFile
+        }.map { file ->
+            SourceFile.fromPath(file)
+        }.toList()
+    }
+}
\ No newline at end of file
diff --git a/room/compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestRunnerTest.kt b/room/compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestRunnerTest.kt
new file mode 100644
index 0000000..59dd47f7
--- /dev/null
+++ b/room/compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestRunnerTest.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing.util
+
+import com.squareup.javapoet.CodeBlock
+import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.TypeSpec
+import org.junit.Test
+import javax.tools.Diagnostic
+
+class TestRunnerTest {
+    @Test
+    fun generatedBadCode_expected() = generatedBadCode(assertFailure = true)
+
+    @Test(expected = AssertionError::class)
+    fun generatedBadCode_unexpected() = generatedBadCode(assertFailure = false)
+
+    private fun generatedBadCode(assertFailure: Boolean) {
+        runProcessorTest {
+            if (it.processingEnv.findTypeElement("foo.Foo") == null) {
+                val badCode = TypeSpec.classBuilder("Foo").apply {
+                    addStaticBlock(
+                        CodeBlock.of("bad code")
+                    )
+                }.build()
+                val badGeneratedFile = JavaFile.builder("foo", badCode).build()
+                it.processingEnv.filer.write(
+                    badGeneratedFile
+                )
+            }
+            if (assertFailure) {
+                it.assertCompilationResult {
+                    compilationDidFail()
+                }
+            }
+        }
+    }
+
+    @Test
+    fun reportedError_expected() = reportedError(assertFailure = true)
+
+    @Test(expected = AssertionError::class)
+    fun reportedError_unexpected() = reportedError(assertFailure = false)
+
+    fun reportedError(assertFailure: Boolean) {
+        runProcessorTest {
+            it.processingEnv.messager.printMessage(
+                kind = Diagnostic.Kind.ERROR,
+                msg = "reported error"
+            )
+            if (assertFailure) {
+                it.assertCompilationResult {
+                    hasError("reported error")
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/room/compiler-processing/build.gradle b/room/compiler-processing/build.gradle
index 795a691..b15e171 100644
--- a/room/compiler-processing/build.gradle
+++ b/room/compiler-processing/build.gradle
@@ -26,12 +26,13 @@
 }
 
 dependencies {
+    api(KOTLIN_STDLIB)
+    api(JAVAPOET)
     implementation("androidx.annotation:annotation:1.1.0")
     implementation(GUAVA)
-    implementation(KOTLIN_STDLIB)
     implementation(AUTO_COMMON)
     implementation(AUTO_VALUE_ANNOTATIONS)
-    implementation(JAVAPOET)
+
     implementation(KOTLIN_METADATA_JVM)
     implementation(INTELLIJ_ANNOTATIONS)
     implementation(KOTLIN_KSP_API) {
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XMessager.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XMessager.kt
index 0701c52..29ff818 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XMessager.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XMessager.kt
@@ -21,7 +21,8 @@
 /**
  * Logging interface for the processor
  */
-interface XMessager {
+abstract class XMessager {
+    private val watchers = mutableListOf<XMessager>()
     /**
      * Prints the given [msg] to the logs while also associating it with the given [element].
      *
@@ -29,5 +30,20 @@
      * @param msg The actual message to report to the compiler
      * @param element The element with whom the message should be associated with
      */
-    fun printMessage(kind: Diagnostic.Kind, msg: String, element: XElement? = null)
+    final fun printMessage(kind: Diagnostic.Kind, msg: String, element: XElement? = null) {
+        watchers.forEach {
+            it.printMessage(kind, msg, element)
+        }
+        onPrintMessage(kind, msg, element)
+    }
+
+    abstract fun onPrintMessage(kind: Diagnostic.Kind, msg: String, element: XElement? = null)
+
+    fun addMessageWatcher(watcher: XMessager) {
+        watchers.add(watcher)
+    }
+
+    fun removeMessageWatcher(watcher: XMessager) {
+        watchers.remove(watcher)
+    }
 }
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacProcessingEnvMessager.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacProcessingEnvMessager.kt
index f5120fa..e49a5f7 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacProcessingEnvMessager.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacProcessingEnvMessager.kt
@@ -27,8 +27,8 @@
 
 internal class JavacProcessingEnvMessager(
     private val processingEnv: ProcessingEnvironment
-) : XMessager {
-    override fun printMessage(kind: Diagnostic.Kind, msg: String, element: XElement?) {
+) : XMessager() {
+    override fun onPrintMessage(kind: Diagnostic.Kind, msg: String, element: XElement?) {
         val javacElement = (element as? JavacElement)?.element
         processingEnv.messager.printMessage(
             kind,
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMessager.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMessager.kt
index 557cfa3..1497d90 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMessager.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMessager.kt
@@ -23,8 +23,8 @@
 
 internal class KspMessager(
     private val logger: KSPLogger
-) : XMessager {
-    override fun printMessage(kind: Diagnostic.Kind, msg: String, element: XElement?) {
+) : XMessager() {
+    override fun onPrintMessage(kind: Diagnostic.Kind, msg: String, element: XElement?) {
         val ksNode = (element as? KspElement)?.declaration
         when (kind) {
             Diagnostic.Kind.ERROR -> logger.error(msg, ksNode)
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
index 60339a5..22993b2 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
@@ -317,4 +317,8 @@
         .filter {
             it.simpleName == this.simpleName
         }
+
+    override fun toString(): String {
+        return declaration.toString()
+    }
 }
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeMapper.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeMapper.kt
index 734f3b1..06a1983 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeMapper.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeMapper.kt
@@ -75,7 +75,7 @@
         mapping["java.lang.Boolean"] = "kotlin.Boolean"
         // collections. default to mutable ones since java types are always mutable
         mapping["java.util.Iterator"] = "kotlin.collections.MutableIterator"
-        mapping["java.util.Iterable"] = "kotlin.collections.Iterable"
+        mapping["java.lang.Iterable"] = "kotlin.collections.Iterable"
         mapping["java.util.Collection"] = "kotlin.collections.MutableCollection"
         mapping["java.util.Set"] = "kotlin.collections.MutableSet"
         mapping["java.util.List"] = "kotlin.collections.MutableList"
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/MethodSpecHelperTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/MethodSpecHelperTest.kt
index 49ad742..b31aa76 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/MethodSpecHelperTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/MethodSpecHelperTest.kt
@@ -22,7 +22,7 @@
 import androidx.room.compiler.processing.util.XTestInvocation
 import androidx.room.compiler.processing.util.javaTypeUtils
 import androidx.room.compiler.processing.util.runKaptTest
-import androidx.room.compiler.processing.util.runProcessorTestIncludingKsp
+import androidx.room.compiler.processing.util.runProcessorTest
 import com.google.auto.common.MoreTypes
 import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.MethodSpec
@@ -291,7 +291,7 @@
     private fun overridesCheck(source: Source, ignoreInheritedMethods: Boolean = false) {
         // first build golden image with Java processor so we can use JavaPoet's API
         val golden = buildMethodsViaJavaPoet(source, ignoreInheritedMethods)
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(source)
         ) { invocation ->
             val (target, methods) = invocation.getOverrideTestTargets(ignoreInheritedMethods)
@@ -316,8 +316,7 @@
     ): List<String> {
         lateinit var result: List<String>
         runKaptTest(
-            sources = listOf(source),
-            succeed = true
+            sources = listOf(source)
         ) { invocation ->
             val (target, methods) = invocation.getOverrideTestTargets(
                 ignoreInheritedMethods
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
index 45f8aad..40cab24 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
@@ -25,7 +25,7 @@
 import androidx.room.compiler.processing.util.getMethod
 import androidx.room.compiler.processing.util.getParameter
 import androidx.room.compiler.processing.util.runProcessorTest
-import androidx.room.compiler.processing.util.runProcessorTestIncludingKsp
+import androidx.room.compiler.processing.util.runProcessorTestWithoutKsp
 import androidx.room.compiler.processing.util.typeName
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
@@ -48,7 +48,6 @@
             }
             """.trimIndent()
         )
-        // TODO add KSP once https://github.com/google/ksp/issues/96 is fixed.
         runProcessorTest(
             sources = listOf(source)
         ) {
@@ -87,7 +86,8 @@
             }
             """.trimIndent()
         )
-        runProcessorTest(
+        // re-enable after fixing b/175144186
+        runProcessorTestWithoutKsp(
             listOf(mySource)
         ) {
             val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
@@ -129,7 +129,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(source)
         ) {
             val element = it.processingEnv.requireTypeElement("Subject")
@@ -169,7 +169,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             listOf(mySource)
         ) { invocation ->
             val element = invocation.processingEnv.requireTypeElement("Subject")
@@ -229,7 +229,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(src)) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             val subject = invocation.processingEnv.requireTypeElement("Subject")
 
             subject.getField("prop1").assertHasSuppressWithValue("onProp1")
@@ -280,7 +280,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(src)) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             val subject = invocation.processingEnv.requireTypeElement("Subject")
             subject.getMethod("noAnnotations").let { method ->
                 method.assertDoesNotHaveAnnotation()
@@ -309,7 +309,7 @@
             )
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(src)) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             val subject = invocation.processingEnv.requireTypeElement("Subject")
             subject.assertHasSuppressWithValue("onClass")
             val constructor = subject.getConstructors().single()
@@ -338,7 +338,7 @@
             class JavaClass {}
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(kotlinSrc, javaSrc)) { invocation ->
+        runProcessorTest(sources = listOf(kotlinSrc, javaSrc)) { invocation ->
             listOf("KotlinClass", "JavaClass")
                 .map {
                     invocation.processingEnv.requireTypeElement(it)
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XArrayTypeTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XArrayTypeTest.kt
index 1f3260d..66b4de0 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XArrayTypeTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XArrayTypeTest.kt
@@ -23,7 +23,6 @@
 import androidx.room.compiler.processing.util.kspResolver
 import androidx.room.compiler.processing.util.runKspTest
 import androidx.room.compiler.processing.util.runProcessorTest
-import androidx.room.compiler.processing.util.runProcessorTestIncludingKsp
 import androidx.room.compiler.processing.util.typeName
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
@@ -43,7 +42,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(source)
         ) { invocation ->
             val type = invocation.processingEnv
@@ -63,7 +62,7 @@
 
     @Test
     fun synthetic() {
-        runProcessorTestIncludingKsp {
+        runProcessorTest {
             val objArray = it.processingEnv.getArrayType(
                 TypeName.OBJECT
             )
@@ -89,7 +88,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(source)
         ) { invocation ->
             val element = invocation.processingEnv.requireTypeElement("foo.bar.Baz")
@@ -140,7 +139,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(listOf(src)) {
+        runProcessorTest(listOf(src)) {
             val subject = it.processingEnv.requireTypeElement("Subject")
             val types = subject.getAllFieldsIncludingPrivateSupers().map {
                 assertWithMessage(it.name).that(it.type.isArray()).isTrue()
@@ -170,8 +169,7 @@
     @Test
     fun createArray() {
         runKspTest(
-            sources = emptyList(),
-            succeed = true
+            sources = emptyList()
         ) { invocation ->
             val intType = invocation.processingEnv.requireType("kotlin.Int")
             invocation.processingEnv.getArrayType(intType).let {
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt
index d84af6e..84623ee 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt
@@ -22,8 +22,8 @@
 import androidx.room.compiler.processing.util.getField
 import androidx.room.compiler.processing.util.getMethod
 import androidx.room.compiler.processing.util.getParameter
+import androidx.room.compiler.processing.util.runProcessorTestWithoutKsp
 import androidx.room.compiler.processing.util.runProcessorTest
-import androidx.room.compiler.processing.util.runProcessorTestIncludingKsp
 import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.TypeName
@@ -36,7 +36,7 @@
 class XElementTest {
     @Test
     fun modifiers() {
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             listOf(
                 Source.java(
                     "foo.bar.Baz",
@@ -146,7 +146,7 @@
                 }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             listOf(genericBase, boundedChild)
         ) {
             fun validateElement(element: XTypeElement, tTypeName: TypeName, rTypeName: TypeName) {
@@ -210,7 +210,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             listOf(source)
         ) {
             val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
@@ -257,7 +257,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             listOf(source)
         ) {
             val element = it.processingEnv.requireTypeElement("java.lang.Object")
@@ -280,7 +280,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(subject)
         ) {
             val inner = ClassName.get("foo.bar", "Baz.Inner")
@@ -317,7 +317,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             listOf(source)
         ) {
             val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
@@ -350,7 +350,8 @@
             }
             """.trimIndent()
         )
-        runProcessorTest(
+        // enable once https://github.com/google/ksp/issues/167 is fixed
+        runProcessorTestWithoutKsp(
             sources = listOf(source)
         ) {
             val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
@@ -371,9 +372,14 @@
 
     @Test
     fun toStringMatchesUnderlyingElement() {
-        runProcessorTest {
-            it.processingEnv.findTypeElement("java.util.List").let { list ->
-                assertThat(list.toString()).isEqualTo("java.util.List")
+        runProcessorTest { invocation ->
+            invocation.processingEnv.findTypeElement("java.util.List").let { list ->
+                val expected = if (invocation.isKsp) {
+                    "MutableList"
+                } else {
+                    "java.util.List"
+                }
+                assertThat(list.toString()).isEqualTo(expected)
             }
         }
     }
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
index a270b74..d4c82e9 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
@@ -23,7 +23,7 @@
 import androidx.room.compiler.processing.util.getDeclaredMethod
 import androidx.room.compiler.processing.util.getMethod
 import androidx.room.compiler.processing.util.getParameter
-import androidx.room.compiler.processing.util.runProcessorTestIncludingKsp
+import androidx.room.compiler.processing.util.runProcessorTest
 import androidx.room.compiler.processing.util.typeName
 import com.google.common.truth.Truth
 import com.google.common.truth.Truth.assertThat
@@ -38,7 +38,7 @@
 class XExecutableElementTest {
     @Test
     fun basic() {
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(
                 Source.java(
                     "foo.bar.Baz",
@@ -89,7 +89,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(subject)
         ) {
             val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
@@ -108,7 +108,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(subject)
         ) {
             val element = it.processingEnv.requireTypeElement("Subject")
@@ -138,7 +138,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(subject)
         ) {
             val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
@@ -182,7 +182,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(src)
         ) { invocation ->
             val subject = invocation.processingEnv.requireTypeElement("Subject")
@@ -269,7 +269,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(src)) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             val klass = invocation.processingEnv.requireTypeElement("MyDataClass")
             val methodNames = klass.getAllMethods().map {
                 it.name
@@ -323,7 +323,7 @@
             class NullableSubject: Base<String?>()
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(source)) { invocation ->
+        runProcessorTest(sources = listOf(source)) { invocation ->
             val base = invocation.processingEnv.requireTypeElement("Base")
             val subject = invocation.processingEnv.requireType("Subject")
                 .asDeclaredType()
@@ -386,7 +386,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(src, javaSrc)) { invocation ->
+        runProcessorTest(sources = listOf(src, javaSrc)) { invocation ->
             val base = invocation.processingEnv.requireTypeElement("MyInterface")
             val impl = invocation.processingEnv.requireTypeElement("MyImpl")
             val javaImpl = invocation.processingEnv.requireTypeElement("JavaImpl")
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt
index b2a03e5..38b007d 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt
@@ -20,7 +20,7 @@
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.UNIT_CLASS_NAME
 import androidx.room.compiler.processing.util.getMethod
-import androidx.room.compiler.processing.util.runProcessorTestIncludingKsp
+import androidx.room.compiler.processing.util.runProcessorTest
 import androidx.room.compiler.processing.util.typeName
 import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.ParameterizedTypeName
@@ -43,7 +43,7 @@
             abstract class Subject : MyInterface<String>
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(src)
         ) { invocation ->
             val myInterface = invocation.processingEnv.requireTypeElement("MyInterface")
@@ -112,7 +112,7 @@
             abstract class NullableSubject: MyInterface<String?>
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(src)) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             val myInterface = invocation.processingEnv.requireTypeElement("MyInterface")
 
             // helper method to get executable types both from sub class and also as direct child of
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt
index 859e553..b808f2c 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt
@@ -23,8 +23,8 @@
 import androidx.room.compiler.processing.util.getField
 import androidx.room.compiler.processing.util.getMethod
 import androidx.room.compiler.processing.util.getParameter
+import androidx.room.compiler.processing.util.runProcessorTestWithoutKsp
 import androidx.room.compiler.processing.util.runProcessorTest
-import androidx.room.compiler.processing.util.runProcessorTestIncludingKsp
 import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.TypeName
 import org.junit.Test
@@ -69,7 +69,7 @@
             """.trimIndent()
         )
         // TODO run with KSP once https://github.com/google/ksp/issues/167 is fixed
-        runProcessorTest(
+        runProcessorTestWithoutKsp(
             sources = listOf(source)
         ) {
             val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
@@ -164,7 +164,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(source)
         ) {
             val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
@@ -257,7 +257,7 @@
 
     @Test
     fun changeNullability_primitives() {
-        runProcessorTestIncludingKsp { invocation ->
+        runProcessorTest { invocation ->
             PRIMITIVE_TYPES.forEach { primitiveTypeName ->
                 val primitive = invocation.processingEnv.requireType(primitiveTypeName)
                 assertThat(primitive.nullability).isEqualTo(NONNULL)
@@ -295,7 +295,7 @@
                 }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(javaSrc, kotlinSrc)) { invocation ->
+        runProcessorTest(sources = listOf(javaSrc, kotlinSrc)) { invocation ->
             listOf("KotlinClass", "JavaClass").forEach {
                 val subject = invocation.processingEnv.requireTypeElement(it)
                     .getField("subject").type
@@ -316,7 +316,7 @@
 
     @Test
     fun changeNullability_declared() {
-        runProcessorTestIncludingKsp { invocation ->
+        runProcessorTest { invocation ->
             val subject = invocation.processingEnv.requireType("java.util.List")
             subject.makeNullable().let {
                 assertThat(it.nullability).isEqualTo(NULLABLE)
@@ -337,7 +337,7 @@
 
     @Test
     fun changeNullability_arrayTypes() {
-        runProcessorTestIncludingKsp { invocation ->
+        runProcessorTest { invocation ->
             val subject = invocation.processingEnv.getArrayType(
                 invocation.processingEnv.requireType("java.util.List")
             )
@@ -368,7 +368,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(src)) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             val voidType = invocation.processingEnv.requireTypeElement("Foo")
                 .getMethod("subject").returnType
             assertThat(voidType.typeName).isEqualTo(TypeName.VOID)
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
index 4ae8c01..2f92301 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
@@ -17,8 +17,7 @@
 package androidx.room.compiler.processing
 
 import androidx.room.compiler.processing.util.Source
-import androidx.room.compiler.processing.util.runProcessorTestForFailedCompilation
-import androidx.room.compiler.processing.util.runProcessorTestIncludingKsp
+import androidx.room.compiler.processing.util.runProcessorTest
 import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.JavaFile
@@ -34,7 +33,7 @@
 class XProcessingEnvTest {
     @Test
     fun getElement() {
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             listOf(
                 Source.java(
                     "foo.bar.Baz",
@@ -98,7 +97,7 @@
 
     @Test
     fun basic() {
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             listOf(
                 Source.java(
                     "foo.bar.Baz",
@@ -138,7 +137,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             listOf(source)
         ) { invocation ->
             PRIMITIVE_TYPES.flatMap {
@@ -163,7 +162,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(src)) {
+        runProcessorTest(sources = listOf(src)) {
             it.processingEnv.requireTypeElement("foo.bar.Outer.Inner").let {
                 val className = it.className
                 assertThat(className.packageName()).isEqualTo("foo.bar")
@@ -175,7 +174,7 @@
 
     @Test
     fun findGeneratedAnnotation() {
-        runProcessorTestIncludingKsp { invocation ->
+        runProcessorTest { invocation ->
             val generatedAnnotation = invocation.processingEnv.findGeneratedAnnotation()
             assertThat(generatedAnnotation?.name).isEqualTo("Generated")
         }
@@ -200,7 +199,7 @@
             """.trimIndent()
         )
         listOf(javaSrc, kotlinSrc).forEach { src ->
-            runProcessorTestIncludingKsp(sources = listOf(src)) { invocation ->
+            runProcessorTest(sources = listOf(src)) { invocation ->
                 val className = ClassName.get("foo.bar", "ToBeGenerated")
                 if (invocation.processingEnv.findTypeElement(className) == null) {
                     // generate only if it doesn't exist to handle multi-round
@@ -223,14 +222,18 @@
             class Foo {}
             """.trimIndent()
         )
-        // TODO include KSP when https://github.com/google/ksp/issues/122 is fixed.
-        runProcessorTestForFailedCompilation(
+        runProcessorTest(
             sources = listOf(src)
         ) {
             it.processingEnv.messager.printMessage(
                 Diagnostic.Kind.ERROR,
                 "intentional failure"
             )
+            it.assertCompilationResult {
+                compilationDidFail()
+                    .and()
+                    .hasError("intentional failure")
+            }
         }
     }
 
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
index 2cd2588..4aa8ac9 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
@@ -24,9 +24,8 @@
 import androidx.room.compiler.processing.util.javaElementUtils
 import androidx.room.compiler.processing.util.kspResolver
 import androidx.room.compiler.processing.util.runKspTest
+import androidx.room.compiler.processing.util.runProcessorTestWithoutKsp
 import androidx.room.compiler.processing.util.runProcessorTest
-import androidx.room.compiler.processing.util.runProcessorTestForFailedCompilation
-import androidx.room.compiler.processing.util.runProcessorTestIncludingKsp
 import androidx.room.compiler.processing.util.typeName
 import com.google.common.truth.Truth
 import com.google.common.truth.Truth.assertThat
@@ -53,7 +52,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(parent)
         ) {
             val type = it.processingEnv.requireType("foo.bar.Parent") as XDeclaredType
@@ -110,8 +109,9 @@
                 }
             """.trimIndent()
         )
-        // TODO run with KSP as well once https://github.com/google/ksp/issues/107 is resolved
-        runProcessorTestForFailedCompilation(
+
+        // enable KSP once https://github.com/google/ksp/issues/107 is fixed.
+        runProcessorTestWithoutKsp(
             sources = listOf(missingTypeRef)
         ) {
             val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
@@ -127,6 +127,9 @@
                     ClassName.get("", "NotExistingType")
                 )
             }
+            it.assertCompilationResult {
+                compilationDidFail()
+            }
         }
     }
 
@@ -141,7 +144,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = listOf(subject)
         ) {
             val type = it.processingEnv.requireType("foo.bar.Baz")
@@ -174,7 +177,7 @@
 
     @Test
     fun isCollection_kotlin() {
-        runKspTest(sources = emptyList(), succeed = true) { invocation ->
+        runKspTest(sources = emptyList()) { invocation ->
             val subjects = listOf("Map" to false, "List" to true, "Set" to true)
             subjects.forEach { (subject, expected) ->
                 invocation.processingEnv.requireType("kotlin.collections.$subject").let { type ->
@@ -187,7 +190,7 @@
 
     @Test
     fun toStringMatchesUnderlyingElement() {
-        runProcessorTestIncludingKsp {
+        runProcessorTest {
             val subject = "java.lang.String"
             val expected = if (it.isKsp) {
                 it.kspResolver.getClassDeclarationByName(subject)?.toString()
@@ -212,12 +215,14 @@
                 }
             """.trimIndent()
         )
-        // TODO run with KSP as well once https://github.com/google/ksp/issues/107 is resolved
-        runProcessorTestForFailedCompilation(
+        runProcessorTest(
             sources = listOf(missingTypeRef)
         ) {
             val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
             assertThat(element.superType?.isError()).isTrue()
+            it.assertCompilationResult {
+                compilationDidFail()
+            }
         }
     }
 
@@ -256,7 +261,7 @@
 
     @Test
     fun rawType() {
-        runProcessorTestIncludingKsp {
+        runProcessorTest {
             val subject = it.processingEnv.getDeclaredType(
                 it.processingEnv.requireTypeElement(List::class),
                 it.processingEnv.requireType(String::class)
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt
index 679af1c..7251136 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt
@@ -18,7 +18,7 @@
 
 import androidx.room.compiler.processing.javac.JavacProcessingEnv
 import androidx.room.compiler.processing.util.Source
-import androidx.room.compiler.processing.util.runProcessorTest
+import androidx.room.compiler.processing.util.runKaptTest
 import com.google.common.truth.Truth.assertThat
 import org.junit.AssumptionViolatedException
 import org.junit.Test
@@ -478,7 +478,7 @@
         sources: List<Source> = emptyList(),
         handler: (ProcessingEnvironment) -> Unit
     ) {
-        runProcessorTest(sources) {
+        runKaptTest(sources) {
             val processingEnv = it.processingEnv
             if (processingEnv !is JavacProcessingEnv) {
                 throw AssumptionViolatedException("This test only works for java/kapt compilation")
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSAsMemberOfTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSAsMemberOfTest.kt
index 1bc7787..5cfa2d6 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSAsMemberOfTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSAsMemberOfTest.kt
@@ -48,7 +48,7 @@
             """.trimIndent()
         )
 
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runKspTest(sources = listOf(src)) { invocation ->
             val base = invocation.processingEnv.requireTypeElement("BaseClass")
             val sub = invocation.processingEnv.requireType("SubClass").asDeclaredType()
             base.getField("normalInt").let { prop ->
@@ -121,7 +121,7 @@
             abstract class NullableSubject: MyInterface<String?>()
             """.trimIndent()
         )
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runKspTest(sources = listOf(src)) { invocation ->
             val myInterface = invocation.processingEnv.requireTypeElement("MyInterface")
             val nonNullSubject = invocation.processingEnv.requireType("NonNullSubject")
                 .asDeclaredType()
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeExtTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeExtTest.kt
index c185f47..2865715 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeExtTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeExtTest.kt
@@ -26,7 +26,6 @@
 import com.google.common.truth.Truth.assertThat
 import com.google.devtools.ksp.getDeclaredFunctions
 import com.google.devtools.ksp.getDeclaredProperties
-import com.google.devtools.ksp.processing.Resolver
 import com.google.devtools.ksp.symbol.KSClassDeclaration
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.ParameterizedTypeName
@@ -55,18 +54,18 @@
             }
             """.trimIndent()
         )
-        runTest(subjectSrc) { resolver ->
-            val subject = resolver.requireClass("foo.bar.Baz")
-            assertThat(subject.propertyType("intField").typeName(resolver))
+        runKspTest(sources = listOf(subjectSrc)) { invocation ->
+            val subject = invocation.kspResolver.requireClass("foo.bar.Baz")
+            assertThat(subject.propertyType("intField").typeName(invocation.kspResolver))
                 .isEqualTo(TypeName.INT)
-            assertThat(subject.propertyType("listOfInts").typeName(resolver))
+            assertThat(subject.propertyType("listOfInts").typeName(invocation.kspResolver))
                 .isEqualTo(
                     ParameterizedTypeName.get(
                         List::class.className(),
                         TypeName.INT.box()
                     )
                 )
-            assertThat(subject.propertyType("mutableMapOfAny").typeName(resolver))
+            assertThat(subject.propertyType("mutableMapOfAny").typeName(invocation.kspResolver))
                 .isEqualTo(
                     ParameterizedTypeName.get(
                         Map::class.className(),
@@ -74,7 +73,7 @@
                         TypeName.OBJECT,
                     )
                 )
-            val typeName = subject.propertyType("nested").typeName(resolver)
+            val typeName = subject.propertyType("nested").typeName(invocation.kspResolver)
             check(typeName is ClassName)
             assertThat(typeName.packageName()).isEqualTo("foo.bar")
             assertThat(typeName.simpleNames()).containsExactly("Baz", "Nested")
@@ -97,22 +96,25 @@
             }
             """.trimIndent()
         )
-        runTest(subjectSrc) { resolver ->
-            val subject = resolver.requireClass("Baz")
-            assertThat(subject.propertyType("intField").typeName(resolver))
-                .isEqualTo(TypeName.INT)
-            assertThat(subject.propertyType("listOfInts").typeName(resolver))
-                .isEqualTo(
-                    ParameterizedTypeName.get(
-                        List::class.className(),
-                        TypeName.INT.box()
-                    )
+        runKspTest(sources = listOf(subjectSrc)) { invocation ->
+            val subject = invocation.kspResolver.requireClass("Baz")
+            assertThat(
+                subject.propertyType("intField").typeName(invocation.kspResolver)
+            ).isEqualTo(TypeName.INT)
+            assertThat(
+                subject.propertyType("listOfInts").typeName(invocation.kspResolver)
+            ).isEqualTo(
+                ParameterizedTypeName.get(
+                    List::class.className(),
+                    TypeName.INT.box()
                 )
-            assertThat(subject.propertyType("incompleteGeneric").typeName(resolver))
-                .isEqualTo(
-                    List::class.className()
-                )
-            assertThat(subject.propertyType("nested").typeName(resolver))
+            )
+            assertThat(
+                subject.propertyType("incompleteGeneric").typeName(invocation.kspResolver)
+            ).isEqualTo(
+                List::class.className()
+            )
+            assertThat(subject.propertyType("nested").typeName(invocation.kspResolver))
                 .isEqualTo(
                     ClassName.get("", "Baz", "Nested")
                 )
@@ -132,25 +134,31 @@
             }
             """.trimIndent()
         )
-        runTest(subjectSrc, succeed = false) { resolver ->
-            val subject = resolver.requireClass("Foo")
-            assertThat(subject.propertyType("errorField").typeName(resolver))
-                .isEqualTo(ERROR_TYPE_NAME)
-            assertThat(subject.propertyType("listOfError").typeName(resolver))
-                .isEqualTo(
-                    ParameterizedTypeName.get(
-                        List::class.className(),
-                        ERROR_TYPE_NAME
-                    )
+        runKspTest(sources = listOf(subjectSrc)) { invocation ->
+            val subject = invocation.kspResolver.requireClass("Foo")
+            assertThat(
+                subject.propertyType("errorField").typeName(invocation.kspResolver)
+            ).isEqualTo(ERROR_TYPE_NAME)
+            assertThat(
+                subject.propertyType("listOfError").typeName(invocation.kspResolver)
+            ).isEqualTo(
+                ParameterizedTypeName.get(
+                    List::class.className(),
+                    ERROR_TYPE_NAME
                 )
-            assertThat(subject.propertyType("mutableMapOfDontExist").typeName(resolver))
-                .isEqualTo(
-                    ParameterizedTypeName.get(
-                        Map::class.className(),
-                        String::class.className(),
-                        ERROR_TYPE_NAME
-                    )
+            )
+            assertThat(
+                subject.propertyType("mutableMapOfDontExist").typeName(invocation.kspResolver)
+            ).isEqualTo(
+                ParameterizedTypeName.get(
+                    Map::class.className(),
+                    String::class.className(),
+                    ERROR_TYPE_NAME
                 )
+            )
+            invocation.assertCompilationResult {
+                compilationDidFail()
+            }
         }
     }
 
@@ -181,8 +189,7 @@
         // methodName -> returnType, ...paramTypes
         val golden = mutableMapOf<String, List<TypeName>>()
         runKaptTest(
-            sources = listOf(src),
-            succeed = true
+            sources = listOf(src)
         ) { invocation ->
             val env = (invocation.processingEnv as JavacProcessingEnv)
             val subject = env.delegate.elementUtils.getTypeElement("Subject")
@@ -196,8 +203,7 @@
         }
         val kspResults = mutableMapOf<String, List<TypeName>>()
         runKspTest(
-            sources = listOf(src),
-            succeed = true
+            sources = listOf(src)
         ) { invocation ->
             val env = (invocation.processingEnv as KspProcessingEnv)
             val subject = env.resolver.requireClass("Subject")
@@ -220,21 +226,6 @@
         assertThat(kspResults).containsExactlyEntriesIn(golden)
     }
 
-    private fun runTest(
-        vararg sources: Source,
-        succeed: Boolean = true,
-        handler: (Resolver) -> Unit
-    ) {
-        runKspTest(
-            sources = sources.toList(),
-            succeed = succeed
-        ) {
-            handler(
-                (it.processingEnv as KspProcessingEnv).resolver
-            )
-        }
-    }
-
     private fun KSClassDeclaration.requireProperty(name: String) = getDeclaredProperties().first {
         it.simpleName.asString() == name
     }
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFieldElementTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFieldElementTest.kt
index 1e8a5a2..f6666c4 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFieldElementTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFieldElementTest.kt
@@ -25,7 +25,7 @@
 import androidx.room.compiler.processing.util.className
 import androidx.room.compiler.processing.util.compileFiles
 import androidx.room.compiler.processing.util.getField
-import androidx.room.compiler.processing.util.runProcessorTestIncludingKsp
+import androidx.room.compiler.processing.util.runProcessorTest
 import androidx.room.compiler.processing.util.typeName
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
@@ -152,7 +152,7 @@
             class Sub1 : Base<Int, String>()
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(src)) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             val sub = invocation.processingEnv.requireTypeElement("Sub1")
             val base = invocation.processingEnv.requireTypeElement("Base")
             val t = base.getField("t")
@@ -197,13 +197,13 @@
     private fun runModifierTest(vararg inputs: ModifierTestInput) {
         // we'll run the test twice. once it is in source and once it is coming from a dependency.
         val sources = inputs.map(ModifierTestInput::source)
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = sources
         ) { invocation ->
             assertModifiers(invocation, inputs)
         }
         val classpath = compileFiles(sources)
-        runProcessorTestIncludingKsp(
+        runProcessorTest(
             sources = emptyList(),
             classpath = listOf(classpath)
         ) { invocation ->
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeElementTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeElementTest.kt
index 437bdc3..804df7b 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeElementTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeElementTest.kt
@@ -23,7 +23,7 @@
 import androidx.room.compiler.processing.util.getField
 import androidx.room.compiler.processing.util.getMethod
 import androidx.room.compiler.processing.util.runKspTest
-import androidx.room.compiler.processing.util.runProcessorTestIncludingKsp
+import androidx.room.compiler.processing.util.runProcessorTest
 import com.google.common.truth.Truth
 import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.ClassName
@@ -52,8 +52,7 @@
             """.trimIndent()
         )
         runKspTest(
-            sources = listOf(src1, src2),
-            succeed = true
+            sources = listOf(src1, src2)
         ) { invocation ->
             invocation.processingEnv.requireTypeElement("TopLevel").let {
                 assertThat(it.packageName).isEqualTo("")
@@ -93,7 +92,7 @@
             interface MyInterface {}
             """.trimIndent()
         )
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runKspTest(sources = listOf(src)) { invocation ->
             invocation.processingEnv.requireTypeElement("foo.bar.Baz").let {
                 assertThat(it.superType).isEqualTo(
                     invocation.processingEnv.requireType("foo.bar.AbstractClass")
@@ -134,7 +133,7 @@
             }
             """.trimIndent()
         )
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runKspTest(sources = listOf(src)) { invocation ->
             invocation.processingEnv.requireTypeElement("foo.bar.Outer").let {
                 assertThat(it.className).isEqualTo(ClassName.get("foo.bar", "Outer"))
                 assertThat(it.enclosingTypeElement).isNull()
@@ -163,7 +162,7 @@
             private class PrivateClass
             """.trimIndent()
         )
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runKspTest(sources = listOf(src)) { invocation ->
             fun getModifiers(element: XTypeElement): Set<String> {
                 val result = mutableSetOf<String>()
                 if (element.isAbstract()) result.add("abstract")
@@ -205,7 +204,7 @@
             interface MyInterface
             """.trimIndent()
         )
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runKspTest(sources = listOf(src)) { invocation ->
             invocation.processingEnv.requireTypeElement("MyClass").let {
                 assertThat(it.kindName()).isEqualTo("class")
             }
@@ -228,7 +227,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(src)) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             val baseClass = invocation.processingEnv.requireTypeElement("BaseClass")
             assertThat(baseClass.getAllFieldNames()).containsExactly("genericProp")
             val subClass = invocation.processingEnv.requireTypeElement("SubClass")
@@ -269,7 +268,7 @@
             ) : BaseClass(value)
             """.trimIndent()
         )
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runKspTest(sources = listOf(src)) { invocation ->
             val baseClass = invocation.processingEnv.requireTypeElement("BaseClass")
             assertThat(baseClass.getAllFieldNames()).containsExactly("value")
             val subClass = invocation.processingEnv.requireTypeElement("SubClass")
@@ -317,7 +316,7 @@
             }
             """.trimIndent()
         )
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runKspTest(sources = listOf(src)) { invocation ->
             val base = invocation.processingEnv.requireTypeElement("Base")
             assertThat(base.getDeclaredMethods().names()).containsExactly(
                 "baseFun", "suspendFun", "privateBaseFun", "staticBaseFun"
@@ -371,7 +370,7 @@
             }
             """.trimIndent()
         )
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runKspTest(sources = listOf(src)) { invocation ->
             val klass = invocation.processingEnv.requireTypeElement("SubClass")
             assertThat(klass.getAllMethods().names()).containsExactly(
                 "baseMethod", "overriddenMethod", "baseCompanionMethod",
@@ -395,7 +394,7 @@
             }
             """.trimIndent()
         )
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runKspTest(sources = listOf(src)) { invocation ->
             invocation.processingEnv.requireTypeElement("JustGetter").let { base ->
                 assertThat(base.getDeclaredMethods().names()).containsExactly(
                     "getX"
@@ -438,7 +437,7 @@
             class SubClass : CompanionSubject()
             """.trimIndent()
         )
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runKspTest(sources = listOf(src)) { invocation ->
             val subject = invocation.processingEnv.requireTypeElement("CompanionSubject")
             assertThat(subject.getAllFieldNames()).containsExactly(
                 "mutableStatic", "immutableStatic"
@@ -471,7 +470,7 @@
             }
             """.trimIndent()
         )
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runKspTest(sources = listOf(src)) { invocation ->
             invocation.processingEnv.requireTypeElement("JustGetter").let { base ->
                 assertThat(base.getDeclaredMethods().names()).containsExactly(
                     "getX"
@@ -520,7 +519,7 @@
             abstract class AbstractExplicit(x:Int)
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(src)) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             val subjects = listOf(
                 "MyInterface", "NoExplicitConstructor", "Base", "ExplicitConstructor",
                 "BaseWithSecondary", "Sub", "SubWith3Constructors",
@@ -576,7 +575,7 @@
             }
             """.trimIndent()
         )
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runKspTest(sources = listOf(src)) { invocation ->
             val subject = invocation.processingEnv.requireTypeElement("MyInterface")
             assertThat(subject.getMethod("notJvmDefault").isJavaDefault()).isFalse()
             assertThat(subject.getMethod("jvmDefault").isJavaDefault()).isTrue()
@@ -623,7 +622,7 @@
             }
             """.trimIndent()
         )
-        runProcessorTestIncludingKsp(sources = listOf(src)) { invocation ->
+        runProcessorTest(sources = listOf(src)) { invocation ->
             val subjects = listOf(
                 "MyInterface", "NoExplicitConstructor", "Base", "ExplicitConstructor",
                 "BaseWithSecondary", "Sub", "SubWith3Constructors",
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeTest.kt
index db0fc7dc..7643b04 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeTest.kt
@@ -50,7 +50,7 @@
             interface MyInterface {}
             """.trimIndent()
         )
-        runKspTest(listOf(src), succeed = true) {
+        runKspTest(listOf(src)) {
             val subject = it.processingEnv.requireType("foo.bar.Baz")
             assertThat(subject.typeName).isEqualTo(
                 ClassName.get("foo.bar", "Baz")
@@ -89,8 +89,7 @@
             """.trimIndent()
         )
         runKspTest(
-            listOf(src),
-            succeed = false
+            listOf(src)
         ) { invocation ->
             invocation.requireDeclaredPropertyType("errorType").let { type ->
                 assertThat(type.isError()).isTrue()
@@ -107,6 +106,9 @@
                     assertThat(typeArg.typeName).isEqualTo(ERROR_TYPE_NAME)
                 }
             }
+            invocation.assertCompilationResult {
+                compilationDidFail()
+            }
         }
     }
 
@@ -121,8 +123,7 @@
             """.trimIndent()
         )
         runKspTest(
-            listOf(src),
-            succeed = true
+            listOf(src)
         ) { invocation ->
             invocation.requireDeclaredPropertyType("listOfNullableStrings").let { type ->
                 assertThat(type.nullability).isEqualTo(NONNULL)
@@ -173,8 +174,7 @@
             """.trimIndent()
         )
         runKspTest(
-            listOf(src),
-            succeed = true
+            listOf(src)
         ) { invocation ->
             val nullableStringList = invocation
                 .requireDeclaredPropertyType("listOfNullableStrings")
@@ -220,8 +220,7 @@
             """.trimIndent()
         )
         runKspTest(
-            listOf(src),
-            succeed = true
+            listOf(src)
         ) { invocation ->
             invocation.requirePropertyType("simple").let {
                 assertThat(it.rawType.typeName).isEqualTo(TypeName.INT)
@@ -257,7 +256,7 @@
             }
             """.trimIndent()
         )
-        runKspTest(sources = listOf(src), succeed = true) { invocation ->
+        runKspTest(sources = listOf(src)) { invocation ->
             val resolver = (invocation.processingEnv as KspProcessingEnv).resolver
             val voidMethod = resolver.getClassDeclarationByName("foo.bar.Baz")!!
                 .getDeclaredFunctions()
@@ -289,8 +288,7 @@
             """.trimIndent()
         )
         runKspTest(
-            listOf(src),
-            succeed = false
+            listOf(src)
         ) { invocation ->
             fun mapProp(name: String) = invocation.requirePropertyType(name).let {
                 listOf(
@@ -313,6 +311,9 @@
             assertThat(mapProp("nullableByteProp")).containsExactly("isByte")
             assertThat(mapProp("errorProp")).containsExactly("isError")
             assertThat(mapProp("nullableErrorProp")).containsExactly("isError")
+            invocation.assertCompilationResult {
+                compilationDidFail()
+            }
         }
     }
 
@@ -334,8 +335,7 @@
             """.trimIndent()
         )
         runKspTest(
-            listOf(src),
-            succeed = false
+            listOf(src)
         ) { invocation ->
             fun getDefaultValue(name: String) = invocation.requirePropertyType(name).defaultValue()
             // javac types do not check nullability but checking it is more correct
@@ -351,6 +351,9 @@
             assertThat(getDefaultValue("errorProp")).isEqualTo("null")
             assertThat(getDefaultValue("nullableErrorProp")).isEqualTo("null")
             assertThat(getDefaultValue("stringProp")).isEqualTo("null")
+            invocation.assertCompilationResult {
+                compilationDidFail()
+            }
         }
     }
 
@@ -366,8 +369,7 @@
             """.trimIndent()
         )
         runKspTest(
-            listOf(src),
-            succeed = true
+            listOf(src)
         ) { invocation ->
             assertThat(
                 invocation.requirePropertyType("stringProp").isTypeOf(
@@ -418,8 +420,7 @@
             """.trimIndent()
         )
         runKspTest(
-            listOf(src),
-            succeed = true
+            listOf(src)
         ) { invocation ->
             fun check(prop1: String, prop2: String): Boolean {
                 return invocation.requirePropertyType(prop1).isSameType(
@@ -450,8 +451,7 @@
             """.trimIndent()
         )
         runKspTest(
-            listOf(src),
-            succeed = true
+            listOf(src)
         ) { invocation ->
             val env = (invocation.processingEnv as KspProcessingEnv)
             val classNames = listOf("Bar", "Bar_NullableFoo")
@@ -491,8 +491,7 @@
             """.trimIndent()
         )
         runKspTest(
-            listOf(src),
-            succeed = true
+            listOf(src)
         ) { invocation ->
             val env = (invocation.processingEnv as KspProcessingEnv)
             val method = env.resolver
diff --git a/room/compiler/build.gradle b/room/compiler/build.gradle
index fc529d1..6cca78d 100644
--- a/room/compiler/build.gradle
+++ b/room/compiler/build.gradle
@@ -114,6 +114,7 @@
     implementation(INTELLIJ_ANNOTATIONS)
     testImplementation(GOOGLE_COMPILE_TESTING)
     testImplementation projectOrArtifact(":paging:paging-common")
+    testImplementation(project(":room:room-compiler-processing-testing"))
     testImplementation(JUNIT)
     testImplementation(JSR250)
     testImplementation(MOCKITO_CORE)
diff --git a/room/compiler/src/main/kotlin/androidx/room/log/RLog.kt b/room/compiler/src/main/kotlin/androidx/room/log/RLog.kt
index d18778e..0ce7b73 100644
--- a/room/compiler/src/main/kotlin/androidx/room/log/RLog.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/log/RLog.kt
@@ -75,9 +75,9 @@
         messager.printMessage(WARNING, msg.safeFormat(args), defaultElement)
     }
 
-    class CollectingMessager : XMessager {
+    class CollectingMessager : XMessager() {
         private val messages = mutableMapOf<Diagnostic.Kind, MutableList<Pair<String, XElement?>>>()
-        override fun printMessage(kind: Diagnostic.Kind, msg: String, element: XElement?) {
+        override fun onPrintMessage(kind: Diagnostic.Kind, msg: String, element: XElement?) {
             messages.getOrPut(
                 kind,
                 {
diff --git a/room/compiler/src/main/kotlin/androidx/room/solver/types/BoxedBooleanToBoxedIntConverter.kt b/room/compiler/src/main/kotlin/androidx/room/solver/types/BoxedBooleanToBoxedIntConverter.kt
index 7a3664c..2e53e25 100644
--- a/room/compiler/src/main/kotlin/androidx/room/solver/types/BoxedBooleanToBoxedIntConverter.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/solver/types/BoxedBooleanToBoxedIntConverter.kt
@@ -25,8 +25,8 @@
  */
 object BoxedBooleanToBoxedIntConverter {
     fun create(processingEnvironment: XProcessingEnv): List<TypeConverter> {
-        val tBoolean = processingEnvironment.requireType("java.lang.Boolean")
-        val tInt = processingEnvironment.requireType("java.lang.Integer")
+        val tBoolean = processingEnvironment.requireType("java.lang.Boolean").makeNullable()
+        val tInt = processingEnvironment.requireType("java.lang.Integer").makeNullable()
         return listOf(
             object : TypeConverter(tBoolean, tInt) {
                 override fun convert(
diff --git a/room/compiler/src/main/kotlin/androidx/room/solver/types/BoxedPrimitiveColumnTypeAdapter.kt b/room/compiler/src/main/kotlin/androidx/room/solver/types/BoxedPrimitiveColumnTypeAdapter.kt
index 6fc5cc5..e47d917 100644
--- a/room/compiler/src/main/kotlin/androidx/room/solver/types/BoxedPrimitiveColumnTypeAdapter.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/solver/types/BoxedPrimitiveColumnTypeAdapter.kt
@@ -34,7 +34,7 @@
 
             return primitiveAdapters.map {
                 BoxedPrimitiveColumnTypeAdapter(
-                    it.out.boxed(),
+                    it.out.boxed().makeNullable(),
                     it
                 )
             }
diff --git a/room/compiler/src/test/kotlin/androidx/room/log/RLogTest.kt b/room/compiler/src/test/kotlin/androidx/room/log/RLogTest.kt
index b87099d..71a4ce0 100644
--- a/room/compiler/src/test/kotlin/androidx/room/log/RLogTest.kt
+++ b/room/compiler/src/test/kotlin/androidx/room/log/RLogTest.kt
@@ -16,20 +16,22 @@
 
 package androidx.room.log
 
+import androidx.room.compiler.processing.XElement
 import androidx.room.compiler.processing.XMessager
 import androidx.room.vo.Warning
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
-import org.mockito.Mockito.mock
+import javax.tools.Diagnostic
 
 @RunWith(JUnit4::class)
 class RLogTest {
-
-    val messager = mock(XMessager::class.java)
-
     @Test
     fun testSafeFormat() {
+        val messager = object : XMessager() {
+            override fun onPrintMessage(kind: Diagnostic.Kind, msg: String, element: XElement?) {
+            }
+        }
         val logger = RLog(messager, emptySet(), null)
 
         // UnknownFormatConversionException
diff --git a/room/compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt b/room/compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
index f7ad69b..ab48fa7a 100644
--- a/room/compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
+++ b/room/compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
@@ -19,9 +19,11 @@
 import COMMON
 import androidx.paging.DataSource
 import androidx.paging.PagingSource
-import androidx.room.Entity
 import androidx.room.compiler.processing.XProcessingEnv
 import androidx.room.compiler.processing.asDeclaredType
+import androidx.room.compiler.processing.util.Source
+import androidx.room.compiler.processing.util.XTestInvocation
+import androidx.room.compiler.processing.util.runProcessorTest
 import androidx.room.ext.GuavaUtilConcurrentTypeNames
 import androidx.room.ext.L
 import androidx.room.ext.LifecyclesTypeNames
@@ -46,12 +48,7 @@
 import androidx.room.solver.types.CompositeAdapter
 import androidx.room.solver.types.EnumColumnTypeAdapter
 import androidx.room.solver.types.TypeConverter
-import androidx.room.testing.TestInvocation
-import androidx.room.testing.TestProcessor
-import com.google.common.truth.Truth
-import com.google.testing.compile.CompileTester
-import com.google.testing.compile.JavaFileObjects
-import com.google.testing.compile.JavaSourcesSubjectFactory
+import androidx.room.testing.context
 import com.squareup.javapoet.TypeName
 import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.CoreMatchers.instanceOf
@@ -61,8 +58,8 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
-import simpleRun
 import testCodeGenScope
+import toSources
 
 @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
 @RunWith(JUnit4::class)
@@ -73,21 +70,24 @@
 
     @Test
     fun testDirect() {
-        singleRun { invocation ->
+        runProcessorTest { invocation ->
             val store = TypeAdapterStore.create(Context(invocation.processingEnv))
             val primitiveType = invocation.processingEnv.requireType(TypeName.INT)
             val adapter = store.findColumnTypeAdapter(primitiveType, null)
             assertThat(adapter, notNullValue())
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun testJavaLangBoolean() {
-        singleRun { invocation ->
-            val store = TypeAdapterStore.create(Context(invocation.processingEnv))
+        runProcessorTest { invocation ->
+            val store = TypeAdapterStore.create(
+                Context(invocation.processingEnv)
+            )
             val boolean = invocation
                 .processingEnv
                 .requireType("java.lang.Boolean")
+                .makeNullable()
             val adapter = store.findColumnTypeAdapter(boolean, null)
             assertThat(adapter, notNullValue())
             assertThat(adapter, instanceOf(CompositeAdapter::class.java))
@@ -100,22 +100,23 @@
                 composite.columnTypeAdapter.out.typeName,
                 `is`(TypeName.INT.box())
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun testJavaLangEnumCompilesWithoutError() {
-        simpleRun(
-            JavaFileObjects.forSourceString(
-                "foo.bar.Fruit",
-                """ package foo.bar;
+        val enumSrc = Source.java(
+            "foo.bar.Fruit",
+            """ package foo.bar;
                 import androidx.room.*;
                 enum Fruit {
                     APPLE,
                     BANANA,
                     STRAWBERRY}
                 """.trimMargin()
-            )
+        )
+        runProcessorTest(
+            sources = listOf(enumSrc)
         ) { invocation ->
             val store = TypeAdapterStore.create(Context(invocation.processingEnv))
             val enum = invocation
@@ -124,12 +125,12 @@
             val adapter = store.findColumnTypeAdapter(enum, null)
             assertThat(adapter, notNullValue())
             assertThat(adapter, instanceOf(EnumColumnTypeAdapter::class.java))
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun testVia1TypeAdapter() {
-        singleRun { invocation ->
+        runProcessorTest { invocation ->
             val store = TypeAdapterStore.create(Context(invocation.processingEnv))
             val booleanType = invocation.processingEnv.requireType(TypeName.BOOLEAN)
             val adapter = store.findColumnTypeAdapter(booleanType, null)
@@ -160,12 +161,35 @@
                     """.trimIndent()
                 )
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun testVia2TypeAdapters() {
-        singleRun { invocation ->
+        val point = Source.java(
+            "foo.bar.Point",
+            """
+            package foo.bar;
+            import androidx.room.*;
+            @Entity
+            public class Point {
+                public int x, y;
+                public Point(int x, int y) {
+                    this.x = x;
+                    this.y = y;
+                }
+                public static Point fromBoolean(boolean val) {
+                    return val ? new Point(1, 1) : new Point(0, 0);
+                }
+                public static boolean toBoolean(Point point) {
+                    return point.x > 0;
+                }
+            }
+            """
+        )
+        runProcessorTest(
+            sources = listOf(point)
+        ) { invocation ->
             val store = TypeAdapterStore.create(
                 Context(invocation.processingEnv),
                 pointTypeConverters(invocation.processingEnv)
@@ -204,17 +228,17 @@
                     """.trimIndent()
                 )
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun testDate() {
-        singleRun { (processingEnv) ->
+        runProcessorTest { invocation ->
             val store = TypeAdapterStore.create(
-                Context(processingEnv),
-                dateTypeConverters(processingEnv)
+                invocation.context,
+                dateTypeConverters(invocation.processingEnv)
             )
-            val tDate = processingEnv.requireType("java.util.Date")
+            val tDate = invocation.processingEnv.requireType("java.util.Date")
             val adapter = store.findCursorValueReader(tDate, SQLTypeAffinity.INTEGER)
             assertThat(adapter, notNullValue())
             assertThat(adapter?.typeMirror(), `is`(tDate))
@@ -234,12 +258,12 @@
                     """.trimIndent()
                 )
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun testIntList() {
-        singleRun { invocation ->
+        runProcessorTest { invocation ->
             val binders = createIntListToStringBinders(invocation)
             val store = TypeAdapterStore.create(
                 Context(invocation.processingEnv), binders[0],
@@ -272,12 +296,12 @@
             )
             assertThat(converter, notNullValue())
             assertThat(store.reverse(converter!!), `is`(binders[1]))
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun testOneWayConversion() {
-        singleRun { invocation ->
+        runProcessorTest { invocation ->
             val binders = createIntListToStringBinders(invocation)
             val store = TypeAdapterStore.create(Context(invocation.processingEnv), binders[0])
             val adapter = store.findColumnTypeAdapter(binders[0].from, null)
@@ -298,7 +322,9 @@
     @Test
     fun testMissingRx2Room() {
         @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-        simpleRun(jfos = arrayOf(COMMON.PUBLISHER, COMMON.RX2_FLOWABLE)) { invocation ->
+        runProcessorTest(
+            sources = listOf(COMMON.PUBLISHER, COMMON.RX2_FLOWABLE).toSources()
+        ) { invocation ->
             val publisherElement = invocation.processingEnv
                 .requireTypeElement(ReactiveStreamsTypeNames.PUBLISHER)
             assertThat(publisherElement, notNullValue())
@@ -308,13 +334,18 @@
                 },
                 `is`(true)
             )
-        }.failsToCompile().withErrorContaining(ProcessorErrors.MISSING_ROOM_RXJAVA2_ARTIFACT)
+            invocation.assertCompilationResult {
+                hasError(ProcessorErrors.MISSING_ROOM_RXJAVA2_ARTIFACT)
+            }
+        }
     }
 
     @Test
     fun testMissingRx3Room() {
         @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-        simpleRun(jfos = arrayOf(COMMON.PUBLISHER, COMMON.RX3_FLOWABLE)) { invocation ->
+        runProcessorTest(
+            sources = listOf(COMMON.PUBLISHER, COMMON.RX3_FLOWABLE).toSources()
+        ) { invocation ->
             val publisherElement = invocation.processingEnv
                 .requireTypeElement(ReactiveStreamsTypeNames.PUBLISHER)
             assertThat(publisherElement, notNullValue())
@@ -324,7 +355,10 @@
                 },
                 `is`(true)
             )
-        }.failsToCompile().withErrorContaining(ProcessorErrors.MISSING_ROOM_RXJAVA3_ARTIFACT)
+            invocation.assertCompilationResult {
+                hasError(ProcessorErrors.MISSING_ROOM_RXJAVA3_ARTIFACT)
+            }
+        }
     }
 
     @Test
@@ -334,8 +368,9 @@
             COMMON.RX3_FLOWABLE to COMMON.RX3_ROOM
         ).forEach { (rxTypeSrc, rxRoomSrc) ->
             @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-            simpleRun(jfos = arrayOf(COMMON.PUBLISHER, rxTypeSrc, rxRoomSrc)) {
-                invocation ->
+            runProcessorTest(
+                sources = listOf(COMMON.PUBLISHER, rxTypeSrc, rxRoomSrc).toSources()
+            ) { invocation ->
                 val publisher = invocation.processingEnv
                     .requireTypeElement(ReactiveStreamsTypeNames.PUBLISHER)
                 assertThat(publisher, notNullValue())
@@ -345,7 +380,7 @@
                     },
                     `is`(true)
                 )
-            }.compilesWithoutError()
+            }
         }
     }
 
@@ -356,8 +391,9 @@
             Triple(COMMON.RX3_FLOWABLE, COMMON.RX3_ROOM, RxJava3TypeNames.FLOWABLE)
         ).forEach { (rxTypeSrc, rxRoomSrc, rxTypeClassName) ->
             @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-            simpleRun(jfos = arrayOf(COMMON.PUBLISHER, rxTypeSrc, rxRoomSrc)) {
-                invocation ->
+            runProcessorTest(
+                sources = listOf(COMMON.PUBLISHER, rxTypeSrc, rxRoomSrc).toSources()
+            ) { invocation ->
                 val flowable = invocation.processingEnv.requireTypeElement(rxTypeClassName)
                 assertThat(
                     RxQueryResultBinderProvider.getAll(invocation.context).any {
@@ -365,7 +401,7 @@
                     },
                     `is`(true)
                 )
-            }.compilesWithoutError()
+            }
         }
     }
 
@@ -376,8 +412,9 @@
             Triple(COMMON.RX3_OBSERVABLE, COMMON.RX3_ROOM, RxJava3TypeNames.OBSERVABLE)
         ).forEach { (rxTypeSrc, rxRoomSrc, rxTypeClassName) ->
             @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-            simpleRun(jfos = arrayOf(rxTypeSrc, rxRoomSrc)) {
-                invocation ->
+            runProcessorTest(
+                sources = listOf(rxTypeSrc, rxRoomSrc).toSources()
+            ) { invocation ->
                 val observable = invocation.processingEnv.requireTypeElement(rxTypeClassName)
                 assertThat(observable, notNullValue())
                 assertThat(
@@ -386,7 +423,7 @@
                     },
                     `is`(true)
                 )
-            }.compilesWithoutError()
+            }
         }
     }
 
@@ -397,8 +434,7 @@
             Triple(COMMON.RX3_SINGLE, COMMON.RX3_ROOM, RxJava3TypeNames.SINGLE)
         ).forEach { (rxTypeSrc, _, rxTypeClassName) ->
             @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-            simpleRun(jfos = arrayOf(rxTypeSrc)) {
-                invocation ->
+            runProcessorTest(sources = listOf(rxTypeSrc).toSources()) { invocation ->
                 val single = invocation.processingEnv.requireTypeElement(rxTypeClassName)
                 assertThat(single, notNullValue())
                 assertThat(
@@ -407,7 +443,7 @@
                     },
                     `is`(true)
                 )
-            }.compilesWithoutError()
+            }
         }
     }
 
@@ -417,9 +453,7 @@
             Triple(COMMON.RX2_MAYBE, COMMON.RX2_ROOM, RxJava2TypeNames.MAYBE),
             Triple(COMMON.RX3_MAYBE, COMMON.RX3_ROOM, RxJava3TypeNames.MAYBE)
         ).forEach { (rxTypeSrc, _, rxTypeClassName) ->
-            @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-            simpleRun(jfos = arrayOf(rxTypeSrc)) {
-                invocation ->
+            runProcessorTest(sources = listOf(rxTypeSrc).toSources()) { invocation ->
                 val maybe = invocation.processingEnv.requireTypeElement(rxTypeClassName)
                 assertThat(
                     RxCallableInsertMethodBinderProvider.getAll(invocation.context).any {
@@ -427,7 +461,7 @@
                     },
                     `is`(true)
                 )
-            }.compilesWithoutError()
+            }
         }
     }
 
@@ -437,9 +471,7 @@
             Triple(COMMON.RX2_COMPLETABLE, COMMON.RX2_ROOM, RxJava2TypeNames.COMPLETABLE),
             Triple(COMMON.RX3_COMPLETABLE, COMMON.RX3_ROOM, RxJava3TypeNames.COMPLETABLE)
         ).forEach { (rxTypeSrc, _, rxTypeClassName) ->
-            @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-            simpleRun(jfos = arrayOf(rxTypeSrc)) {
-                invocation ->
+            runProcessorTest(sources = listOf(rxTypeSrc).toSources()) { invocation ->
                 val completable = invocation.processingEnv.requireTypeElement(rxTypeClassName)
                 assertThat(
                     RxCallableInsertMethodBinderProvider.getAll(invocation.context).any {
@@ -447,14 +479,13 @@
                     },
                     `is`(true)
                 )
-            }.compilesWithoutError()
+            }
         }
     }
 
     @Test
     fun testFindInsertListenableFuture() {
-        @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-        simpleRun(jfos = arrayOf(COMMON.LISTENABLE_FUTURE)) {
+        runProcessorTest(sources = listOf(COMMON.LISTENABLE_FUTURE).toSources()) {
             invocation ->
             val future = invocation.processingEnv
                 .requireTypeElement(GuavaUtilConcurrentTypeNames.LISTENABLE_FUTURE)
@@ -464,14 +495,12 @@
                 ),
                 `is`(true)
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun testFindDeleteOrUpdateSingle() {
-        @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-        simpleRun(jfos = arrayOf(COMMON.RX2_SINGLE)) {
-            invocation ->
+        runProcessorTest(sources = listOf(COMMON.RX2_SINGLE).toSources()) { invocation ->
             val single = invocation.processingEnv.requireTypeElement(RxJava2TypeNames.SINGLE)
             assertThat(single, notNullValue())
             assertThat(
@@ -480,13 +509,12 @@
                 },
                 `is`(true)
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun testFindDeleteOrUpdateMaybe() {
-        @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-        simpleRun(jfos = arrayOf(COMMON.RX2_MAYBE)) {
+        runProcessorTest(sources = listOf(COMMON.RX2_MAYBE).toSources()) {
             invocation ->
             val maybe = invocation.processingEnv.requireTypeElement(RxJava2TypeNames.MAYBE)
             assertThat(maybe, notNullValue())
@@ -496,13 +524,12 @@
                 },
                 `is`(true)
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun testFindDeleteOrUpdateCompletable() {
-        @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-        simpleRun(jfos = arrayOf(COMMON.RX2_COMPLETABLE)) {
+        runProcessorTest(sources = listOf(COMMON.RX2_COMPLETABLE).toSources()) {
             invocation ->
             val completable = invocation.processingEnv
                 .requireTypeElement(RxJava2TypeNames.COMPLETABLE)
@@ -513,14 +540,14 @@
                 },
                 `is`(true)
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun testFindDeleteOrUpdateListenableFuture() {
-        @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-        simpleRun(jfos = arrayOf(COMMON.LISTENABLE_FUTURE)) {
-            invocation ->
+        runProcessorTest(
+            sources = listOf(COMMON.LISTENABLE_FUTURE).toSources()
+        ) { invocation ->
             val future = invocation.processingEnv
                 .requireTypeElement(GuavaUtilConcurrentTypeNames.LISTENABLE_FUTURE)
             assertThat(future, notNullValue())
@@ -529,14 +556,14 @@
                     .matches(future.asDeclaredType()),
                 `is`(true)
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun testFindLiveData() {
-        @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-        simpleRun(jfos = arrayOf(COMMON.COMPUTABLE_LIVE_DATA, COMMON.LIVE_DATA)) {
-            invocation ->
+        runProcessorTest(
+            sources = listOf(COMMON.COMPUTABLE_LIVE_DATA, COMMON.LIVE_DATA).toSources()
+        ) { invocation ->
             val liveData = invocation.processingEnv
                 .requireTypeElement(LifecyclesTypeNames.LIVE_DATA)
             assertThat(liveData, notNullValue())
@@ -546,12 +573,12 @@
                 ),
                 `is`(true)
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun findPagingSourceIntKey() {
-        simpleRun { invocation ->
+        runProcessorTest { invocation ->
             val pagingSourceElement = invocation.processingEnv
                 .requireTypeElement(PagingSource::class)
             val intType = invocation.processingEnv.requireType(Integer::class)
@@ -569,7 +596,7 @@
 
     @Test
     fun findPagingSourceStringKey() {
-        simpleRun { invocation ->
+        runProcessorTest { invocation ->
             val pagingSourceElement = invocation.processingEnv
                 .requireTypeElement(PagingSource::class)
             val stringType = invocation.processingEnv.requireType(String::class)
@@ -582,12 +609,15 @@
                     .matches(pagingSourceIntIntType.asDeclaredType()),
                 `is`(true)
             )
-        }.failsToCompile().withErrorContaining(ProcessorErrors.PAGING_SPECIFY_PAGING_SOURCE_TYPE)
+            invocation.assertCompilationResult {
+                hasError(ProcessorErrors.PAGING_SPECIFY_PAGING_SOURCE_TYPE)
+            }
+        }
     }
 
     @Test
     fun findDataSource() {
-        simpleRun {
+        runProcessorTest {
             invocation ->
             val dataSource = invocation.processingEnv.requireTypeElement(DataSource::class)
             assertThat(dataSource, notNullValue())
@@ -597,12 +627,15 @@
                 ),
                 `is`(true)
             )
-        }.failsToCompile().withErrorContaining(ProcessorErrors.PAGING_SPECIFY_DATA_SOURCE_TYPE)
+            invocation.assertCompilationResult {
+                hasError(ProcessorErrors.PAGING_SPECIFY_DATA_SOURCE_TYPE)
+            }
+        }
     }
 
     @Test
     fun findPositionalDataSource() {
-        simpleRun {
+        runProcessorTest {
             invocation ->
             @Suppress("DEPRECATION")
             val dataSource = invocation.processingEnv
@@ -614,13 +647,12 @@
                 ),
                 `is`(true)
             )
-        }.compilesWithoutError()
+        }
     }
 
     @Test
     fun findDataSourceFactory() {
-        @Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
-        simpleRun(jfos = arrayOf(COMMON.DATA_SOURCE_FACTORY)) {
+        runProcessorTest(sources = listOf(COMMON.DATA_SOURCE_FACTORY).toSources()) {
             invocation ->
             val pagedListProvider = invocation.processingEnv
                 .requireTypeElement(PagingTypeNames.DATA_SOURCE_FACTORY)
@@ -631,14 +663,13 @@
                 ),
                 `is`(true)
             )
-        }.compilesWithoutError()
+        }
     }
 
-    private fun createIntListToStringBinders(invocation: TestInvocation): List<TypeConverter> {
+    private fun createIntListToStringBinders(invocation: XTestInvocation): List<TypeConverter> {
         val intType = invocation.processingEnv.requireType(Integer::class)
         val listElement = invocation.processingEnv.requireTypeElement(java.util.List::class)
         val listOfInts = invocation.processingEnv.getDeclaredType(listElement, intType)
-
         val intListConverter = object : TypeConverter(
             listOfInts,
             invocation.context.COMMON_TYPES.STRING
@@ -676,53 +707,6 @@
         return listOf(intListConverter, stringToIntListConverter)
     }
 
-    fun singleRun(handler: (TestInvocation) -> Unit): CompileTester {
-        return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
-            .that(
-                listOf(
-                    JavaFileObjects.forSourceString(
-                        "foo.bar.DummyClass",
-                        """
-                        package foo.bar;
-                        import androidx.room.*;
-                        @Entity
-                        public class DummyClass {}
-                        """
-                    ),
-                    JavaFileObjects.forSourceString(
-                        "foo.bar.Point",
-                        """
-                        package foo.bar;
-                        import androidx.room.*;
-                        @Entity
-                        public class Point {
-                            public int x, y;
-                            public Point(int x, int y) {
-                                this.x = x;
-                                this.y = y;
-                            }
-                            public static Point fromBoolean(boolean val) {
-                                return val ? new Point(1, 1) : new Point(0, 0);
-                            }
-                            public static boolean toBoolean(Point point) {
-                                return point.x > 0;
-                            }
-                        }
-                        """
-                    )
-                )
-            )
-            .processedWith(
-                TestProcessor.builder()
-                    .forAnnotations(Entity::class)
-                    .nextRunHandler { invocation ->
-                        handler(invocation)
-                        true
-                    }
-                    .build()
-            )
-    }
-
     fun pointTypeConverters(env: XProcessingEnv): List<TypeConverter> {
         val tPoint = env.requireType("foo.bar.Point")
         val tBoolean = env.requireType(TypeName.BOOLEAN)
@@ -759,8 +743,8 @@
     }
 
     fun dateTypeConverters(env: XProcessingEnv): List<TypeConverter> {
-        val tDate = env.requireType("java.util.Date")
-        val tLong = env.requireType("java.lang.Long")
+        val tDate = env.requireType("java.util.Date").makeNullable()
+        val tLong = env.requireType("java.lang.Long").makeNullable()
         return listOf(
             object : TypeConverter(tDate, tLong) {
                 override fun convert(
diff --git a/room/compiler/src/test/kotlin/androidx/room/testing/InProcessorTest.kt b/room/compiler/src/test/kotlin/androidx/room/testing/InProcessorTest.kt
index 2d00be6..25c629a 100644
--- a/room/compiler/src/test/kotlin/androidx/room/testing/InProcessorTest.kt
+++ b/room/compiler/src/test/kotlin/androidx/room/testing/InProcessorTest.kt
@@ -16,46 +16,37 @@
 
 package androidx.room.testing
 
-import androidx.room.Query
-import com.google.common.truth.Truth
-import com.google.testing.compile.JavaFileObjects
-import com.google.testing.compile.JavaSourceSubjectFactory
-import org.hamcrest.CoreMatchers.`is`
-import org.hamcrest.MatcherAssert.assertThat
+import androidx.room.compiler.processing.util.Source
+import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
-import java.util.concurrent.atomic.AtomicBoolean
 
 @RunWith(JUnit4::class)
 class InProcessorTest {
     @Test
     fun testInProcessorTestRuns() {
-        val didRun = AtomicBoolean(false)
-        Truth.assertAbout(JavaSourceSubjectFactory.javaSource())
-            .that(
-                JavaFileObjects.forSourceString(
-                    "foo.bar.MyClass",
-                    """
-                        package foo.bar;
-                        abstract public class MyClass {
-                        @androidx.room.Query("foo")
-                        abstract public void setFoo(String foo);
-                        }
-                        """
-                )
-            )
-            .processedWith(
-                TestProcessor.builder()
-                    .nextRunHandler { invocation ->
-                        didRun.set(true)
-                        assertThat(invocation.annotations.size, `is`(1))
-                        true
-                    }
-                    .forAnnotations(Query::class)
-                    .build()
-            )
-            .compilesWithoutError()
-        assertThat(didRun.get(), `is`(true))
+        val source = Source.java(
+            qName = "foo.bar.MyClass",
+            code = """
+                package foo.bar;
+                abstract public class MyClass {
+                @androidx.room.Query("foo")
+                abstract public void setFoo(String foo);
+                }
+            """.trimIndent()
+        )
+        var runCount = 0
+        runProcessorTest(sources = listOf(source)) {
+            assertThat(
+                it.processingEnv.findTypeElement("foo.bar.MyClass")
+            ).isNotNull()
+            runCount++
+        }
+        // run 3 times: javac, kapt, ksp
+        assertThat(
+            runCount
+        ).isEqualTo(3)
     }
 }
diff --git a/room/compiler/src/test/kotlin/androidx/room/testing/XTestInvocationExt.kt b/room/compiler/src/test/kotlin/androidx/room/testing/XTestInvocationExt.kt
new file mode 100644
index 0000000..62e386a
--- /dev/null
+++ b/room/compiler/src/test/kotlin/androidx/room/testing/XTestInvocationExt.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.testing
+
+import androidx.room.compiler.processing.util.XTestInvocation
+import androidx.room.processor.Context
+
+val XTestInvocation.context
+    get() = getOrPutUserData(Context::class) {
+        Context(processingEnv)
+    }
\ No newline at end of file
diff --git a/room/compiler/src/test/kotlin/androidx/room/testing/test_util.kt b/room/compiler/src/test/kotlin/androidx/room/testing/test_util.kt
index b186bff..febe6e2 100644
--- a/room/compiler/src/test/kotlin/androidx/room/testing/test_util.kt
+++ b/room/compiler/src/test/kotlin/androidx/room/testing/test_util.kt
@@ -19,6 +19,7 @@
 import androidx.room.compiler.processing.XElement
 import androidx.room.compiler.processing.XFieldElement
 import androidx.room.compiler.processing.XType
+import androidx.room.compiler.processing.util.Source
 import androidx.room.ext.GuavaUtilConcurrentTypeNames
 import androidx.room.ext.KotlinTypeNames
 import androidx.room.ext.LifecyclesTypeNames
@@ -314,4 +315,25 @@
     return System.getProperty("java.class.path")!!.split(pathSeparator).map { File(it) }.toSet()
 }
 
-fun String.toJFO(qName: String): JavaFileObject = JavaFileObjects.forSourceLines(qName, this)
\ No newline at end of file
+fun String.toJFO(qName: String): JavaFileObject = JavaFileObjects.forSourceLines(qName, this)
+
+/**
+ * Convenience method to convert JFO's to the Source objects in XProcessing so that we can
+ * convert room tests to the common API w/o major code refactor
+ */
+fun JavaFileObject.toSource(): Source {
+    val uri = this.toUri()
+    // parse name from uri
+    val contents = this.openReader(true).use {
+        it.readText()
+    }
+    val qName = uri.path.replace('/', '.')
+    val javaExt = ".java"
+    check(qName.endsWith(javaExt)) {
+        "expected a java source file, $qName does not seem like one"
+    }
+
+    return Source.java(qName.dropLast(javaExt.length), contents)
+}
+
+fun Collection<JavaFileObject>.toSources() = map { it.toSource() }