[Native][tests] Add a directive to specify test runner type

This commit is contained in:
Dmitriy Dolovov
2021-12-06 19:13:50 +03:00
parent 580328ece7
commit 3e54bb06ee
13 changed files with 202 additions and 16 deletions
@@ -0,0 +1,23 @@
// TEST_RUNNER: DEFAULT
import kotlin.test.*
@Test
fun addition() {
assertEquals(42, 40 + 2)
}
@Test
fun multiplication () {
assertEquals(42, 21 * 2)
}
@Test
fun subtraction () {
assertEquals(42, 50 - 8)
}
@Test
fun division () {
assertEquals(42, 126 / 3)
}
@@ -0,0 +1,23 @@
// TEST_RUNNER: NO_EXIT
import kotlin.test.*
@Test
fun addition() {
assertEquals(42, 40 + 2)
}
@Test
fun multiplication () {
assertEquals(42, 21 * 2)
}
@Test
fun subtraction () {
assertEquals(42, 50 - 8)
}
@Test
fun division () {
assertEquals(42, 126 / 3)
}
@@ -0,0 +1,23 @@
// TEST_RUNNER: WORKER
import kotlin.test.*
@Test
fun addition() {
assertEquals(42, 40 + 2)
}
@Test
fun multiplication () {
assertEquals(42, 21 * 2)
}
@Test
fun subtraction () {
assertEquals(42, 50 - 8)
}
@Test
fun division () {
assertEquals(42, 126 / 3)
}
@@ -0,0 +1,24 @@
// KIND: STANDALONE
// TEST_RUNNER: DEFAULT
import kotlin.test.*
@Test
fun addition() {
assertEquals(42, 40 + 2)
}
@Test
fun multiplication () {
assertEquals(42, 21 * 2)
}
@Test
fun subtraction () {
assertEquals(42, 50 - 8)
}
@Test
fun division () {
assertEquals(42, 126 / 3)
}
@@ -0,0 +1,24 @@
// KIND: STANDALONE
// TEST_RUNNER: NO_EXIT
import kotlin.test.*
@Test
fun addition() {
assertEquals(42, 40 + 2)
}
@Test
fun multiplication () {
assertEquals(42, 21 * 2)
}
@Test
fun subtraction () {
assertEquals(42, 50 - 8)
}
@Test
fun division () {
assertEquals(42, 126 / 3)
}
@@ -0,0 +1,24 @@
// KIND: STANDALONE
// TEST_RUNNER: WORKER
import kotlin.test.*
@Test
fun addition() {
assertEquals(42, 40 + 2)
}
@Test
fun multiplication () {
assertEquals(42, 21 * 2)
}
@Test
fun subtraction () {
assertEquals(42, 50 - 8)
}
@Test
fun division () {
assertEquals(42, 126 / 3)
}
@@ -7,6 +7,7 @@
package org.jetbrains.kotlin.konan.blackboxtest.support package org.jetbrains.kotlin.konan.blackboxtest.support
import org.jetbrains.kotlin.konan.blackboxtest.support.TestCase.WithTestRunnerExtras
import org.jetbrains.kotlin.konan.blackboxtest.support.TestModule.Companion.allDependencies import org.jetbrains.kotlin.konan.blackboxtest.support.TestModule.Companion.allDependencies
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.TestMode import org.jetbrains.kotlin.konan.blackboxtest.support.settings.TestMode
import org.jetbrains.kotlin.konan.blackboxtest.support.util.* import org.jetbrains.kotlin.konan.blackboxtest.support.util.*
@@ -177,7 +178,7 @@ internal class TestCase(
) { ) {
sealed interface Extras sealed interface Extras
class NoTestRunnerExtras(val entryPoint: String, val inputDataFile: File?) : Extras class NoTestRunnerExtras(val entryPoint: String, val inputDataFile: File?) : Extras
object WithTestRunnerExtras : Extras class WithTestRunnerExtras(val runnerType: TestRunnerType) : Extras
init { init {
when (kind) { when (kind) {
@@ -256,7 +257,7 @@ internal interface TestCaseGroupId {
internal interface TestCaseGroup { internal interface TestCaseGroup {
fun isEnabled(testCaseId: TestCaseId): Boolean fun isEnabled(testCaseId: TestCaseId): Boolean
fun getByName(testCaseId: TestCaseId): TestCase? fun getByName(testCaseId: TestCaseId): TestCase?
fun getRegularOnlyByCompilerArgs(freeCompilerArgs: TestCompilerArgs): Collection<TestCase> fun getRegularOnly(freeCompilerArgs: TestCompilerArgs, runnerType: TestRunnerType): Collection<TestCase>
class Default( class Default(
private val disabledTestCaseIds: Set<TestCaseId>, private val disabledTestCaseIds: Set<TestCaseId>,
@@ -267,15 +268,20 @@ internal interface TestCaseGroup {
override fun isEnabled(testCaseId: TestCaseId) = testCaseId !in disabledTestCaseIds override fun isEnabled(testCaseId: TestCaseId) = testCaseId !in disabledTestCaseIds
override fun getByName(testCaseId: TestCaseId) = testCasesById[testCaseId] override fun getByName(testCaseId: TestCaseId) = testCasesById[testCaseId]
override fun getRegularOnlyByCompilerArgs(freeCompilerArgs: TestCompilerArgs) = override fun getRegularOnly(freeCompilerArgs: TestCompilerArgs, runnerType: TestRunnerType) =
testCasesById.values.filter { it.kind == TestKind.REGULAR && it.freeCompilerArgs == freeCompilerArgs } testCasesById.values.filter { testCase ->
testCase.kind == TestKind.REGULAR
&& testCase.extras<WithTestRunnerExtras>().runnerType == runnerType
&& testCase.freeCompilerArgs == freeCompilerArgs
}
} }
companion object { companion object {
val ALL_DISABLED = object : TestCaseGroup { val ALL_DISABLED = object : TestCaseGroup {
override fun isEnabled(testCaseId: TestCaseId) = false override fun isEnabled(testCaseId: TestCaseId) = false
override fun getByName(testCaseId: TestCaseId) = fail { "This function should not be called" } override fun getByName(testCaseId: TestCaseId) = unsupported()
override fun getRegularOnlyByCompilerArgs(freeCompilerArgs: TestCompilerArgs) = fail { "This function should not be called" } override fun getRegularOnly(freeCompilerArgs: TestCompilerArgs, runnerType: TestRunnerType) = unsupported()
private fun unsupported(): Nothing = fail { "This function should not be called" }
} }
} }
} }
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl
import org.jetbrains.kotlin.compilerRunner.processCompilerOutput import org.jetbrains.kotlin.compilerRunner.processCompilerOutput
import org.jetbrains.kotlin.config.Services import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.konan.blackboxtest.support.TestCase.NoTestRunnerExtras import org.jetbrains.kotlin.konan.blackboxtest.support.TestCase.NoTestRunnerExtras
import org.jetbrains.kotlin.konan.blackboxtest.support.TestCase.WithTestRunnerExtras
import org.jetbrains.kotlin.konan.blackboxtest.support.TestCompilation.Companion.resultingArtifactPath import org.jetbrains.kotlin.konan.blackboxtest.support.TestCompilation.Companion.resultingArtifactPath
import org.jetbrains.kotlin.konan.blackboxtest.support.TestCompilationResult.Companion.assertSuccess import org.jetbrains.kotlin.konan.blackboxtest.support.TestCompilationResult.Companion.assertSuccess
import org.jetbrains.kotlin.konan.blackboxtest.support.TestModule.Companion.allDependencies import org.jetbrains.kotlin.konan.blackboxtest.support.TestModule.Companion.allDependencies
@@ -41,13 +42,12 @@ internal class TestCompilationFactory(private val settings: Settings) {
cachedCompilations[cacheKey]?.let { return it } cachedCompilations[cacheKey]?.let { return it }
// Long pass. // Long pass.
val freeCompilerArgs = rootModules.first().testCase.freeCompilerArgs val freeCompilerArgs = rootModules.first().testCase.freeCompilerArgs // Should be identical inside the same test case group.
val extras = testCases.first().extras // Should be identical inside the same test case group.
val libraries = rootModules.flatMapToSet { it.allDependencies }.map { moduleToKlib(it, freeCompilerArgs) } val libraries = rootModules.flatMapToSet { it.allDependencies }.map { moduleToKlib(it, freeCompilerArgs) }
val friends = rootModules.flatMapToSet { it.allFriends }.map { moduleToKlib(it, freeCompilerArgs) } val friends = rootModules.flatMapToSet { it.allFriends }.map { moduleToKlib(it, freeCompilerArgs) }
return cachedCompilations.computeIfAbsent(cacheKey) { return cachedCompilations.computeIfAbsent(cacheKey) {
val entryPoint = testCases.singleOrNull()?.safeExtras<NoTestRunnerExtras>()?.entryPoint
TestCompilationImpl( TestCompilationImpl(
settings = settings, settings = settings,
freeCompilerArgs = freeCompilerArgs, freeCompilerArgs = freeCompilerArgs,
@@ -56,7 +56,17 @@ internal class TestCompilationFactory(private val settings: Settings) {
expectedArtifactFile = artifactFileForExecutable(rootModules), expectedArtifactFile = artifactFileForExecutable(rootModules),
specificCompilerArgs = { specificCompilerArgs = {
add("-produce", "program") add("-produce", "program")
if (entryPoint != null) add("-entry", entryPoint) else add("-generate-test-runner") when (extras) {
is NoTestRunnerExtras -> add("-entry", extras.entryPoint)
is WithTestRunnerExtras -> {
val testRunnerArg = when (extras.runnerType) {
TestRunnerType.DEFAULT -> "-generate-test-runner"
TestRunnerType.WORKER -> "-generate-worker-test-runner"
TestRunnerType.NO_EXIT -> "-generate-no-exit-test-runner"
}
add(testRunnerArg)
}
}
settings.get<GlobalSettings>().getRootCacheDirectory(debuggable = true)?.let { rootCacheDir -> settings.get<GlobalSettings>().getRootCacheDirectory(debuggable = true)?.let { rootCacheDir ->
add("-Xcache-directory=$rootCacheDir") add("-Xcache-directory=$rootCacheDir")
} }
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.konan.blackboxtest.support.TestDirectives.FREE_COMPI
import org.jetbrains.kotlin.konan.blackboxtest.support.TestDirectives.INPUT_DATA_FILE import org.jetbrains.kotlin.konan.blackboxtest.support.TestDirectives.INPUT_DATA_FILE
import org.jetbrains.kotlin.konan.blackboxtest.support.TestDirectives.KIND import org.jetbrains.kotlin.konan.blackboxtest.support.TestDirectives.KIND
import org.jetbrains.kotlin.konan.blackboxtest.support.TestDirectives.OUTPUT_DATA_FILE import org.jetbrains.kotlin.konan.blackboxtest.support.TestDirectives.OUTPUT_DATA_FILE
import org.jetbrains.kotlin.konan.blackboxtest.support.TestDirectives.TEST_RUNNER
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
import org.jetbrains.kotlin.test.directives.model.StringDirective import org.jetbrains.kotlin.test.directives.model.StringDirective
@@ -35,6 +36,14 @@ internal object TestDirectives : SimpleDirectivesContainer() {
""".trimIndent() """.trimIndent()
) )
val TEST_RUNNER by enumDirective<TestRunnerType>(
description = """
Usage: // TEST_RUNNER: [DEFAULT, WORKER, NO_EXIT]
Specify test runner type.
Note that this directive makes sense only in combination with // KIND: REGULAR or // KIND: STANDALONE
""".trimIndent()
)
val ENTRY_POINT by stringDirective( val ENTRY_POINT by stringDirective(
description = """ description = """
Specify custom program entry point. The default entry point is `main` function in the root package. Specify custom program entry point. The default entry point is `main` function in the root package.
@@ -81,6 +90,12 @@ internal enum class TestKind {
STANDALONE_NO_TR; STANDALONE_NO_TR;
} }
internal enum class TestRunnerType {
DEFAULT,
WORKER,
NO_EXIT
}
internal class TestCompilerArgs(val compilerArgs: List<String>) { internal class TestCompilerArgs(val compilerArgs: List<String>) {
private val uniqueCompilerArgs = compilerArgs.toSet() private val uniqueCompilerArgs = compilerArgs.toSet()
override fun hashCode() = uniqueCompilerArgs.hashCode() override fun hashCode() = uniqueCompilerArgs.hashCode()
@@ -115,6 +130,14 @@ internal fun parseTestKind(registeredDirectives: RegisteredDirectives, location:
return values.singleOrNull() ?: fail { "$location: Exactly one test kind expected in $KIND directive: $values" } return values.singleOrNull() ?: fail { "$location: Exactly one test kind expected in $KIND directive: $values" }
} }
internal fun parseTestRunner(registeredDirectives: RegisteredDirectives, location: Location): TestRunnerType {
if (TEST_RUNNER !in registeredDirectives)
return TestRunnerType.DEFAULT // The default one.
val values = registeredDirectives[TEST_RUNNER]
return values.singleOrNull() ?: fail { "$location: Exactly one test runner type expected in $TEST_RUNNER directive: $values" }
}
internal fun parseEntryPoint(registeredDirectives: RegisteredDirectives, location: Location): String { internal fun parseEntryPoint(registeredDirectives: RegisteredDirectives, location: Location): String {
if (ENTRY_POINT !in registeredDirectives) if (ENTRY_POINT !in registeredDirectives)
return "main" // The default one. return "main" // The default one.
@@ -9,6 +9,7 @@ package org.jetbrains.kotlin.konan.blackboxtest.support
import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Disposer
import org.jetbrains.kotlin.konan.blackboxtest.support.TestCase.NoTestRunnerExtras import org.jetbrains.kotlin.konan.blackboxtest.support.TestCase.NoTestRunnerExtras
import org.jetbrains.kotlin.konan.blackboxtest.support.TestCase.WithTestRunnerExtras
import org.jetbrains.kotlin.konan.blackboxtest.support.TestCompilationResult.Companion.assertSuccess import org.jetbrains.kotlin.konan.blackboxtest.support.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.AbstractRunner import org.jetbrains.kotlin.konan.blackboxtest.support.runner.AbstractRunner
@@ -143,9 +144,10 @@ internal class TestRunProvider(
} }
TestKind.REGULAR -> { TestKind.REGULAR -> {
// Group regular test cases by compiler arguments. // Group regular test cases by compiler arguments.
val cacheKey = TestCompilationCacheKey.Grouped(testCaseId.testCaseGroupId, testCase.freeCompilerArgs) val testRunnerType = testCase.extras<WithTestRunnerExtras>().runnerType
val cacheKey = TestCompilationCacheKey.Grouped(testCaseId.testCaseGroupId, testCase.freeCompilerArgs, testRunnerType)
val testCompilation = cachedCompilations.computeIfAbsent(cacheKey) { val testCompilation = cachedCompilations.computeIfAbsent(cacheKey) {
val testCases = testCaseGroup.getRegularOnlyByCompilerArgs(testCase.freeCompilerArgs) val testCases = testCaseGroup.getRegularOnly(testCase.freeCompilerArgs, testRunnerType)
assertTrue(testCases.isNotEmpty()) assertTrue(testCases.isNotEmpty())
compilationFactory.testCasesToExecutable(testCases) compilationFactory.testCasesToExecutable(testCases)
} }
@@ -217,6 +219,10 @@ internal class TestRunProvider(
private sealed class TestCompilationCacheKey { private sealed class TestCompilationCacheKey {
data class Standalone(val testCaseId: TestCaseId) : TestCompilationCacheKey() data class Standalone(val testCaseId: TestCaseId) : TestCompilationCacheKey()
data class Grouped(val testCaseGroupId: TestCaseGroupId, val freeCompilerArgs: TestCompilerArgs) : TestCompilationCacheKey() data class Grouped(
val testCaseGroupId: TestCaseGroupId,
val freeCompilerArgs: TestCompilerArgs,
val runnerType: TestRunnerType
) : TestCompilationCacheKey()
} }
} }
@@ -594,7 +594,7 @@ private class ExtTestDataFile(
freeCompilerArgs = assembleFreeCompilerArgs(), freeCompilerArgs = assembleFreeCompilerArgs(),
nominalPackageName = testDataFileSettings.nominalPackageName, nominalPackageName = testDataFileSettings.nominalPackageName,
expectedOutputDataFile = null, expectedOutputDataFile = null,
extras = WithTestRunnerExtras extras = WithTestRunnerExtras(runnerType = TestRunnerType.DEFAULT)
) )
testCase.initialize(sharedModules::get) testCase.initialize(sharedModules::get)
@@ -58,7 +58,7 @@ internal class PredefinedTestCaseGroupProvider(private val settings: Settings, a
.parseCompilerArgs { "Failed to parse free compiler arguments for test case $testCaseId" }, .parseCompilerArgs { "Failed to parse free compiler arguments for test case $testCaseId" },
nominalPackageName = PackageName(testCaseId.uniqueName), nominalPackageName = PackageName(testCaseId.uniqueName),
expectedOutputDataFile = null, expectedOutputDataFile = null,
extras = WithTestRunnerExtras extras = WithTestRunnerExtras(runnerType = TestRunnerType.DEFAULT) // TODO: pass TR from the predefined configuration here
) )
testCase.initialize(null) testCase.initialize(null)
@@ -213,7 +213,7 @@ internal class StandardTestCaseGroupProvider(private val settings: Settings) : T
inputDataFile = parseInputDataFile(baseDir = testDataFile.parentFile, registeredDirectives, location) inputDataFile = parseInputDataFile(baseDir = testDataFile.parentFile, registeredDirectives, location)
) )
else else
WithTestRunnerExtras WithTestRunnerExtras(runnerType = parseTestRunner(registeredDirectives, location))
) )
testCase.initialize(findSharedModule = null) testCase.initialize(findSharedModule = null)