diff --git a/native/native.tests/tests/org/jetbrains/kotlin/generators/tests/GenerateNativeBlackboxTests.kt b/native/native.tests/tests/org/jetbrains/kotlin/generators/tests/GenerateNativeBlackboxTests.kt index c01f1ff264c..21e2b58cf10 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/generators/tests/GenerateNativeBlackboxTests.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/generators/tests/GenerateNativeBlackboxTests.kt @@ -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.test.TargetBackend import org.junit.jupiter.api.Tag -import org.junit.jupiter.api.TestInstance fun main() { System.setProperty("java.awt.headless", "true") @@ -23,7 +22,7 @@ fun main() { testGroup("native/native.tests/tests-gen", "compiler/testData") { testClass( suiteTestClassName = "ExternalTestGenerated", - annotations = listOf(external(), provider(), testInstancePerClass()) + annotations = listOf(external(), provider()) ) { model("codegen/box", targetBackend = TargetBackend.NATIVE) model("codegen/boxInline", targetBackend = TargetBackend.NATIVE) @@ -47,4 +46,3 @@ private inline fun provider() = annotation(T::class.jav private fun external() = annotation(Tag::class.java, "external") private fun infrastructure() = annotation(Tag::class.java, "infrastructure") -private fun testInstancePerClass() = annotation(TestInstance::class.java, TestInstance.Lifecycle.PER_CLASS) diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/AbstractExternalNativeBlackBoxTest.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/AbstractExternalNativeBlackBoxTest.kt index 13fb583375c..6c032575cb5 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/AbstractExternalNativeBlackBoxTest.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/AbstractExternalNativeBlackBoxTest.kt @@ -6,15 +6,13 @@ package org.jetbrains.kotlin.konan.blackboxtest 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.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.getAbsoluteFile -import org.junit.jupiter.api.TestInstance 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() { private val registeredSourceTransformers: ThreadSafeCache> = ThreadSafeCache() diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/AbstractNativeBlackBoxTest.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/AbstractNativeBlackBoxTest.kt index cb47aad8510..51cfe8ff1e5 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/AbstractNativeBlackBoxTest.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/AbstractNativeBlackBoxTest.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.konan.blackboxtest import com.intellij.testFramework.TestDataFile 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.getAbsoluteFile import org.jetbrains.kotlin.konan.blackboxtest.support.util.joinPackageNames @@ -17,6 +18,7 @@ import org.junit.jupiter.api.extension.ExtendWith @ExtendWith(NativeBlackBoxTestSupport::class) abstract class AbstractNativeBlackBoxTest { + internal lateinit var testRunSettings: TestRunSettings 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]. */ internal fun runTestCase(testCaseId: TestCaseId) { - val testRun = testRunProvider.getSingleTestRun(testCaseId) + val testRun = testRunProvider.getSingleTestRun(testCaseId, testRunSettings) performTestRun(testRun) } @@ -55,7 +57,7 @@ abstract class AbstractNativeBlackBoxTest { * This function should be called from a method annotated with [org.junit.jupiter.api.TestFactory]. */ internal fun dynamicTestCase(testCaseId: TestCaseId): Collection { - val testRunNodes = testRunProvider.getTestRuns(testCaseId) + val testRunNodes = testRunProvider.getTestRuns(testCaseId, testRunSettings) return buildJUnitDynamicNodes(testRunNodes) } @@ -90,7 +92,7 @@ abstract class AbstractNativeBlackBoxTest { } private fun performTestRun(testRun: TestRun) { - val testRunner = testRunProvider.createRunner(testRun) + val testRunner = testRunProvider.createRunner(testRun, testRunSettings) testRunner.run() } } diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/NativeBlackBoxTestSupport.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/NativeBlackBoxTestSupport.kt index 99b116b061f..a90a6032d35 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/NativeBlackBoxTestSupport.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/NativeBlackBoxTestSupport.kt @@ -5,11 +5,10 @@ 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.settings.* -import org.jetbrains.kotlin.konan.blackboxtest.support.settings.GlobalSettings 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.services.JUnit5Assertions.assertEquals 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.TestInstancePostProcessor import java.io.File +import java.net.URLClassLoader import kotlin.reflect.KClass import kotlin.reflect.KParameter import kotlin.reflect.full.findAnnotation +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.seconds 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. */ override fun beforeEach(extensionContext: ExtensionContext): Unit = with(extensionContext) { - enclosingTestInstance.testRunProvider = getOrCreateTestRunProvider() + val settings = createTestRunSettings() // Set the essential compiler property. - System.setProperty("kotlin.native.home", getOrCreateGlobalSettings().kotlinNativeHome.path) + System.setProperty("kotlin.native.home", settings.get().path) + + // Inject the required properties to test instance. + with(settings.get().enclosingTestInstance) { + testRunSettings = settings + testRunProvider = getOrCreateTestRunProvider() + } } companion object { - private val NAMESPACE = ExtensionContext.Namespace.create(NativeBlackBoxTestSupport::class.java.simpleName) - /** Creates a single instance of [TestRunProvider] per test class. */ - private fun ExtensionContext.getOrCreateTestRunProvider(): TestRunProvider { - val enclosingTestClass = enclosingTestClass + /*************** Test process settings ***************/ - return root.getStore(NAMESPACE).getOrComputeIfAbsent(enclosingTestClass.sanitizedName) { - val computedTestSettings = computeTestSettings(enclosingTestClass) - val requiredSettings: Set> = buildSet { - // Always required: - this += GlobalSettings::class - this += Binaries::class - - // Custom: - addAll(computedTestSettings.testSettings.requiredSettings) - } - - 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) + private fun ExtensionContext.getOrCreateTestProcessSettings(): TestProcessSettings = + root.getStore(NAMESPACE).getOrComputeIfAbsent(TestProcessSettings::class.java.name) { + TestProcessSettings( + computeNativeTargets(), + computeNativeHome(), + computeNativeClassLoader(), + computeTestMode(), + CacheKind::class to computeCacheKind(), + computeBaseDirs(), + computeTimeouts() + ) }.cast() + + private fun computeNativeTargets(): KotlinNativeTargets { + val hostTarget = HostManager.host + return KotlinNativeTargets(testTarget = hostTarget, hostTarget = hostTarget) } - private val ExtensionContext.enclosingTestInstance: AbstractNativeBlackBoxTest - get() = requiredTestInstances.allInstances.firstOrNull().cast() + private fun computeNativeHome(): KotlinNativeHome = KotlinNativeHome(File(requiredSystemProperty(KOTLIN_NATIVE_HOME))) - private val ExtensionContext.enclosingTestClass: Class<*> - get() = generateSequence(requiredTestClass) { it.enclosingClass }.last() + private fun computeNativeClassLoader(): KotlinNativeClassLoader = KotlinNativeClassLoader( + lazy { + val nativeClassPath = requiredSystemProperty(COMPILER_CLASSPATH) + .split(':', ';') + .map { File(it).toURI().toURL() } + .toTypedArray() - private fun ExtensionContext.getOrCreateGlobalSettings(): GlobalSettings = - root.getStore(NAMESPACE).getOrComputeIfAbsent(GlobalSettings::class.java.sanitizedName) { - // Create with the default settings. - GlobalSettings() + URLClassLoader(nativeClassPath, /* no parent class loader */ null).apply { setDefaultAssertionStatus(true) } + } + ) + + 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 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()) { + 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() - private fun computeTestSettings(enclosingTestClass: Class<*>): ComputedTestSettings { - val findTestSettings: Class<*>.() -> ComputedTestSettings? = { + private fun computeTestConfiguration(enclosingTestClass: Class<*>): ComputedTestConfiguration { + val findTestConfiguration: Class<*>.() -> ComputedTestConfiguration? = { annotations.asSequence().mapNotNull { annotation -> - val testSettings = annotation.annotationClass.findAnnotation() ?: return@mapNotNull null - ComputedTestSettings(testSettings, annotation) + val testConfiguration = annotation.annotationClass.findAnnotation() ?: return@mapNotNull null + ComputedTestConfiguration(testConfiguration, annotation) }.firstOrNull() } - return enclosingTestClass.findTestSettings() - ?: enclosingTestClass.declaredClasses.firstNotNullOfOrNull { it.findTestSettings() } - ?: fail { "No @${TestSettings::class.simpleName} annotation found on test classes" } + return enclosingTestClass.findTestConfiguration() + ?: enclosingTestClass.declaredClasses.firstNotNullOfOrNull { it.findTestConfiguration() } + ?: fail { "No @${TestConfiguration::class.simpleName} annotation found on test classes" } } private fun computeTestRoots(enclosingTestClass: Class<*>): TestRoots { + fun TestMetadata.testRoot() = getAbsoluteFile(localPath = value) + val testRoots: Set = when (val outermostTestMetadata = enclosingTestClass.getAnnotation(TestMetadata::class.java)) { null -> { 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) { @@ -125,10 +215,14 @@ class NativeBlackBoxTestSupport : BeforeEachCallback { return TestRoots(testRoots, baseDir) } - private fun computeGeneratedSourceDirs(globalSettings: GlobalSettings, enclosingTestClass: Class<*>): GeneratedSources { - val testSourcesDir = globalSettings.baseBuildDir + private fun computeGeneratedSourceDirs( + baseDirs: BaseDirs, + targets: KotlinNativeTargets, + enclosingTestClass: Class<*> + ): GeneratedSources { + val testSourcesDir = baseDirs.buildDir .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. val sharedSourcesDir = testSourcesDir @@ -138,10 +232,10 @@ class NativeBlackBoxTestSupport : BeforeEachCallback { return GeneratedSources(testSourcesDir, sharedSourcesDir) } - private fun computeBinariesDirs(globalSettings: GlobalSettings, enclosingTestClass: Class<*>): Binaries { - val testBinariesDir = globalSettings.baseBuildDir + private fun computeBinariesDirs(baseDirs: BaseDirs, targets: KotlinNativeTargets, enclosingTestClass: Class<*>): Binaries { + val testBinariesDir = baseDirs.buildDir .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. val sharedBinariesDir = testBinariesDir @@ -151,26 +245,45 @@ class NativeBlackBoxTestSupport : BeforeEachCallback { return Binaries(testBinariesDir, sharedBinariesDir) } - private fun createTestCaseGroupProvider( - settings: Settings, - computedTestSettings: ComputedTestSettings, - enclosingTestInstance: AbstractNativeBlackBoxTest - ): TestCaseGroupProvider { - val (testSettings: TestSettings, testSettingsAnnotation: Annotation?) = computedTestSettings - val providerClass: KClass = testSettings.providerClass + /*************** Test run settings ***************/ + + // Note: TestRunSettings is not cached! + private fun ExtensionContext.createTestRunSettings(): TestRunSettings { + val testInstances = computeTestInstances() + + return TestRunSettings( + parent = getOrCreateTestClassSettings(), + listOfNotNull( + testInstances, + ExternalSourceTransformersProvider::class to testInstances.enclosingTestInstance.safeAs() + ) + ) + } + + private fun ExtensionContext.computeTestInstances(): TestInstances = TestInstances(requiredTestInstances.allInstances) + + /*************** Test run provider ***************/ + + private fun ExtensionContext.getOrCreateTestRunProvider(): TestRunProvider = + root.getStore(NAMESPACE).getOrComputeIfAbsent(testClassKeyFor()) { + val testCaseGroupProvider = createTestCaseGroupProvider(getOrCreateTestClassSettings().get()) + TestRunProvider(testCaseGroupProvider) + }.cast() + + private fun createTestCaseGroupProvider(computedTestConfiguration: ComputedTestConfiguration): TestCaseGroupProvider { + val (testConfiguration: TestConfiguration, testConfigurationAnnotation: Annotation) = computedTestConfiguration + val providerClass: KClass = testConfiguration.providerClass // Assumption: For simplicity’s sake TestCaseGroupProvider has just one constructor. val constructor = providerClass.constructors.singleOrNull() ?: fail { "No or multiple constructors found for $providerClass" } - val testSettingsAnnotationClass: KClass? = testSettingsAnnotation?.annotationClass - val sourceTransformersProvider = enclosingTestInstance.safeAs() + val testConfigurationAnnotationClass: KClass = testConfigurationAnnotation.annotationClass val arguments = constructor.parameters.map { parameter -> when { - parameter.hasTypeOf() -> settings - sourceTransformersProvider != null && parameter.hasTypeOf() -> sourceTransformersProvider - testSettingsAnnotationClass != null && parameter.hasTypeOf(testSettingsAnnotationClass) -> testSettingsAnnotation + parameter.hasTypeOf(testConfigurationAnnotationClass) -> testConfigurationAnnotation + // maybe add other arguments??? else -> fail { "Can't provide all arguments for $constructor" } } } @@ -178,11 +291,14 @@ class NativeBlackBoxTestSupport : BeforeEachCallback { return constructor.call(*arguments.toTypedArray()).cast() } - private inline fun KParameter.hasTypeOf(): Boolean = hasTypeOf(T::class) 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 ExtensionContext.testClassKeyFor(): String = + enclosingTestClass.name + "#" + T::class.java.name } } - -private data class ComputedTestSettings(val testSettings: TestSettings, val annotation: Annotation?) diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/TestCompiler.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/TestCompiler.kt index 40d9eb52007..708e6d68eb3 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/TestCompiler.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/TestCompiler.kt @@ -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.TestModule.Companion.allDependencies 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.GlobalSettings -import org.jetbrains.kotlin.konan.blackboxtest.support.settings.Settings +import org.jetbrains.kotlin.konan.blackboxtest.support.settings.* import org.jetbrains.kotlin.konan.blackboxtest.support.util.* import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail import java.io.* @@ -26,7 +24,7 @@ import kotlin.time.Duration import kotlin.time.ExperimentalTime import kotlin.time.measureTime -internal class TestCompilationFactory(private val settings: Settings) { +internal class TestCompilationFactory { private val cachedCompilations = ThreadSafeCache() private sealed interface TestCompilationCacheKey { @@ -34,7 +32,7 @@ internal class TestCompilationFactory(private val settings: Settings) { data class Executable(val sourceModules: Set) : TestCompilationCacheKey } - fun testCasesToExecutable(testCases: Collection): TestCompilation { + fun testCasesToExecutable(testCases: Collection, settings: Settings): TestCompilation { val rootModules = testCases.flatMapToSet { testCase -> testCase.rootModules } val cacheKey = TestCompilationCacheKey.Executable(rootModules) @@ -44,16 +42,18 @@ internal class TestCompilationFactory(private val settings: Settings) { // Long pass. val freeCompilerArgs = rootModules.first().testCase.freeCompilerArgs // Should be identical inside the same test case group. val extras = testCases.first().extras // Should be identical inside the same test case group. - val libraries = rootModules.flatMapToSet { it.allDependencies }.map { moduleToKlib(it, freeCompilerArgs) } - val friends = rootModules.flatMapToSet { it.allFriends }.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, settings) } return cachedCompilations.computeIfAbsent(cacheKey) { TestCompilationImpl( - settings = settings, + targets = settings.get(), + home = settings.get(), + classLoader = settings.get(), freeCompilerArgs = freeCompilerArgs, sourceModules = rootModules, dependencies = TestCompilationDependencies(libraries = libraries, friends = friends), - expectedArtifactFile = artifactFileForExecutable(rootModules), + expectedArtifactFile = settings.artifactFileForExecutable(rootModules), specificCompilerArgs = { add("-produce", "program") when (extras) { @@ -67,7 +67,7 @@ internal class TestCompilationFactory(private val settings: Settings) { add(testRunnerArg) } } - settings.get().getRootCacheDirectory(debuggable = true)?.let { rootCacheDir -> + settings.getRootCacheDirectory(debuggable = true)?.let { 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 cacheKey = TestCompilationCacheKey.Klib(sourceModules, freeCompilerArgs) @@ -83,35 +83,37 @@ internal class TestCompilationFactory(private val settings: Settings) { cachedCompilations[cacheKey]?.let { return it } // Long pass. - val libraries = sourceModule.allDependencies.map { moduleToKlib(it, freeCompilerArgs) } - val friends = sourceModule.allFriends.map { moduleToKlib(it, freeCompilerArgs) } + val libraries = sourceModule.allDependencies.map { moduleToKlib(it, freeCompilerArgs, settings) } + val friends = sourceModule.allFriends.map { moduleToKlib(it, freeCompilerArgs, settings) } return cachedCompilations.computeIfAbsent(cacheKey) { TestCompilationImpl( - settings = settings, + targets = settings.get(), + home = settings.get(), + classLoader = settings.get(), freeCompilerArgs = freeCompilerArgs, sourceModules = sourceModules, dependencies = TestCompilationDependencies(libraries = libraries, friends = friends), - expectedArtifactFile = artifactFileForKlib(sourceModule, freeCompilerArgs), + expectedArtifactFile = settings.artifactFileForKlib(sourceModule, freeCompilerArgs), specificCompilerArgs = { add("-produce", "library") } ) } } - private fun artifactFileForExecutable(modules: Set) = when (modules.size) { + private fun Settings.artifactFileForExecutable(modules: Set) = when (modules.size) { 1 -> artifactFileForExecutable(modules.first()) - else -> multiModuleArtifactFile(modules, settings.get().target.family.exeSuffix) + else -> multiModuleArtifactFile(modules, get().testTarget.family.exeSuffix) } - private fun artifactFileForExecutable(module: TestModule.Exclusive) = - singleModuleArtifactFile(module, settings.get().target.family.exeSuffix) + private fun Settings.artifactFileForExecutable(module: TestModule.Exclusive) = + singleModuleArtifactFile(module, get().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.Shared -> settings.get().sharedBinariesDir.resolve("${module.name}-${prettyHash(freeCompilerArgs.hashCode())}.klib") + is TestModule.Shared -> get().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 { append(module.testCase.nominalPackageName.compressedPackageName).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) } - private fun multiModuleArtifactFile(modules: Collection, extension: String): File { + private fun Settings.multiModuleArtifactFile(modules: Collection, extension: String): File { var filesCount = 0 var hash = 0 val uniquePackageNames = hashSetOf() @@ -153,8 +155,8 @@ internal class TestCompilationFactory(private val settings: Settings) { return artifactDirForPackageName(commonPackageName).resolve(artifactFileName) } - private fun artifactDirForPackageName(packageName: PackageName?): File { - val baseDir = settings.get().testBinariesDir + private fun Settings.artifactDirForPackageName(packageName: PackageName?): File { + val baseDir = get().testBinariesDir val outputDir = if (packageName != null) baseDir.resolve(packageName.compressedPackageName) else baseDir outputDir.mkdirs() @@ -234,7 +236,9 @@ internal class TestCompilationDependencies( } 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 sourceModules: Collection, private val dependencies: TestCompilationDependencies, @@ -254,8 +258,8 @@ private class TestCompilationImpl( add( "-enable-assertions", "-g", - "-target", settings.get().target.name, - "-repo", settings.get().kotlinNativeHome.resolve("klib").path, + "-target", targets.testTarget.name, + "-repo", home.dir.resolve("klib").path, "-output", expectedArtifactFile.path, "-Xskip-prerelease-check", "-Xverify-ir", @@ -286,7 +290,7 @@ private class TestCompilationImpl( val (loggedCompilerCall: LoggedData, result: TestCompilationResult.ImmediateResult) = try { val (exitCode, compilerOutput, compilerOutputHasErrors, duration) = callCompiler( compilerArgs = compilerArgs, - lazyKotlinNativeClassLoader = settings.get().lazyKotlinNativeClassLoader + kotlinNativeClassLoader = classLoader.classLoader ) val loggedCompilerCall = @@ -341,14 +345,12 @@ private inline fun buildArgs(builderAction: ArgsBuilder.() -> Unit): Array, lazyKotlinNativeClassLoader: Lazy): CompilerCallResult { +private fun callCompiler(compilerArgs: Array, kotlinNativeClassLoader: ClassLoader): CompilerCallResult { val compilerXmlOutput: ByteArrayOutputStream val exitCode: ExitCode @OptIn(ExperimentalTime::class) val duration = measureTime { - val kotlinNativeClassLoader by lazyKotlinNativeClassLoader - val servicesClass = Class.forName(Services::class.java.canonicalName, true, kotlinNativeClassLoader) val emptyServices = servicesClass.getField("EMPTY").get(servicesClass) diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/TestRunProvider.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/TestRunProvider.kt index cc3c758eace..58f7cf6fa85 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/TestRunProvider.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/TestRunProvider.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.konan.blackboxtest.support +import com.intellij.openapi.Disposable import com.intellij.openapi.util.Disposer import org.jetbrains.kotlin.konan.blackboxtest.support.TestCase.NoTestRunnerExtras 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.LocalTestNameExtractor 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.Timeouts 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.buildTree @@ -27,10 +29,9 @@ import org.junit.jupiter.api.Assumptions.assumeTrue import org.junit.jupiter.api.extension.ExtensionContext internal class TestRunProvider( - private val settings: Settings, private val testCaseGroupProvider: TestCaseGroupProvider ) : ExtensionContext.Store.CloseableResource { - private val compilationFactory = TestCompilationFactory(settings) + private val compilationFactory = TestCompilationFactory() private val cachedCompilations = ThreadSafeCache() private val cachedTestNames = ThreadSafeCache>() @@ -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) TestRun(displayName = /* Unimportant. Used only in dynamic tests. */ "", executable, runParameters, testCase.id) } @@ -95,8 +99,9 @@ internal class TestRunProvider( * } */ fun getTestRuns( - testCaseId: TestCaseId - ): Collection> = withTestExecutable(testCaseId) { testCase, executable, cacheKey -> + testCaseId: TestCaseId, + settings: Settings + ): Collection> = withTestExecutable(testCaseId, settings) { testCase, executable, cacheKey -> fun createTestRun(testRunName: String, testName: TestName?): TestRun { val runParameters = getRunParameters(testCase, testName) return TestRun(testRunName, executable, runParameters, testCase.id) @@ -110,7 +115,7 @@ internal class TestRunProvider( } TestKind.REGULAR, TestKind.STANDALONE -> { val testNames = cachedTestNames.computeIfAbsent(cacheKey) { - extractTestNames(executable) + extractTestNames(executable, settings) }.filterIrrelevant(testCase) testNames.buildTree(TestName::packageName) { testName -> @@ -120,10 +125,14 @@ internal class TestRunProvider( } } - private fun withTestExecutable(testCaseId: TestCaseId, action: (TestCase, TestExecutable, TestCompilationCacheKey) -> T): T { - settings.assertNotDisposed() + private fun withTestExecutable( + 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") 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. val cacheKey = TestCompilationCacheKey.Standalone(testCaseId) val testCompilation = cachedCompilations.computeIfAbsent(cacheKey) { - compilationFactory.testCasesToExecutable(listOf(testCase)) + compilationFactory.testCasesToExecutable(listOf(testCase), settings) } testCompilation to cacheKey } @@ -144,7 +153,7 @@ internal class TestRunProvider( val testCompilation = cachedCompilations.computeIfAbsent(cacheKey) { val testCases = testCaseGroup.getRegularOnly(testCase.freeCompilerArgs, testRunnerType) assertTrue(testCases.isNotEmpty()) - compilationFactory.testCasesToExecutable(testCases) + compilationFactory.testCasesToExecutable(testCases, settings) } testCompilation to cacheKey } @@ -179,19 +188,23 @@ internal class TestRunProvider( } // Currently, only local test runner is supported. - fun createRunner(testRun: TestRun): AbstractRunner<*> = with(settings.get()) { - if (target == hostTarget) - LocalTestRunner(testRun, executionTimeout) - else - runningAtNonHostTarget() + fun createRunner(testRun: TestRun, settings: Settings): AbstractRunner<*> = with(settings) { + with(get()) { + if (testTarget == hostTarget) + LocalTestRunner(testRun, get().executionTimeout) + else + runningAtNonHostTarget() + } } // Currently, only local test name extractor is supported. - private fun extractTestNames(executable: TestExecutable): Collection = with(settings.get()) { - if (target == hostTarget) - LocalTestNameExtractor(executable, executionTimeout).run() - else - runningAtNonHostTarget() + private fun extractTestNames(executable: TestExecutable, settings: Settings): Collection = with(settings) { + with(get()) { + if (testTarget == hostTarget) + LocalTestNameExtractor(executable, get().executionTimeout).run() + else + runningAtNonHostTarget() + } } private fun Collection.filterIrrelevant(testCase: TestCase) = @@ -200,16 +213,18 @@ internal class TestRunProvider( else this - private fun GlobalSettings.runningAtNonHostTarget(): Nothing = fail { + private fun KotlinNativeTargets.runningAtNonHostTarget(): Nothing = fail { """ Running at non-host target is not supported yet. - Compilation target: $target + Compilation target: $testTarget Host target: $hostTarget """.trimIndent() } override fun close() { - Disposer.dispose(settings) + if (testCaseGroupProvider is Disposable) { + Disposer.dispose(testCaseGroupProvider) + } } private sealed class TestCompilationCacheKey { diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/ExtTestCaseGroupProvider.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/ExtTestCaseGroupProvider.kt index 297b28b6221..1c1aac00d9b 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/ExtTestCaseGroupProvider.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/ExtTestCaseGroupProvider.kt @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.konan.blackboxtest.support.* 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.Settings 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.fail import org.jetbrains.kotlin.utils.addIfNotNull -import org.jetbrains.kotlin.utils.addToStdlib.cast import java.io.File -internal class ExtTestCaseGroupProvider( - private val settings: Settings, - private val sourceTransformersProvider: ExternalSourceTransformersProvider -) : TestCaseGroupProvider, TestDisposable(parentDisposable = settings) { +internal class ExtTestCaseGroupProvider : TestCaseGroupProvider, TestDisposable(parentDisposable = null) { private val structureFactory = ExtTestDataFileStructureFactory(parentDisposable = this) private val sharedModules = ThreadSafeCache() - private val lazyTestCaseGroups = ThreadSafeFactory { testCaseGroupId -> - if (testCaseGroupId.dir in excludes) return@ThreadSafeFactory TestCaseGroup.ALL_DISABLED + private val cachedTestCaseGroups = ThreadSafeCache() - val (excludedTestDataFiles, testDataFiles) = testCaseGroupId.dir.listFiles() - ?.filter { file -> file.isFile && file.extension == "kt" } - ?.partition { file -> file in excludes } - ?: return@ThreadSafeFactory null - - val disabledTestCaseIds = hashSetOf() - excludedTestDataFiles.mapTo(disabledTestCaseIds, TestCaseId::TestDataFile) - - val testCases = mutableListOf() - - 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? { + override fun getTestCaseGroup(testCaseGroupId: TestCaseGroupId, settings: Settings): TestCaseGroup? { assertNotDisposed() - assertTrue(testCaseGroupId is TestCaseGroupId.TestDataDir) - return lazyTestCaseGroups[testCaseGroupId.cast()] + check(testCaseGroupId is TestCaseGroupId.TestDataDir) + + 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() + excludedTestDataFiles.mapTo(disabledTestCaseIds, TestCaseId::TestDataFile) + + val testCases = mutableListOf() + + testDataFiles.forEach { testDataFile -> + val extTestDataFile = ExtTestDataFile( + testDataFile = testDataFile, + structureFactory = structureFactory, + customSourceTransformers = settings.get().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 { @@ -122,19 +126,19 @@ internal class ExtTestCaseGroupProvider( } private class ExtTestDataFile( - private val settings: Settings, + private val testDataFile: File, structureFactory: ExtTestDataFileStructureFactory, - sourceTransformersProvider: ExternalSourceTransformersProvider, - private val testDataFile: File + customSourceTransformers: ExternalSourceTransformers?, + testRoots: TestRoots, + private val generatedSources: GeneratedSources ) { private val structure by lazy { - val customTransformers: ExternalSourceTransformers? = sourceTransformersProvider.getSourceTransformers(testDataFile) - val allTransformers: ExternalSourceTransformers = if (customTransformers.isNullOrEmpty()) + val allSourceTransformers: ExternalSourceTransformers = if (customSourceTransformers.isNullOrEmpty()) MANDATORY_SOURCE_TRANSFORMERS else - MANDATORY_SOURCE_TRANSFORMERS + customTransformers + MANDATORY_SOURCE_TRANSFORMERS + customSourceTransformers - structureFactory.ExtTestDataFileStructure(testDataFile, allTransformers) + structureFactory.ExtTestDataFileStructure(testDataFile, allSourceTransformers) } private val testDataFileSettings by lazy { @@ -152,12 +156,12 @@ private class ExtTestDataFile( optInsForCompiler = optInsForCompiler, expectActualLinker = EXPECT_ACTUAL_LINKER_DIRECTIVE in structure.directives, generatedSourcesDir = computeGeneratedSourcesDir( - testDataBaseDir = settings.get().baseDir, + testDataBaseDir = testRoots.baseDir, testDataFile = testDataFile, - generatedSourcesBaseDir = settings.get().testSourcesDir + generatedSourcesBaseDir = generatedSources.testSourcesDir ), nominalPackageName = computePackageName( - testDataBaseDir = settings.get().baseDir, + testDataBaseDir = testRoots.baseDir, testDataFile = testDataFile ) ) @@ -575,7 +579,7 @@ private class ExtTestDataFile( testCaseDir = testDataFileSettings.generatedSourcesDir, findOrGenerateSharedModule = { moduleName: String, generator: SharedModuleGenerator -> sharedModules.computeIfAbsent(moduleName) { - generator(settings.get().sharedSourcesDir) + generator(generatedSources.sharedSourcesDir) } } ) diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/PredefinedTestCaseGroupProvider.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/PredefinedTestCaseGroupProvider.kt index c42d867f14f..46607d548de 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/PredefinedTestCaseGroupProvider.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/PredefinedTestCaseGroupProvider.kt @@ -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.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.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.getAbsoluteFile import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertTrue -import org.jetbrains.kotlin.utils.addToStdlib.cast import org.junit.jupiter.api.fail import java.io.File -internal class PredefinedTestCaseGroupProvider(private val settings: Settings, annotation: PredefinedTestCases) : TestCaseGroupProvider { +internal class PredefinedTestCaseGroupProvider(annotation: PredefinedTestCases) : TestCaseGroupProvider { private val testCaseIdToPredefinedTestCase: Map = buildMap { annotation.testCases.forEach { predefinedTestCase -> val testCaseId = TestCaseId.Named(predefinedTestCase.name) @@ -36,55 +35,56 @@ internal class PredefinedTestCaseGroupProvider(private val settings: Settings, a } } - private val lazyTestCaseGroups = ThreadSafeFactory { testCaseGroupId -> - val testCaseId = testCaseGroupIdToTestCaseId[testCaseGroupId] ?: return@ThreadSafeFactory null - val predefinedTestCase = testCaseIdToPredefinedTestCase[testCaseId] ?: return@ThreadSafeFactory null + private val cachedTestCaseGroups = ThreadSafeCache() - val module = TestModule.Exclusive( - name = testCaseId.uniqueName, - directDependencySymbols = emptySet(), - directFriendSymbols = emptySet() - ) + override fun getTestCaseGroup(testCaseGroupId: TestCaseGroupId, settings: Settings): TestCaseGroup? { + check(testCaseGroupId is TestCaseGroupId.Named) - predefinedTestCase.sourceLocations - .expandGlobs { "No files found for test case $testCaseId" } - .forEach { file -> module.files += TestFile.createCommitted(file, module) } + return cachedTestCaseGroups.computeIfAbsent(testCaseGroupId) { + val testCaseId = testCaseGroupIdToTestCaseId[testCaseGroupId] ?: return@computeIfAbsent null + val predefinedTestCase = testCaseIdToPredefinedTestCase[testCaseId] ?: return@computeIfAbsent null - val testCase = TestCase( - id = testCaseId, - kind = TestKind.STANDALONE, - modules = setOf(module), - 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) + val module = TestModule.Exclusive( + name = testCaseId.uniqueName, + directDependencySymbols = emptySet(), + directFriendSymbols = emptySet() + ) - 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? { - assertTrue(testCaseGroupId is TestCaseGroupId.Named) - return lazyTestCaseGroups[testCaseGroupId.cast()] - } - - private fun Array.expandGlobs(noExpandedFilesErrorMessage: () -> String): Set { + private fun Array.expandGlobs(settings: Settings, noExpandedFilesErrorMessage: () -> String): Set { val files = buildSet { this@expandGlobs.forEach { pathPattern -> - expandGlobTo(getAbsoluteFile(substituteRealPaths(pathPattern)), this) + expandGlobTo(getAbsoluteFile(substituteRealPaths(pathPattern, settings)), this) } } assertTrue(files.isNotEmpty(), noExpandedFilesErrorMessage) return files } - private fun Array.parseCompilerArgs(parsingErrorMessage: () -> String): TestCompilerArgs = + private fun Array.parseCompilerArgs(settings: Settings, parsingErrorMessage: () -> String): TestCompilerArgs = if (isEmpty()) TestCompilerArgs.EMPTY else { - val freeCompilerArgs = map { arg -> substituteRealPaths(arg) } + val freeCompilerArgs = map { arg -> substituteRealPaths(arg, settings) } val forbiddenCompilerArgs = TestCompilerArgs.findForbiddenArgs(freeCompilerArgs) assertTrue(forbiddenCompilerArgs.isEmpty()) { """ @@ -98,10 +98,10 @@ internal class PredefinedTestCaseGroupProvider(private val settings: Settings, a TestCompilerArgs(freeCompilerArgs) } - private fun substituteRealPaths(value: String): String = + private fun substituteRealPaths(value: String, settings: Settings): String = if ('$' in value) { // N.B. Here, more substitutions can be supported in the future if it would be necessary. - value.replace(PredefinedPaths.KOTLIN_NATIVE_DISTRIBUTION, settings.get().kotlinNativeHome.path) + value.replace(PredefinedPaths.KOTLIN_NATIVE_DISTRIBUTION, settings.get().path) } else value } diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/PredefinedTestCases.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/PredefinedTestCases.kt index b417ead357c..58ddb9a2dee 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/PredefinedTestCases.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/PredefinedTestCases.kt @@ -6,10 +6,10 @@ package org.jetbrains.kotlin.konan.blackboxtest.support.group 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) -@TestSettings(providerClass = PredefinedTestCaseGroupProvider::class) +@TestConfiguration(providerClass = PredefinedTestCaseGroupProvider::class) internal annotation class PredefinedTestCases(vararg val testCases: PredefinedTestCase) @Target() diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/StandardTestCaseGroupProvider.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/StandardTestCaseGroupProvider.kt index 5ef9d11f0d5..06710b707f6 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/StandardTestCaseGroupProvider.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/StandardTestCaseGroupProvider.kt @@ -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.fail import org.jetbrains.kotlin.test.services.impl.RegisteredDirectivesParser -import org.jetbrains.kotlin.utils.addToStdlib.cast import java.io.File -internal class StandardTestCaseGroupProvider(private val settings: Settings) : TestCaseGroupProvider { - val sourceTransformers: MutableMap String>> = mutableMapOf() +internal class StandardTestCaseGroupProvider : TestCaseGroupProvider { + // Create and cache test cases in groups on demand. + private val cachedTestCaseGroups = ThreadSafeCache() - // Load test cases in groups on demand. - private val lazyTestCaseGroups = ThreadSafeFactory { testCaseGroupId -> - val testDataFiles = testCaseGroupId.dir.listFiles() - ?: return@ThreadSafeFactory null // `null` means that there is no such testDataDir. + override fun getTestCaseGroup(testCaseGroupId: TestCaseGroupId, settings: Settings): TestCaseGroup? { + check(testCaseGroupId is TestCaseGroupId.TestDataDir) - val testCases = testDataFiles.mapNotNull { testDataFile -> - if (!testDataFile.isFile || testDataFile.extension != "kt") - return@mapNotNull null + return cachedTestCaseGroups.computeIfAbsent(testCaseGroupId) { + val testDataFiles = testCaseGroupId.dir.listFiles() + ?: 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? { - assertTrue(testCaseGroupId is TestCaseGroupId.TestDataDir) - return lazyTestCaseGroups[testCaseGroupId.cast()] - } - - private fun createTestCase(testDataFile: File): TestCase { + private fun createTestCase(testDataFile: File, settings: Settings): TestCase { val generatedSourcesDir = computeGeneratedSourcesDir( testDataBaseDir = settings.get().baseDir, testDataFile = testDataFile, diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/TestCaseGroupProvider.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/TestCaseGroupProvider.kt index bc126bbee16..a721fd3dd59 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/TestCaseGroupProvider.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/TestCaseGroupProvider.kt @@ -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.TestCaseGroupId +import org.jetbrains.kotlin.konan.blackboxtest.support.settings.Settings internal interface TestCaseGroupProvider { - fun getTestCaseGroup(testCaseGroupId: TestCaseGroupId): TestCaseGroup? + fun getTestCaseGroup(testCaseGroupId: TestCaseGroupId, settings: Settings): TestCaseGroup? } \ No newline at end of file diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/UseExtTestCaseGroupProvider.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/UseExtTestCaseGroupProvider.kt index 5bfd555bda4..a23daa08f5c 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/UseExtTestCaseGroupProvider.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/UseExtTestCaseGroupProvider.kt @@ -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.TestRoots -import org.jetbrains.kotlin.konan.blackboxtest.support.settings.TestSettings +import org.jetbrains.kotlin.konan.blackboxtest.support.settings.TestConfiguration @Target(AnnotationTarget.CLASS) -@TestSettings(providerClass = ExtTestCaseGroupProvider::class, requiredSettings = [TestRoots::class, GeneratedSources::class]) +@TestConfiguration(providerClass = ExtTestCaseGroupProvider::class, requiredSettings = [TestRoots::class, GeneratedSources::class]) annotation class UseExtTestCaseGroupProvider diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/UseStandardTestCaseGroupProvider.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/UseStandardTestCaseGroupProvider.kt index e035b7c474b..ccd1fa5632a 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/UseStandardTestCaseGroupProvider.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/group/UseStandardTestCaseGroupProvider.kt @@ -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.TestRoots -import org.jetbrains.kotlin.konan.blackboxtest.support.settings.TestSettings +import org.jetbrains.kotlin.konan.blackboxtest.support.settings.TestConfiguration @Target(AnnotationTarget.CLASS) -@TestSettings(providerClass = StandardTestCaseGroupProvider::class, requiredSettings = [TestRoots::class, GeneratedSources::class]) +@TestConfiguration(providerClass = StandardTestCaseGroupProvider::class, requiredSettings = [TestRoots::class, GeneratedSources::class]) annotation class UseStandardTestCaseGroupProvider diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/GlobalSettings.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/GlobalSettings.kt deleted file mode 100644 index 2dfa5814293..00000000000 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/GlobalSettings.kt +++ /dev/null @@ -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 = 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 = 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" - } -} diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/SettingsContainers.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/SettingsContainers.kt new file mode 100644 index 00000000000..eefa9d29d13 --- /dev/null +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/SettingsContainers.kt @@ -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) { + private val map: Map, Any> = THashMap, 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 get(clazz: KClass): T = map[clazz] as T? + ?: parent?.get(clazz) + ?: fail { "No such setting: $clazz" } + + inline fun 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) : Settings(parent, settings) +internal class TestRunSettings(parent: TestClassSettings, settings: Iterable) : Settings(parent, settings) diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/Settings.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/TestClassSettings.kt similarity index 53% rename from native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/Settings.kt rename to native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/TestClassSettings.kt index f788950a997..68819279d6c 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/Settings.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/TestClassSettings.kt @@ -5,28 +5,7 @@ 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 kotlin.reflect.KClass - -internal class Settings(settings: Iterable) : TestDisposable(parentDisposable = null) { - private val map: Map, Any> = THashMap, 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 get(clazz: KClass): T = map[clazz] as T? ?: fail { "No such setting: $clazz" } - - inline fun get(): T = get(T::class) -} /** * 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). */ 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) diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/TestSettings.java b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/TestConfiguration.java similarity index 94% rename from native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/TestSettings.java rename to native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/TestConfiguration.java index 6b682912282..e859d54773c 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/TestSettings.java +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/TestConfiguration.java @@ -14,7 +14,7 @@ import java.lang.annotation.*; */ @Target(ElementType.ANNOTATION_TYPE) @Retention(RetentionPolicy.RUNTIME) -public @interface TestSettings { +public @interface TestConfiguration { Class providerClass(); Class[] requiredSettings() default {}; } diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/TestMode.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/TestMode.kt deleted file mode 100644 index 154c6ec4e9f..00000000000 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/TestMode.kt +++ /dev/null @@ -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." - ) -} diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/TestProcessSettings.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/TestProcessSettings.kt new file mode 100644 index 00000000000..7672361bbad --- /dev/null +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/TestProcessSettings.kt @@ -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) { + 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().safeAs()?.getRootCacheDirectory(get(), get(), debuggable) diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/TestRunSettings.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/TestRunSettings.kt new file mode 100644 index 00000000000..d05aa4fc5dd --- /dev/null +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/settings/TestRunSettings.kt @@ -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) { + val enclosingTestInstance: AbstractNativeBlackBoxTest + get() = allInstances.firstOrNull().cast() +} + +internal interface ExternalSourceTransformersProvider { + fun getSourceTransformers(testDataFile: File): ExternalSourceTransformers? +} diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/util/ExternalSourcesTransformation.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/util/ExternalSourcesTransformation.kt index eaa2d40dcd2..9aea95519bd 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/util/ExternalSourcesTransformation.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/util/ExternalSourcesTransformation.kt @@ -6,7 +6,6 @@ package org.jetbrains.kotlin.konan.blackboxtest.support.util 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. @@ -15,10 +14,6 @@ import java.io.File internal typealias ExternalSourceTransformer = (/* file contents */ String) -> /* patched file contents */ String internal typealias ExternalSourceTransformers = List -internal interface ExternalSourceTransformersProvider { - fun getSourceTransformers(testDataFile: File): ExternalSourceTransformers? -} - internal object DiagnosticsRemovingSourceTransformer : ExternalSourceTransformer { override fun invoke(source: String) = source.lineSequence().joinToString("\n") { line -> // Remove all diagnostic parameters from the text. Examples: