blob: 159e83a057054dc8c3c48075d878580c5514d749 [file] [log] [blame]
/*
* 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.XTestInvocation
/**
* Common interface for SyntheticProcessors that we create for testing.
*/
@ExperimentalProcessingApi
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>
/**
* Should return any assertion error that happened during processing.
*
* When assertions fail, we don't fail the compilation to keep the stack trace, instead,
* dispatch them afterwards.
*/
fun getProcessingException(): Throwable?
/**
* Returns true if the processor expected to run another round.
*/
fun expectsAnotherRound(): Boolean
}
/**
* Helper class to implement [SyntheticProcessor] processor that handles the communication with
* the testing infrastructure.
*/
@ExperimentalProcessingApi
internal class SyntheticProcessorImpl(
handlers: List<(XTestInvocation) -> Unit>
) : SyntheticProcessor {
private var result: Result<Unit>? = null
override val invocationInstances = mutableListOf<XTestInvocation>()
private val nextRunHandlers = handlers.toMutableList()
internal fun processingSteps() = listOf<XProcessingStep>(
// A processing step that just ensures we're run every round.
object : XProcessingStep {
override fun annotations(): Set<String> = setOf("*")
override fun process(
env: XProcessingEnv,
elementsByAnnotation: Map<String, Set<XElement>>
): Set<XTypeElement> = emptySet()
}
)
internal fun postRound(env: XProcessingEnv, round: XRoundEnv) {
if (canRunAnotherRound()) {
runNextRound(XTestInvocation(env, round))
}
}
override fun expectsAnotherRound(): Boolean {
return nextRunHandlers.isNotEmpty()
}
/**
* Returns true if this can run another round, which means previous round didn't throw an
* exception and there is another handler in the queue.
*/
fun canRunAnotherRound(): Boolean {
if (result?.exceptionOrNull() != null) {
// if there is an existing failure from a previous run, stop
return false
}
return expectsAnotherRound()
}
override fun getProcessingException(): Throwable? {
val result = this.result ?: return AssertionError("processor didn't run")
result.exceptionOrNull()?.let {
return it
}
if (result.isFailure) {
return AssertionError("processor failed but no exception is reported")
}
return null
}
/**
* Runs the next handler with the given test invocation.
*/
fun runNextRound(
invocation: XTestInvocation
) {
check(nextRunHandlers.isNotEmpty()) {
"Called run next round w/o a runner to handle it. Looks like a testing infra bug"
}
val handler = nextRunHandlers.removeAt(0)
invocationInstances.add(invocation)
result = kotlin.runCatching {
handler(invocation)
invocation.dispose()
}
}
}