[Native][tests] Add an ability to fail tests on exceeded timeout

This commit is contained in:
Dmitriy Dolovov
2021-11-22 17:38:31 +03:00
parent a2ad9026a6
commit 34627633c4
8 changed files with 130 additions and 77 deletions
+3 -1
View File
@@ -46,7 +46,8 @@ enum class TestProperty(shortName: String) {
KOTLIN_NATIVE_HOME("nativeHome"), KOTLIN_NATIVE_HOME("nativeHome"),
COMPILER_CLASSPATH("compilerClasspath"), COMPILER_CLASSPATH("compilerClasspath"),
TEST_MODE("mode"), TEST_MODE("mode"),
USE_CACHE("useCache"); USE_CACHE("useCache"),
EXECUTION_TIMEOUT("executionTimeout");
private val propertyName = "kotlin.internal.native.test.$shortName" private val propertyName = "kotlin.internal.native.test.$shortName"
@@ -81,6 +82,7 @@ if (kotlinBuildProperties.isKotlinNativeEnabled) {
// Pass Gradle properties as JVM properties so test process can read them. // Pass Gradle properties as JVM properties so test process can read them.
TestProperty.TEST_MODE.setUpFromGradleProperty(this) TestProperty.TEST_MODE.setUpFromGradleProperty(this)
TestProperty.USE_CACHE.setUpFromGradleProperty(this) TestProperty.USE_CACHE.setUpFromGradleProperty(this)
TestProperty.EXECUTION_TIMEOUT.setUpFromGradleProperty(this)
useJUnitPlatform() useJUnitPlatform()
} }
@@ -6,7 +6,10 @@
package org.jetbrains.kotlin.konan.blackboxtest.support package org.jetbrains.kotlin.konan.blackboxtest.support
import org.jetbrains.kotlin.cli.common.ExitCode import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.konan.blackboxtest.support.runner.AbstractRunner
import java.io.File import java.io.File
import kotlin.time.Duration
import kotlin.time.DurationUnit
/** /**
* A piece of information that makes sense for the user, and that can be logged to a file or * A piece of information that makes sense for the user, and that can be logged to a file or
@@ -19,6 +22,12 @@ internal abstract class LoggedData {
protected abstract fun computeText(): String protected abstract fun computeText(): String
final override fun toString() = text final override fun toString() = text
fun withErrorMessageHeader(errorMessageHeader: String): String = buildString {
appendLine(errorMessageHeader)
appendLine()
appendLine(this@LoggedData)
}
class CompilerParameters( class CompilerParameters(
private val compilerArgs: Array<String>, private val compilerArgs: Array<String>,
private val sourceModules: Collection<TestModule> private val sourceModules: Collection<TestModule>
@@ -42,7 +51,7 @@ internal abstract class LoggedData {
private val exitCode: ExitCode, private val exitCode: ExitCode,
private val compilerOutput: String, private val compilerOutput: String,
private val compilerOutputHasErrors: Boolean, private val compilerOutputHasErrors: Boolean,
private val durationMillis: Long private val duration: Duration
) : LoggedData() { ) : LoggedData() {
override fun computeText(): String { override fun computeText(): String {
val problems = listOfNotNull( val problems = listOfNotNull(
@@ -59,7 +68,7 @@ internal abstract class LoggedData {
appendLine("COMPILER CALL:") appendLine("COMPILER CALL:")
appendLine("- Exit code: ${exitCode.code} (${exitCode.name})") appendLine("- Exit code: ${exitCode.code} (${exitCode.name})")
appendDuration(durationMillis) appendDuration(duration)
appendLine() appendLine()
appendLine("========== BEGIN: RAW COMPILER OUTPUT ==========") appendLine("========== BEGIN: RAW COMPILER OUTPUT ==========")
if (compilerOutput.isNotEmpty()) appendLine(compilerOutput.trimEnd()) if (compilerOutput.isNotEmpty()) appendLine(compilerOutput.trimEnd())
@@ -100,22 +109,19 @@ internal abstract class LoggedData {
class TestRun( class TestRun(
private val parameters: TestRunParameters, private val parameters: TestRunParameters,
private val exitCode: Int, private val runResult: AbstractRunner.RunResult.Completed
private val stdOut: String,
private val stdErr: String,
private val durationMillis: Long
) : LoggedData() { ) : LoggedData() {
override fun computeText() = buildString { override fun computeText() = buildString {
appendLine("TEST RUN:") appendLine("TEST RUN:")
appendLine("- Exit code: $exitCode") appendLine("- Exit code: ${runResult.exitCode}")
appendDuration(durationMillis) appendDuration(runResult.duration)
appendLine() appendLine()
appendLine("========== BEGIN: TEST STDOUT ==========") appendLine("========== BEGIN: TEST STDOUT ==========")
if (stdOut.isNotEmpty()) appendLine(stdOut.trimEnd()) if (runResult.stdOut.isNotEmpty()) appendLine(runResult.stdOut.trimEnd())
appendLine("========== END: TEST STDOUT ==========") appendLine("========== END: TEST STDOUT ==========")
appendLine() appendLine()
appendLine("========== BEGIN: TEST STDERR ==========") appendLine("========== BEGIN: TEST STDERR ==========")
if (stdErr.isNotEmpty()) appendLine(stdErr.trimEnd()) if (runResult.stdErr.isNotEmpty()) appendLine(runResult.stdErr.trimEnd())
appendLine("========== END: TEST STDERR ==========") appendLine("========== END: TEST STDERR ==========")
appendLine() appendLine()
appendLine(parameters) appendLine(parameters)
@@ -139,15 +145,27 @@ internal abstract class LoggedData {
} }
} }
class TestRunTimeoutExceeded(parameters: TestRunParameters, timeout: Duration) : TimeoutExceeded(parameters, timeout)
abstract class TimeoutExceeded(
private val parameters: LoggedData,
private val timeout: Duration
) : LoggedData() {
override fun computeText() = buildString {
appendLine("TIMED OUT:")
appendLine("- Max permitted duration: $timeout")
appendLine()
appendLine(parameters)
}
}
companion object { companion object {
@JvmStatic
protected fun StringBuilder.appendList(header: String, list: Iterable<Any?>): StringBuilder { protected fun StringBuilder.appendList(header: String, list: Iterable<Any?>): StringBuilder {
appendLine(header) appendLine(header)
list.forEach(::appendLine) list.forEach(::appendLine)
return this return this
} }
@JvmStatic
protected fun StringBuilder.appendArguments(header: String, args: Iterable<String>): StringBuilder { protected fun StringBuilder.appendArguments(header: String, args: Iterable<String>): StringBuilder {
appendLine(header) appendLine(header)
@@ -171,7 +189,7 @@ internal abstract class LoggedData {
return this return this
} }
protected fun StringBuilder.appendDuration(durationMillis: Long): StringBuilder = protected fun StringBuilder.appendDuration(duration: Duration): StringBuilder =
append("- Duration: ").append(String.format("%.2f", durationMillis.toDouble() / 1000)).appendLine(" seconds") append("- Duration: ").appendLine(duration.toString(DurationUnit.SECONDS, 2))
} }
} }
@@ -17,7 +17,9 @@ import org.jetbrains.kotlin.konan.blackboxtest.support.TestModule.Companion.allF
import org.jetbrains.kotlin.konan.blackboxtest.support.util.* import org.jetbrains.kotlin.konan.blackboxtest.support.util.*
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
import java.io.* import java.io.*
import kotlin.system.measureTimeMillis import kotlin.time.Duration
import kotlin.time.ExperimentalTime
import kotlin.time.measureTime
internal class TestCompilationFactory(private val environment: TestEnvironment) { internal class TestCompilationFactory(private val environment: TestEnvironment) {
private val cachedCompilations = ThreadSafeCache<TestCompilationCacheKey, TestCompilation>() private val cachedCompilations = ThreadSafeCache<TestCompilationCacheKey, TestCompilation>()
@@ -175,14 +177,12 @@ internal sealed interface TestCompilationResult {
is DependencyFailures -> fail { describeDependencyFailures() } is DependencyFailures -> fail { describeDependencyFailures() }
} }
private fun Failure.describeFailure() = buildString { private fun Failure.describeFailure() = loggedData.withErrorMessageHeader(
when (this@describeFailure) { when (this@describeFailure) {
is CompilerFailure -> appendLine("Compilation failed.") is CompilerFailure -> "Compilation failed."
is UnexpectedFailure -> appendLine("Compilation failed with unexpected exception.") is UnexpectedFailure -> "Compilation failed with unexpected exception."
} }
appendLine() )
appendLine(loggedData)
}
private fun DependencyFailures.describeDependencyFailures() = private fun DependencyFailures.describeDependencyFailures() =
buildString { buildString {
@@ -270,13 +270,13 @@ private class TestCompilationImpl(
val loggedCompilerParameters = LoggedData.CompilerParameters(compilerArgs, sourceModules) val loggedCompilerParameters = LoggedData.CompilerParameters(compilerArgs, sourceModules)
val (loggedCompilerCall: LoggedData, result: TestCompilationResult.ImmediateResult) = try { val (loggedCompilerCall: LoggedData, result: TestCompilationResult.ImmediateResult) = try {
val (exitCode, compilerOutput, compilerOutputHasErrors, durationMillis) = callCompiler( val (exitCode, compilerOutput, compilerOutputHasErrors, duration) = callCompiler(
compilerArgs = compilerArgs, compilerArgs = compilerArgs,
lazyKotlinNativeClassLoader = environment.globalEnvironment.lazyKotlinNativeClassLoader lazyKotlinNativeClassLoader = environment.globalEnvironment.lazyKotlinNativeClassLoader
) )
val loggedCompilerCall = val loggedCompilerCall =
LoggedData.CompilerCall(loggedCompilerParameters, exitCode, compilerOutput, compilerOutputHasErrors, durationMillis) LoggedData.CompilerCall(loggedCompilerParameters, exitCode, compilerOutput, compilerOutputHasErrors, duration)
val result = if (exitCode != ExitCode.OK || compilerOutputHasErrors) val result = if (exitCode != ExitCode.OK || compilerOutputHasErrors)
TestCompilationResult.CompilerFailure(loggedCompilerCall) TestCompilationResult.CompilerFailure(loggedCompilerCall)
@@ -331,7 +331,8 @@ private fun callCompiler(compilerArgs: Array<String>, lazyKotlinNativeClassLoade
val compilerXmlOutput: ByteArrayOutputStream val compilerXmlOutput: ByteArrayOutputStream
val exitCode: ExitCode val exitCode: ExitCode
val durationMillis = measureTimeMillis { @OptIn(ExperimentalTime::class)
val duration = measureTime {
val kotlinNativeClassLoader by lazyKotlinNativeClassLoader val kotlinNativeClassLoader by lazyKotlinNativeClassLoader
val servicesClass = Class.forName(Services::class.java.canonicalName, true, kotlinNativeClassLoader) val servicesClass = Class.forName(Services::class.java.canonicalName, true, kotlinNativeClassLoader)
@@ -372,14 +373,14 @@ private fun callCompiler(compilerArgs: Array<String>, lazyKotlinNativeClassLoade
compilerOutput = outputStream.toString(Charsets.UTF_8.name()) compilerOutput = outputStream.toString(Charsets.UTF_8.name())
} }
return CompilerCallResult(exitCode, compilerOutput, messageCollector.hasErrors(), durationMillis) return CompilerCallResult(exitCode, compilerOutput, messageCollector.hasErrors(), duration)
} }
private data class CompilerCallResult( private data class CompilerCallResult(
val exitCode: ExitCode, val exitCode: ExitCode,
val compilerOutput: String, val compilerOutput: String,
val compilerOutputHasErrors: Boolean, val compilerOutputHasErrors: Boolean,
val durationMillis: Long val duration: Duration
) )
private fun getLogFile(expectedArtifactFile: File): File = expectedArtifactFile.resolveSibling(expectedArtifactFile.name + ".log") private fun getLogFile(expectedArtifactFile: File): File = expectedArtifactFile.resolveSibling(expectedArtifactFile.name + ".log")
@@ -11,6 +11,9 @@ import org.jetbrains.kotlin.konan.blackboxtest.support.util.TestDisposable
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
import java.io.File import java.io.File
import java.net.URLClassLoader import java.net.URLClassLoader
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import kotlin.time.Duration.Companion.milliseconds
internal class TestEnvironment( internal class TestEnvironment(
val globalEnvironment: GlobalTestEnvironment, val globalEnvironment: GlobalTestEnvironment,
@@ -27,6 +30,7 @@ internal class GlobalTestEnvironment(
val lazyKotlinNativeClassLoader: Lazy<ClassLoader> = defaultKotlinNativeClassLoader, val lazyKotlinNativeClassLoader: Lazy<ClassLoader> = defaultKotlinNativeClassLoader,
val testMode: TestMode = defaultTestMode, val testMode: TestMode = defaultTestMode,
val cacheSettings: TestCacheSettings = defaultCacheSettings, val cacheSettings: TestCacheSettings = defaultCacheSettings,
val executionTimeout: Duration = defaultExecutionTimeout,
val baseBuildDir: File = projectBuildDir val baseBuildDir: File = projectBuildDir
) { ) {
val hostTarget: KonanTarget = HostManager.host val hostTarget: KonanTarget = HostManager.host
@@ -73,6 +77,15 @@ internal class GlobalTestEnvironment(
if (useCache) TestCacheSettings.WithCache else TestCacheSettings.WithoutCache if (useCache) TestCacheSettings.WithCache else TestCacheSettings.WithoutCache
} }
private val defaultExecutionTimeout: Duration = run {
val executionTimeoutValue = System.getProperty(EXECUTION_TIMEOUT)
if (executionTimeoutValue != null) {
executionTimeoutValue.toLongOrNull()?.milliseconds
?: fail { "Invalid value for $EXECUTION_TIMEOUT system property: $executionTimeoutValue" }
} else
DEFAULT_EXECUTION_TIMEOUT
}
private val projectBuildDir: File private val projectBuildDir: File
get() = System.getenv(PROJECT_BUILD_DIR)?.let(::File) ?: fail { "Non-specified $PROJECT_BUILD_DIR environment variable" } get() = System.getenv(PROJECT_BUILD_DIR)?.let(::File) ?: fail { "Non-specified $PROJECT_BUILD_DIR environment variable" }
@@ -80,7 +93,10 @@ internal class GlobalTestEnvironment(
private const val COMPILER_CLASSPATH = "kotlin.internal.native.test.compilerClasspath" private const val COMPILER_CLASSPATH = "kotlin.internal.native.test.compilerClasspath"
private const val TEST_MODE = "kotlin.internal.native.test.mode" private const val TEST_MODE = "kotlin.internal.native.test.mode"
private const val USE_CACHE = "kotlin.internal.native.test.useCache" private const val USE_CACHE = "kotlin.internal.native.test.useCache"
private const val EXECUTION_TIMEOUT = "kotlin.internal.native.test.executionTimeout"
private const val PROJECT_BUILD_DIR = "PROJECT_BUILD_DIR" private const val PROJECT_BUILD_DIR = "PROJECT_BUILD_DIR"
private val DEFAULT_EXECUTION_TIMEOUT get() = 10.seconds // Use no backing field to avoid null-initialized value.
} }
} }
@@ -76,7 +76,7 @@ internal class TestRunProvider(
// Currently, only local test runner is supported. // Currently, only local test runner is supported.
fun createRunner(testRun: TestRun): AbstractRunner<*> = when (val target = environment.globalEnvironment.target) { fun createRunner(testRun: TestRun): AbstractRunner<*> = when (val target = environment.globalEnvironment.target) {
environment.globalEnvironment.hostTarget -> LocalTestRunner(testRun) environment.globalEnvironment.hostTarget -> LocalTestRunner(testRun, environment.globalEnvironment.executionTimeout)
else -> fail { else -> fail {
""" """
Running at non-host target is not supported yet. Running at non-host target is not supported yet.
@@ -6,32 +6,47 @@
package org.jetbrains.kotlin.konan.blackboxtest.support.runner package org.jetbrains.kotlin.konan.blackboxtest.support.runner
import org.jetbrains.kotlin.konan.blackboxtest.support.TestExecutable import org.jetbrains.kotlin.konan.blackboxtest.support.TestExecutable
import org.jetbrains.kotlin.konan.blackboxtest.support.runner.AbstractRunner.AbstractRun
import kotlin.time.*
internal abstract class AbstractLocalProcessRunner<R> : AbstractRunner<R>() { internal abstract class AbstractLocalProcessRunner<R>(private val executionTimeout: Duration) : 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>
protected open fun customizeProcess(process: Process) = Unit protected open fun customizeProcess(process: Process) = Unit
@OptIn(ExperimentalTime::class)
final override fun buildRun() = AbstractRun { final override fun buildRun() = AbstractRun {
val startTimeMillis = System.currentTimeMillis() val exitCode: Int
val process = ProcessBuilder(programArgs).directory(executable.executableFile.parentFile).start() val stdOut: String
customizeProcess(process) val stdErr: String
val exitCode = process.waitFor() val duration = measureTime {
val finishTimeMillis = System.currentTimeMillis() val process = ProcessBuilder(programArgs).directory(executable.executableFile.parentFile).start()
customizeProcess(process)
val stdOut = process.inputStream.bufferedReader().readText() val hasFinishedInTime = process.waitFor(
val stdErr = process.errorStream.bufferedReader().readText() executionTimeout.toLong(DurationUnit.MILLISECONDS),
DurationUnit.MILLISECONDS.toTimeUnit()
)
RunResult(exitCode, finishTimeMillis - startTimeMillis, stdOut, stdErr) if (!hasFinishedInTime)
return@AbstractRun RunResult.TimeoutExceeded(executionTimeout)
exitCode = process.exitValue()
stdOut = process.inputStream.bufferedReader().readText()
stdErr = process.errorStream.bufferedReader().readText()
}
RunResult.Completed(exitCode, duration, stdOut, stdErr)
} }
abstract override fun buildResultHandler(runResult: RunResult): ResultHandler // Narrow returned type. abstract override fun buildResultHandler(runResult: RunResult.Completed): ResultHandler // Narrow returned type.
abstract inner class ResultHandler(runResult: RunResult) : AbstractRunner<R>.ResultHandler(runResult) { abstract inner class ResultHandler(runResult: RunResult.Completed) : AbstractRunner<R>.ResultHandler(runResult) {
override fun handle(): R { override fun handle(): R {
verifyExpectation(0, runResult.exitCode) { "$visibleProcessName exited with non-zero code." } verifyExpectation(0, runResult.exitCode) { "$visibleProcessName exited with non-zero code." }
@@ -8,17 +8,26 @@ package org.jetbrains.kotlin.konan.blackboxtest.support.runner
import org.jetbrains.kotlin.konan.blackboxtest.support.LoggedData import org.jetbrains.kotlin.konan.blackboxtest.support.LoggedData
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertEquals import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertEquals
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertTrue import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertTrue
import java.lang.AssertionError import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
import kotlin.time.Duration
internal abstract class AbstractRunner<R> { internal abstract class AbstractRunner<R> {
protected abstract fun buildRun(): AbstractRun protected abstract fun buildRun(): AbstractRun
protected abstract fun buildResultHandler(runResult: RunResult): ResultHandler protected abstract fun buildResultHandler(runResult: RunResult.Completed): ResultHandler
protected abstract fun getLoggedParameters(): LoggedData.TestRunParameters
protected abstract fun handleUnexpectedFailure(t: Throwable): Nothing protected abstract fun handleUnexpectedFailure(t: Throwable): Nothing
fun run(): R = try { fun run(): R = try {
val run = buildRun() val run = buildRun()
val runResult = run.run()
val resultHandler = buildResultHandler(runResult) val resultHandler = when (val runResult = run.run()) {
is RunResult.TimeoutExceeded -> fail {
LoggedData.TestRunTimeoutExceeded(getLoggedParameters(), runResult.timeout)
.withErrorMessageHeader("Timeout exceeded during test execution.")
}
is RunResult.Completed -> buildResultHandler(runResult)
}
resultHandler.handle() resultHandler.handle()
} catch (t: Throwable) { } catch (t: Throwable) {
if (t is AssertionError) if (t is AssertionError)
@@ -33,29 +42,21 @@ internal abstract class AbstractRunner<R> {
fun run(): RunResult fun run(): RunResult
} }
data class RunResult(val exitCode: Int, val durationMillis: Long, val stdOut: String, val stdErr: String) sealed interface RunResult {
data class Completed(val exitCode: Int, val duration: Duration, val stdOut: String, val stdErr: String) : RunResult
data class TimeoutExceeded(val timeout: Duration) : RunResult
}
abstract inner class ResultHandler(protected val runResult: RunResult) { abstract inner class ResultHandler(protected val runResult: RunResult.Completed) {
abstract fun getLoggedRun(): LoggedData abstract fun getLoggedRun(): LoggedData
abstract fun handle(): R abstract fun handle(): R
val exitCode get() = runResult.exitCode
val durationMillis get() = runResult.durationMillis
val stdOut get() = runResult.stdOut
val stdErr get() = runResult.stdErr
protected inline fun <T> verifyExpectation(expected: T, actual: T, crossinline errorMessageHeader: () -> String) { protected inline fun <T> verifyExpectation(expected: T, actual: T, crossinline errorMessageHeader: () -> String) {
assertEquals(expected, actual) { formatErrorMessage(errorMessageHeader) } assertEquals(expected, actual) { getLoggedRun().withErrorMessageHeader(errorMessageHeader()) }
} }
protected inline fun verifyExpectation(shouldBeTrue: Boolean, crossinline errorMessageHeader: () -> String) { protected inline fun verifyExpectation(shouldBeTrue: Boolean, crossinline errorMessageHeader: () -> String) {
assertTrue(shouldBeTrue) { formatErrorMessage(errorMessageHeader) } assertTrue(shouldBeTrue) { getLoggedRun().withErrorMessageHeader(errorMessageHeader()) }
}
private inline fun formatErrorMessage(errorMessageHeader: () -> String) = buildString {
appendLine(errorMessageHeader())
appendLine()
appendLine(getLoggedRun())
} }
} }
} }
@@ -8,8 +8,12 @@ package org.jetbrains.kotlin.konan.blackboxtest.support.runner
import com.intellij.openapi.util.text.StringUtilRt.convertLineSeparators import com.intellij.openapi.util.text.StringUtilRt.convertLineSeparators
import org.jetbrains.kotlin.konan.blackboxtest.support.* import org.jetbrains.kotlin.konan.blackboxtest.support.*
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
import kotlin.time.Duration
internal class LocalTestRunner(private val testRun: TestRun) : AbstractLocalProcessRunner<Unit>() { internal class LocalTestRunner(
private val testRun: TestRun,
executionTimeout: Duration
) : AbstractLocalProcessRunner<Unit>(executionTimeout) {
override val visibleProcessName get() = "Tested process" override val visibleProcessName get() = "Tested process"
override val executable get() = testRun.executable override val executable get() = testRun.executable
@@ -18,13 +22,12 @@ internal class LocalTestRunner(private val testRun: TestRun) : AbstractLocalProc
testRun.runParameters.forEach { it.applyTo(this) } testRun.runParameters.forEach { it.applyTo(this) }
} }
private val loggedParameters: LoggedData.TestRunParameters override fun getLoggedParameters() = LoggedData.TestRunParameters(
get() = LoggedData.TestRunParameters( compilerCall = executable.loggedCompilerCall,
compilerCall = executable.loggedCompilerCall, origin = testRun.origin,
origin = testRun.origin, runArgs = programArgs,
runArgs = programArgs, runParameters = testRun.runParameters
runParameters = testRun.runParameters )
)
override fun customizeProcess(process: Process) { override fun customizeProcess(process: Process) {
testRun.runParameters.get<TestRunParameter.WithInputData> { testRun.runParameters.get<TestRunParameter.WithInputData> {
@@ -33,18 +36,15 @@ internal class LocalTestRunner(private val testRun: TestRun) : AbstractLocalProc
} }
} }
override fun buildResultHandler(runResult: RunResult) = ResultHandler(runResult) override fun buildResultHandler(runResult: RunResult.Completed) = ResultHandler(runResult)
override fun handleUnexpectedFailure(t: Throwable) = fail { override fun handleUnexpectedFailure(t: Throwable) = fail {
buildString { LoggedData.TestRunUnexpectedFailure(getLoggedParameters(), t)
appendLine("Test execution failed with unexpected exception.") .withErrorMessageHeader("Test execution failed with unexpected exception.")
appendLine()
appendLine(LoggedData.TestRunUnexpectedFailure(loggedParameters, t))
}
} }
inner class ResultHandler(runResult: RunResult) : AbstractLocalProcessRunner<Unit>.ResultHandler(runResult) { inner class ResultHandler(runResult: RunResult.Completed) : AbstractLocalProcessRunner<Unit>.ResultHandler(runResult) {
override fun getLoggedRun() = LoggedData.TestRun(loggedParameters, exitCode, stdOut, stdErr, durationMillis) override fun getLoggedRun() = LoggedData.TestRun(getLoggedParameters(), runResult)
override fun doHandle() { override fun doHandle() {
if (testRun.runParameters.has<TestRunParameter.WithGTestLogger>()) { if (testRun.runParameters.has<TestRunParameter.WithGTestLogger>()) {
@@ -59,7 +59,7 @@ internal class LocalTestRunner(private val testRun: TestRun) : AbstractLocalProc
val cleanStdOut = StringBuilder() val cleanStdOut = StringBuilder()
var expectStatusLine = false var expectStatusLine = false
stdOut.lines().forEach { line -> runResult.stdOut.lines().forEach { line ->
when { when {
expectStatusLine -> { expectStatusLine -> {
val matcher = GTEST_STATUS_LINE_REGEX.matchEntire(line) val matcher = GTEST_STATUS_LINE_REGEX.matchEntire(line)
@@ -94,10 +94,10 @@ internal class LocalTestRunner(private val testRun: TestRun) : AbstractLocalProc
val failedTests = (testStatuses - GTEST_STATUS_OK).values.sumOf { it.size } val failedTests = (testStatuses - GTEST_STATUS_OK).values.sumOf { it.size }
verifyExpectation(0, failedTests) { "There are failed tests." } verifyExpectation(0, failedTests) { "There are failed tests." }
verifyOutputData(mergedOutput = cleanStdOut.toString() + stdErr) verifyOutputData(mergedOutput = cleanStdOut.toString() + runResult.stdErr)
} }
private fun verifyPlainTest() = verifyOutputData(mergedOutput = stdOut + stdErr) private fun verifyPlainTest() = verifyOutputData(mergedOutput = runResult.stdOut + runResult.stdErr)
private fun verifyOutputData(mergedOutput: String) { private fun verifyOutputData(mergedOutput: String) {
testRun.runParameters.get<TestRunParameter.WithExpectedOutputData> { testRun.runParameters.get<TestRunParameter.WithExpectedOutputData> {