[Native][tests] Do not run test executable to fetch the list of test names

Use test listing produced during compilation instead.

^KT-50316
This commit is contained in:
Dmitriy Dolovov
2022-02-14 13:06:23 +03:00
parent b44c0f6789
commit ab765f6c87
5 changed files with 62 additions and 28 deletions
@@ -88,7 +88,7 @@ abstract class AbstractNativeKlibABITest : AbstractNativeSimpleTest() {
) )
val compilationResult = compilation.result.assertSuccess() // <-- trigger compilation val compilationResult = compilation.result.assertSuccess() // <-- trigger compilation
val executable = TestExecutable(executableFile, compilationResult.loggedData) val executable = TestExecutable.fromCompilationResult(testCase, compilationResult)
runExecutableAndVerify(testCase, executable) // <-- run executable and verify runExecutableAndVerify(testCase, executable) // <-- run executable and verify
} }
@@ -88,7 +88,7 @@ class InfrastructureDumpedTestListingTest : AbstractNativeSimpleTest() {
assertTrue(dumpedTestListing.isNotEmpty()) assertTrue(dumpedTestListing.isNotEmpty())
// parse test listing obtained from executable file with the help of --ktest_list_tests flag: // parse test listing obtained from executable file with the help of --ktest_list_tests flag:
val testExecutable = TestExecutable(executable.executableFile, executableCompilationResult.loggedData) val testExecutable = TestExecutable.fromCompilationResult(executableTestCase, executableCompilationResult)
val extractedTestListing = TestRunners.extractTestNames(testExecutable, testRunSettings).toSet() val extractedTestListing = TestRunners.extractTestNames(testExecutable, testRunSettings).toSet()
assertEquals(extractedTestListing, dumpedTestListing) assertEquals(extractedTestListing, dumpedTestListing)
@@ -28,9 +28,13 @@ 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 withErrorMessage(errorMessage: String): String = buildString { fun withErrorMessage(errorMessage: String, t: Throwable? = null): String = buildString {
appendLine(errorMessage) appendLine(errorMessage)
appendLine() appendLine()
if (t != null) {
appendLine(t.stackTraceToString())
appendLine()
}
appendLine(this@LoggedData) appendLine(this@LoggedData)
} }
@@ -5,19 +5,50 @@
package org.jetbrains.kotlin.konan.blackboxtest.support.runner package org.jetbrains.kotlin.konan.blackboxtest.support.runner
import org.jetbrains.kotlin.konan.blackboxtest.support.LoggedData import org.jetbrains.kotlin.konan.blackboxtest.support.*
import org.jetbrains.kotlin.konan.blackboxtest.support.PackageName import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationArtifact.Executable
import org.jetbrains.kotlin.konan.blackboxtest.support.TestCaseId import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationResult.Success
import org.jetbrains.kotlin.konan.blackboxtest.support.TestName import org.jetbrains.kotlin.konan.blackboxtest.support.util.DumpedTestListing
import org.jetbrains.kotlin.konan.blackboxtest.support.util.startsWith import org.jetbrains.kotlin.konan.blackboxtest.support.util.startsWith
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertFalse import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertFalse
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.io.File import java.io.File
import java.io.IOException
internal class TestExecutable( internal class TestExecutable(
val executableFile: File, val executableFile: File,
val loggedCompilerCall: LoggedData.CompilerCall val loggedCompilerCall: LoggedData.CompilerCall,
) val testNames: Collection<TestName>
) {
companion object {
fun fromCompilationResult(testCase: TestCase, compilationResult: Success<out Executable>): TestExecutable {
val testNames = when (testCase.kind) {
TestKind.REGULAR, TestKind.STANDALONE -> {
val testDumpFile = compilationResult.resultingArtifact.testDumpFile
val testDump = try {
testDumpFile.readText()
} catch (e: IOException) {
fail { compilationResult.loggedData.withErrorMessage("Failed to read test dump file: $testDumpFile", e) }
}
try {
DumpedTestListing.parse(testDump)
} catch (e: Exception) {
fail { compilationResult.loggedData.withErrorMessage("Failed to parse test dump file: $testDumpFile", e) }
}
}
else -> emptyList()
}
return TestExecutable(
executableFile = compilationResult.resultingArtifact.executableFile,
loggedCompilerCall = compilationResult.loggedData,
testNames = testNames
)
}
}
}
internal class TestRun( internal class TestRun(
val displayName: String, val displayName: String,
@@ -15,7 +15,6 @@ import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilati
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationFactory import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationFactory
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationResult.Companion.assertSuccess import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationResult.Companion.assertSuccess
import org.jetbrains.kotlin.konan.blackboxtest.support.group.TestCaseGroupProvider import org.jetbrains.kotlin.konan.blackboxtest.support.group.TestCaseGroupProvider
import org.jetbrains.kotlin.konan.blackboxtest.support.runner.TestRunners.extractTestNames
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.util.ThreadSafeCache import org.jetbrains.kotlin.konan.blackboxtest.support.util.ThreadSafeCache
import org.jetbrains.kotlin.konan.blackboxtest.support.util.TreeNode import org.jetbrains.kotlin.konan.blackboxtest.support.util.TreeNode
@@ -34,7 +33,6 @@ internal class TestRunProvider(
) : BaseTestRunProvider(), ExtensionContext.Store.CloseableResource { ) : BaseTestRunProvider(), ExtensionContext.Store.CloseableResource {
private val compilationFactory = TestCompilationFactory() private val compilationFactory = TestCompilationFactory()
private val cachedCompilations = ThreadSafeCache<TestCompilationCacheKey, TestCompilation<Executable>>() private val cachedCompilations = ThreadSafeCache<TestCompilationCacheKey, TestCompilation<Executable>>()
private val cachedTestNames = ThreadSafeCache<TestCompilationCacheKey, Collection<TestName>>()
/** /**
* Produces a single [TestRun] per [TestCase]. So-called "one test case/one test run" mode. * Produces a single [TestRun] per [TestCase]. So-called "one test case/one test run" mode.
@@ -64,7 +62,7 @@ internal class TestRunProvider(
fun getSingleTestRun( fun getSingleTestRun(
testCaseId: TestCaseId, testCaseId: TestCaseId,
settings: Settings settings: Settings
): TestRun = withTestExecutable(testCaseId, settings) { testCase, executable, _ -> ): TestRun = withTestExecutable(testCaseId, settings) { testCase, executable ->
createSingleTestRun(testCase, executable) createSingleTestRun(testCase, executable)
} }
@@ -101,7 +99,7 @@ internal class TestRunProvider(
fun getTestRuns( fun getTestRuns(
testCaseId: TestCaseId, testCaseId: TestCaseId,
settings: Settings settings: Settings
): Collection<TreeNode<TestRun>> = withTestExecutable(testCaseId, settings) { testCase, executable, cacheKey -> ): Collection<TreeNode<TestRun>> = withTestExecutable(testCaseId, settings) { testCase, executable ->
fun createTestRun(testRunName: String, testName: TestName?) = createTestRun(testCase, executable, testRunName, testName) fun createTestRun(testRunName: String, testName: TestName?) = createTestRun(testCase, executable, testRunName, testName)
when (testCase.kind) { when (testCase.kind) {
@@ -111,10 +109,7 @@ internal class TestRunProvider(
TreeNode.oneLevel(testRun) TreeNode.oneLevel(testRun)
} }
TestKind.REGULAR, TestKind.STANDALONE -> { TestKind.REGULAR, TestKind.STANDALONE -> {
val testNames = cachedTestNames.computeIfAbsent(cacheKey) { val testNames = executable.testNames.filterIrrelevant(testCase)
extractTestNames(executable, settings)
}.filterIrrelevant(testCase)
testNames.buildTree(TestName::packageName) { testName -> testNames.buildTree(TestName::packageName) { testName ->
createTestRun(testName.functionName, testName) createTestRun(testName.functionName, testName)
} }
@@ -125,7 +120,7 @@ internal class TestRunProvider(
private fun <T> withTestExecutable( private fun <T> withTestExecutable(
testCaseId: TestCaseId, testCaseId: TestCaseId,
settings: Settings, settings: Settings,
action: (TestCase, TestExecutable, TestCompilationCacheKey) -> T action: (TestCase, TestExecutable) -> T
): T { ): T {
val testCaseGroup = testCaseGroupProvider.getTestCaseGroup(testCaseId.testCaseGroupId, settings) val testCaseGroup = testCaseGroupProvider.getTestCaseGroup(testCaseId.testCaseGroupId, settings)
?: fail { "No test case for $testCaseId" } ?: fail { "No test case for $testCaseId" }
@@ -134,32 +129,36 @@ internal class TestRunProvider(
val testCase = testCaseGroup.getByName(testCaseId) ?: fail { "No test case for $testCaseId" } val testCase = testCaseGroup.getByName(testCaseId) ?: fail { "No test case for $testCaseId" }
val (testCompilation, cacheKey) = when (testCase.kind) { val testCompilation = when (testCase.kind) {
TestKind.STANDALONE, TestKind.STANDALONE_NO_TR -> { TestKind.STANDALONE, TestKind.STANDALONE_NO_TR -> {
// Create a separate compilation for each standalone test case. // Create a separate compilation for each standalone test case.
val cacheKey = TestCompilationCacheKey.Standalone(testCaseId) cachedCompilations.computeIfAbsent(
val testCompilation = cachedCompilations.computeIfAbsent(cacheKey) { TestCompilationCacheKey.Standalone(testCaseId)
) {
compilationFactory.testCasesToExecutable(listOf(testCase), settings) compilationFactory.testCasesToExecutable(listOf(testCase), settings)
} }
testCompilation to cacheKey
} }
TestKind.REGULAR -> { TestKind.REGULAR -> {
// Group regular test cases by compiler arguments. // Group regular test cases by compiler arguments.
val testRunnerType = testCase.extras<WithTestRunnerExtras>().runnerType val testRunnerType = testCase.extras<WithTestRunnerExtras>().runnerType
val cacheKey = TestCompilationCacheKey.Grouped(testCaseId.testCaseGroupId, testCase.freeCompilerArgs, testRunnerType) cachedCompilations.computeIfAbsent(
val testCompilation = cachedCompilations.computeIfAbsent(cacheKey) { TestCompilationCacheKey.Grouped(
testCaseGroupId = testCaseId.testCaseGroupId,
freeCompilerArgs = testCase.freeCompilerArgs,
runnerType = testRunnerType
)
) {
val testCases = testCaseGroup.getRegularOnly(testCase.freeCompilerArgs, testRunnerType) val testCases = testCaseGroup.getRegularOnly(testCase.freeCompilerArgs, testRunnerType)
assertTrue(testCases.isNotEmpty()) assertTrue(testCases.isNotEmpty())
compilationFactory.testCasesToExecutable(testCases, settings) compilationFactory.testCasesToExecutable(testCases, settings)
} }
testCompilation to cacheKey
} }
} }
val (artifact, loggedCompilerCall) = testCompilation.result.assertSuccess() // <-- Compilation happens here. val compilationResult = testCompilation.result.assertSuccess() // <-- Compilation happens here.
val executable = TestExecutable(artifact.executableFile, loggedCompilerCall) val executable = TestExecutable.fromCompilationResult(testCase, compilationResult)
return action(testCase, executable, cacheKey) return action(testCase, executable)
} }
private fun Collection<TestName>.filterIrrelevant(testCase: TestCase) = private fun Collection<TestName>.filterIrrelevant(testCase: TestCase) =