[Native][tests] Fix parallel test execution
This commit is contained in:
+1
-3
@@ -13,7 +13,6 @@ import org.jetbrains.kotlin.konan.blackboxtest.support.group.UseExtTestCaseGroup
|
|||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.group.UseStandardTestCaseGroupProvider
|
import org.jetbrains.kotlin.konan.blackboxtest.support.group.UseStandardTestCaseGroupProvider
|
||||||
import org.jetbrains.kotlin.test.TargetBackend
|
import org.jetbrains.kotlin.test.TargetBackend
|
||||||
import org.junit.jupiter.api.Tag
|
import org.junit.jupiter.api.Tag
|
||||||
import org.junit.jupiter.api.TestInstance
|
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
System.setProperty("java.awt.headless", "true")
|
System.setProperty("java.awt.headless", "true")
|
||||||
@@ -23,7 +22,7 @@ fun main() {
|
|||||||
testGroup("native/native.tests/tests-gen", "compiler/testData") {
|
testGroup("native/native.tests/tests-gen", "compiler/testData") {
|
||||||
testClass<AbstractExternalNativeBlackBoxTest>(
|
testClass<AbstractExternalNativeBlackBoxTest>(
|
||||||
suiteTestClassName = "ExternalTestGenerated",
|
suiteTestClassName = "ExternalTestGenerated",
|
||||||
annotations = listOf(external(), provider<UseExtTestCaseGroupProvider>(), testInstancePerClass())
|
annotations = listOf(external(), provider<UseExtTestCaseGroupProvider>())
|
||||||
) {
|
) {
|
||||||
model("codegen/box", targetBackend = TargetBackend.NATIVE)
|
model("codegen/box", targetBackend = TargetBackend.NATIVE)
|
||||||
model("codegen/boxInline", targetBackend = TargetBackend.NATIVE)
|
model("codegen/boxInline", targetBackend = TargetBackend.NATIVE)
|
||||||
@@ -47,4 +46,3 @@ private inline fun <reified T : Annotation> provider() = annotation(T::class.jav
|
|||||||
|
|
||||||
private fun external() = annotation(Tag::class.java, "external")
|
private fun external() = annotation(Tag::class.java, "external")
|
||||||
private fun infrastructure() = annotation(Tag::class.java, "infrastructure")
|
private fun infrastructure() = annotation(Tag::class.java, "infrastructure")
|
||||||
private fun testInstancePerClass() = annotation(TestInstance::class.java, TestInstance.Lifecycle.PER_CLASS)
|
|
||||||
|
|||||||
+1
-3
@@ -6,15 +6,13 @@
|
|||||||
package org.jetbrains.kotlin.konan.blackboxtest
|
package org.jetbrains.kotlin.konan.blackboxtest
|
||||||
|
|
||||||
import com.intellij.testFramework.TestDataFile
|
import com.intellij.testFramework.TestDataFile
|
||||||
|
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.ExternalSourceTransformersProvider
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.ExternalSourceTransformer
|
import org.jetbrains.kotlin.konan.blackboxtest.support.util.ExternalSourceTransformer
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.ExternalSourceTransformers
|
import org.jetbrains.kotlin.konan.blackboxtest.support.util.ExternalSourceTransformers
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.ExternalSourceTransformersProvider
|
|
||||||
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.getAbsoluteFile
|
import org.jetbrains.kotlin.konan.blackboxtest.support.util.getAbsoluteFile
|
||||||
import org.junit.jupiter.api.TestInstance
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS) // Create only one instance of every test class for consistent caching of source transformers.
|
|
||||||
abstract class AbstractExternalNativeBlackBoxTest : ExternalSourceTransformersProvider, AbstractNativeBlackBoxTest() {
|
abstract class AbstractExternalNativeBlackBoxTest : ExternalSourceTransformersProvider, AbstractNativeBlackBoxTest() {
|
||||||
private val registeredSourceTransformers: ThreadSafeCache<File, MutableList<ExternalSourceTransformer>> = ThreadSafeCache()
|
private val registeredSourceTransformers: ThreadSafeCache<File, MutableList<ExternalSourceTransformer>> = ThreadSafeCache()
|
||||||
|
|
||||||
|
|||||||
+5
-3
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.konan.blackboxtest
|
|||||||
|
|
||||||
import com.intellij.testFramework.TestDataFile
|
import com.intellij.testFramework.TestDataFile
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.*
|
import org.jetbrains.kotlin.konan.blackboxtest.support.*
|
||||||
|
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.TestRunSettings
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.TreeNode
|
import org.jetbrains.kotlin.konan.blackboxtest.support.util.TreeNode
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.getAbsoluteFile
|
import org.jetbrains.kotlin.konan.blackboxtest.support.util.getAbsoluteFile
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.joinPackageNames
|
import org.jetbrains.kotlin.konan.blackboxtest.support.util.joinPackageNames
|
||||||
@@ -17,6 +18,7 @@ import org.junit.jupiter.api.extension.ExtendWith
|
|||||||
|
|
||||||
@ExtendWith(NativeBlackBoxTestSupport::class)
|
@ExtendWith(NativeBlackBoxTestSupport::class)
|
||||||
abstract class AbstractNativeBlackBoxTest {
|
abstract class AbstractNativeBlackBoxTest {
|
||||||
|
internal lateinit var testRunSettings: TestRunSettings
|
||||||
internal lateinit var testRunProvider: TestRunProvider
|
internal lateinit var testRunProvider: TestRunProvider
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,7 +37,7 @@ abstract class AbstractNativeBlackBoxTest {
|
|||||||
* This function should be called from a method annotated with [org.junit.jupiter.api.Test].
|
* This function should be called from a method annotated with [org.junit.jupiter.api.Test].
|
||||||
*/
|
*/
|
||||||
internal fun runTestCase(testCaseId: TestCaseId) {
|
internal fun runTestCase(testCaseId: TestCaseId) {
|
||||||
val testRun = testRunProvider.getSingleTestRun(testCaseId)
|
val testRun = testRunProvider.getSingleTestRun(testCaseId, testRunSettings)
|
||||||
performTestRun(testRun)
|
performTestRun(testRun)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +57,7 @@ abstract class AbstractNativeBlackBoxTest {
|
|||||||
* This function should be called from a method annotated with [org.junit.jupiter.api.TestFactory].
|
* This function should be called from a method annotated with [org.junit.jupiter.api.TestFactory].
|
||||||
*/
|
*/
|
||||||
internal fun dynamicTestCase(testCaseId: TestCaseId): Collection<DynamicNode> {
|
internal fun dynamicTestCase(testCaseId: TestCaseId): Collection<DynamicNode> {
|
||||||
val testRunNodes = testRunProvider.getTestRuns(testCaseId)
|
val testRunNodes = testRunProvider.getTestRuns(testCaseId, testRunSettings)
|
||||||
return buildJUnitDynamicNodes(testRunNodes)
|
return buildJUnitDynamicNodes(testRunNodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +92,7 @@ abstract class AbstractNativeBlackBoxTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun performTestRun(testRun: TestRun) {
|
private fun performTestRun(testRun: TestRun) {
|
||||||
val testRunner = testRunProvider.createRunner(testRun)
|
val testRunner = testRunProvider.createRunner(testRun, testRunSettings)
|
||||||
testRunner.run()
|
testRunner.run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+189
-73
@@ -5,11 +5,10 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.konan.blackboxtest.support
|
package org.jetbrains.kotlin.konan.blackboxtest.support
|
||||||
|
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.AbstractNativeBlackBoxTest
|
|
||||||
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.settings.*
|
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.*
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.GlobalSettings
|
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.*
|
import org.jetbrains.kotlin.konan.blackboxtest.support.util.*
|
||||||
|
import org.jetbrains.kotlin.konan.target.HostManager
|
||||||
import org.jetbrains.kotlin.test.TestMetadata
|
import org.jetbrains.kotlin.test.TestMetadata
|
||||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertEquals
|
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertEquals
|
||||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
|
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
|
||||||
@@ -20,9 +19,12 @@ import org.junit.jupiter.api.extension.BeforeEachCallback
|
|||||||
import org.junit.jupiter.api.extension.ExtensionContext
|
import org.junit.jupiter.api.extension.ExtensionContext
|
||||||
import org.junit.jupiter.api.extension.TestInstancePostProcessor
|
import org.junit.jupiter.api.extension.TestInstancePostProcessor
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.net.URLClassLoader
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.KParameter
|
import kotlin.reflect.KParameter
|
||||||
import kotlin.reflect.full.findAnnotation
|
import kotlin.reflect.full.findAnnotation
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
class NativeBlackBoxTestSupport : BeforeEachCallback {
|
class NativeBlackBoxTestSupport : BeforeEachCallback {
|
||||||
/**
|
/**
|
||||||
@@ -32,81 +34,169 @@ class NativeBlackBoxTestSupport : BeforeEachCallback {
|
|||||||
* not allow accessing its parent test instance in case there are inner test classes in the generated test suite.
|
* not allow accessing its parent test instance in case there are inner test classes in the generated test suite.
|
||||||
*/
|
*/
|
||||||
override fun beforeEach(extensionContext: ExtensionContext): Unit = with(extensionContext) {
|
override fun beforeEach(extensionContext: ExtensionContext): Unit = with(extensionContext) {
|
||||||
enclosingTestInstance.testRunProvider = getOrCreateTestRunProvider()
|
val settings = createTestRunSettings()
|
||||||
|
|
||||||
// Set the essential compiler property.
|
// Set the essential compiler property.
|
||||||
System.setProperty("kotlin.native.home", getOrCreateGlobalSettings().kotlinNativeHome.path)
|
System.setProperty("kotlin.native.home", settings.get<KotlinNativeHome>().path)
|
||||||
|
|
||||||
|
// Inject the required properties to test instance.
|
||||||
|
with(settings.get<TestInstances>().enclosingTestInstance) {
|
||||||
|
testRunSettings = settings
|
||||||
|
testRunProvider = getOrCreateTestRunProvider()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val NAMESPACE = ExtensionContext.Namespace.create(NativeBlackBoxTestSupport::class.java.simpleName)
|
|
||||||
|
|
||||||
/** Creates a single instance of [TestRunProvider] per test class. */
|
/*************** Test process settings ***************/
|
||||||
private fun ExtensionContext.getOrCreateTestRunProvider(): TestRunProvider {
|
|
||||||
val enclosingTestClass = enclosingTestClass
|
|
||||||
|
|
||||||
return root.getStore(NAMESPACE).getOrComputeIfAbsent(enclosingTestClass.sanitizedName) {
|
private fun ExtensionContext.getOrCreateTestProcessSettings(): TestProcessSettings =
|
||||||
val computedTestSettings = computeTestSettings(enclosingTestClass)
|
root.getStore(NAMESPACE).getOrComputeIfAbsent(TestProcessSettings::class.java.name) {
|
||||||
val requiredSettings: Set<KClass<*>> = buildSet {
|
TestProcessSettings(
|
||||||
// Always required:
|
computeNativeTargets(),
|
||||||
this += GlobalSettings::class
|
computeNativeHome(),
|
||||||
this += Binaries::class
|
computeNativeClassLoader(),
|
||||||
|
computeTestMode(),
|
||||||
// Custom:
|
CacheKind::class to computeCacheKind(),
|
||||||
addAll(computedTestSettings.testSettings.requiredSettings)
|
computeBaseDirs(),
|
||||||
}
|
computeTimeouts()
|
||||||
|
)
|
||||||
val globalSettings = getOrCreateGlobalSettings()
|
|
||||||
|
|
||||||
val settings = Settings(requiredSettings.map { clazz ->
|
|
||||||
when (clazz) {
|
|
||||||
GlobalSettings::class -> globalSettings
|
|
||||||
TestRoots::class -> computeTestRoots(enclosingTestClass)
|
|
||||||
GeneratedSources::class -> computeGeneratedSourceDirs(globalSettings, enclosingTestClass)
|
|
||||||
Binaries::class -> computeBinariesDirs(globalSettings, enclosingTestClass)
|
|
||||||
else -> fail { "Unknown test setting type: $clazz" }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
val testCaseGroupProvider = createTestCaseGroupProvider(settings, computedTestSettings, enclosingTestInstance)
|
|
||||||
|
|
||||||
TestRunProvider(settings, testCaseGroupProvider)
|
|
||||||
}.cast()
|
}.cast()
|
||||||
|
|
||||||
|
private fun computeNativeTargets(): KotlinNativeTargets {
|
||||||
|
val hostTarget = HostManager.host
|
||||||
|
return KotlinNativeTargets(testTarget = hostTarget, hostTarget = hostTarget)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val ExtensionContext.enclosingTestInstance: AbstractNativeBlackBoxTest
|
private fun computeNativeHome(): KotlinNativeHome = KotlinNativeHome(File(requiredSystemProperty(KOTLIN_NATIVE_HOME)))
|
||||||
get() = requiredTestInstances.allInstances.firstOrNull().cast()
|
|
||||||
|
|
||||||
private val ExtensionContext.enclosingTestClass: Class<*>
|
private fun computeNativeClassLoader(): KotlinNativeClassLoader = KotlinNativeClassLoader(
|
||||||
get() = generateSequence(requiredTestClass) { it.enclosingClass }.last()
|
lazy {
|
||||||
|
val nativeClassPath = requiredSystemProperty(COMPILER_CLASSPATH)
|
||||||
|
.split(':', ';')
|
||||||
|
.map { File(it).toURI().toURL() }
|
||||||
|
.toTypedArray()
|
||||||
|
|
||||||
private fun ExtensionContext.getOrCreateGlobalSettings(): GlobalSettings =
|
URLClassLoader(nativeClassPath, /* no parent class loader */ null).apply { setDefaultAssertionStatus(true) }
|
||||||
root.getStore(NAMESPACE).getOrComputeIfAbsent(GlobalSettings::class.java.sanitizedName) {
|
}
|
||||||
// Create with the default settings.
|
)
|
||||||
GlobalSettings()
|
|
||||||
|
private fun computeTestMode(): TestMode = systemProperty(
|
||||||
|
name = TEST_MODE,
|
||||||
|
transform = { testModeName ->
|
||||||
|
TestMode.values().firstOrNull { it.name == testModeName } ?: fail {
|
||||||
|
buildString {
|
||||||
|
appendLine("Unknown test mode name $testModeName.")
|
||||||
|
appendLine("One of the following test modes should be passed through $TEST_MODE system property:")
|
||||||
|
TestMode.values().forEach { testMode ->
|
||||||
|
appendLine("- ${testMode.name}: ${testMode.description}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
default = TestMode.WITH_MODULES
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun computeCacheKind(): CacheKind {
|
||||||
|
val useCache = systemProperty(
|
||||||
|
name = USE_CACHE,
|
||||||
|
transform = { useCacheValue ->
|
||||||
|
useCacheValue.toBooleanStrictOrNull() ?: fail { "Invalid value for $USE_CACHE system property: $useCacheValue" }
|
||||||
|
},
|
||||||
|
default = true
|
||||||
|
)
|
||||||
|
|
||||||
|
return if (useCache) CacheKind.WithStaticCache else CacheKind.WithoutCache
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun computeBaseDirs(): BaseDirs = BaseDirs(File(requiredEnvironmentVariable(PROJECT_BUILD_DIR)))
|
||||||
|
|
||||||
|
private fun computeTimeouts(): Timeouts {
|
||||||
|
val executionTimeout = systemProperty(
|
||||||
|
name = EXECUTION_TIMEOUT,
|
||||||
|
transform = { executionTimeoutValue ->
|
||||||
|
executionTimeoutValue.toLongOrNull()?.milliseconds
|
||||||
|
?: fail { "Invalid value for $EXECUTION_TIMEOUT system property: $executionTimeoutValue" }
|
||||||
|
},
|
||||||
|
default = 10.seconds
|
||||||
|
)
|
||||||
|
|
||||||
|
return Timeouts(executionTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun requiredSystemProperty(name: String): String =
|
||||||
|
System.getProperty(name) ?: fail { "Unspecified $name system property" }
|
||||||
|
|
||||||
|
private fun <T> systemProperty(name: String, transform: (String) -> T, default: T): T =
|
||||||
|
System.getProperty(name)?.let(transform) ?: default
|
||||||
|
|
||||||
|
private fun requiredEnvironmentVariable(name: String): String =
|
||||||
|
System.getenv(name) ?: fail { "Unspecified $name environment variable" }
|
||||||
|
|
||||||
|
private val NAMESPACE = ExtensionContext.Namespace.create(NativeBlackBoxTestSupport::class.java.simpleName)
|
||||||
|
|
||||||
|
private const val KOTLIN_NATIVE_HOME = "kotlin.internal.native.test.nativeHome"
|
||||||
|
private const val COMPILER_CLASSPATH = "kotlin.internal.native.test.compilerClasspath"
|
||||||
|
private const val TEST_MODE = "kotlin.internal.native.test.mode"
|
||||||
|
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"
|
||||||
|
|
||||||
|
/*************** Test class settings ***************/
|
||||||
|
|
||||||
|
private fun ExtensionContext.getOrCreateTestClassSettings(): TestClassSettings =
|
||||||
|
root.getStore(NAMESPACE).getOrComputeIfAbsent(testClassKeyFor<TestClassSettings>()) {
|
||||||
|
val enclosingTestClass = enclosingTestClass
|
||||||
|
|
||||||
|
val testProcessSettings = getOrCreateTestProcessSettings()
|
||||||
|
val computedTestConfiguration = computeTestConfiguration(enclosingTestClass)
|
||||||
|
|
||||||
|
/// Put settings that are always required:
|
||||||
|
val settings = mutableListOf(
|
||||||
|
computedTestConfiguration,
|
||||||
|
computeBinariesDirs(testProcessSettings.get(), testProcessSettings.get(), enclosingTestClass)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Add custom settings:
|
||||||
|
computedTestConfiguration.configuration.requiredSettings.mapTo(settings) { clazz ->
|
||||||
|
when (clazz) {
|
||||||
|
TestRoots::class -> computeTestRoots(enclosingTestClass)
|
||||||
|
GeneratedSources::class -> computeGeneratedSourceDirs(
|
||||||
|
testProcessSettings.get(),
|
||||||
|
testProcessSettings.get(),
|
||||||
|
enclosingTestClass
|
||||||
|
)
|
||||||
|
else -> fail { "Unknown test class setting type: $clazz" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TestClassSettings(parent = testProcessSettings, settings)
|
||||||
}.cast()
|
}.cast()
|
||||||
|
|
||||||
private fun computeTestSettings(enclosingTestClass: Class<*>): ComputedTestSettings {
|
private fun computeTestConfiguration(enclosingTestClass: Class<*>): ComputedTestConfiguration {
|
||||||
val findTestSettings: Class<*>.() -> ComputedTestSettings? = {
|
val findTestConfiguration: Class<*>.() -> ComputedTestConfiguration? = {
|
||||||
annotations.asSequence().mapNotNull { annotation ->
|
annotations.asSequence().mapNotNull { annotation ->
|
||||||
val testSettings = annotation.annotationClass.findAnnotation<TestSettings>() ?: return@mapNotNull null
|
val testConfiguration = annotation.annotationClass.findAnnotation<TestConfiguration>() ?: return@mapNotNull null
|
||||||
ComputedTestSettings(testSettings, annotation)
|
ComputedTestConfiguration(testConfiguration, annotation)
|
||||||
}.firstOrNull()
|
}.firstOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
return enclosingTestClass.findTestSettings()
|
return enclosingTestClass.findTestConfiguration()
|
||||||
?: enclosingTestClass.declaredClasses.firstNotNullOfOrNull { it.findTestSettings() }
|
?: enclosingTestClass.declaredClasses.firstNotNullOfOrNull { it.findTestConfiguration() }
|
||||||
?: fail { "No @${TestSettings::class.simpleName} annotation found on test classes" }
|
?: fail { "No @${TestConfiguration::class.simpleName} annotation found on test classes" }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun computeTestRoots(enclosingTestClass: Class<*>): TestRoots {
|
private fun computeTestRoots(enclosingTestClass: Class<*>): TestRoots {
|
||||||
|
fun TestMetadata.testRoot() = getAbsoluteFile(localPath = value)
|
||||||
|
|
||||||
val testRoots: Set<File> = when (val outermostTestMetadata = enclosingTestClass.getAnnotation(TestMetadata::class.java)) {
|
val testRoots: Set<File> = when (val outermostTestMetadata = enclosingTestClass.getAnnotation(TestMetadata::class.java)) {
|
||||||
null -> {
|
null -> {
|
||||||
enclosingTestClass.declaredClasses.mapNotNullToSet { nestedClass ->
|
enclosingTestClass.declaredClasses.mapNotNullToSet { nestedClass ->
|
||||||
nestedClass.getAnnotation(TestMetadata::class.java)?.testRoot
|
nestedClass.getAnnotation(TestMetadata::class.java)?.testRoot()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> setOf(outermostTestMetadata.testRoot)
|
else -> setOf(outermostTestMetadata.testRoot())
|
||||||
}
|
}
|
||||||
|
|
||||||
val baseDir: File = when (testRoots.size) {
|
val baseDir: File = when (testRoots.size) {
|
||||||
@@ -125,10 +215,14 @@ class NativeBlackBoxTestSupport : BeforeEachCallback {
|
|||||||
return TestRoots(testRoots, baseDir)
|
return TestRoots(testRoots, baseDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun computeGeneratedSourceDirs(globalSettings: GlobalSettings, enclosingTestClass: Class<*>): GeneratedSources {
|
private fun computeGeneratedSourceDirs(
|
||||||
val testSourcesDir = globalSettings.baseBuildDir
|
baseDirs: BaseDirs,
|
||||||
|
targets: KotlinNativeTargets,
|
||||||
|
enclosingTestClass: Class<*>
|
||||||
|
): GeneratedSources {
|
||||||
|
val testSourcesDir = baseDirs.buildDir
|
||||||
.resolve("bbtest.src")
|
.resolve("bbtest.src")
|
||||||
.resolve("${globalSettings.target.compressedName}_${enclosingTestClass.compressedSimpleName}")
|
.resolve("${targets.testTarget.compressedName}_${enclosingTestClass.compressedSimpleName}")
|
||||||
.ensureExistsAndIsEmptyDirectory() // Clean-up the directory with all potentially stale generated sources.
|
.ensureExistsAndIsEmptyDirectory() // Clean-up the directory with all potentially stale generated sources.
|
||||||
|
|
||||||
val sharedSourcesDir = testSourcesDir
|
val sharedSourcesDir = testSourcesDir
|
||||||
@@ -138,10 +232,10 @@ class NativeBlackBoxTestSupport : BeforeEachCallback {
|
|||||||
return GeneratedSources(testSourcesDir, sharedSourcesDir)
|
return GeneratedSources(testSourcesDir, sharedSourcesDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun computeBinariesDirs(globalSettings: GlobalSettings, enclosingTestClass: Class<*>): Binaries {
|
private fun computeBinariesDirs(baseDirs: BaseDirs, targets: KotlinNativeTargets, enclosingTestClass: Class<*>): Binaries {
|
||||||
val testBinariesDir = globalSettings.baseBuildDir
|
val testBinariesDir = baseDirs.buildDir
|
||||||
.resolve("bbtest.bin")
|
.resolve("bbtest.bin")
|
||||||
.resolve("${globalSettings.target.compressedName}_${enclosingTestClass.compressedSimpleName}")
|
.resolve("${targets.testTarget.compressedName}_${enclosingTestClass.compressedSimpleName}")
|
||||||
.ensureExistsAndIsEmptyDirectory() // Clean-up the directory with all potentially stale artifacts.
|
.ensureExistsAndIsEmptyDirectory() // Clean-up the directory with all potentially stale artifacts.
|
||||||
|
|
||||||
val sharedBinariesDir = testBinariesDir
|
val sharedBinariesDir = testBinariesDir
|
||||||
@@ -151,26 +245,45 @@ class NativeBlackBoxTestSupport : BeforeEachCallback {
|
|||||||
return Binaries(testBinariesDir, sharedBinariesDir)
|
return Binaries(testBinariesDir, sharedBinariesDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createTestCaseGroupProvider(
|
/*************** Test run settings ***************/
|
||||||
settings: Settings,
|
|
||||||
computedTestSettings: ComputedTestSettings,
|
// Note: TestRunSettings is not cached!
|
||||||
enclosingTestInstance: AbstractNativeBlackBoxTest
|
private fun ExtensionContext.createTestRunSettings(): TestRunSettings {
|
||||||
): TestCaseGroupProvider {
|
val testInstances = computeTestInstances()
|
||||||
val (testSettings: TestSettings, testSettingsAnnotation: Annotation?) = computedTestSettings
|
|
||||||
val providerClass: KClass<out TestCaseGroupProvider> = testSettings.providerClass
|
return TestRunSettings(
|
||||||
|
parent = getOrCreateTestClassSettings(),
|
||||||
|
listOfNotNull(
|
||||||
|
testInstances,
|
||||||
|
ExternalSourceTransformersProvider::class to testInstances.enclosingTestInstance.safeAs<ExternalSourceTransformersProvider>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ExtensionContext.computeTestInstances(): TestInstances = TestInstances(requiredTestInstances.allInstances)
|
||||||
|
|
||||||
|
/*************** Test run provider ***************/
|
||||||
|
|
||||||
|
private fun ExtensionContext.getOrCreateTestRunProvider(): TestRunProvider =
|
||||||
|
root.getStore(NAMESPACE).getOrComputeIfAbsent(testClassKeyFor<TestRunProvider>()) {
|
||||||
|
val testCaseGroupProvider = createTestCaseGroupProvider(getOrCreateTestClassSettings().get())
|
||||||
|
TestRunProvider(testCaseGroupProvider)
|
||||||
|
}.cast()
|
||||||
|
|
||||||
|
private fun createTestCaseGroupProvider(computedTestConfiguration: ComputedTestConfiguration): TestCaseGroupProvider {
|
||||||
|
val (testConfiguration: TestConfiguration, testConfigurationAnnotation: Annotation) = computedTestConfiguration
|
||||||
|
val providerClass: KClass<out TestCaseGroupProvider> = testConfiguration.providerClass
|
||||||
|
|
||||||
// Assumption: For simplicity’s sake TestCaseGroupProvider has just one constructor.
|
// Assumption: For simplicity’s sake TestCaseGroupProvider has just one constructor.
|
||||||
val constructor = providerClass.constructors.singleOrNull()
|
val constructor = providerClass.constructors.singleOrNull()
|
||||||
?: fail { "No or multiple constructors found for $providerClass" }
|
?: fail { "No or multiple constructors found for $providerClass" }
|
||||||
|
|
||||||
val testSettingsAnnotationClass: KClass<out Annotation>? = testSettingsAnnotation?.annotationClass
|
val testConfigurationAnnotationClass: KClass<out Annotation> = testConfigurationAnnotation.annotationClass
|
||||||
val sourceTransformersProvider = enclosingTestInstance.safeAs<ExternalSourceTransformersProvider>()
|
|
||||||
|
|
||||||
val arguments = constructor.parameters.map { parameter ->
|
val arguments = constructor.parameters.map { parameter ->
|
||||||
when {
|
when {
|
||||||
parameter.hasTypeOf<Settings>() -> settings
|
parameter.hasTypeOf(testConfigurationAnnotationClass) -> testConfigurationAnnotation
|
||||||
sourceTransformersProvider != null && parameter.hasTypeOf<ExternalSourceTransformersProvider>() -> sourceTransformersProvider
|
// maybe add other arguments???
|
||||||
testSettingsAnnotationClass != null && parameter.hasTypeOf(testSettingsAnnotationClass) -> testSettingsAnnotation
|
|
||||||
else -> fail { "Can't provide all arguments for $constructor" }
|
else -> fail { "Can't provide all arguments for $constructor" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -178,11 +291,14 @@ class NativeBlackBoxTestSupport : BeforeEachCallback {
|
|||||||
return constructor.call(*arguments.toTypedArray()).cast()
|
return constructor.call(*arguments.toTypedArray()).cast()
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun <reified T : Any> KParameter.hasTypeOf(): Boolean = hasTypeOf(T::class)
|
|
||||||
private fun KParameter.hasTypeOf(clazz: KClass<*>): Boolean = (type.classifier as? KClass<*>)?.qualifiedName == clazz.qualifiedName
|
private fun KParameter.hasTypeOf(clazz: KClass<*>): Boolean = (type.classifier as? KClass<*>)?.qualifiedName == clazz.qualifiedName
|
||||||
|
|
||||||
private val TestMetadata.testRoot: File get() = getAbsoluteFile(localPath = value)
|
/*************** Common ***************/
|
||||||
|
|
||||||
|
private val ExtensionContext.enclosingTestClass: Class<*>
|
||||||
|
get() = generateSequence(requiredTestClass) { it.enclosingClass }.last()
|
||||||
|
|
||||||
|
private inline fun <reified T : Any> ExtensionContext.testClassKeyFor(): String =
|
||||||
|
enclosingTestClass.name + "#" + T::class.java.name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class ComputedTestSettings(val testSettings: TestSettings, val annotation: Annotation?)
|
|
||||||
|
|||||||
+34
-32
@@ -16,9 +16,7 @@ import org.jetbrains.kotlin.konan.blackboxtest.support.TestCompilation.Companion
|
|||||||
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
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.TestModule.Companion.allFriends
|
import org.jetbrains.kotlin.konan.blackboxtest.support.TestModule.Companion.allFriends
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.Binaries
|
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.*
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.GlobalSettings
|
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.Settings
|
|
||||||
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.*
|
||||||
@@ -26,7 +24,7 @@ import kotlin.time.Duration
|
|||||||
import kotlin.time.ExperimentalTime
|
import kotlin.time.ExperimentalTime
|
||||||
import kotlin.time.measureTime
|
import kotlin.time.measureTime
|
||||||
|
|
||||||
internal class TestCompilationFactory(private val settings: Settings) {
|
internal class TestCompilationFactory {
|
||||||
private val cachedCompilations = ThreadSafeCache<TestCompilationCacheKey, TestCompilation>()
|
private val cachedCompilations = ThreadSafeCache<TestCompilationCacheKey, TestCompilation>()
|
||||||
|
|
||||||
private sealed interface TestCompilationCacheKey {
|
private sealed interface TestCompilationCacheKey {
|
||||||
@@ -34,7 +32,7 @@ internal class TestCompilationFactory(private val settings: Settings) {
|
|||||||
data class Executable(val sourceModules: Set<TestModule>) : TestCompilationCacheKey
|
data class Executable(val sourceModules: Set<TestModule>) : TestCompilationCacheKey
|
||||||
}
|
}
|
||||||
|
|
||||||
fun testCasesToExecutable(testCases: Collection<TestCase>): TestCompilation {
|
fun testCasesToExecutable(testCases: Collection<TestCase>, settings: Settings): TestCompilation {
|
||||||
val rootModules = testCases.flatMapToSet { testCase -> testCase.rootModules }
|
val rootModules = testCases.flatMapToSet { testCase -> testCase.rootModules }
|
||||||
val cacheKey = TestCompilationCacheKey.Executable(rootModules)
|
val cacheKey = TestCompilationCacheKey.Executable(rootModules)
|
||||||
|
|
||||||
@@ -44,16 +42,18 @@ internal class TestCompilationFactory(private val settings: Settings) {
|
|||||||
// Long pass.
|
// Long pass.
|
||||||
val freeCompilerArgs = rootModules.first().testCase.freeCompilerArgs // Should be identical inside the same test case group.
|
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 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, settings) }
|
||||||
val friends = rootModules.flatMapToSet { it.allFriends }.map { moduleToKlib(it, freeCompilerArgs) }
|
val friends = rootModules.flatMapToSet { it.allFriends }.map { moduleToKlib(it, freeCompilerArgs, settings) }
|
||||||
|
|
||||||
return cachedCompilations.computeIfAbsent(cacheKey) {
|
return cachedCompilations.computeIfAbsent(cacheKey) {
|
||||||
TestCompilationImpl(
|
TestCompilationImpl(
|
||||||
settings = settings,
|
targets = settings.get(),
|
||||||
|
home = settings.get(),
|
||||||
|
classLoader = settings.get(),
|
||||||
freeCompilerArgs = freeCompilerArgs,
|
freeCompilerArgs = freeCompilerArgs,
|
||||||
sourceModules = rootModules,
|
sourceModules = rootModules,
|
||||||
dependencies = TestCompilationDependencies(libraries = libraries, friends = friends),
|
dependencies = TestCompilationDependencies(libraries = libraries, friends = friends),
|
||||||
expectedArtifactFile = artifactFileForExecutable(rootModules),
|
expectedArtifactFile = settings.artifactFileForExecutable(rootModules),
|
||||||
specificCompilerArgs = {
|
specificCompilerArgs = {
|
||||||
add("-produce", "program")
|
add("-produce", "program")
|
||||||
when (extras) {
|
when (extras) {
|
||||||
@@ -67,7 +67,7 @@ internal class TestCompilationFactory(private val settings: Settings) {
|
|||||||
add(testRunnerArg)
|
add(testRunnerArg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
settings.get<GlobalSettings>().getRootCacheDirectory(debuggable = true)?.let { rootCacheDir ->
|
settings.getRootCacheDirectory(debuggable = true)?.let { rootCacheDir ->
|
||||||
add("-Xcache-directory=$rootCacheDir")
|
add("-Xcache-directory=$rootCacheDir")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,7 +75,7 @@ internal class TestCompilationFactory(private val settings: Settings) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun moduleToKlib(sourceModule: TestModule, freeCompilerArgs: TestCompilerArgs): TestCompilation {
|
private fun moduleToKlib(sourceModule: TestModule, freeCompilerArgs: TestCompilerArgs, settings: Settings): TestCompilation {
|
||||||
val sourceModules = setOf(sourceModule)
|
val sourceModules = setOf(sourceModule)
|
||||||
val cacheKey = TestCompilationCacheKey.Klib(sourceModules, freeCompilerArgs)
|
val cacheKey = TestCompilationCacheKey.Klib(sourceModules, freeCompilerArgs)
|
||||||
|
|
||||||
@@ -83,35 +83,37 @@ internal class TestCompilationFactory(private val settings: Settings) {
|
|||||||
cachedCompilations[cacheKey]?.let { return it }
|
cachedCompilations[cacheKey]?.let { return it }
|
||||||
|
|
||||||
// Long pass.
|
// Long pass.
|
||||||
val libraries = sourceModule.allDependencies.map { moduleToKlib(it, freeCompilerArgs) }
|
val libraries = sourceModule.allDependencies.map { moduleToKlib(it, freeCompilerArgs, settings) }
|
||||||
val friends = sourceModule.allFriends.map { moduleToKlib(it, freeCompilerArgs) }
|
val friends = sourceModule.allFriends.map { moduleToKlib(it, freeCompilerArgs, settings) }
|
||||||
|
|
||||||
return cachedCompilations.computeIfAbsent(cacheKey) {
|
return cachedCompilations.computeIfAbsent(cacheKey) {
|
||||||
TestCompilationImpl(
|
TestCompilationImpl(
|
||||||
settings = settings,
|
targets = settings.get(),
|
||||||
|
home = settings.get(),
|
||||||
|
classLoader = settings.get(),
|
||||||
freeCompilerArgs = freeCompilerArgs,
|
freeCompilerArgs = freeCompilerArgs,
|
||||||
sourceModules = sourceModules,
|
sourceModules = sourceModules,
|
||||||
dependencies = TestCompilationDependencies(libraries = libraries, friends = friends),
|
dependencies = TestCompilationDependencies(libraries = libraries, friends = friends),
|
||||||
expectedArtifactFile = artifactFileForKlib(sourceModule, freeCompilerArgs),
|
expectedArtifactFile = settings.artifactFileForKlib(sourceModule, freeCompilerArgs),
|
||||||
specificCompilerArgs = { add("-produce", "library") }
|
specificCompilerArgs = { add("-produce", "library") }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun artifactFileForExecutable(modules: Set<TestModule.Exclusive>) = when (modules.size) {
|
private fun Settings.artifactFileForExecutable(modules: Set<TestModule.Exclusive>) = when (modules.size) {
|
||||||
1 -> artifactFileForExecutable(modules.first())
|
1 -> artifactFileForExecutable(modules.first())
|
||||||
else -> multiModuleArtifactFile(modules, settings.get<GlobalSettings>().target.family.exeSuffix)
|
else -> multiModuleArtifactFile(modules, get<KotlinNativeTargets>().testTarget.family.exeSuffix)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun artifactFileForExecutable(module: TestModule.Exclusive) =
|
private fun Settings.artifactFileForExecutable(module: TestModule.Exclusive) =
|
||||||
singleModuleArtifactFile(module, settings.get<GlobalSettings>().target.family.exeSuffix)
|
singleModuleArtifactFile(module, get<KotlinNativeTargets>().testTarget.family.exeSuffix)
|
||||||
|
|
||||||
private fun artifactFileForKlib(module: TestModule, freeCompilerArgs: TestCompilerArgs) = when (module) {
|
private fun Settings.artifactFileForKlib(module: TestModule, freeCompilerArgs: TestCompilerArgs) = when (module) {
|
||||||
is TestModule.Exclusive -> singleModuleArtifactFile(module, "klib")
|
is TestModule.Exclusive -> singleModuleArtifactFile(module, "klib")
|
||||||
is TestModule.Shared -> settings.get<Binaries>().sharedBinariesDir.resolve("${module.name}-${prettyHash(freeCompilerArgs.hashCode())}.klib")
|
is TestModule.Shared -> get<Binaries>().sharedBinariesDir.resolve("${module.name}-${prettyHash(freeCompilerArgs.hashCode())}.klib")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun singleModuleArtifactFile(module: TestModule.Exclusive, extension: String): File {
|
private fun Settings.singleModuleArtifactFile(module: TestModule.Exclusive, extension: String): File {
|
||||||
val artifactFileName = buildString {
|
val artifactFileName = buildString {
|
||||||
append(module.testCase.nominalPackageName.compressedPackageName).append('.')
|
append(module.testCase.nominalPackageName.compressedPackageName).append('.')
|
||||||
if (extension == "klib") append(module.name).append('.')
|
if (extension == "klib") append(module.name).append('.')
|
||||||
@@ -120,7 +122,7 @@ internal class TestCompilationFactory(private val settings: Settings) {
|
|||||||
return artifactDirForPackageName(module.testCase.nominalPackageName).resolve(artifactFileName)
|
return artifactDirForPackageName(module.testCase.nominalPackageName).resolve(artifactFileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun multiModuleArtifactFile(modules: Collection<TestModule>, extension: String): File {
|
private fun Settings.multiModuleArtifactFile(modules: Collection<TestModule>, extension: String): File {
|
||||||
var filesCount = 0
|
var filesCount = 0
|
||||||
var hash = 0
|
var hash = 0
|
||||||
val uniquePackageNames = hashSetOf<PackageName>()
|
val uniquePackageNames = hashSetOf<PackageName>()
|
||||||
@@ -153,8 +155,8 @@ internal class TestCompilationFactory(private val settings: Settings) {
|
|||||||
return artifactDirForPackageName(commonPackageName).resolve(artifactFileName)
|
return artifactDirForPackageName(commonPackageName).resolve(artifactFileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun artifactDirForPackageName(packageName: PackageName?): File {
|
private fun Settings.artifactDirForPackageName(packageName: PackageName?): File {
|
||||||
val baseDir = settings.get<Binaries>().testBinariesDir
|
val baseDir = get<Binaries>().testBinariesDir
|
||||||
val outputDir = if (packageName != null) baseDir.resolve(packageName.compressedPackageName) else baseDir
|
val outputDir = if (packageName != null) baseDir.resolve(packageName.compressedPackageName) else baseDir
|
||||||
|
|
||||||
outputDir.mkdirs()
|
outputDir.mkdirs()
|
||||||
@@ -234,7 +236,9 @@ internal class TestCompilationDependencies(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class TestCompilationImpl(
|
private class TestCompilationImpl(
|
||||||
private val settings: Settings,
|
private val targets: KotlinNativeTargets,
|
||||||
|
private val home: KotlinNativeHome,
|
||||||
|
private val classLoader: KotlinNativeClassLoader,
|
||||||
private val freeCompilerArgs: TestCompilerArgs,
|
private val freeCompilerArgs: TestCompilerArgs,
|
||||||
private val sourceModules: Collection<TestModule>,
|
private val sourceModules: Collection<TestModule>,
|
||||||
private val dependencies: TestCompilationDependencies,
|
private val dependencies: TestCompilationDependencies,
|
||||||
@@ -254,8 +258,8 @@ private class TestCompilationImpl(
|
|||||||
add(
|
add(
|
||||||
"-enable-assertions",
|
"-enable-assertions",
|
||||||
"-g",
|
"-g",
|
||||||
"-target", settings.get<GlobalSettings>().target.name,
|
"-target", targets.testTarget.name,
|
||||||
"-repo", settings.get<GlobalSettings>().kotlinNativeHome.resolve("klib").path,
|
"-repo", home.dir.resolve("klib").path,
|
||||||
"-output", expectedArtifactFile.path,
|
"-output", expectedArtifactFile.path,
|
||||||
"-Xskip-prerelease-check",
|
"-Xskip-prerelease-check",
|
||||||
"-Xverify-ir",
|
"-Xverify-ir",
|
||||||
@@ -286,7 +290,7 @@ private class TestCompilationImpl(
|
|||||||
val (loggedCompilerCall: LoggedData, result: TestCompilationResult.ImmediateResult) = try {
|
val (loggedCompilerCall: LoggedData, result: TestCompilationResult.ImmediateResult) = try {
|
||||||
val (exitCode, compilerOutput, compilerOutputHasErrors, duration) = callCompiler(
|
val (exitCode, compilerOutput, compilerOutputHasErrors, duration) = callCompiler(
|
||||||
compilerArgs = compilerArgs,
|
compilerArgs = compilerArgs,
|
||||||
lazyKotlinNativeClassLoader = settings.get<GlobalSettings>().lazyKotlinNativeClassLoader
|
kotlinNativeClassLoader = classLoader.classLoader
|
||||||
)
|
)
|
||||||
|
|
||||||
val loggedCompilerCall =
|
val loggedCompilerCall =
|
||||||
@@ -341,14 +345,12 @@ private inline fun buildArgs(builderAction: ArgsBuilder.() -> Unit): Array<Strin
|
|||||||
return ArgsBuilder().apply(builderAction).build()
|
return ArgsBuilder().apply(builderAction).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun callCompiler(compilerArgs: Array<String>, lazyKotlinNativeClassLoader: Lazy<ClassLoader>): CompilerCallResult {
|
private fun callCompiler(compilerArgs: Array<String>, kotlinNativeClassLoader: ClassLoader): CompilerCallResult {
|
||||||
val compilerXmlOutput: ByteArrayOutputStream
|
val compilerXmlOutput: ByteArrayOutputStream
|
||||||
val exitCode: ExitCode
|
val exitCode: ExitCode
|
||||||
|
|
||||||
@OptIn(ExperimentalTime::class)
|
@OptIn(ExperimentalTime::class)
|
||||||
val duration = measureTime {
|
val duration = measureTime {
|
||||||
val kotlinNativeClassLoader by lazyKotlinNativeClassLoader
|
|
||||||
|
|
||||||
val servicesClass = Class.forName(Services::class.java.canonicalName, true, kotlinNativeClassLoader)
|
val servicesClass = Class.forName(Services::class.java.canonicalName, true, kotlinNativeClassLoader)
|
||||||
val emptyServices = servicesClass.getField("EMPTY").get(servicesClass)
|
val emptyServices = servicesClass.getField("EMPTY").get(servicesClass)
|
||||||
|
|
||||||
|
|||||||
+40
-25
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.konan.blackboxtest.support
|
package org.jetbrains.kotlin.konan.blackboxtest.support
|
||||||
|
|
||||||
|
import com.intellij.openapi.Disposable
|
||||||
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.TestCase.WithTestRunnerExtras
|
||||||
@@ -15,8 +16,9 @@ import org.jetbrains.kotlin.konan.blackboxtest.support.group.TestCaseGroupProvid
|
|||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.runner.AbstractRunner
|
import org.jetbrains.kotlin.konan.blackboxtest.support.runner.AbstractRunner
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.runner.LocalTestNameExtractor
|
import org.jetbrains.kotlin.konan.blackboxtest.support.runner.LocalTestNameExtractor
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.runner.LocalTestRunner
|
import org.jetbrains.kotlin.konan.blackboxtest.support.runner.LocalTestRunner
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.GlobalSettings
|
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.KotlinNativeTargets
|
||||||
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.settings.Timeouts
|
||||||
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
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.buildTree
|
import org.jetbrains.kotlin.konan.blackboxtest.support.util.buildTree
|
||||||
@@ -27,10 +29,9 @@ import org.junit.jupiter.api.Assumptions.assumeTrue
|
|||||||
import org.junit.jupiter.api.extension.ExtensionContext
|
import org.junit.jupiter.api.extension.ExtensionContext
|
||||||
|
|
||||||
internal class TestRunProvider(
|
internal class TestRunProvider(
|
||||||
private val settings: Settings,
|
|
||||||
private val testCaseGroupProvider: TestCaseGroupProvider
|
private val testCaseGroupProvider: TestCaseGroupProvider
|
||||||
) : ExtensionContext.Store.CloseableResource {
|
) : ExtensionContext.Store.CloseableResource {
|
||||||
private val compilationFactory = TestCompilationFactory(settings)
|
private val compilationFactory = TestCompilationFactory()
|
||||||
private val cachedCompilations = ThreadSafeCache<TestCompilationCacheKey, TestCompilation>()
|
private val cachedCompilations = ThreadSafeCache<TestCompilationCacheKey, TestCompilation>()
|
||||||
private val cachedTestNames = ThreadSafeCache<TestCompilationCacheKey, Collection<TestName>>()
|
private val cachedTestNames = ThreadSafeCache<TestCompilationCacheKey, Collection<TestName>>()
|
||||||
|
|
||||||
@@ -59,7 +60,10 @@ internal class TestRunProvider(
|
|||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
fun getSingleTestRun(testCaseId: TestCaseId): TestRun = withTestExecutable(testCaseId) { testCase, executable, _ ->
|
fun getSingleTestRun(
|
||||||
|
testCaseId: TestCaseId,
|
||||||
|
settings: Settings
|
||||||
|
): TestRun = withTestExecutable(testCaseId, settings) { testCase, executable, _ ->
|
||||||
val runParameters = getRunParameters(testCase, testName = null)
|
val runParameters = getRunParameters(testCase, testName = null)
|
||||||
TestRun(displayName = /* Unimportant. Used only in dynamic tests. */ "", executable, runParameters, testCase.id)
|
TestRun(displayName = /* Unimportant. Used only in dynamic tests. */ "", executable, runParameters, testCase.id)
|
||||||
}
|
}
|
||||||
@@ -95,8 +99,9 @@ internal class TestRunProvider(
|
|||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
fun getTestRuns(
|
fun getTestRuns(
|
||||||
testCaseId: TestCaseId
|
testCaseId: TestCaseId,
|
||||||
): Collection<TreeNode<TestRun>> = withTestExecutable(testCaseId) { testCase, executable, cacheKey ->
|
settings: Settings
|
||||||
|
): Collection<TreeNode<TestRun>> = withTestExecutable(testCaseId, settings) { testCase, executable, cacheKey ->
|
||||||
fun createTestRun(testRunName: String, testName: TestName?): TestRun {
|
fun createTestRun(testRunName: String, testName: TestName?): TestRun {
|
||||||
val runParameters = getRunParameters(testCase, testName)
|
val runParameters = getRunParameters(testCase, testName)
|
||||||
return TestRun(testRunName, executable, runParameters, testCase.id)
|
return TestRun(testRunName, executable, runParameters, testCase.id)
|
||||||
@@ -110,7 +115,7 @@ internal class TestRunProvider(
|
|||||||
}
|
}
|
||||||
TestKind.REGULAR, TestKind.STANDALONE -> {
|
TestKind.REGULAR, TestKind.STANDALONE -> {
|
||||||
val testNames = cachedTestNames.computeIfAbsent(cacheKey) {
|
val testNames = cachedTestNames.computeIfAbsent(cacheKey) {
|
||||||
extractTestNames(executable)
|
extractTestNames(executable, settings)
|
||||||
}.filterIrrelevant(testCase)
|
}.filterIrrelevant(testCase)
|
||||||
|
|
||||||
testNames.buildTree(TestName::packageName) { testName ->
|
testNames.buildTree(TestName::packageName) { testName ->
|
||||||
@@ -120,10 +125,14 @@ internal class TestRunProvider(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T> withTestExecutable(testCaseId: TestCaseId, action: (TestCase, TestExecutable, TestCompilationCacheKey) -> T): T {
|
private fun <T> withTestExecutable(
|
||||||
settings.assertNotDisposed()
|
testCaseId: TestCaseId,
|
||||||
|
settings: Settings,
|
||||||
|
action: (TestCase, TestExecutable, TestCompilationCacheKey) -> T
|
||||||
|
): T {
|
||||||
|
val testCaseGroup = testCaseGroupProvider.getTestCaseGroup(testCaseId.testCaseGroupId, settings)
|
||||||
|
?: fail { "No test case for $testCaseId" }
|
||||||
|
|
||||||
val testCaseGroup = testCaseGroupProvider.getTestCaseGroup(testCaseId.testCaseGroupId) ?: fail { "No test case for $testCaseId" }
|
|
||||||
assumeTrue(testCaseGroup.isEnabled(testCaseId), "Test case is disabled")
|
assumeTrue(testCaseGroup.isEnabled(testCaseId), "Test case is disabled")
|
||||||
|
|
||||||
val testCase = testCaseGroup.getByName(testCaseId) ?: fail { "No test case for $testCaseId" }
|
val testCase = testCaseGroup.getByName(testCaseId) ?: fail { "No test case for $testCaseId" }
|
||||||
@@ -133,7 +142,7 @@ internal class TestRunProvider(
|
|||||||
// Create a separate compilation for each standalone test case.
|
// Create a separate compilation for each standalone test case.
|
||||||
val cacheKey = TestCompilationCacheKey.Standalone(testCaseId)
|
val cacheKey = TestCompilationCacheKey.Standalone(testCaseId)
|
||||||
val testCompilation = cachedCompilations.computeIfAbsent(cacheKey) {
|
val testCompilation = cachedCompilations.computeIfAbsent(cacheKey) {
|
||||||
compilationFactory.testCasesToExecutable(listOf(testCase))
|
compilationFactory.testCasesToExecutable(listOf(testCase), settings)
|
||||||
}
|
}
|
||||||
testCompilation to cacheKey
|
testCompilation to cacheKey
|
||||||
}
|
}
|
||||||
@@ -144,7 +153,7 @@ internal class TestRunProvider(
|
|||||||
val testCompilation = cachedCompilations.computeIfAbsent(cacheKey) {
|
val testCompilation = cachedCompilations.computeIfAbsent(cacheKey) {
|
||||||
val testCases = testCaseGroup.getRegularOnly(testCase.freeCompilerArgs, testRunnerType)
|
val testCases = testCaseGroup.getRegularOnly(testCase.freeCompilerArgs, testRunnerType)
|
||||||
assertTrue(testCases.isNotEmpty())
|
assertTrue(testCases.isNotEmpty())
|
||||||
compilationFactory.testCasesToExecutable(testCases)
|
compilationFactory.testCasesToExecutable(testCases, settings)
|
||||||
}
|
}
|
||||||
testCompilation to cacheKey
|
testCompilation to cacheKey
|
||||||
}
|
}
|
||||||
@@ -179,19 +188,23 @@ internal class TestRunProvider(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Currently, only local test runner is supported.
|
// Currently, only local test runner is supported.
|
||||||
fun createRunner(testRun: TestRun): AbstractRunner<*> = with(settings.get<GlobalSettings>()) {
|
fun createRunner(testRun: TestRun, settings: Settings): AbstractRunner<*> = with(settings) {
|
||||||
if (target == hostTarget)
|
with(get<KotlinNativeTargets>()) {
|
||||||
LocalTestRunner(testRun, executionTimeout)
|
if (testTarget == hostTarget)
|
||||||
else
|
LocalTestRunner(testRun, get<Timeouts>().executionTimeout)
|
||||||
runningAtNonHostTarget()
|
else
|
||||||
|
runningAtNonHostTarget()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Currently, only local test name extractor is supported.
|
// Currently, only local test name extractor is supported.
|
||||||
private fun extractTestNames(executable: TestExecutable): Collection<TestName> = with(settings.get<GlobalSettings>()) {
|
private fun extractTestNames(executable: TestExecutable, settings: Settings): Collection<TestName> = with(settings) {
|
||||||
if (target == hostTarget)
|
with(get<KotlinNativeTargets>()) {
|
||||||
LocalTestNameExtractor(executable, executionTimeout).run()
|
if (testTarget == hostTarget)
|
||||||
else
|
LocalTestNameExtractor(executable, get<Timeouts>().executionTimeout).run()
|
||||||
runningAtNonHostTarget()
|
else
|
||||||
|
runningAtNonHostTarget()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Collection<TestName>.filterIrrelevant(testCase: TestCase) =
|
private fun Collection<TestName>.filterIrrelevant(testCase: TestCase) =
|
||||||
@@ -200,16 +213,18 @@ internal class TestRunProvider(
|
|||||||
else
|
else
|
||||||
this
|
this
|
||||||
|
|
||||||
private fun GlobalSettings.runningAtNonHostTarget(): Nothing = fail {
|
private fun KotlinNativeTargets.runningAtNonHostTarget(): Nothing = fail {
|
||||||
"""
|
"""
|
||||||
Running at non-host target is not supported yet.
|
Running at non-host target is not supported yet.
|
||||||
Compilation target: $target
|
Compilation target: $testTarget
|
||||||
Host target: $hostTarget
|
Host target: $hostTarget
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
Disposer.dispose(settings)
|
if (testCaseGroupProvider is Disposable) {
|
||||||
|
Disposer.dispose(testCaseGroupProvider)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class TestCompilationCacheKey {
|
private sealed class TestCompilationCacheKey {
|
||||||
|
|||||||
+50
-46
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
|||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.*
|
import org.jetbrains.kotlin.konan.blackboxtest.support.*
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.TestCase.WithTestRunnerExtras
|
import org.jetbrains.kotlin.konan.blackboxtest.support.TestCase.WithTestRunnerExtras
|
||||||
|
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.ExternalSourceTransformersProvider
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.GeneratedSources
|
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.GeneratedSources
|
||||||
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.settings.TestRoots
|
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.TestRoots
|
||||||
@@ -37,48 +38,51 @@ import org.jetbrains.kotlin.test.InTextDirectivesUtils.isIgnoredTarget
|
|||||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertTrue
|
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertTrue
|
||||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
|
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
|
||||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
internal class ExtTestCaseGroupProvider(
|
internal class ExtTestCaseGroupProvider : TestCaseGroupProvider, TestDisposable(parentDisposable = null) {
|
||||||
private val settings: Settings,
|
|
||||||
private val sourceTransformersProvider: ExternalSourceTransformersProvider
|
|
||||||
) : TestCaseGroupProvider, TestDisposable(parentDisposable = settings) {
|
|
||||||
private val structureFactory = ExtTestDataFileStructureFactory(parentDisposable = this)
|
private val structureFactory = ExtTestDataFileStructureFactory(parentDisposable = this)
|
||||||
private val sharedModules = ThreadSafeCache<String, TestModule.Shared?>()
|
private val sharedModules = ThreadSafeCache<String, TestModule.Shared?>()
|
||||||
|
|
||||||
private val lazyTestCaseGroups = ThreadSafeFactory<TestCaseGroupId.TestDataDir, TestCaseGroup?> { testCaseGroupId ->
|
private val cachedTestCaseGroups = ThreadSafeCache<TestCaseGroupId.TestDataDir, TestCaseGroup?>()
|
||||||
if (testCaseGroupId.dir in excludes) return@ThreadSafeFactory TestCaseGroup.ALL_DISABLED
|
|
||||||
|
|
||||||
val (excludedTestDataFiles, testDataFiles) = testCaseGroupId.dir.listFiles()
|
override fun getTestCaseGroup(testCaseGroupId: TestCaseGroupId, settings: Settings): TestCaseGroup? {
|
||||||
?.filter { file -> file.isFile && file.extension == "kt" }
|
|
||||||
?.partition { file -> file in excludes }
|
|
||||||
?: return@ThreadSafeFactory null
|
|
||||||
|
|
||||||
val disabledTestCaseIds = hashSetOf<TestCaseId>()
|
|
||||||
excludedTestDataFiles.mapTo(disabledTestCaseIds, TestCaseId::TestDataFile)
|
|
||||||
|
|
||||||
val testCases = mutableListOf<TestCase>()
|
|
||||||
|
|
||||||
testDataFiles.forEach { testDataFile ->
|
|
||||||
val extTestDataFile = ExtTestDataFile(settings, structureFactory, sourceTransformersProvider, testDataFile)
|
|
||||||
|
|
||||||
if (extTestDataFile.isRelevant)
|
|
||||||
testCases += extTestDataFile.createTestCase(
|
|
||||||
definitelyStandaloneTest = testDataFile in standalones,
|
|
||||||
sharedModules = sharedModules
|
|
||||||
)
|
|
||||||
else
|
|
||||||
disabledTestCaseIds += TestCaseId.TestDataFile(testDataFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
TestCaseGroup.Default(disabledTestCaseIds, testCases)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getTestCaseGroup(testCaseGroupId: TestCaseGroupId): TestCaseGroup? {
|
|
||||||
assertNotDisposed()
|
assertNotDisposed()
|
||||||
assertTrue(testCaseGroupId is TestCaseGroupId.TestDataDir)
|
check(testCaseGroupId is TestCaseGroupId.TestDataDir)
|
||||||
return lazyTestCaseGroups[testCaseGroupId.cast()]
|
|
||||||
|
return cachedTestCaseGroups.computeIfAbsent(testCaseGroupId) {
|
||||||
|
if (testCaseGroupId.dir in excludes) return@computeIfAbsent TestCaseGroup.ALL_DISABLED
|
||||||
|
|
||||||
|
val (excludedTestDataFiles, testDataFiles) = testCaseGroupId.dir.listFiles()
|
||||||
|
?.filter { file -> file.isFile && file.extension == "kt" }
|
||||||
|
?.partition { file -> file in excludes }
|
||||||
|
?: return@computeIfAbsent null
|
||||||
|
|
||||||
|
val disabledTestCaseIds = hashSetOf<TestCaseId>()
|
||||||
|
excludedTestDataFiles.mapTo(disabledTestCaseIds, TestCaseId::TestDataFile)
|
||||||
|
|
||||||
|
val testCases = mutableListOf<TestCase>()
|
||||||
|
|
||||||
|
testDataFiles.forEach { testDataFile ->
|
||||||
|
val extTestDataFile = ExtTestDataFile(
|
||||||
|
testDataFile = testDataFile,
|
||||||
|
structureFactory = structureFactory,
|
||||||
|
customSourceTransformers = settings.get<ExternalSourceTransformersProvider>().getSourceTransformers(testDataFile),
|
||||||
|
testRoots = settings.get(),
|
||||||
|
generatedSources = settings.get()
|
||||||
|
)
|
||||||
|
|
||||||
|
if (extTestDataFile.isRelevant)
|
||||||
|
testCases += extTestDataFile.createTestCase(
|
||||||
|
definitelyStandaloneTest = testDataFile in standalones,
|
||||||
|
sharedModules = sharedModules
|
||||||
|
)
|
||||||
|
else
|
||||||
|
disabledTestCaseIds += TestCaseId.TestDataFile(testDataFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
TestCaseGroup.Default(disabledTestCaseIds, testCases)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -122,19 +126,19 @@ internal class ExtTestCaseGroupProvider(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class ExtTestDataFile(
|
private class ExtTestDataFile(
|
||||||
private val settings: Settings,
|
private val testDataFile: File,
|
||||||
structureFactory: ExtTestDataFileStructureFactory,
|
structureFactory: ExtTestDataFileStructureFactory,
|
||||||
sourceTransformersProvider: ExternalSourceTransformersProvider,
|
customSourceTransformers: ExternalSourceTransformers?,
|
||||||
private val testDataFile: File
|
testRoots: TestRoots,
|
||||||
|
private val generatedSources: GeneratedSources
|
||||||
) {
|
) {
|
||||||
private val structure by lazy {
|
private val structure by lazy {
|
||||||
val customTransformers: ExternalSourceTransformers? = sourceTransformersProvider.getSourceTransformers(testDataFile)
|
val allSourceTransformers: ExternalSourceTransformers = if (customSourceTransformers.isNullOrEmpty())
|
||||||
val allTransformers: ExternalSourceTransformers = if (customTransformers.isNullOrEmpty())
|
|
||||||
MANDATORY_SOURCE_TRANSFORMERS
|
MANDATORY_SOURCE_TRANSFORMERS
|
||||||
else
|
else
|
||||||
MANDATORY_SOURCE_TRANSFORMERS + customTransformers
|
MANDATORY_SOURCE_TRANSFORMERS + customSourceTransformers
|
||||||
|
|
||||||
structureFactory.ExtTestDataFileStructure(testDataFile, allTransformers)
|
structureFactory.ExtTestDataFileStructure(testDataFile, allSourceTransformers)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val testDataFileSettings by lazy {
|
private val testDataFileSettings by lazy {
|
||||||
@@ -152,12 +156,12 @@ private class ExtTestDataFile(
|
|||||||
optInsForCompiler = optInsForCompiler,
|
optInsForCompiler = optInsForCompiler,
|
||||||
expectActualLinker = EXPECT_ACTUAL_LINKER_DIRECTIVE in structure.directives,
|
expectActualLinker = EXPECT_ACTUAL_LINKER_DIRECTIVE in structure.directives,
|
||||||
generatedSourcesDir = computeGeneratedSourcesDir(
|
generatedSourcesDir = computeGeneratedSourcesDir(
|
||||||
testDataBaseDir = settings.get<TestRoots>().baseDir,
|
testDataBaseDir = testRoots.baseDir,
|
||||||
testDataFile = testDataFile,
|
testDataFile = testDataFile,
|
||||||
generatedSourcesBaseDir = settings.get<GeneratedSources>().testSourcesDir
|
generatedSourcesBaseDir = generatedSources.testSourcesDir
|
||||||
),
|
),
|
||||||
nominalPackageName = computePackageName(
|
nominalPackageName = computePackageName(
|
||||||
testDataBaseDir = settings.get<TestRoots>().baseDir,
|
testDataBaseDir = testRoots.baseDir,
|
||||||
testDataFile = testDataFile
|
testDataFile = testDataFile
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -575,7 +579,7 @@ private class ExtTestDataFile(
|
|||||||
testCaseDir = testDataFileSettings.generatedSourcesDir,
|
testCaseDir = testDataFileSettings.generatedSourcesDir,
|
||||||
findOrGenerateSharedModule = { moduleName: String, generator: SharedModuleGenerator ->
|
findOrGenerateSharedModule = { moduleName: String, generator: SharedModuleGenerator ->
|
||||||
sharedModules.computeIfAbsent(moduleName) {
|
sharedModules.computeIfAbsent(moduleName) {
|
||||||
generator(settings.get<GeneratedSources>().sharedSourcesDir)
|
generator(generatedSources.sharedSourcesDir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
+38
-38
@@ -7,17 +7,16 @@ package org.jetbrains.kotlin.konan.blackboxtest.support.group
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.*
|
import org.jetbrains.kotlin.konan.blackboxtest.support.*
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.TestCase.WithTestRunnerExtras
|
import org.jetbrains.kotlin.konan.blackboxtest.support.TestCase.WithTestRunnerExtras
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.GlobalSettings
|
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.KotlinNativeHome
|
||||||
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.ThreadSafeFactory
|
import org.jetbrains.kotlin.konan.blackboxtest.support.util.ThreadSafeCache
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.expandGlobTo
|
import org.jetbrains.kotlin.konan.blackboxtest.support.util.expandGlobTo
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.getAbsoluteFile
|
import org.jetbrains.kotlin.konan.blackboxtest.support.util.getAbsoluteFile
|
||||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertTrue
|
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertTrue
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
|
||||||
import org.junit.jupiter.api.fail
|
import org.junit.jupiter.api.fail
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
internal class PredefinedTestCaseGroupProvider(private val settings: Settings, annotation: PredefinedTestCases) : TestCaseGroupProvider {
|
internal class PredefinedTestCaseGroupProvider(annotation: PredefinedTestCases) : TestCaseGroupProvider {
|
||||||
private val testCaseIdToPredefinedTestCase: Map<TestCaseId.Named, PredefinedTestCase> = buildMap {
|
private val testCaseIdToPredefinedTestCase: Map<TestCaseId.Named, PredefinedTestCase> = buildMap {
|
||||||
annotation.testCases.forEach { predefinedTestCase ->
|
annotation.testCases.forEach { predefinedTestCase ->
|
||||||
val testCaseId = TestCaseId.Named(predefinedTestCase.name)
|
val testCaseId = TestCaseId.Named(predefinedTestCase.name)
|
||||||
@@ -36,55 +35,56 @@ internal class PredefinedTestCaseGroupProvider(private val settings: Settings, a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val lazyTestCaseGroups = ThreadSafeFactory<TestCaseGroupId.Named, TestCaseGroup?> { testCaseGroupId ->
|
private val cachedTestCaseGroups = ThreadSafeCache<TestCaseGroupId.Named, TestCaseGroup?>()
|
||||||
val testCaseId = testCaseGroupIdToTestCaseId[testCaseGroupId] ?: return@ThreadSafeFactory null
|
|
||||||
val predefinedTestCase = testCaseIdToPredefinedTestCase[testCaseId] ?: return@ThreadSafeFactory null
|
|
||||||
|
|
||||||
val module = TestModule.Exclusive(
|
override fun getTestCaseGroup(testCaseGroupId: TestCaseGroupId, settings: Settings): TestCaseGroup? {
|
||||||
name = testCaseId.uniqueName,
|
check(testCaseGroupId is TestCaseGroupId.Named)
|
||||||
directDependencySymbols = emptySet(),
|
|
||||||
directFriendSymbols = emptySet()
|
|
||||||
)
|
|
||||||
|
|
||||||
predefinedTestCase.sourceLocations
|
return cachedTestCaseGroups.computeIfAbsent(testCaseGroupId) {
|
||||||
.expandGlobs { "No files found for test case $testCaseId" }
|
val testCaseId = testCaseGroupIdToTestCaseId[testCaseGroupId] ?: return@computeIfAbsent null
|
||||||
.forEach { file -> module.files += TestFile.createCommitted(file, module) }
|
val predefinedTestCase = testCaseIdToPredefinedTestCase[testCaseId] ?: return@computeIfAbsent null
|
||||||
|
|
||||||
val testCase = TestCase(
|
val module = TestModule.Exclusive(
|
||||||
id = testCaseId,
|
name = testCaseId.uniqueName,
|
||||||
kind = TestKind.STANDALONE,
|
directDependencySymbols = emptySet(),
|
||||||
modules = setOf(module),
|
directFriendSymbols = emptySet()
|
||||||
freeCompilerArgs = predefinedTestCase.freeCompilerArgs
|
)
|
||||||
.parseCompilerArgs { "Failed to parse free compiler arguments for test case $testCaseId" },
|
|
||||||
nominalPackageName = PackageName(testCaseId.uniqueName),
|
|
||||||
expectedOutputDataFile = null,
|
|
||||||
extras = WithTestRunnerExtras(runnerType = predefinedTestCase.runnerType)
|
|
||||||
)
|
|
||||||
testCase.initialize(null)
|
|
||||||
|
|
||||||
TestCaseGroup.Default(disabledTestCaseIds = emptySet(), testCases = listOf(testCase))
|
predefinedTestCase.sourceLocations
|
||||||
|
.expandGlobs(settings) { "No files found for test case $testCaseId" }
|
||||||
|
.forEach { file -> module.files += TestFile.createCommitted(file, module) }
|
||||||
|
|
||||||
|
val testCase = TestCase(
|
||||||
|
id = testCaseId,
|
||||||
|
kind = TestKind.STANDALONE,
|
||||||
|
modules = setOf(module),
|
||||||
|
freeCompilerArgs = predefinedTestCase.freeCompilerArgs
|
||||||
|
.parseCompilerArgs(settings) { "Failed to parse free compiler arguments for test case $testCaseId" },
|
||||||
|
nominalPackageName = PackageName(testCaseId.uniqueName),
|
||||||
|
expectedOutputDataFile = null,
|
||||||
|
extras = WithTestRunnerExtras(runnerType = predefinedTestCase.runnerType)
|
||||||
|
)
|
||||||
|
testCase.initialize(null)
|
||||||
|
|
||||||
|
TestCaseGroup.Default(disabledTestCaseIds = emptySet(), testCases = listOf(testCase))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTestCaseGroup(testCaseGroupId: TestCaseGroupId): TestCaseGroup? {
|
private fun Array<String>.expandGlobs(settings: Settings, noExpandedFilesErrorMessage: () -> String): Set<File> {
|
||||||
assertTrue(testCaseGroupId is TestCaseGroupId.Named)
|
|
||||||
return lazyTestCaseGroups[testCaseGroupId.cast()]
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Array<String>.expandGlobs(noExpandedFilesErrorMessage: () -> String): Set<File> {
|
|
||||||
val files = buildSet {
|
val files = buildSet {
|
||||||
this@expandGlobs.forEach { pathPattern ->
|
this@expandGlobs.forEach { pathPattern ->
|
||||||
expandGlobTo(getAbsoluteFile(substituteRealPaths(pathPattern)), this)
|
expandGlobTo(getAbsoluteFile(substituteRealPaths(pathPattern, settings)), this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertTrue(files.isNotEmpty(), noExpandedFilesErrorMessage)
|
assertTrue(files.isNotEmpty(), noExpandedFilesErrorMessage)
|
||||||
return files
|
return files
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Array<String>.parseCompilerArgs(parsingErrorMessage: () -> String): TestCompilerArgs =
|
private fun Array<String>.parseCompilerArgs(settings: Settings, parsingErrorMessage: () -> String): TestCompilerArgs =
|
||||||
if (isEmpty())
|
if (isEmpty())
|
||||||
TestCompilerArgs.EMPTY
|
TestCompilerArgs.EMPTY
|
||||||
else {
|
else {
|
||||||
val freeCompilerArgs = map { arg -> substituteRealPaths(arg) }
|
val freeCompilerArgs = map { arg -> substituteRealPaths(arg, settings) }
|
||||||
val forbiddenCompilerArgs = TestCompilerArgs.findForbiddenArgs(freeCompilerArgs)
|
val forbiddenCompilerArgs = TestCompilerArgs.findForbiddenArgs(freeCompilerArgs)
|
||||||
assertTrue(forbiddenCompilerArgs.isEmpty()) {
|
assertTrue(forbiddenCompilerArgs.isEmpty()) {
|
||||||
"""
|
"""
|
||||||
@@ -98,10 +98,10 @@ internal class PredefinedTestCaseGroupProvider(private val settings: Settings, a
|
|||||||
TestCompilerArgs(freeCompilerArgs)
|
TestCompilerArgs(freeCompilerArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun substituteRealPaths(value: String): String =
|
private fun substituteRealPaths(value: String, settings: Settings): String =
|
||||||
if ('$' in value) {
|
if ('$' in value) {
|
||||||
// N.B. Here, more substitutions can be supported in the future if it would be necessary.
|
// N.B. Here, more substitutions can be supported in the future if it would be necessary.
|
||||||
value.replace(PredefinedPaths.KOTLIN_NATIVE_DISTRIBUTION, settings.get<GlobalSettings>().kotlinNativeHome.path)
|
value.replace(PredefinedPaths.KOTLIN_NATIVE_DISTRIBUTION, settings.get<KotlinNativeHome>().path)
|
||||||
} else
|
} else
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -6,10 +6,10 @@
|
|||||||
package org.jetbrains.kotlin.konan.blackboxtest.support.group
|
package org.jetbrains.kotlin.konan.blackboxtest.support.group
|
||||||
|
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.TestRunnerType
|
import org.jetbrains.kotlin.konan.blackboxtest.support.TestRunnerType
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.TestSettings
|
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.TestConfiguration
|
||||||
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
@Target(AnnotationTarget.CLASS)
|
||||||
@TestSettings(providerClass = PredefinedTestCaseGroupProvider::class)
|
@TestConfiguration(providerClass = PredefinedTestCaseGroupProvider::class)
|
||||||
internal annotation class PredefinedTestCases(vararg val testCases: PredefinedTestCase)
|
internal annotation class PredefinedTestCases(vararg val testCases: PredefinedTestCase)
|
||||||
|
|
||||||
@Target()
|
@Target()
|
||||||
|
|||||||
+17
-19
@@ -20,33 +20,31 @@ import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertNotEquals
|
|||||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertTrue
|
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertTrue
|
||||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
|
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
|
||||||
import org.jetbrains.kotlin.test.services.impl.RegisteredDirectivesParser
|
import org.jetbrains.kotlin.test.services.impl.RegisteredDirectivesParser
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
internal class StandardTestCaseGroupProvider(private val settings: Settings) : TestCaseGroupProvider {
|
internal class StandardTestCaseGroupProvider : TestCaseGroupProvider {
|
||||||
val sourceTransformers: MutableMap<String, List<(String) -> String>> = mutableMapOf()
|
// Create and cache test cases in groups on demand.
|
||||||
|
private val cachedTestCaseGroups = ThreadSafeCache<TestCaseGroupId.TestDataDir, TestCaseGroup?>()
|
||||||
|
|
||||||
// Load test cases in groups on demand.
|
override fun getTestCaseGroup(testCaseGroupId: TestCaseGroupId, settings: Settings): TestCaseGroup? {
|
||||||
private val lazyTestCaseGroups = ThreadSafeFactory<TestCaseGroupId.TestDataDir, TestCaseGroup?> { testCaseGroupId ->
|
check(testCaseGroupId is TestCaseGroupId.TestDataDir)
|
||||||
val testDataFiles = testCaseGroupId.dir.listFiles()
|
|
||||||
?: return@ThreadSafeFactory null // `null` means that there is no such testDataDir.
|
|
||||||
|
|
||||||
val testCases = testDataFiles.mapNotNull { testDataFile ->
|
return cachedTestCaseGroups.computeIfAbsent(testCaseGroupId) {
|
||||||
if (!testDataFile.isFile || testDataFile.extension != "kt")
|
val testDataFiles = testCaseGroupId.dir.listFiles()
|
||||||
return@mapNotNull null
|
?: return@computeIfAbsent null // `null` means that there is no such testDataDir.
|
||||||
|
|
||||||
createTestCase(testDataFile)
|
val testCases = testDataFiles.mapNotNull { testDataFile ->
|
||||||
|
if (!testDataFile.isFile || testDataFile.extension != "kt")
|
||||||
|
return@mapNotNull null
|
||||||
|
|
||||||
|
createTestCase(testDataFile, settings)
|
||||||
|
}
|
||||||
|
|
||||||
|
TestCaseGroup.Default(disabledTestCaseIds = emptySet(), testCases = testCases)
|
||||||
}
|
}
|
||||||
|
|
||||||
TestCaseGroup.Default(disabledTestCaseIds = emptySet(), testCases = testCases)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTestCaseGroup(testCaseGroupId: TestCaseGroupId): TestCaseGroup? {
|
private fun createTestCase(testDataFile: File, settings: Settings): TestCase {
|
||||||
assertTrue(testCaseGroupId is TestCaseGroupId.TestDataDir)
|
|
||||||
return lazyTestCaseGroups[testCaseGroupId.cast()]
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createTestCase(testDataFile: File): TestCase {
|
|
||||||
val generatedSourcesDir = computeGeneratedSourcesDir(
|
val generatedSourcesDir = computeGeneratedSourcesDir(
|
||||||
testDataBaseDir = settings.get<TestRoots>().baseDir,
|
testDataBaseDir = settings.get<TestRoots>().baseDir,
|
||||||
testDataFile = testDataFile,
|
testDataFile = testDataFile,
|
||||||
|
|||||||
+2
-1
@@ -7,7 +7,8 @@ package org.jetbrains.kotlin.konan.blackboxtest.support.group
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.TestCaseGroup
|
import org.jetbrains.kotlin.konan.blackboxtest.support.TestCaseGroup
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.TestCaseGroupId
|
import org.jetbrains.kotlin.konan.blackboxtest.support.TestCaseGroupId
|
||||||
|
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.Settings
|
||||||
|
|
||||||
internal interface TestCaseGroupProvider {
|
internal interface TestCaseGroupProvider {
|
||||||
fun getTestCaseGroup(testCaseGroupId: TestCaseGroupId): TestCaseGroup?
|
fun getTestCaseGroup(testCaseGroupId: TestCaseGroupId, settings: Settings): TestCaseGroup?
|
||||||
}
|
}
|
||||||
+2
-2
@@ -7,8 +7,8 @@ package org.jetbrains.kotlin.konan.blackboxtest.support.group
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.GeneratedSources
|
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.GeneratedSources
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.TestRoots
|
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.TestRoots
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.TestSettings
|
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.TestConfiguration
|
||||||
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
@Target(AnnotationTarget.CLASS)
|
||||||
@TestSettings(providerClass = ExtTestCaseGroupProvider::class, requiredSettings = [TestRoots::class, GeneratedSources::class])
|
@TestConfiguration(providerClass = ExtTestCaseGroupProvider::class, requiredSettings = [TestRoots::class, GeneratedSources::class])
|
||||||
annotation class UseExtTestCaseGroupProvider
|
annotation class UseExtTestCaseGroupProvider
|
||||||
|
|||||||
+2
-2
@@ -7,8 +7,8 @@ package org.jetbrains.kotlin.konan.blackboxtest.support.group
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.GeneratedSources
|
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.GeneratedSources
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.TestRoots
|
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.TestRoots
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.TestSettings
|
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.TestConfiguration
|
||||||
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
@Target(AnnotationTarget.CLASS)
|
||||||
@TestSettings(providerClass = StandardTestCaseGroupProvider::class, requiredSettings = [TestRoots::class, GeneratedSources::class])
|
@TestConfiguration(providerClass = StandardTestCaseGroupProvider::class, requiredSettings = [TestRoots::class, GeneratedSources::class])
|
||||||
annotation class UseStandardTestCaseGroupProvider
|
annotation class UseStandardTestCaseGroupProvider
|
||||||
|
|||||||
-105
@@ -1,105 +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.settings
|
|
||||||
|
|
||||||
import org.jetbrains.kotlin.konan.target.HostManager
|
|
||||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
|
||||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
|
|
||||||
import java.io.File
|
|
||||||
import java.net.URLClassLoader
|
|
||||||
import kotlin.time.Duration
|
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
|
||||||
import kotlin.time.Duration.Companion.seconds
|
|
||||||
|
|
||||||
internal class GlobalSettings(
|
|
||||||
val target: KonanTarget = HostManager.host,
|
|
||||||
val kotlinNativeHome: File = defaultKotlinNativeHome,
|
|
||||||
val lazyKotlinNativeClassLoader: Lazy<ClassLoader> = defaultKotlinNativeClassLoader,
|
|
||||||
val testMode: TestMode = defaultTestMode,
|
|
||||||
val cacheSettings: CacheSettings = defaultCacheSettings,
|
|
||||||
val executionTimeout: Duration = defaultExecutionTimeout,
|
|
||||||
val baseBuildDir: File = projectBuildDir
|
|
||||||
) {
|
|
||||||
val hostTarget: KonanTarget = HostManager.host
|
|
||||||
|
|
||||||
fun getRootCacheDirectory(debuggable: Boolean): File? =
|
|
||||||
(cacheSettings as? CacheSettings.WithCache)?.getRootCacheDirectory(this, debuggable)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val defaultKotlinNativeHome: File
|
|
||||||
get() = System.getProperty(KOTLIN_NATIVE_HOME)?.let(::File) ?: fail { "Non-specified $KOTLIN_NATIVE_HOME system property" }
|
|
||||||
|
|
||||||
// Use isolated cached class loader.
|
|
||||||
private val defaultKotlinNativeClassLoader: Lazy<ClassLoader> = lazy {
|
|
||||||
val nativeClassPath = System.getProperty(COMPILER_CLASSPATH)
|
|
||||||
?.split(':', ';')
|
|
||||||
?.map { File(it).toURI().toURL() }
|
|
||||||
?.toTypedArray()
|
|
||||||
?: fail { "Non-specified $COMPILER_CLASSPATH system property" }
|
|
||||||
|
|
||||||
URLClassLoader(nativeClassPath, /* no parent class loader */ null).apply { setDefaultAssertionStatus(true) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private val defaultTestMode: TestMode = run {
|
|
||||||
val testModeName = System.getProperty(TEST_MODE) ?: return@run TestMode.WITH_MODULES
|
|
||||||
|
|
||||||
TestMode.values().firstOrNull { it.name == testModeName } ?: fail {
|
|
||||||
buildString {
|
|
||||||
appendLine("Unknown test mode name $testModeName.")
|
|
||||||
appendLine("One of the following test modes should be passed through $TEST_MODE system property:")
|
|
||||||
TestMode.values().forEach { testMode ->
|
|
||||||
appendLine("- ${testMode.name}: ${testMode.description}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val defaultCacheSettings: CacheSettings = run {
|
|
||||||
val useCacheValue = System.getProperty(USE_CACHE)
|
|
||||||
val useCache = if (useCacheValue != null) {
|
|
||||||
useCacheValue.toBooleanStrictOrNull() ?: fail { "Invalid value for $USE_CACHE system property: $useCacheValue" }
|
|
||||||
} else
|
|
||||||
true
|
|
||||||
|
|
||||||
if (useCache) CacheSettings.WithCache else CacheSettings.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
|
|
||||||
get() = System.getenv(PROJECT_BUILD_DIR)?.let(::File) ?: fail { "Non-specified $PROJECT_BUILD_DIR environment variable" }
|
|
||||||
|
|
||||||
private const val KOTLIN_NATIVE_HOME = "kotlin.internal.native.test.nativeHome"
|
|
||||||
private const val COMPILER_CLASSPATH = "kotlin.internal.native.test.compilerClasspath"
|
|
||||||
private const val TEST_MODE = "kotlin.internal.native.test.mode"
|
|
||||||
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 val DEFAULT_EXECUTION_TIMEOUT get() = 10.seconds // Use no backing field to avoid null-initialized value.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed interface CacheSettings {
|
|
||||||
object WithoutCache : CacheSettings
|
|
||||||
|
|
||||||
object WithCache : CacheSettings {
|
|
||||||
fun getRootCacheDirectory(settings: GlobalSettings, debuggable: Boolean): File? = with(settings) {
|
|
||||||
kotlinNativeHome.resolve("klib/cache").resolve(getCacheDirName(target, debuggable)).takeIf { it.exists() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private const val DEFAULT_CACHE_KIND = "STATIC"
|
|
||||||
|
|
||||||
private fun getCacheDirName(target: KonanTarget, debuggable: Boolean) = "$target${if (debuggable) "-g" else ""}$DEFAULT_CACHE_KIND"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* 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.settings
|
||||||
|
|
||||||
|
import gnu.trove.THashMap
|
||||||
|
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertTrue
|
||||||
|
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
internal abstract class Settings(private val parent: Settings?, settings: Iterable<Any>) {
|
||||||
|
private val map: Map<KClass<*>, Any> = THashMap<KClass<*>, Any>().apply {
|
||||||
|
settings.forEach { it ->
|
||||||
|
val (settingClass: KClass<*>, setting: Any) = if (it is Pair<*, *>) it.cast() else it::class to it
|
||||||
|
val previous = put(settingClass, setting)
|
||||||
|
assertTrue(previous == null) { "Duplicated settings: $settingClass, $previous, $setting" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
fun <T : Any> get(clazz: KClass<out T>): T = map[clazz] as T?
|
||||||
|
?: parent?.get(clazz)
|
||||||
|
?: fail { "No such setting: $clazz" }
|
||||||
|
|
||||||
|
inline fun <reified T : Any> get(): T = get(T::class)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The hierarchy of settings containers:
|
||||||
|
*
|
||||||
|
* | Settings container | Parent | Scope |
|
||||||
|
* | --------------------- | ---------------------- | ------------------------------------------- |
|
||||||
|
* | [TestProcessSettings] | `null` | The whole Gradle test executor process |
|
||||||
|
* | [TestClassSettings] | [TestProcessSettings] | The single top-level (enclosing) test class |
|
||||||
|
* | [TestRunSettings] | [TestClassSettings] | The single test run of a test function |
|
||||||
|
*/
|
||||||
|
internal class TestProcessSettings(vararg settings: Any) : Settings(parent = null, settings.asIterable())
|
||||||
|
internal class TestClassSettings(parent: TestProcessSettings, settings: Iterable<Any>) : Settings(parent, settings)
|
||||||
|
internal class TestRunSettings(parent: TestClassSettings, settings: Iterable<Any>) : Settings(parent, settings)
|
||||||
+5
-21
@@ -5,28 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.konan.blackboxtest.support.settings
|
package org.jetbrains.kotlin.konan.blackboxtest.support.settings
|
||||||
|
|
||||||
import gnu.trove.THashMap
|
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.TestDisposable
|
|
||||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertTrue
|
|
||||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
internal class Settings(settings: Iterable<Any>) : TestDisposable(parentDisposable = null) {
|
|
||||||
private val map: Map<KClass<*>, Any> = THashMap<KClass<*>, Any>().apply {
|
|
||||||
settings.forEach { setting ->
|
|
||||||
val previous = put(setting::class, setting)
|
|
||||||
assertTrue(previous == null) { "Duplicated settings: ${setting::class}, $previous, $setting" }
|
|
||||||
}
|
|
||||||
|
|
||||||
compact()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
fun <T : Any> get(clazz: KClass<out T>): T = map[clazz] as T? ?: fail { "No such setting: $clazz" }
|
|
||||||
|
|
||||||
inline fun <reified T : Any> get(): T = get(T::class)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The directories with original sources (aka testData).
|
* The directories with original sources (aka testData).
|
||||||
@@ -44,3 +23,8 @@ internal class GeneratedSources(val testSourcesDir: File, val sharedSourcesDir:
|
|||||||
* [sharedBinariesDir] - The directory with compiled shared modules (klibs).
|
* [sharedBinariesDir] - The directory with compiled shared modules (klibs).
|
||||||
*/
|
*/
|
||||||
internal class Binaries(val testBinariesDir: File, val sharedBinariesDir: File)
|
internal class Binaries(val testBinariesDir: File, val sharedBinariesDir: File)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [TestConfiguration] of the current test class and the [Annotation] with specific parameters.
|
||||||
|
*/
|
||||||
|
internal data class ComputedTestConfiguration(val configuration: TestConfiguration, val annotation: Annotation)
|
||||||
+1
-1
@@ -14,7 +14,7 @@ import java.lang.annotation.*;
|
|||||||
*/
|
*/
|
||||||
@Target(ElementType.ANNOTATION_TYPE)
|
@Target(ElementType.ANNOTATION_TYPE)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public @interface TestSettings {
|
public @interface TestConfiguration {
|
||||||
Class<? extends TestCaseGroupProvider> providerClass();
|
Class<? extends TestCaseGroupProvider> providerClass();
|
||||||
Class<?>[] requiredSettings() default {};
|
Class<?>[] requiredSettings() default {};
|
||||||
}
|
}
|
||||||
-20
@@ -1,20 +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.settings
|
|
||||||
|
|
||||||
// TODO: in fact, only WITH_MODULES mode is supported now
|
|
||||||
internal enum class TestMode(val description: String) {
|
|
||||||
ONE_STAGE(
|
|
||||||
description = "Compile test files altogether without producing intermediate KLIBs."
|
|
||||||
),
|
|
||||||
TWO_STAGE(
|
|
||||||
description = "Compile test files altogether and produce an intermediate KLIB. Then produce a program from the KLIB using -Xinclude."
|
|
||||||
),
|
|
||||||
WITH_MODULES(
|
|
||||||
description = "Compile each test file as one or many modules (depending on MODULE directives declared in the file)." +
|
|
||||||
" Then link the KLIBs into the single executable file."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
+82
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* 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.settings
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
|
import java.io.File
|
||||||
|
import kotlin.time.Duration
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The tested and the host Kotlin/Native targets.
|
||||||
|
*/
|
||||||
|
internal class KotlinNativeTargets(val testTarget: KonanTarget, val hostTarget: KonanTarget)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Kotlin/Native home.
|
||||||
|
*/
|
||||||
|
internal class KotlinNativeHome(val dir: File) {
|
||||||
|
val path: String get() = dir.path
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lazy-initialized class loader with the Kotlin/Native embedded compiler.
|
||||||
|
*/
|
||||||
|
internal class KotlinNativeClassLoader(private val lazyClassLoader: Lazy<ClassLoader>) {
|
||||||
|
val classLoader: ClassLoader get() = lazyClassLoader.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: in fact, only WITH_MODULES mode is supported now
|
||||||
|
internal enum class TestMode(val description: String) {
|
||||||
|
ONE_STAGE(
|
||||||
|
description = "Compile test files altogether without producing intermediate KLIBs."
|
||||||
|
),
|
||||||
|
TWO_STAGE(
|
||||||
|
description = "Compile test files altogether and produce an intermediate KLIB. Then produce a program from the KLIB using -Xinclude."
|
||||||
|
),
|
||||||
|
WITH_MODULES(
|
||||||
|
description = "Compile each test file as one or many modules (depending on MODULE directives declared in the file)." +
|
||||||
|
" Then link the KLIBs into the single executable file."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current project's directories.
|
||||||
|
*/
|
||||||
|
internal class BaseDirs(val buildDir: File)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timeouts.
|
||||||
|
*/
|
||||||
|
internal class Timeouts(val executionTimeout: Duration)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used cache kind.
|
||||||
|
*/
|
||||||
|
internal sealed interface CacheKind {
|
||||||
|
object WithoutCache : CacheKind
|
||||||
|
|
||||||
|
object WithStaticCache : CacheKind {
|
||||||
|
fun getRootCacheDirectory(
|
||||||
|
kotlinNativeHome: KotlinNativeHome,
|
||||||
|
kotlinNativeTargets: KotlinNativeTargets,
|
||||||
|
debuggable: Boolean
|
||||||
|
): File? = kotlinNativeHome.dir
|
||||||
|
.resolve("klib/cache")
|
||||||
|
.resolve(computeCacheDirName(kotlinNativeTargets.testTarget, CACHE_KIND, debuggable))
|
||||||
|
.takeIf { it.exists() }
|
||||||
|
|
||||||
|
private const val CACHE_KIND = "STATIC"
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private fun computeCacheDirName(testTarget: KonanTarget, cacheKind: String, debuggable: Boolean) =
|
||||||
|
"$testTarget${if (debuggable) "-g" else ""}$cacheKind"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun Settings.getRootCacheDirectory(debuggable: Boolean): File? =
|
||||||
|
get<CacheKind>().safeAs<CacheKind.WithStaticCache>()?.getRootCacheDirectory(get(), get(), debuggable)
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* 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.settings
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.konan.blackboxtest.AbstractNativeBlackBoxTest
|
||||||
|
import org.jetbrains.kotlin.konan.blackboxtest.support.util.ExternalSourceTransformers
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All instances of test classes.
|
||||||
|
*
|
||||||
|
* [allInstances] - all test class instances ordered from innermost to outermost
|
||||||
|
* [enclosingTestInstance] - the outermost test instance
|
||||||
|
*/
|
||||||
|
internal class TestInstances(val allInstances: List<Any>) {
|
||||||
|
val enclosingTestInstance: AbstractNativeBlackBoxTest
|
||||||
|
get() = allInstances.firstOrNull().cast()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal interface ExternalSourceTransformersProvider {
|
||||||
|
fun getSourceTransformers(testDataFile: File): ExternalSourceTransformers?
|
||||||
|
}
|
||||||
-5
@@ -6,7 +6,6 @@
|
|||||||
package org.jetbrains.kotlin.konan.blackboxtest.support.util
|
package org.jetbrains.kotlin.konan.blackboxtest.support.util
|
||||||
|
|
||||||
import org.jetbrains.kotlin.konan.blackboxtest.AbstractExternalNativeBlackBoxTest
|
import org.jetbrains.kotlin.konan.blackboxtest.AbstractExternalNativeBlackBoxTest
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This relates only to external codegen tests (see [AbstractExternalNativeBlackBoxTest]) that may have their own source transformers.
|
* This relates only to external codegen tests (see [AbstractExternalNativeBlackBoxTest]) that may have their own source transformers.
|
||||||
@@ -15,10 +14,6 @@ import java.io.File
|
|||||||
internal typealias ExternalSourceTransformer = (/* file contents */ String) -> /* patched file contents */ String
|
internal typealias ExternalSourceTransformer = (/* file contents */ String) -> /* patched file contents */ String
|
||||||
internal typealias ExternalSourceTransformers = List<ExternalSourceTransformer>
|
internal typealias ExternalSourceTransformers = List<ExternalSourceTransformer>
|
||||||
|
|
||||||
internal interface ExternalSourceTransformersProvider {
|
|
||||||
fun getSourceTransformers(testDataFile: File): ExternalSourceTransformers?
|
|
||||||
}
|
|
||||||
|
|
||||||
internal object DiagnosticsRemovingSourceTransformer : ExternalSourceTransformer {
|
internal object DiagnosticsRemovingSourceTransformer : ExternalSourceTransformer {
|
||||||
override fun invoke(source: String) = source.lineSequence().joinToString("\n") { line ->
|
override fun invoke(source: String) = source.lineSequence().joinToString("\n") { line ->
|
||||||
// Remove all diagnostic parameters from the text. Examples:
|
// Remove all diagnostic parameters from the text. Examples:
|
||||||
|
|||||||
Reference in New Issue
Block a user