[Native][tests] Refactoring: Rework Settings/GlobalSettings

This commit is contained in:
Dmitriy Dolovov
2021-11-25 12:53:49 +03:00
parent 3c47880eba
commit 30e51452b4
12 changed files with 178 additions and 137 deletions
@@ -6,9 +6,11 @@
package org.jetbrains.kotlin.konan.blackboxtest.support
import org.jetbrains.kotlin.konan.blackboxtest.AbstractNativeBlackBoxTest
import org.jetbrains.kotlin.konan.blackboxtest.support.group.StandardTestCaseGroupProvider
import org.jetbrains.kotlin.konan.blackboxtest.support.group.TestCaseGroupProvider
import org.jetbrains.kotlin.konan.blackboxtest.support.group.TestCaseGroupProviding
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.TestRoots
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.TestSettings
import org.jetbrains.kotlin.konan.blackboxtest.support.util.*
import org.jetbrains.kotlin.test.TestMetadata
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertEquals
@@ -35,7 +37,7 @@ class NativeBlackBoxTestSupport : BeforeEachCallback {
enclosingTestInstance.onRunProviderSet()
// Set the essential compiler property.
System.setProperty("kotlin.native.home", getOrCreateGlobalEnvironment().kotlinNativeHome.path)
System.setProperty("kotlin.native.home", getOrCreateGlobalSettings().kotlinNativeHome.path)
}
companion object {
@@ -46,13 +48,14 @@ class NativeBlackBoxTestSupport : BeforeEachCallback {
val enclosingTestClass = enclosingTestClass
return root.getStore(NAMESPACE).getOrComputeIfAbsent(enclosingTestClass.sanitizedName) {
val globalEnvironment = getOrCreateGlobalEnvironment()
val globalSettings = getOrCreateGlobalSettings()
val (testSettings, testSettingsAnnotation) = computeTestSettings(enclosingTestClass)
val testRoots = computeTestRoots()
val testRoots = computeTestRoots(enclosingTestClass)
val uniqueEnclosingClassDirName = globalEnvironment.target.compressedName + "_" + enclosingTestClass.compressedSimpleName
val uniqueEnclosingClassDirName = globalSettings.target.compressedName + "_" + enclosingTestClass.compressedSimpleName
val testSourcesDir = globalEnvironment.baseBuildDir
val testSourcesDir = globalSettings.baseBuildDir
.resolve("bbtest.src")
.resolve(uniqueEnclosingClassDirName)
.ensureExistsAndIsEmptyDirectory() // Clean-up the directory with all potentially stale generated sources.
@@ -61,7 +64,7 @@ class NativeBlackBoxTestSupport : BeforeEachCallback {
.resolve("__shared_modules__")
.ensureExistsAndIsEmptyDirectory()
val testBinariesDir = globalEnvironment.baseBuildDir
val testBinariesDir = globalSettings.baseBuildDir
.resolve("bbtest.bin")
.resolve(uniqueEnclosingClassDirName)
.ensureExistsAndIsEmptyDirectory() // Clean-up the directory with all potentially stale artifacts.
@@ -70,8 +73,8 @@ class NativeBlackBoxTestSupport : BeforeEachCallback {
.resolve("__shared_modules__")
.ensureExistsAndIsEmptyDirectory()
val environment = TestEnvironment(
globalEnvironment = globalEnvironment,
val settings = Settings(
global = globalSettings,
testRoots = testRoots,
testSourcesDir = testSourcesDir,
sharedSourcesDir = sharedSourcesDir,
@@ -79,16 +82,16 @@ class NativeBlackBoxTestSupport : BeforeEachCallback {
sharedBinariesDir = sharedBinariesDir
)
val testCaseGroupProvider = requiredTestClass.createTestCaseGroupProvider(environment)
val testCaseGroupProvider = createTestCaseGroupProvider(settings, testSettings, testSettingsAnnotation)
TestRunProvider(environment, testCaseGroupProvider)
TestRunProvider(settings, testCaseGroupProvider)
}.cast()
}
private fun ExtensionContext.getOrCreateGlobalEnvironment(): GlobalTestEnvironment =
root.getStore(NAMESPACE).getOrComputeIfAbsent(GlobalTestEnvironment::class.java.sanitizedName) {
private fun ExtensionContext.getOrCreateGlobalSettings(): GlobalSettings =
root.getStore(NAMESPACE).getOrComputeIfAbsent(GlobalSettings::class.java.sanitizedName) {
// Create with the default settings.
GlobalTestEnvironment()
GlobalSettings()
}.cast()
private val ExtensionContext.enclosingTestInstance: AbstractNativeBlackBoxTest
@@ -97,9 +100,7 @@ class NativeBlackBoxTestSupport : BeforeEachCallback {
private val ExtensionContext.enclosingTestClass: Class<*>
get() = generateSequence(requiredTestClass) { it.enclosingClass }.last()
private fun ExtensionContext.computeTestRoots(): TestRoots {
val enclosingTestClass = enclosingTestClass
private fun computeTestRoots(enclosingTestClass: Class<*>): TestRoots {
val testRoots: Set<File> = when (val outermostTestMetadata = enclosingTestClass.getAnnotation(TestMetadata::class.java)) {
null -> {
enclosingTestClass.declaredClasses.mapNotNullToSet { nestedClass ->
@@ -125,35 +126,48 @@ class NativeBlackBoxTestSupport : BeforeEachCallback {
return TestRoots(testRoots, baseDir)
}
private fun Class<*>.createTestCaseGroupProvider(environment: TestEnvironment): TestCaseGroupProvider {
val (providerAnnotation, providerAnnotationClass, providerClass) = annotations.asSequence()
.mapNotNull { annotation ->
val annotationClass = annotation.annotationClass
val providerClass = annotationClass.findAnnotation<TestCaseGroupProviding>()?.providerClass ?: return@mapNotNull null
Triple(annotation, annotationClass, providerClass)
private fun computeTestSettings(enclosingTestClass: Class<*>): Pair<TestSettings, Annotation?> {
val findTestSettings: Class<*>.() -> Pair<TestSettings, Annotation>? = {
annotations.asSequence().mapNotNull { annotation ->
val testSettings = annotation.annotationClass.findAnnotation<TestSettings>() ?: return@mapNotNull null
testSettings to annotation
}.firstOrNull()
?: return StandardTestCaseGroupProvider(environment) // Fallback if there is no annotation.
}
return enclosingTestClass.findTestSettings()
?: enclosingTestClass.declaredClasses.firstNotNullOfOrNull { it.findTestSettings() }
?: (TestSettings.DEFAULT to null) // Fallback if there is no annotation.
}
private fun createTestCaseGroupProvider(
settings: Settings,
testSettings: TestSettings,
testSettingsAnnotation: Annotation?
): TestCaseGroupProvider {
// First, try to find two-argument constructor.
providerClass.constructors.asSequence()
.forEach { c ->
val (p1, p2) = c.parameters.takeIf { it.size == 2 } ?: return@forEach
val provider = when {
p1.hasTypeOf<TestEnvironment>() && p2.hasTypeOf(providerAnnotationClass) -> c.call(environment, providerAnnotation)
p1.hasTypeOf(providerAnnotationClass) && p2.hasTypeOf<TestEnvironment>() -> c.call(providerAnnotation, environment)
else -> return@forEach
if (testSettingsAnnotation != null) {
val testSettingsAnnotationClass = testSettingsAnnotation.annotationClass
testSettings.providerClass.constructors.asSequence()
.forEach { c ->
val (p1, p2) = c.parameters.takeIf { it.size == 2 } ?: return@forEach
@Suppress("Reformat") val provider = when {
p1.hasTypeOf<Settings>() && p2.hasTypeOf(testSettingsAnnotationClass) -> c.call(settings, testSettingsAnnotation)
p1.hasTypeOf(testSettingsAnnotationClass) && p2.hasTypeOf<Settings>() -> c.call(testSettingsAnnotation, settings)
else -> return@forEach
}
return provider.cast()
}
return provider.cast()
}
}
// Next, try to find a single-argument constructor.
providerClass.constructors.asSequence()
testSettings.providerClass.constructors.asSequence()
.forEach { c ->
val p = c.parameters.singleOrNull() ?: return@forEach
if (p.hasTypeOf<TestEnvironment>()) return c.call(environment).cast()
if (p.hasTypeOf<Settings>()) return c.call(settings).cast()
}
fail { "No suitable constructor for $providerClass" }
fail { "No suitable constructor for ${testSettings.providerClass}" }
}
private inline fun <reified T : Any> KParameter.hasTypeOf(): Boolean = hasTypeOf(T::class)
@@ -8,6 +8,7 @@
package org.jetbrains.kotlin.konan.blackboxtest.support
import org.jetbrains.kotlin.konan.blackboxtest.support.TestModule.Companion.allDependencies
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.TestMode
import org.jetbrains.kotlin.konan.blackboxtest.support.util.*
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertTrue
@@ -15,6 +15,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.Settings
import org.jetbrains.kotlin.konan.blackboxtest.support.util.*
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
import java.io.*
@@ -22,7 +23,7 @@ import kotlin.time.Duration
import kotlin.time.ExperimentalTime
import kotlin.time.measureTime
internal class TestCompilationFactory(private val environment: TestEnvironment) {
internal class TestCompilationFactory(private val settings: Settings) {
private val cachedCompilations = ThreadSafeCache<TestCompilationCacheKey, TestCompilation>()
private sealed interface TestCompilationCacheKey {
@@ -46,7 +47,7 @@ internal class TestCompilationFactory(private val environment: TestEnvironment)
val entryPoint = testCases.singleOrNull()?.safeExtras<NoTestRunnerExtras>()?.entryPoint
TestCompilationImpl(
environment = environment,
settings = settings,
freeCompilerArgs = freeCompilerArgs,
sourceModules = rootModules,
dependencies = TestCompilationDependencies(libraries = libraries, friends = friends),
@@ -54,7 +55,7 @@ internal class TestCompilationFactory(private val environment: TestEnvironment)
specificCompilerArgs = {
add("-produce", "program")
if (entryPoint != null) add("-entry", entryPoint) else add("-generate-test-runner")
environment.globalEnvironment.getRootCacheDirectory(debuggable = true)?.let { rootCacheDir ->
settings.global.getRootCacheDirectory(debuggable = true)?.let { rootCacheDir ->
add("-Xcache-directory=$rootCacheDir")
}
}
@@ -75,7 +76,7 @@ internal class TestCompilationFactory(private val environment: TestEnvironment)
return cachedCompilations.computeIfAbsent(cacheKey) {
TestCompilationImpl(
environment = environment,
settings = settings,
freeCompilerArgs = freeCompilerArgs,
sourceModules = sourceModules,
dependencies = TestCompilationDependencies(libraries = libraries, friends = friends),
@@ -87,15 +88,15 @@ internal class TestCompilationFactory(private val environment: TestEnvironment)
private fun artifactFileForExecutable(modules: Set<TestModule.Exclusive>) = when (modules.size) {
1 -> artifactFileForExecutable(modules.first())
else -> multiModuleArtifactFile(modules, environment.globalEnvironment.target.family.exeSuffix)
else -> multiModuleArtifactFile(modules, settings.global.target.family.exeSuffix)
}
private fun artifactFileForExecutable(module: TestModule.Exclusive) =
singleModuleArtifactFile(module, environment.globalEnvironment.target.family.exeSuffix)
singleModuleArtifactFile(module, settings.global.target.family.exeSuffix)
private fun artifactFileForKlib(module: TestModule, freeCompilerArgs: TestCompilerArgs) = when (module) {
is TestModule.Exclusive -> singleModuleArtifactFile(module, "klib")
is TestModule.Shared -> environment.sharedBinariesDir.resolve("${module.name}-${prettyHash(freeCompilerArgs.hashCode())}.klib")
is TestModule.Shared -> settings.sharedBinariesDir.resolve("${module.name}-${prettyHash(freeCompilerArgs.hashCode())}.klib")
}
private fun singleModuleArtifactFile(module: TestModule.Exclusive, extension: String): File {
@@ -141,7 +142,7 @@ internal class TestCompilationFactory(private val environment: TestEnvironment)
}
private fun artifactDirForPackageName(packageName: PackageFQN?): File {
val baseDir = environment.testBinariesDir
val baseDir = settings.testBinariesDir
val outputDir = if (packageName != null) baseDir.resolve(packageName.compressedPackageName) else baseDir
outputDir.mkdirs()
@@ -221,7 +222,7 @@ internal class TestCompilationDependencies(
}
private class TestCompilationImpl(
private val environment: TestEnvironment,
private val settings: Settings,
private val freeCompilerArgs: TestCompilerArgs,
private val sourceModules: Collection<TestModule>,
private val dependencies: TestCompilationDependencies,
@@ -241,8 +242,8 @@ private class TestCompilationImpl(
add(
"-enable-assertions",
"-g",
"-target", environment.globalEnvironment.target.name,
"-repo", environment.globalEnvironment.kotlinNativeHome.resolve("klib").path,
"-target", settings.global.target.name,
"-repo", settings.global.kotlinNativeHome.resolve("klib").path,
"-output", expectedArtifactFile.path,
"-Xskip-prerelease-check",
"-Xverify-ir",
@@ -273,7 +274,7 @@ private class TestCompilationImpl(
val (loggedCompilerCall: LoggedData, result: TestCompilationResult.ImmediateResult) = try {
val (exitCode, compilerOutput, compilerOutputHasErrors, duration) = callCompiler(
compilerArgs = compilerArgs,
lazyKotlinNativeClassLoader = environment.globalEnvironment.lazyKotlinNativeClassLoader
lazyKotlinNativeClassLoader = settings.global.lazyKotlinNativeClassLoader
)
val loggedCompilerCall =
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.konan.blackboxtest.support.TestCompilationResult.Com
import org.jetbrains.kotlin.konan.blackboxtest.support.group.TestCaseGroupProvider
import org.jetbrains.kotlin.konan.blackboxtest.support.runner.AbstractRunner
import org.jetbrains.kotlin.konan.blackboxtest.support.runner.LocalTestRunner
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.Settings
import org.jetbrains.kotlin.konan.blackboxtest.support.util.ThreadSafeCache
import org.jetbrains.kotlin.konan.blackboxtest.support.util.TreeNode
import org.jetbrains.kotlin.konan.blackboxtest.support.util.buildTree
@@ -24,10 +25,10 @@ import org.junit.jupiter.api.extension.ExtensionContext
import java.io.File
internal class TestRunProvider(
private val environment: TestEnvironment,
private val settings: Settings,
private val testCaseGroupProvider: TestCaseGroupProvider
) : ExtensionContext.Store.CloseableResource {
private val compilationFactory = TestCompilationFactory(environment)
private val compilationFactory = TestCompilationFactory(settings)
private val cachedCompilations = ThreadSafeCache<TestCompilationCacheKey, TestCompilation>()
fun setProcessors(testDataFile: File, sourceTransformers: List<(String) -> String>) {
@@ -114,7 +115,7 @@ internal class TestRunProvider(
}
private fun <T> withTestExecutable(testDataFile: File, action: (TestCase, TestExecutable) -> T): T {
environment.assertNotDisposed()
settings.assertNotDisposed()
val testDataDir = testDataFile.parentFile
val testDataFileName = testDataFile.name
@@ -173,19 +174,19 @@ internal class TestRunProvider(
}
// Currently, only local test runner is supported.
fun createRunner(testRun: TestRun): AbstractRunner<*> = when (val target = environment.globalEnvironment.target) {
environment.globalEnvironment.hostTarget -> LocalTestRunner(testRun, environment.globalEnvironment.executionTimeout)
fun createRunner(testRun: TestRun): AbstractRunner<*> = when (val target = settings.global.target) {
settings.global.hostTarget -> LocalTestRunner(testRun, settings.global.executionTimeout)
else -> fail {
"""
Running at non-host target is not supported yet.
Compilation target: $target
Host target: ${environment.globalEnvironment.hostTarget}
Host target: ${settings.global.hostTarget}
""".trimIndent()
}
}
override fun close() {
Disposer.dispose(environment)
Disposer.dispose(settings)
}
private sealed class TestCompilationCacheKey {
@@ -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.Settings
import org.jetbrains.kotlin.konan.blackboxtest.support.util.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
@@ -37,8 +38,8 @@ import org.jetbrains.kotlin.utils.addIfNotNull
import java.io.File
internal class ExtTestCaseGroupProvider(
private val environment: TestEnvironment
) : TestCaseGroupProvider, TestDisposable(parentDisposable = environment) {
private val settings: Settings
) : TestCaseGroupProvider, TestDisposable(parentDisposable = settings) {
private val sourceTransformers: MutableMap<String, List<(String) -> String>> = mutableMapOf()
private val structureFactory = ExtTestDataFileStructureFactory(parentDisposable = this)
private val sharedModules = ThreadSafeCache<String, TestModule.Shared?>()
@@ -58,7 +59,7 @@ internal class ExtTestCaseGroupProvider(
testDataFiles.forEach { testDataFile ->
val extTestDataFile = ExtTestDataFile(
environment, structureFactory, testDataFile, sourceTransformers[testDataFile.canonicalPath] ?: listOf()
settings, structureFactory, testDataFile, sourceTransformers[testDataFile.canonicalPath] ?: listOf()
)
if (extTestDataFile.isRelevant)
@@ -126,7 +127,7 @@ internal class ExtTestCaseGroupProvider(
}
private class ExtTestDataFile(
private val environment: TestEnvironment,
private val settings: Settings,
structureFactory: ExtTestDataFileStructureFactory,
private val testDataFile: File,
sourceTransformers: List<(String) -> String>
@@ -139,7 +140,7 @@ private class ExtTestDataFile(
}
}
private val settings by lazy {
private val testDataFileSettings by lazy {
val optIns = structure.directives.multiValues(OPT_IN_DIRECTIVE)
val optInsForSourceCode = optIns subtract OPT_INS_PURELY_FOR_COMPILER
val optInsForCompiler = optIns intersect OPT_INS_PURELY_FOR_COMPILER
@@ -154,12 +155,12 @@ private class ExtTestDataFile(
optInsForCompiler = optInsForCompiler,
expectActualLinker = EXPECT_ACTUAL_LINKER_DIRECTIVE in structure.directives,
generatedSourcesDir = computeGeneratedSourcesDir(
testDataBaseDir = environment.testRoots.baseDir,
testDataBaseDir = settings.testRoots.baseDir,
testDataFile = testDataFile,
generatedSourcesBaseDir = environment.testSourcesDir
generatedSourcesBaseDir = settings.testSourcesDir
),
nominalPackageName = computePackageName(
testDataBaseDir = environment.testRoots.baseDir,
testDataBaseDir = settings.testRoots.baseDir,
testDataFile = testDataFile
)
)
@@ -168,16 +169,16 @@ private class ExtTestDataFile(
val isRelevant: Boolean =
isCompatibleTarget(TargetBackend.NATIVE, testDataFile) // Checks TARGET_BACKEND/DONT_TARGET_EXACT_BACKEND directives.
&& !isIgnoredTarget(TargetBackend.NATIVE, testDataFile) // Checks IGNORE_BACKEND directive.
&& settings.languageSettings.none { it in INCOMPATIBLE_LANGUAGE_SETTINGS }
&& testDataFileSettings.languageSettings.none { it in INCOMPATIBLE_LANGUAGE_SETTINGS }
&& INCOMPATIBLE_DIRECTIVES.none { it in structure.directives }
&& structure.directives[API_VERSION_DIRECTIVE] !in INCOMPATIBLE_API_VERSIONS
&& structure.directives[LANGUAGE_VERSION_DIRECTIVE] !in INCOMPATIBLE_LANGUAGE_VERSIONS
private fun assembleFreeCompilerArgs(): TestCompilerArgs {
val args = mutableListOf<String>()
settings.languageSettings.sorted().mapTo(args) { "-XXLanguage:$it" }
settings.optInsForCompiler.sorted().mapTo(args) { "-Xopt-in=$it" }
if (settings.expectActualLinker) args += "-Xexpect-actual-linker"
testDataFileSettings.languageSettings.sorted().mapTo(args) { "-XXLanguage:$it" }
testDataFileSettings.optInsForCompiler.sorted().mapTo(args) { "-Xopt-in=$it" }
if (testDataFileSettings.expectActualLinker) args += "-Xexpect-actual-linker"
return TestCompilerArgs(args)
}
@@ -274,7 +275,7 @@ private class ExtTestDataFile(
private fun patchPackageNames(isStandaloneTest: Boolean) = with(structure) {
if (isStandaloneTest) return // Don't patch packages for standalone tests.
val basePackageName = FqName(settings.nominalPackageName)
val basePackageName = FqName(testDataFileSettings.nominalPackageName)
val oldPackageNames: Set<FqName> = filesToTransform.mapToSet { it.packageFqName }
val oldToNewPackageNameMapping: Map<FqName, FqName> = oldPackageNames.associateWith { oldPackageName ->
@@ -450,7 +451,7 @@ private class ExtTestDataFile(
fun getAnnotationText(fullyQualifiedName: String) = "@file:${OPT_IN_ANNOTATION_NAME.asString()}($fullyQualifiedName::class)"
// Every OptIn specified in test directive should be represented as a file-level annotation.
settings.optInsForSourceCode.mapTo(allFileLevelAnnotations, ::getAnnotationText)
testDataFileSettings.optInsForSourceCode.mapTo(allFileLevelAnnotations, ::getAnnotationText)
// Now, collect file-level annotations already present in test files.
filesToTransform.forEach { handler ->
@@ -550,7 +551,7 @@ private class ExtTestDataFile(
}
if (!isStandaloneTest) {
append("package ").appendLine(settings.nominalPackageName)
append("package ").appendLine(testDataFileSettings.nominalPackageName)
appendLine()
}
@@ -574,10 +575,10 @@ private class ExtTestDataFile(
sharedModules: ThreadSafeCache<String, TestModule.Shared?>
): TestCase = with(structure) {
val modules = generateModules(
testCaseDir = settings.generatedSourcesDir,
testCaseDir = testDataFileSettings.generatedSourcesDir,
findOrGenerateSharedModule = { moduleName: String, generator: SharedModuleGenerator ->
sharedModules.computeIfAbsent(moduleName) {
generator(environment.sharedSourcesDir)
generator(settings.sharedSourcesDir)
}
}
)
@@ -587,7 +588,7 @@ private class ExtTestDataFile(
modules = modules,
freeCompilerArgs = assembleFreeCompilerArgs(),
origin = TestOrigin.SingleTestDataFile(testDataFile),
nominalPackageName = settings.nominalPackageName,
nominalPackageName = testDataFileSettings.nominalPackageName,
expectedOutputDataFile = null,
extras = WithTestRunnerExtras.EMPTY
)
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.konan.blackboxtest.support.group
import org.jetbrains.kotlin.konan.blackboxtest.support.*
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.settings.Settings
import org.jetbrains.kotlin.konan.blackboxtest.support.util.DEFAULT_FILE_NAME
import org.jetbrains.kotlin.konan.blackboxtest.support.util.ThreadSafeFactory
import org.jetbrains.kotlin.konan.blackboxtest.support.util.computeGeneratedSourcesDir
@@ -22,7 +23,7 @@ import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
import org.jetbrains.kotlin.test.services.impl.RegisteredDirectivesParser
import java.io.File
internal class StandardTestCaseGroupProvider(private val environment: TestEnvironment) : TestCaseGroupProvider {
internal class StandardTestCaseGroupProvider(private val settings: Settings) : TestCaseGroupProvider {
val sourceTransformers: MutableMap<String, List<(String) -> String>> = mutableMapOf()
// Load test cases in groups on demand.
@@ -51,13 +52,13 @@ internal class StandardTestCaseGroupProvider(private val environment: TestEnviro
private fun createTestCase(testDataFile: File): TestCase {
val generatedSourcesDir = computeGeneratedSourcesDir(
testDataBaseDir = environment.testRoots.baseDir,
testDataBaseDir = settings.testRoots.baseDir,
testDataFile = testDataFile,
generatedSourcesBaseDir = environment.testSourcesDir
generatedSourcesBaseDir = settings.testSourcesDir
)
val nominalPackageName = computePackageName(
testDataBaseDir = environment.testRoots.baseDir,
testDataBaseDir = settings.testRoots.baseDir,
testDataFile = testDataFile
)
@@ -1,11 +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.group
import kotlin.reflect.KClass
@Target(AnnotationTarget.ANNOTATION_CLASS)
internal annotation class TestCaseGroupProviding(val providerClass: KClass<out TestCaseGroupProvider>)
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.konan.blackboxtest.support.group
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.TestSettings
@Target(AnnotationTarget.CLASS)
@TestCaseGroupProviding(ExtTestCaseGroupProvider::class)
@TestSettings(ExtTestCaseGroupProvider::class)
annotation class UseExtTestCaseGroupProvider
@@ -3,44 +3,34 @@
* 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
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.konan.blackboxtest.support.util.TestDisposable
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
import org.jetbrains.kotlin.test.services.JUnit5Assertions
import java.io.File
import java.net.URLClassLoader
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
internal class TestEnvironment(
val globalEnvironment: GlobalTestEnvironment,
val testRoots: TestRoots, // The directories with original sources (aka testData).
val testSourcesDir: File, // The directory with generated (preprocessed) test sources.
val sharedSourcesDir: File, // The directory with the sources of the shared modules (i.e. the modules that are widely used in multiple tests).
val testBinariesDir: File, // The directory with compiled test binaries (klibs) and executable files).
val sharedBinariesDir: File // The directory with compiled shared modules (klibs).
) : TestDisposable(parentDisposable = null)
internal class GlobalTestEnvironment(
internal class GlobalSettings(
val target: KonanTarget = HostManager.host,
val kotlinNativeHome: File = defaultKotlinNativeHome,
val lazyKotlinNativeClassLoader: Lazy<ClassLoader> = defaultKotlinNativeClassLoader,
val testMode: TestMode = defaultTestMode,
val cacheSettings: TestCacheSettings = defaultCacheSettings,
val cacheSettings: CacheSettings = defaultCacheSettings,
val executionTimeout: Duration = defaultExecutionTimeout,
val baseBuildDir: File = projectBuildDir
) {
val hostTarget: KonanTarget = HostManager.host
fun getRootCacheDirectory(debuggable: Boolean): File? =
(cacheSettings as? TestCacheSettings.WithCache)?.getRootCacheDirectory(this, debuggable)
(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" }
get() = System.getProperty(KOTLIN_NATIVE_HOME)?.let(::File) ?: JUnit5Assertions.fail { "Non-specified $KOTLIN_NATIVE_HOME system property" }
// Use isolated cached class loader.
private val defaultKotlinNativeClassLoader: Lazy<ClassLoader> = lazy {
@@ -48,7 +38,7 @@ internal class GlobalTestEnvironment(
?.split(':', ';')
?.map { File(it).toURI().toURL() }
?.toTypedArray()
?: fail { "Non-specified $COMPILER_CLASSPATH system property" }
?: JUnit5Assertions.fail { "Non-specified $COMPILER_CLASSPATH system property" }
URLClassLoader(nativeClassPath, /* no parent class loader */ null).apply { setDefaultAssertionStatus(true) }
}
@@ -56,7 +46,7 @@ internal class GlobalTestEnvironment(
private val defaultTestMode: TestMode = run {
val testModeName = System.getProperty(TEST_MODE) ?: return@run TestMode.WITH_MODULES
TestMode.values().firstOrNull { it.name == testModeName } ?: fail {
TestMode.values().firstOrNull { it.name == testModeName } ?: JUnit5Assertions.fail {
buildString {
appendLine("Unknown test mode name $testModeName.")
appendLine("One of the following test modes should be passed through $TEST_MODE system property:")
@@ -67,27 +57,27 @@ internal class GlobalTestEnvironment(
}
}
private val defaultCacheSettings: TestCacheSettings = run {
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" }
useCacheValue.toBooleanStrictOrNull() ?: JUnit5Assertions.fail { "Invalid value for $USE_CACHE system property: $useCacheValue" }
} else
true
if (useCache) TestCacheSettings.WithCache else TestCacheSettings.WithoutCache
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" }
?: JUnit5Assertions.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" }
get() = System.getenv(PROJECT_BUILD_DIR)?.let(::File) ?: JUnit5Assertions.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"
@@ -100,30 +90,11 @@ internal class GlobalTestEnvironment(
}
}
internal class TestRoots(
val roots: Set<File>,
val baseDir: File
)
internal sealed interface CacheSettings {
object WithoutCache : CacheSettings
// 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."
)
}
internal sealed interface TestCacheSettings {
object WithoutCache : TestCacheSettings
object WithCache : TestCacheSettings {
fun getRootCacheDirectory(globalEnvironment: GlobalTestEnvironment, debuggable: Boolean): File? = with(globalEnvironment) {
object WithCache : CacheSettings {
fun getRootCacheDirectory(settings: GlobalSettings, debuggable: Boolean): File? = with(settings) {
kotlinNativeHome.resolve("klib/cache").resolve(getCacheDirName(target, debuggable)).takeIf { it.exists() }
}
@@ -0,0 +1,23 @@
/*
* 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.support.util.TestDisposable
import java.io.File
internal class Settings(
val global: GlobalSettings,
val testRoots: TestRoots, // The directories with original sources (aka testData).
val testSourcesDir: File, // The directory with generated (preprocessed) test sources.
val sharedSourcesDir: File, // The directory with the sources of the shared modules (i.e. the modules that are widely used in multiple tests).
val testBinariesDir: File, // The directory with compiled test binaries (klibs) and executable files).
val sharedBinariesDir: File // The directory with compiled shared modules (klibs).
) : TestDisposable(parentDisposable = null)
internal class TestRoots(
val roots: Set<File>,
val baseDir: File
)
@@ -0,0 +1,20 @@
/*
* 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."
)
}
@@ -0,0 +1,17 @@
/*
* 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.support.group.StandardTestCaseGroupProvider
import org.jetbrains.kotlin.konan.blackboxtest.support.group.TestCaseGroupProvider
import kotlin.reflect.KClass
@Target(AnnotationTarget.ANNOTATION_CLASS)
internal annotation class TestSettings(val providerClass: KClass<out TestCaseGroupProvider>) {
companion object {
val DEFAULT = TestSettings(StandardTestCaseGroupProvider::class)
}
}