[K/N][test] Use Executors in the New test infra

Add RunnerWithExecutor class that uses Executors
Re-use checkers in the new executor

Merge-request: KT-MR-8964
This commit is contained in:
Pavel Punegov
2023-02-22 15:11:21 +01:00
committed by Space Team
parent 76ab130011
commit 60f43d6d4f
9 changed files with 234 additions and 109 deletions
+2
View File
@@ -18,6 +18,8 @@ dependencies {
testImplementation(projectTests(":compiler:tests-common-new")) testImplementation(projectTests(":compiler:tests-common-new"))
testImplementation(projectTests(":compiler:test-infrastructure")) testImplementation(projectTests(":compiler:test-infrastructure"))
testImplementation(projectTests(":generators:test-generator")) testImplementation(projectTests(":generators:test-generator"))
testImplementation(project(":native:kotlin-native-utils"))
testImplementation(project(":native:executors"))
testApiJUnit5() testApiJUnit5()
testImplementation(commonDependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) { isTransitive = false } testImplementation(commonDependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) { isTransitive = false }
@@ -16,7 +16,7 @@ import org.junit.jupiter.api.Assertions.fail
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import kotlin.time.* import kotlin.time.*
internal abstract class AbstractLocalProcessRunner<R>(private val checks: TestRunChecks) : AbstractRunner<R>() { internal abstract class AbstractLocalProcessRunner<R>(protected val checks: TestRunChecks) : AbstractRunner<R>() {
protected abstract val visibleProcessName: String protected abstract val visibleProcessName: String
protected abstract val executable: TestExecutable protected abstract val executable: TestExecutable
protected abstract val programArgs: List<String> protected abstract val programArgs: List<String>
@@ -72,9 +72,14 @@ internal abstract class AbstractLocalProcessRunner<R>(private val checks: TestRu
} }
} }
abstract override fun buildResultHandler(runResult: RunResult): ResultHandler // Narrow returned type. abstract override fun buildResultHandler(runResult: RunResult): LocalResultHandler<R> // ?? Narrow returned type.
}
abstract inner class ResultHandler(runResult: RunResult) : AbstractRunner<R>.ResultHandler(runResult) { internal abstract class LocalResultHandler<R>(
runResult: RunResult,
private val visibleProcessName: String,
private val checks: TestRunChecks
) : AbstractResultHandler<R>(runResult) {
override fun handle(): R { override fun handle(): R {
checks.forEach { check -> checks.forEach { check ->
when (check) { when (check) {
@@ -130,7 +135,6 @@ internal abstract class AbstractLocalProcessRunner<R>(private val checks: TestRu
protected abstract fun doHandle(): R protected abstract fun doHandle(): R
} }
}
private class UnfilteredProcessOutput { private class UnfilteredProcessOutput {
private val stdOut = ByteArrayOutputStream() private val stdOut = ByteArrayOutputStream()
@@ -11,7 +11,7 @@ import org.opentest4j.TestAbortedException
internal abstract class AbstractRunner<R> : Runner<R> { internal abstract class AbstractRunner<R> : Runner<R> {
protected abstract fun buildRun(): AbstractRun protected abstract fun buildRun(): AbstractRun
protected abstract fun buildResultHandler(runResult: RunResult): ResultHandler protected abstract fun buildResultHandler(runResult: RunResult): AbstractResultHandler<R>
protected abstract fun getLoggedParameters(): LoggedData.TestRunParameters protected abstract fun getLoggedParameters(): LoggedData.TestRunParameters
protected abstract fun handleUnexpectedFailure(t: Throwable): Nothing protected abstract fun handleUnexpectedFailure(t: Throwable): Nothing
@@ -33,8 +33,9 @@ internal abstract class AbstractRunner<R>: Runner<R> {
fun interface AbstractRun { fun interface AbstractRun {
fun run(): RunResult fun run(): RunResult
} }
}
abstract inner class ResultHandler(protected val runResult: RunResult) { internal abstract class AbstractResultHandler<R>(protected val runResult: RunResult) {
abstract fun getLoggedRun(): LoggedData abstract fun getLoggedRun(): LoggedData
abstract fun handle(): R abstract fun handle(): R
@@ -42,4 +43,3 @@ internal abstract class AbstractRunner<R>: Runner<R> {
assertTrue(shouldBeTrue) { getLoggedRun().withErrorMessage(errorMessage()) } assertTrue(shouldBeTrue) { getLoggedRun().withErrorMessage(errorMessage()) }
} }
} }
}
@@ -26,17 +26,21 @@ internal class LocalTestNameExtractor(
runParameters = null runParameters = null
) )
override fun buildResultHandler(runResult: RunResult) = ResultHandler(runResult) override fun buildResultHandler(runResult: RunResult) =
TestNameResultHandler(runResult, visibleProcessName, checks, getLoggedParameters())
override fun handleUnexpectedFailure(t: Throwable) = fail { override fun handleUnexpectedFailure(t: Throwable) = fail {
LoggedData.TestRunUnexpectedFailure(getLoggedParameters(), t) LoggedData.TestRunUnexpectedFailure(getLoggedParameters(), t)
.withErrorMessage("Test name extraction failed with unexpected exception.") .withErrorMessage("Test name extraction failed with unexpected exception.")
} }
}
inner class ResultHandler( internal class TestNameResultHandler(
runResult: RunResult runResult: RunResult,
) : AbstractLocalProcessRunner<Collection<TestName>>.ResultHandler(runResult) { visibleProcessName: String,
override fun getLoggedRun() = LoggedData.TestRun(getLoggedParameters(), runResult) checks: TestRunChecks,
private val loggedParameters: LoggedData.TestRunParameters
) : LocalResultHandler<Collection<TestName>>(runResult, visibleProcessName, checks) {
override fun getLoggedRun() = LoggedData.TestRun(loggedParameters, runResult)
override fun doHandle() = GTestListing.parse(runResult.processOutput.stdOut.filteredOutput) override fun doHandle() = GTestListing.parse(runResult.processOutput.stdOut.filteredOutput)
} }
}
@@ -38,15 +38,28 @@ internal class LocalTestRunner(private val testRun: TestRun) : AbstractLocalProc
} }
} }
override fun buildResultHandler(runResult: RunResult) = ResultHandler(runResult) override fun buildResultHandler(runResult: RunResult) = ResultHandler(
runResult = runResult,
visibleProcessName = visibleProcessName,
checks = testRun.checks,
testRun = testRun,
loggedParameters = getLoggedParameters()
)
override fun handleUnexpectedFailure(t: Throwable) = fail { override fun handleUnexpectedFailure(t: Throwable) = fail {
LoggedData.TestRunUnexpectedFailure(getLoggedParameters(), t) LoggedData.TestRunUnexpectedFailure(getLoggedParameters(), t)
.withErrorMessage("Test execution failed with unexpected exception.") .withErrorMessage("Test execution failed with unexpected exception.")
} }
}
inner class ResultHandler(runResult: RunResult) : AbstractLocalProcessRunner<Unit>.ResultHandler(runResult) { internal class ResultHandler(
override fun getLoggedRun() = LoggedData.TestRun(getLoggedParameters(), runResult) runResult: RunResult,
visibleProcessName: String,
checks: TestRunChecks,
private val testRun: TestRun,
private val loggedParameters: LoggedData.TestRunParameters
) : LocalResultHandler<Unit>(runResult, visibleProcessName, checks) {
override fun getLoggedRun() = LoggedData.TestRun(loggedParameters, runResult)
override fun doHandle() { override fun doHandle() {
verifyTestReport(runResult.processOutput.stdOut.testReport) verifyTestReport(runResult.processOutput.stdOut.testReport)
@@ -81,4 +94,3 @@ internal class LocalTestRunner(private val testRun: TestRun) : AbstractLocalProc
} }
} }
} }
}
@@ -0,0 +1,82 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.konan.blackboxtest.support.runner
import org.jetbrains.kotlin.native.executors.ExecuteRequest
import org.jetbrains.kotlin.native.executors.Executor
import org.jetbrains.kotlin.konan.blackboxtest.support.LoggedData
import org.jetbrains.kotlin.konan.blackboxtest.support.util.TCTestOutputFilter
import org.jetbrains.kotlin.konan.blackboxtest.support.util.TestOutputFilter
import org.jetbrains.kotlin.test.services.JUnit5Assertions
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.InputStream
internal class RunnerWithExecutor(
private val executor: Executor,
private val testRun: TestRun
) : AbstractRunner<Unit>() {
private val executable get() = testRun.executable
private val outputFilter: TestOutputFilter
get() = if (testRun.runParameters.has<TestRunParameter.WithTCTestLogger>()) TCTestOutputFilter else TestOutputFilter.NO_FILTERING
private val programArgs = mutableListOf<String>().apply {
testRun.runParameters.forEach { it.applyTo(this) }
}
private fun inputStreamFromTestParameter(): InputStream =
testRun.runParameters.firstIsInstanceOrNull<TestRunParameter.WithInputData>()
?.let {
ByteArrayInputStream(it.inputDataFile.readBytes())
} ?: ByteArrayInputStream(byteArrayOf())
override fun buildRun() = AbstractRun {
val stdin = inputStreamFromTestParameter()
val stdout = ByteArrayOutputStream()
val stderr = ByteArrayOutputStream()
val request = ExecuteRequest(
executableAbsolutePath = executable.executableFile.absolutePath,
args = programArgs,
stdin = stdin,
stdout = stdout,
stderr = stderr,
timeout = testRun.checks.executionTimeoutCheck.timeout
)
val response = executor.execute(request)
RunResult(
exitCode = response.exitCode,
timeout = request.timeout,
duration = response.executionTime,
hasFinishedOnTime = request.timeout >= response.executionTime,
processOutput = ProcessOutput(
stdOut = outputFilter.filter(stdout.toString("UTF-8")),
stdErr = stderr.toString("UTF-8")
)
)
}
override fun buildResultHandler(runResult: RunResult): ResultHandler = ResultHandler(
runResult = runResult,
visibleProcessName = "Test process under Executor ${executor::class.simpleName}",
checks = testRun.checks,
testRun = testRun,
loggedParameters = getLoggedParameters()
)
override fun getLoggedParameters() = LoggedData.TestRunParameters(
compilationToolCall = executable.loggedCompilationToolCall,
testCaseId = testRun.testCaseId,
runArgs = programArgs,
runParameters = testRun.runParameters
)
override fun handleUnexpectedFailure(t: Throwable) = JUnit5Assertions.fail {
LoggedData.TestRunUnexpectedFailure(getLoggedParameters(), t)
.withErrorMessage("Test execution failed with unexpected exception.")
}
}
@@ -7,23 +7,46 @@ package org.jetbrains.kotlin.konan.blackboxtest.support.runner
import org.jetbrains.kotlin.konan.blackboxtest.support.TestName import org.jetbrains.kotlin.konan.blackboxtest.support.TestName
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.ForcedNoopTestRunner import org.jetbrains.kotlin.konan.blackboxtest.support.settings.ForcedNoopTestRunner
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.KotlinNativeHome
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.KotlinNativeTargets import org.jetbrains.kotlin.konan.blackboxtest.support.settings.KotlinNativeTargets
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.Settings import org.jetbrains.kotlin.konan.blackboxtest.support.settings.Settings
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.Timeouts import org.jetbrains.kotlin.konan.blackboxtest.support.settings.Timeouts
import org.jetbrains.kotlin.native.executors.Executor
import org.jetbrains.kotlin.native.executors.EmulatorExecutor
import org.jetbrains.kotlin.native.executors.XcodeSimulatorExecutor
import org.jetbrains.kotlin.konan.target.*
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
import java.util.concurrent.ConcurrentHashMap
internal object TestRunners { internal object TestRunners {
// Currently, only local and noop test runners are supported.
fun createProperTestRunner(testRun: TestRun, settings: Settings): Runner<Unit> = with(settings) { fun createProperTestRunner(testRun: TestRun, settings: Settings): Runner<Unit> = with(settings) {
if (get<ForcedNoopTestRunner>().value) { if (get<ForcedNoopTestRunner>().value) {
NoopTestRunner NoopTestRunner
} else with(get<KotlinNativeTargets>()) { } else with(get<KotlinNativeTargets>()) {
if (testTarget == hostTarget) if (testTarget == hostTarget) {
LocalTestRunner(testRun) LocalTestRunner(testRun)
else } else {
runningAtNonHostTarget() val nativeHome = get<KotlinNativeHome>()
val distribution = Distribution(nativeHome.dir.path)
val configurables = PlatformManager(distribution, true).platform(testTarget).configurables
val executor = cached(
when {
configurables is ConfigurablesWithEmulator -> EmulatorExecutor(configurables)
configurables is AppleConfigurables && configurables.targetTriple.isSimulator ->
XcodeSimulatorExecutor(configurables)
else -> runningOnUnsupportedTarget()
}
)
RunnerWithExecutor(executor, testRun)
} }
} }
}
private val runnersCache: ConcurrentHashMap<String, Executor> = ConcurrentHashMap()
private inline fun <reified T : Executor> cached(executor: T): Executor =
runnersCache.computeIfAbsent(T::class.java.simpleName) { executor }
// Currently, only local test name extractor is supported. // Currently, only local test name extractor is supported.
fun extractTestNames(executable: TestExecutable, settings: Settings): Collection<TestName> = with(settings) { fun extractTestNames(executable: TestExecutable, settings: Settings): Collection<TestName> = with(settings) {
@@ -31,15 +54,11 @@ internal object TestRunners {
if (testTarget == hostTarget) if (testTarget == hostTarget)
LocalTestNameExtractor(executable, TestRunChecks.Default(get<Timeouts>().executionTimeout)).run() LocalTestNameExtractor(executable, TestRunChecks.Default(get<Timeouts>().executionTimeout)).run()
else else
runningAtNonHostTarget() runningOnUnsupportedTarget()
} }
} }
private fun KotlinNativeTargets.runningAtNonHostTarget(): Nothing = fail { private fun KotlinNativeTargets.runningOnUnsupportedTarget(): Nothing = fail {
""" "Running tests for $testTarget on $hostTarget is not supported yet."
Running at non-host target is not supported yet.
Compilation target: $testTarget
Host target: $hostTarget
""".trimIndent()
} }
} }
@@ -23,7 +23,9 @@ import kotlin.time.Duration.Companion.seconds
/** /**
* The tested and the host Kotlin/Native targets. * The tested and the host Kotlin/Native targets.
*/ */
internal class KotlinNativeTargets(val testTarget: KonanTarget, val hostTarget: KonanTarget) internal class KotlinNativeTargets(val testTarget: KonanTarget, val hostTarget: KonanTarget) {
fun areDifferentTargets() = testTarget != hostTarget
}
/** /**
* The Kotlin/Native home. * The Kotlin/Native home.