[Native][tests] Use TEAMCITY test logger instead of GTEST one
That's because currently K/N tests do not report ignored tests with GTEST logger, but do so with TEAMCITY logger.
This commit is contained in:
@@ -12,6 +12,7 @@ dependencies {
|
||||
testImplementation(project(":kotlin-reflect"))
|
||||
testImplementation(intellijCore())
|
||||
testImplementation(commonDependency("commons-lang:commons-lang"))
|
||||
testImplementation(commonDependency("org.jetbrains.teamcity:serviceMessages"))
|
||||
testImplementation(project(":kotlin-compiler-runner-unshaded"))
|
||||
testImplementation(projectTests(":compiler:tests-common"))
|
||||
testImplementation(projectTests(":compiler:tests-common-new"))
|
||||
|
||||
+3
-2
@@ -66,9 +66,10 @@ internal sealed interface TestRunParameter {
|
||||
}
|
||||
}
|
||||
|
||||
object WithGTestLogger : TestRunParameter {
|
||||
object WithTCTestLogger : TestRunParameter {
|
||||
override fun applyTo(programArgs: MutableList<String>) {
|
||||
programArgs += "--ktest_logger=GTEST"
|
||||
programArgs += "--ktest_logger=TEAMCITY"
|
||||
programArgs += "--ktest_no_exit_code"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -169,12 +169,12 @@ internal class TestRunProvider(
|
||||
)
|
||||
}
|
||||
TestKind.STANDALONE -> listOfNotNull(
|
||||
TestRunParameter.WithGTestLogger,
|
||||
TestRunParameter.WithTCTestLogger,
|
||||
testFunction?.let(TestRunParameter::WithFunctionFilter),
|
||||
expectedOutputDataFile?.let(TestRunParameter::WithExpectedOutputData)
|
||||
)
|
||||
TestKind.REGULAR -> listOfNotNull(
|
||||
TestRunParameter.WithGTestLogger,
|
||||
TestRunParameter.WithTCTestLogger,
|
||||
testFunction?.let(TestRunParameter::WithFunctionFilter) ?: TestRunParameter.WithPackageFilter(nominalPackageName),
|
||||
expectedOutputDataFile?.let(TestRunParameter::WithExpectedOutputData)
|
||||
)
|
||||
|
||||
+7
-5
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.konan.blackboxtest.support.LoggedData
|
||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertEquals
|
||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertTrue
|
||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
|
||||
import org.opentest4j.TestAbortedException
|
||||
|
||||
internal abstract class AbstractRunner<R> {
|
||||
protected abstract fun buildRun(): AbstractRun
|
||||
@@ -29,11 +30,12 @@ internal abstract class AbstractRunner<R> {
|
||||
|
||||
resultHandler.handle()
|
||||
} catch (t: Throwable) {
|
||||
if (t is AssertionError)
|
||||
throw t
|
||||
else {
|
||||
// An unexpected failure.
|
||||
handleUnexpectedFailure(t)
|
||||
when (t) {
|
||||
is AssertionError, is TestAbortedException -> throw t
|
||||
else -> {
|
||||
// An unexpected failure.
|
||||
handleUnexpectedFailure(t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+32
-18
@@ -7,8 +7,10 @@ package org.jetbrains.kotlin.konan.blackboxtest.support.runner
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtilRt.convertLineSeparators
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.*
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.parseGTestReport
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.TestName
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.parseTCTestReport
|
||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
|
||||
import org.junit.jupiter.api.Assumptions.assumeFalse
|
||||
import kotlin.time.Duration
|
||||
|
||||
internal class LocalTestRunner(
|
||||
@@ -48,37 +50,49 @@ internal class LocalTestRunner(
|
||||
override fun getLoggedRun() = LoggedData.TestRun(getLoggedParameters(), runResult)
|
||||
|
||||
override fun doHandle() {
|
||||
if (testRun.runParameters.has<TestRunParameter.WithGTestLogger>()) {
|
||||
verifyTestWithGTestRunner()
|
||||
if (testRun.runParameters.has<TestRunParameter.WithTCTestLogger>()) {
|
||||
verifyTCTestLoggerOutput()
|
||||
} else {
|
||||
verifyPlainTest()
|
||||
verifyPlainOutput()
|
||||
}
|
||||
}
|
||||
|
||||
private fun verifyTestWithGTestRunner() {
|
||||
val testReport = parseGTestReport(runResult.output.stdOut)
|
||||
private fun verifyTCTestLoggerOutput() {
|
||||
val testReport = parseTCTestReport(runResult.output.stdOut)
|
||||
|
||||
verifyExpectation(!testReport.isEmpty()) { "No tests have been executed." }
|
||||
|
||||
val passedTests = testReport.getPassedTests()
|
||||
verifyExpectation(passedTests.isNotEmpty()) { "No passed tests." }
|
||||
verifyExpectation(!testReport.isEmpty()) { "No tests have been found." }
|
||||
|
||||
testRun.runParameters.get<TestRunParameter.WithFilter> {
|
||||
val excessiveTests = passedTests.filter { testName -> !testMatches(testName) }
|
||||
verifyExpectation(excessiveTests.isEmpty()) { "Excessive tests have been executed: $excessiveTests." }
|
||||
verifyNoSuchTests(
|
||||
testReport.passedTests.filter { testName -> !testMatches(testName) },
|
||||
"Excessive tests have been executed"
|
||||
)
|
||||
|
||||
verifyNoSuchTests(
|
||||
testReport.ignoredTests.filter { testName -> !testMatches(testName) },
|
||||
"Excessive tests have been ignored"
|
||||
)
|
||||
}
|
||||
|
||||
val failedTests = testReport.getFailedTests().size
|
||||
verifyExpectation(0, failedTests) { "There are $failedTests failed tests." }
|
||||
verifyNoSuchTests(testReport.failedTests, "There are failed tests")
|
||||
|
||||
verifyOutputData(mergedOutput = testReport.cleanStdOut + runResult.output.stdErr)
|
||||
assumeFalse(testReport.ignoredTests.isNotEmpty() && testReport.passedTests.isEmpty(), "Test case is disabled")
|
||||
|
||||
verifyNonTestOutput { testReport.nonTestOutput + runResult.output.stdErr }
|
||||
}
|
||||
|
||||
private fun verifyPlainTest() = verifyOutputData(mergedOutput = runResult.output.stdOut + runResult.output.stdErr)
|
||||
private fun verifyNoSuchTests(tests: Collection<TestName>, subject: String) = verifyExpectation(tests.isEmpty()) {
|
||||
buildString {
|
||||
append(subject).appendLine(":")
|
||||
tests.forEach { append(" - ").appendLine(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun verifyOutputData(mergedOutput: String) {
|
||||
private fun verifyPlainOutput() = verifyNonTestOutput { runResult.output.stdOut + runResult.output.stdErr }
|
||||
|
||||
private fun verifyNonTestOutput(nonTestOutput: () -> String) {
|
||||
testRun.runParameters.get<TestRunParameter.WithExpectedOutputData> {
|
||||
verifyExpectation(convertLineSeparators(expectedOutputDataFile.readText()), convertLineSeparators(mergedOutput)) {
|
||||
verifyExpectation(convertLineSeparators(expectedOutputDataFile.readText()), convertLineSeparators(nonTestOutput())) {
|
||||
"Tested process output mismatch. See \"TEST STDOUT\" and \"EXPECTED OUTPUT DATA FILE\" below."
|
||||
}
|
||||
}
|
||||
|
||||
-146
@@ -1,146 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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.util
|
||||
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.PackageFQN
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.TestFunction
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.GTestListingParseState.*
|
||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
|
||||
|
||||
internal typealias TestName = String
|
||||
internal typealias TestStatus = String
|
||||
|
||||
internal class GTestReport(
|
||||
private val testStatuses: Map<TestStatus, Collection<TestName>>,
|
||||
val cleanStdOut: String
|
||||
) {
|
||||
fun getPassedTests(): Collection<TestName> = testStatuses[STATUS_OK].orEmpty()
|
||||
fun getFailedTests(): Collection<TestName> = (testStatuses - STATUS_OK).flatMap { it.value }
|
||||
|
||||
fun isEmpty(): Boolean = testStatuses.isEmpty()
|
||||
|
||||
companion object {
|
||||
private const val STATUS_OK = "OK"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses GTest reports like this:
|
||||
*
|
||||
* [==========] Running 2 tests from 1 test cases.
|
||||
* [----------] Global test environment set-up.
|
||||
* [----------] 2 tests from sample.test.SampleTestKt
|
||||
* [ RUN ] sample.test.SampleTestKt.one
|
||||
* [ OK ] sample.test.SampleTestKt.one (0 ms)
|
||||
* [ RUN ] sample.test.SampleTestKt.two
|
||||
* [ OK ] sample.test.SampleTestKt.two (0 ms)
|
||||
* [----------] 2 tests from sample.test.SampleTestKt (0 ms total)
|
||||
*
|
||||
* [----------] Global test environment tear-down
|
||||
* [==========] 2 tests from 1 test cases ran. (0 ms total)
|
||||
* [ PASSED ] 2 tests.
|
||||
*/
|
||||
internal fun parseGTestReport(stdOut: String): GTestReport {
|
||||
val testStatuses = hashMapOf<TestStatus, MutableSet<TestName>>()
|
||||
val cleanStdOut = StringBuilder()
|
||||
|
||||
var expectStatusLine = false
|
||||
stdOut.lineSequence().forEachIndexed { index, line ->
|
||||
when {
|
||||
index == 0 && line.startsWith(STDLIB_TESTS_IGNORED_LINE_PREFIX) -> Unit
|
||||
expectStatusLine -> {
|
||||
val matcher = STATUS_LINE_REGEX.matchEntire(line)
|
||||
if (matcher != null) {
|
||||
// Read the line with test status.
|
||||
val testStatus = matcher.groupValues[1]
|
||||
val testName = matcher.groupValues[2]
|
||||
testStatuses.getOrPut(testStatus) { hashSetOf() } += testName
|
||||
expectStatusLine = false
|
||||
} else {
|
||||
// If current line is not a status line then it could be only the line with the process' output.
|
||||
cleanStdOut.appendLine(line)
|
||||
}
|
||||
}
|
||||
line.matches(RUN_LINE_REGEX) -> {
|
||||
expectStatusLine = true // Next line contains either test status.
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
return GTestReport(testStatuses, cleanStdOut.toString())
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts [TestFunction]s from GTest listing.
|
||||
*
|
||||
* Example:
|
||||
* sample.test.SampleTestKt.
|
||||
* one
|
||||
* two
|
||||
*
|
||||
* yields TestFunction(sample.test, one) and TestFunction(sample.test, two).
|
||||
*/
|
||||
internal fun parseGTestListing(rawGTestListing: String): Collection<TestFunction> = buildList {
|
||||
var state: GTestListingParseState = Begin
|
||||
|
||||
rawGTestListing.lineSequence().forEachIndexed { index, line ->
|
||||
fun parseError(message: String): Nothing = fail {
|
||||
buildString {
|
||||
appendLine("$message at line #$index: \"$line\"")
|
||||
appendLine()
|
||||
appendLine("Full listing:")
|
||||
appendLine(rawGTestListing)
|
||||
}
|
||||
}
|
||||
|
||||
state = when {
|
||||
index == 0 && line.startsWith(STDLIB_TESTS_IGNORED_LINE_PREFIX) -> state
|
||||
line.isBlank() -> when (state) {
|
||||
is NewTest, is End -> End
|
||||
else -> parseError("Unexpected empty line")
|
||||
}
|
||||
line[0].isWhitespace() -> when (val s = state) {
|
||||
is HasPackageName -> {
|
||||
this += TestFunction(s.packageName, line.trim())
|
||||
NewTest(s.packageName)
|
||||
}
|
||||
else -> parseError("Test name encountered before test suite name")
|
||||
}
|
||||
else -> when (state) {
|
||||
is Begin, is NewTest -> {
|
||||
val packageParts = line.trimEnd().removeSuffix(".").split('.')
|
||||
if (packageParts.isEmpty()) parseError("Malformed test suite name")
|
||||
|
||||
// Drop the last part because it is related to class name (or file-class name).
|
||||
// TODO: How to handle nested classes?
|
||||
val packageName = packageParts.dropLast(1).joinToString(".")
|
||||
|
||||
NewTestSuite(packageName)
|
||||
}
|
||||
else -> parseError("Unexpected test suite name")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val RUN_LINE_REGEX = Regex("""^\[\s+RUN\s+]\s+.*""")
|
||||
private val STATUS_LINE_REGEX = Regex("""^\[\s+([A-Z]+)\s+]\s+(\S+)\s+.*""")
|
||||
|
||||
// The very first line of stdlib test output may contain seed of Random. Such line should be ignored.
|
||||
private const val STDLIB_TESTS_IGNORED_LINE_PREFIX = "Seed: "
|
||||
|
||||
private sealed interface GTestListingParseState {
|
||||
object Begin : GTestListingParseState
|
||||
object End : GTestListingParseState
|
||||
|
||||
interface HasPackageName : GTestListingParseState {
|
||||
val packageName: PackageFQN
|
||||
}
|
||||
|
||||
class NewTestSuite(override val packageName: PackageFQN) : HasPackageName
|
||||
class NewTest(override val packageName: PackageFQN) : HasPackageName
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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.util
|
||||
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.PackageFQN
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.TestFunction
|
||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.GTestListingParseState as State
|
||||
|
||||
/**
|
||||
* Extracts [TestFunction]s from GTest listing.
|
||||
*
|
||||
* Example:
|
||||
* sample.test.SampleTestKt.
|
||||
* one
|
||||
* two
|
||||
*
|
||||
* yields TestFunction(sample.test, one) and TestFunction(sample.test, two).
|
||||
*/
|
||||
internal fun parseGTestListing(rawGTestListing: String): Collection<TestFunction> = buildList {
|
||||
var state: State = State.Begin
|
||||
|
||||
rawGTestListing.lineSequence().forEachIndexed { index, line ->
|
||||
fun parseError(message: String): Nothing = fail {
|
||||
buildString {
|
||||
appendLine("$message at line #$index: \"$line\"")
|
||||
appendLine()
|
||||
appendLine("Full listing:")
|
||||
appendLine(rawGTestListing)
|
||||
}
|
||||
}
|
||||
|
||||
state = when {
|
||||
index == 0 && line.startsWith(STDLIB_TESTS_IGNORED_LINE_PREFIX) -> state
|
||||
line.isBlank() -> when (state) {
|
||||
is State.NewTest, is State.End -> State.End
|
||||
else -> parseError("Unexpected empty line")
|
||||
}
|
||||
line[0].isWhitespace() -> when (val s = state) {
|
||||
is State.HasPackageName -> {
|
||||
this += TestFunction(s.packageName, line.trim())
|
||||
State.NewTest(s.packageName)
|
||||
}
|
||||
else -> parseError("Test name encountered before test suite name")
|
||||
}
|
||||
else -> when (state) {
|
||||
is State.Begin, is State.NewTest -> {
|
||||
val packageParts = line.trimEnd().removeSuffix(".").split('.')
|
||||
if (packageParts.isEmpty()) parseError("Malformed test suite name")
|
||||
|
||||
// Drop the last part because it is related to class name (or file-class name).
|
||||
// TODO: How to handle nested classes?
|
||||
val packageName = packageParts.dropLast(1).joinToString(".")
|
||||
|
||||
State.NewTestSuite(packageName)
|
||||
}
|
||||
else -> parseError("Unexpected test suite name")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed interface GTestListingParseState {
|
||||
object Begin : State
|
||||
object End : State
|
||||
class NewTestSuite(override val packageName: PackageFQN) : State, HasPackageName
|
||||
class NewTest(override val packageName: PackageFQN) : State, HasPackageName
|
||||
|
||||
interface HasPackageName {
|
||||
val packageName: PackageFQN
|
||||
}
|
||||
}
|
||||
|
||||
// The very first line of stdlib test output may contain seed of Random. Such line should be ignored.
|
||||
private const val STDLIB_TESTS_IGNORED_LINE_PREFIX = "Seed: "
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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.util
|
||||
|
||||
import jetbrains.buildServer.messages.serviceMessages.*
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.FunctionName
|
||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertTrue
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.TCTestReportParseState as State
|
||||
import java.text.ParseException
|
||||
|
||||
internal typealias TestName = String
|
||||
|
||||
internal class TestReport(
|
||||
val passedTests: Collection<TestName>,
|
||||
val failedTests: Collection<TestName>,
|
||||
val ignoredTests: Collection<TestName>,
|
||||
val nonTestOutput: String
|
||||
) {
|
||||
fun isEmpty(): Boolean = passedTests.isEmpty() && failedTests.isEmpty() && ignoredTests.isEmpty()
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses TC test reports like this:
|
||||
*
|
||||
* ##teamcity[testSuiteStarted name='sample.test.SampleTestKt' locationHint='ktest:suite://sample.test.SampleTestKt']
|
||||
* ##teamcity[testStarted name='one' locationHint='ktest:test://sample.test.SampleTestKt.one']
|
||||
* ##teamcity[testFinished name='one' duration='0']
|
||||
* ##teamcity[testIgnored name='two']
|
||||
* ##teamcity[testSuiteFinished name='sample.test.SampleTestKt']
|
||||
*/
|
||||
internal fun parseTCTestReport(rawTCTestOutput: String): TestReport {
|
||||
val callback = TCTestMessageParserCallback()
|
||||
ServiceMessagesParser().parse(rawTCTestOutput, callback)
|
||||
|
||||
assertTrue(callback.errors.isEmpty()) {
|
||||
buildString {
|
||||
appendLine("Failed to parse TC test output.")
|
||||
callback.errors.forEach { error ->
|
||||
appendLine()
|
||||
append("Error: ").appendLine(error)
|
||||
}
|
||||
appendLine()
|
||||
appendLine("Test output:")
|
||||
append(rawTCTestOutput)
|
||||
}
|
||||
}
|
||||
|
||||
return callback.getReport()
|
||||
}
|
||||
|
||||
private class TCTestMessageParserCallback : ServiceMessageParserCallback {
|
||||
private var afterMessage = false
|
||||
private var state: State = State.Begin
|
||||
|
||||
val passedTests = mutableListOf<TestName>()
|
||||
val failedTests = mutableListOf<TestName>()
|
||||
val ignoredTests = mutableListOf<TestName>()
|
||||
|
||||
val nonTestOutput = StringBuilder()
|
||||
val errors = mutableListOf<String>()
|
||||
|
||||
override fun regularText(text: String) {
|
||||
val actualText = if (afterMessage) {
|
||||
if (text.startsWith("\r\n"))
|
||||
text.removePrefix("\r\n")
|
||||
else
|
||||
text.removePrefix("\n")
|
||||
} else
|
||||
text
|
||||
|
||||
nonTestOutput.append(actualText)
|
||||
afterMessage = false
|
||||
}
|
||||
|
||||
override fun serviceMessage(message: ServiceMessage) {
|
||||
fun unexpectedMessage(): State {
|
||||
addError {
|
||||
"""
|
||||
Unexpected TC test message: "$message"
|
||||
State: $state
|
||||
""".trimIndent()
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
state = when (message) {
|
||||
is TestSuiteStarted -> when (state) {
|
||||
is State.Begin,
|
||||
is State.TestSuiteFinished -> State.TestSuiteStarted(message.suiteName)
|
||||
else -> unexpectedMessage()
|
||||
}
|
||||
is TestSuiteFinished -> when (state) {
|
||||
is State.TestSuiteStarted,
|
||||
is State.TestIgnored,
|
||||
is State.TestFinished -> State.TestSuiteFinished
|
||||
else -> unexpectedMessage()
|
||||
}
|
||||
is TestIgnored -> when (state) {
|
||||
is State.TestSuiteStarted,
|
||||
is State.TestIgnored,
|
||||
is State.TestFinished -> State.TestIgnored(state.testSuite, message.functionName).also { ignoredTests += it.testName }
|
||||
else -> unexpectedMessage()
|
||||
}
|
||||
is TestStarted -> when (state) {
|
||||
is State.TestSuiteStarted,
|
||||
is State.TestIgnored,
|
||||
is State.TestFinished -> State.TestStarted(state.testSuite, message.testName)
|
||||
else -> unexpectedMessage()
|
||||
}
|
||||
is TestFailed -> when (val s = state) {
|
||||
is State.TestStarted -> {
|
||||
nonTestOutput.append(message.stacktrace)
|
||||
State.TestFailed(s.testSuite, message.testName).also { failedTests += it.testName }
|
||||
}
|
||||
else -> unexpectedMessage()
|
||||
}
|
||||
is TestFinished -> when (state) {
|
||||
is State.TestStarted -> State.TestFinished(state.testSuite, message.testName).also { passedTests += it.testName }
|
||||
is State.TestFailed -> State.TestFinished(state.testSuite, message.testName)
|
||||
else -> unexpectedMessage()
|
||||
}
|
||||
else -> {
|
||||
addError { "Unsupported TC test message: $message" }
|
||||
state
|
||||
}
|
||||
}
|
||||
afterMessage = true
|
||||
}
|
||||
|
||||
override fun parseException(e: ParseException, text: String) {
|
||||
errors += buildString {
|
||||
append("Failed to parse TC test message: \"").append(text).appendLine("\"")
|
||||
appendLine(e.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun addError(error: () -> String) {
|
||||
errors += error()
|
||||
}
|
||||
|
||||
fun getReport(): TestReport {
|
||||
// The last test state is "TestStarted" this likely means that the test process terminated during test execution (SIGSEGV, etc).
|
||||
state.safeAs<State.TestStarted>()?.let { failedTests += it.testName }
|
||||
|
||||
return TestReport(passedTests, failedTests, ignoredTests, nonTestOutput.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private sealed interface TCTestReportParseState {
|
||||
object Begin : State
|
||||
|
||||
class TestSuiteStarted(val testSuiteName: String) : State
|
||||
object TestSuiteFinished : State
|
||||
|
||||
sealed class TestState(val testSuite: TestSuiteStarted, val functionName: FunctionName) : State {
|
||||
val testName: TestName get() = "${testSuite.testSuiteName}.$functionName"
|
||||
}
|
||||
|
||||
class TestIgnored(testSuite: TestSuiteStarted, functionName: FunctionName) : TestState(testSuite, functionName)
|
||||
class TestStarted(testSuite: TestSuiteStarted, functionName: FunctionName) : TestState(testSuite, functionName)
|
||||
class TestFailed(testSuite: TestSuiteStarted, functionName: FunctionName) : TestState(testSuite, functionName)
|
||||
class TestFinished(testSuite: TestSuiteStarted, functionName: FunctionName) : TestState(testSuite, functionName)
|
||||
}
|
||||
|
||||
private val State.testSuite: State.TestSuiteStarted
|
||||
get() = if (this is State.TestSuiteStarted) this else cast<State.TestState>().testSuite
|
||||
|
||||
private val BaseTestMessage.functionName: FunctionName get() = testName
|
||||
Reference in New Issue
Block a user