[Native][tests] Extract GTest report verification logic

This commit is contained in:
Dmitriy Dolovov
2021-12-01 12:09:12 +03:00
parent f73ab305e6
commit 87873f9824
2 changed files with 81 additions and 41 deletions
@@ -7,6 +7,7 @@ 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.test.services.JUnit5Assertions.fail
import kotlin.time.Duration
@@ -55,46 +56,22 @@ internal class LocalTestRunner(
}
private fun verifyTestWithGTestRunner() {
val testStatuses = hashMapOf<TestStatus, MutableSet<TestName>>()
val cleanStdOut = StringBuilder()
val testReport = parseGTestReport(runResult.output.stdOut)
var expectStatusLine = false
runResult.output.stdOut.lines().forEach { line ->
when {
expectStatusLine -> {
val matcher = GTEST_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.startsWith(GTEST_RUN_LINE_PREFIX) -> {
expectStatusLine = true // Next line contains either test status.
}
else -> Unit
}
}
verifyExpectation(!testReport.isEmpty()) { "No tests have been executed." }
verifyExpectation(testStatuses.isNotEmpty()) { "No tests have been executed." }
val passedTests = testStatuses[GTEST_STATUS_OK]?.size ?: 0
verifyExpectation(passedTests > 0) { "No passed tests." }
val passedTests = testReport.getPassedTests()
verifyExpectation(passedTests.isNotEmpty()) { "No passed tests." }
testRun.runParameters.get<TestRunParameter.WithFilter> {
val excessiveTests = testStatuses.getValue(GTEST_STATUS_OK).filter { testName -> !testMatches(testName) }
val excessiveTests = passedTests.filter { testName -> !testMatches(testName) }
verifyExpectation(excessiveTests.isEmpty()) { "Excessive tests have been executed: $excessiveTests." }
}
val failedTests = (testStatuses - GTEST_STATUS_OK).values.sumOf { it.size }
verifyExpectation(0, failedTests) { "There are failed tests." }
val failedTests = testReport.getFailedTests().size
verifyExpectation(0, failedTests) { "There are $failedTests failed tests." }
verifyOutputData(mergedOutput = cleanStdOut.toString() + runResult.output.stdErr)
verifyOutputData(mergedOutput = testReport.cleanStdOut + runResult.output.stdErr)
}
private fun verifyPlainTest() = verifyOutputData(mergedOutput = runResult.output.stdOut + runResult.output.stdErr)
@@ -107,13 +84,4 @@ internal class LocalTestRunner(
}
}
}
companion object {
private const val GTEST_RUN_LINE_PREFIX = "[ RUN ]"
private val GTEST_STATUS_LINE_REGEX = Regex("^\\[\\s+([A-Z]+)\\s+]\\s+(\\S+)\\s+.*")
private const val GTEST_STATUS_OK = "OK"
}
}
private typealias TestStatus = String
private typealias TestName = String
@@ -0,0 +1,72 @@
/*
* 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
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().forEach { line ->
when {
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())
}
private val RUN_LINE_REGEX = Regex("""^\[\s+RUN\s+]\s+.*""")
private val STATUS_LINE_REGEX = Regex("""^\[\s+([A-Z]+)\s+]\s+(\S+)\s+.*""")