[Native][tests] Refactoring: Use typed compilations and artifacts
^KT-50775
This commit is contained in:
+16
-19
@@ -9,11 +9,13 @@ import com.intellij.testFramework.TestDataFile
|
||||
import org.jetbrains.kotlin.klib.AbstractKlibABITestCase
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.*
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.TestCase.WithTestRunnerExtras
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilation
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationDependencies
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationResult
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.ExecutableCompilation
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.ExistingLibraryDependency
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.LibraryCompilation
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationArtifact
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationArtifact.Executable
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationDependencyType.Library
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationResult.Companion.assertSuccess
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationResult.Companion.assertSuccessfullyCompiled
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.runner.TestExecutable
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.KotlinNativeHome
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.KotlinNativeTargets
|
||||
@@ -27,10 +29,6 @@ import java.io.File
|
||||
|
||||
@Tag("klib")
|
||||
abstract class AbstractNativeKlibABITest : AbstractNativeSimpleTest() {
|
||||
private val compilationResults: MutableMap<File, TestCompilationResult.Success> by lazy {
|
||||
mutableMapOf(stdlibFile to TestCompilationResult.ExistingArtifact(stdlibFile))
|
||||
}
|
||||
|
||||
protected fun runTest(@TestDataFile testPath: String): Unit = AbstractKlibABITestCase.doTest(
|
||||
testDir = getAbsoluteFile(testPath),
|
||||
buildDir = buildDir,
|
||||
@@ -48,15 +46,15 @@ abstract class AbstractNativeKlibABITest : AbstractNativeSimpleTest() {
|
||||
|
||||
val testCase = createTestCase(module, COMPILER_ARGS_FOR_KLIB)
|
||||
|
||||
val compilation = TestCompilation.createForKlib(
|
||||
val compilation = LibraryCompilation(
|
||||
settings = testRunSettings,
|
||||
freeCompilerArgs = testCase.freeCompilerArgs,
|
||||
sourceModules = testCase.modules,
|
||||
dependencies = createCompilationDependencies(moduleDependencies),
|
||||
expectedKlibFile = klibFile,
|
||||
dependencies = createExistingDependencies(moduleDependencies),
|
||||
expectedArtifact = TestCompilationArtifact.KLIB(klibFile),
|
||||
)
|
||||
|
||||
compilationResults[klibFile] = compilation.result.assertSuccess() // <-- trigger compilation
|
||||
compilation.result.assertSuccess() // <-- trigger compilation
|
||||
}
|
||||
|
||||
private fun buildBinaryAndRun(allDependencies: Collection<File>) {
|
||||
@@ -72,16 +70,16 @@ abstract class AbstractNativeKlibABITest : AbstractNativeSimpleTest() {
|
||||
|
||||
val testCase = createTestCase(module, COMPILER_ARGS_FOR_EXECUTABLE)
|
||||
|
||||
val compilation = TestCompilation.createForExecutable(
|
||||
val compilation = ExecutableCompilation(
|
||||
settings = testRunSettings,
|
||||
freeCompilerArgs = testCase.freeCompilerArgs,
|
||||
sourceModules = testCase.modules,
|
||||
extras = testCase.extras,
|
||||
dependencies = createCompilationDependencies(allDependencies),
|
||||
expectedExecutableFile = executableFile
|
||||
dependencies = createExistingDependencies(allDependencies),
|
||||
expectedArtifact = Executable(executableFile)
|
||||
)
|
||||
|
||||
val compilationResult = compilation.result.assertSuccessfullyCompiled() // <-- trigger compilation
|
||||
val compilationResult = compilation.result.assertSuccess() // <-- trigger compilation
|
||||
val executable = TestExecutable(executableFile, compilationResult.loggedData)
|
||||
|
||||
runExecutableAndVerify(testCase, executable) // <-- run executable and verify
|
||||
@@ -106,9 +104,8 @@ abstract class AbstractNativeKlibABITest : AbstractNativeSimpleTest() {
|
||||
initialize(null)
|
||||
}
|
||||
|
||||
private fun createCompilationDependencies(dependencies: Collection<File>) = TestCompilationDependencies(
|
||||
libraries = dependencies.map(TestCompilation.Companion::createForExistingArtifact)
|
||||
)
|
||||
private fun createExistingDependencies(dependencies: Collection<File>) =
|
||||
dependencies.map { ExistingLibraryDependency(TestCompilationArtifact.KLIB(it), Library) }
|
||||
|
||||
private val buildDir: File get() = testRunSettings.get<SimpleTestDirectories>().testBuildDir
|
||||
private val stdlibFile: File get() = testRunSettings.get<KotlinNativeHome>().dir.resolve("klib/common/stdlib")
|
||||
|
||||
+95
-198
@@ -8,133 +8,21 @@ package org.jetbrains.kotlin.konan.blackboxtest.support.compilation
|
||||
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.*
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.TestCase.*
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilation.Companion.resultingArtifactPath
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationResult.Companion.assertSuccess
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationArtifact.Executable
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationArtifact.KLIB
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationDependencyType.*
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.*
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.CacheKind.Companion.rootCacheDir
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.ArgsBuilder
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.buildArgs
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.flatMapToSet
|
||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
|
||||
import java.io.File
|
||||
|
||||
internal interface TestCompilation {
|
||||
val result: TestCompilationResult
|
||||
|
||||
companion object {
|
||||
val TestCompilation.resultingArtifactPath: String
|
||||
get() = result.assertSuccess().resultingArtifact.path
|
||||
|
||||
fun createForKlib(
|
||||
settings: Settings,
|
||||
freeCompilerArgs: TestCompilerArgs,
|
||||
sourceModules: Collection<TestModule>,
|
||||
dependencies: TestCompilationDependencies,
|
||||
expectedKlibFile: File
|
||||
): TestCompilation = TestCompilationImpl.create(
|
||||
settings = settings,
|
||||
freeCompilerArgs = freeCompilerArgs,
|
||||
sourceModules = sourceModules,
|
||||
dependencies = dependencies,
|
||||
expectedArtifactFile = expectedKlibFile,
|
||||
specificCompilerArgs = TestCompilationImpl.compilerArgsForKlib()
|
||||
)
|
||||
|
||||
fun createForExecutable(
|
||||
settings: Settings,
|
||||
freeCompilerArgs: TestCompilerArgs,
|
||||
sourceModules: Collection<TestModule>,
|
||||
extras: Extras,
|
||||
dependencies: TestCompilationDependencies,
|
||||
expectedExecutableFile: File
|
||||
): TestCompilation = TestCompilationImpl.create(
|
||||
settings = settings,
|
||||
freeCompilerArgs = freeCompilerArgs,
|
||||
sourceModules = sourceModules,
|
||||
dependencies = dependencies,
|
||||
expectedArtifactFile = expectedExecutableFile,
|
||||
specificCompilerArgs = TestCompilationImpl.compilerArgsForExecutable(settings.get(), extras)
|
||||
)
|
||||
|
||||
fun createForExistingArtifact(resultingArtifact: File): TestCompilation =
|
||||
object : TestCompilation {
|
||||
override val result = TestCompilationResult.ExistingArtifact(resultingArtifact)
|
||||
}
|
||||
}
|
||||
internal abstract class TestCompilation<A : TestCompilationArtifact> {
|
||||
abstract val result: TestCompilationResult<out A>
|
||||
}
|
||||
|
||||
internal sealed interface TestCompilationResult {
|
||||
sealed interface ImmediateResult : TestCompilationResult {
|
||||
val loggedData: LoggedData
|
||||
}
|
||||
|
||||
sealed interface Success : TestCompilationResult {
|
||||
val resultingArtifact: File
|
||||
}
|
||||
|
||||
sealed interface Failure : ImmediateResult
|
||||
|
||||
data class ExistingArtifact(override val resultingArtifact: File) : Success
|
||||
data class CompiledArtifact(override val resultingArtifact: File, override val loggedData: LoggedData.CompilerCall) :
|
||||
ImmediateResult, Success
|
||||
|
||||
data class CompilerFailure(override val loggedData: LoggedData.CompilerCall) : Failure
|
||||
data class UnexpectedFailure(override val loggedData: LoggedData.CompilerCallUnexpectedFailure) : Failure
|
||||
data class DependencyFailures(val causes: Set<Failure>) : TestCompilationResult
|
||||
|
||||
companion object {
|
||||
fun TestCompilationResult.assertSuccess(): Success = when (this) {
|
||||
is Success -> this
|
||||
is Failure -> fail { describeFailure() }
|
||||
is DependencyFailures -> fail { describeDependencyFailures() }
|
||||
}
|
||||
|
||||
fun TestCompilationResult.assertSuccessfullyCompiled(): CompiledArtifact = when (val result = assertSuccess()) {
|
||||
is CompiledArtifact -> result
|
||||
is ExistingArtifact -> fail { "Test compilation result contains pre-existing artifact: ${result.resultingArtifact}" }
|
||||
}
|
||||
|
||||
private fun Failure.describeFailure() = loggedData.withErrorMessage(
|
||||
when (this@describeFailure) {
|
||||
is CompilerFailure -> "Compilation failed."
|
||||
is UnexpectedFailure -> "Compilation failed with unexpected exception."
|
||||
}
|
||||
)
|
||||
|
||||
private fun DependencyFailures.describeDependencyFailures() =
|
||||
buildString {
|
||||
appendLine("Compilation aborted due to errors in dependency compilations (${causes.size} items). See details below.")
|
||||
appendLine()
|
||||
causes.forEachIndexed { index, cause ->
|
||||
append("#").append(index + 1).append(". ")
|
||||
appendLine(cause.describeFailure())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The dependencies of a particular [TestCompilation].
|
||||
*
|
||||
* [libraries] - the [TestCompilation]s (modules) that should yield KLIBs to be consumed as dependency libraries in the current compilation.
|
||||
* [friends] - similarly but friend modules (-friend-modules).
|
||||
* [includedLibraries] - similarly but included modules (-Xinclude).
|
||||
*/
|
||||
internal class TestCompilationDependencies(
|
||||
val libraries: Collection<TestCompilation> = emptyList(),
|
||||
val friends: Collection<TestCompilation> = emptyList(),
|
||||
val includedLibraries: Collection<TestCompilation> = emptyList()
|
||||
) {
|
||||
fun collectFailures(): Set<TestCompilationResult.Failure> = listOf(libraries, friends, includedLibraries)
|
||||
.flatten()
|
||||
.flatMapToSet { compilation ->
|
||||
when (val result = compilation.result) {
|
||||
is TestCompilationResult.Failure -> listOf(result)
|
||||
is TestCompilationResult.DependencyFailures -> result.causes
|
||||
is TestCompilationResult.Success -> emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TestCompilationImpl(
|
||||
internal abstract class BaseCompilation<A : TestCompilationArtifact>(
|
||||
private val targets: KotlinNativeTargets,
|
||||
private val home: KotlinNativeHome,
|
||||
private val classLoader: KotlinNativeClassLoader,
|
||||
@@ -144,12 +32,11 @@ private class TestCompilationImpl(
|
||||
private val gcType: GCType,
|
||||
private val freeCompilerArgs: TestCompilerArgs,
|
||||
private val sourceModules: Collection<TestModule>,
|
||||
private val dependencies: TestCompilationDependencies,
|
||||
private val expectedArtifactFile: File,
|
||||
private val specificCompilerArgs: ArgsBuilder.() -> Unit
|
||||
) : TestCompilation {
|
||||
private val dependencies: Collection<TestCompilationDependency<*>>,
|
||||
private val expectedArtifact: A
|
||||
) : TestCompilation<A>() {
|
||||
// Runs the compiler and memorizes the result on property access.
|
||||
override val result: TestCompilationResult by lazy {
|
||||
final override val result: TestCompilationResult<out A> by lazy {
|
||||
val failures = dependencies.collectFailures()
|
||||
if (failures.isNotEmpty())
|
||||
TestCompilationResult.DependencyFailures(causes = failures)
|
||||
@@ -162,7 +49,7 @@ private class TestCompilationImpl(
|
||||
"-enable-assertions",
|
||||
"-target", targets.testTarget.name,
|
||||
"-repo", home.dir.resolve("klib").path,
|
||||
"-output", expectedArtifactFile.path,
|
||||
"-output", expectedArtifact.path,
|
||||
"-Xskip-prerelease-check",
|
||||
"-Xverify-ir",
|
||||
"-Xbinary=runtimeAssertionsMode=panic"
|
||||
@@ -172,28 +59,30 @@ private class TestCompilationImpl(
|
||||
threadStateChecker.compilerFlag?.let { compilerFlag -> add(compilerFlag) }
|
||||
gcType.compilerFlag?.let { compilerFlag -> add(compilerFlag) }
|
||||
|
||||
addFlattened(dependencies.libraries) { library -> listOf("-l", library.resultingArtifactPath) }
|
||||
addFlattened(dependencies.libraries) { library -> listOf("-l", library.path) }
|
||||
dependencies.friends.takeIf(Collection<*>::isNotEmpty)?.let { friends ->
|
||||
add("-friend-modules", friends.joinToString(File.pathSeparator) { friend -> friend.resultingArtifactPath })
|
||||
add("-friend-modules", friends.joinToString(File.pathSeparator) { friend -> friend.path })
|
||||
}
|
||||
add(dependencies.includedLibraries) { include -> "-Xinclude=${include.resultingArtifactPath}" }
|
||||
add(dependencies.includedLibraries) { include -> "-Xinclude=${include.path}" }
|
||||
add(freeCompilerArgs.compilerArgs)
|
||||
}
|
||||
|
||||
protected abstract fun ArgsBuilder.applySpecificCompilerArgs()
|
||||
|
||||
private fun ArgsBuilder.applySources() {
|
||||
addFlattenedTwice(sourceModules, { it.files }) { it.location.path }
|
||||
}
|
||||
|
||||
private fun doCompile(): TestCompilationResult.ImmediateResult {
|
||||
private fun doCompile(): TestCompilationResult.ImmediateResult<out A> {
|
||||
val compilerArgs = buildArgs {
|
||||
applyCommonArgs()
|
||||
specificCompilerArgs()
|
||||
applySpecificCompilerArgs()
|
||||
applySources()
|
||||
}
|
||||
|
||||
val loggedCompilerParameters = LoggedData.CompilerParameters(compilerArgs, sourceModules)
|
||||
|
||||
val (loggedCompilerCall: LoggedData, result: TestCompilationResult.ImmediateResult) = try {
|
||||
val (loggedCompilerCall: LoggedData, result: TestCompilationResult.ImmediateResult<out A>) = try {
|
||||
val (exitCode, compilerOutput, compilerOutputHasErrors, duration) = callCompiler(
|
||||
compilerArgs = compilerArgs,
|
||||
kotlinNativeClassLoader = classLoader.classLoader
|
||||
@@ -205,7 +94,7 @@ private class TestCompilationImpl(
|
||||
val result = if (exitCode != ExitCode.OK || compilerOutputHasErrors)
|
||||
TestCompilationResult.CompilerFailure(loggedCompilerCall)
|
||||
else
|
||||
TestCompilationResult.CompiledArtifact(expectedArtifactFile, loggedCompilerCall)
|
||||
TestCompilationResult.Success(expectedArtifact, loggedCompilerCall)
|
||||
|
||||
loggedCompilerCall to result
|
||||
} catch (unexpectedThrowable: Throwable) {
|
||||
@@ -215,83 +104,91 @@ private class TestCompilationImpl(
|
||||
loggedFailure to result
|
||||
}
|
||||
|
||||
getLogFile(expectedArtifactFile).writeText(loggedCompilerCall.toString())
|
||||
getLogFile(expectedArtifact.file).writeText(loggedCompilerCall.toString())
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun compilerArgsForKlib(): ArgsBuilder.() -> Unit = { add("-produce", "library") }
|
||||
|
||||
fun compilerArgsForExecutable(cacheKind: CacheKind, extras: Extras): ArgsBuilder.() -> Unit = {
|
||||
add("-produce", "program")
|
||||
when (extras) {
|
||||
is NoTestRunnerExtras -> add("-entry", extras.entryPoint)
|
||||
is WithTestRunnerExtras -> {
|
||||
val testRunnerArg = when (extras.runnerType) {
|
||||
TestRunnerType.DEFAULT -> "-generate-test-runner"
|
||||
TestRunnerType.WORKER -> "-generate-worker-test-runner"
|
||||
TestRunnerType.NO_EXIT -> "-generate-no-exit-test-runner"
|
||||
}
|
||||
add(testRunnerArg)
|
||||
}
|
||||
private fun Collection<TestCompilationDependency<*>>.collectFailures() = flatMapToSet { dependency ->
|
||||
when (val result = (dependency as? TestCompilation<*>)?.result) {
|
||||
is TestCompilationResult.Failure -> listOf(result)
|
||||
is TestCompilationResult.DependencyFailures -> result.causes
|
||||
is TestCompilationResult.Success -> emptyList()
|
||||
null -> emptyList()
|
||||
}
|
||||
cacheKind.rootCacheDir?.let { rootCacheDir -> add("-Xcache-directory=$rootCacheDir") }
|
||||
}
|
||||
|
||||
// Just a shortcut to reduce the number of arguments.
|
||||
fun create(
|
||||
settings: Settings,
|
||||
freeCompilerArgs: TestCompilerArgs,
|
||||
sourceModules: Collection<TestModule>,
|
||||
dependencies: TestCompilationDependencies,
|
||||
expectedArtifactFile: File,
|
||||
specificCompilerArgs: ArgsBuilder.() -> Unit
|
||||
) = TestCompilationImpl(
|
||||
targets = settings.get(),
|
||||
home = settings.get(),
|
||||
classLoader = settings.get(),
|
||||
optimizationMode = settings.get(),
|
||||
memoryModel = settings.get(),
|
||||
threadStateChecker = settings.get(),
|
||||
gcType = settings.get(),
|
||||
freeCompilerArgs = freeCompilerArgs,
|
||||
sourceModules = sourceModules,
|
||||
dependencies = dependencies,
|
||||
expectedArtifactFile = expectedArtifactFile,
|
||||
specificCompilerArgs = specificCompilerArgs
|
||||
)
|
||||
private inline fun <reified A : TestCompilationArtifact, reified T : TestCompilationDependencyType<A>> Collection<TestCompilationDependency<*>>.collectArtifacts() =
|
||||
mapNotNull { dependency -> if (dependency.type is T) dependency.artifact as A else null }
|
||||
|
||||
private val Collection<TestCompilationDependency<*>>.libraries get() = collectArtifacts<KLIB, Library>()
|
||||
private val Collection<TestCompilationDependency<*>>.friends get() = collectArtifacts<KLIB, FriendLibrary>()
|
||||
private val Collection<TestCompilationDependency<*>>.includedLibraries get() = collectArtifacts<KLIB, IncludedLibrary>()
|
||||
|
||||
private fun getLogFile(expectedArtifactFile: File): File = expectedArtifactFile.resolveSibling(expectedArtifactFile.name + ".log")
|
||||
}
|
||||
}
|
||||
|
||||
private class ArgsBuilder {
|
||||
private val args = mutableListOf<String>()
|
||||
|
||||
fun add(vararg args: String) {
|
||||
this.args += args
|
||||
internal class LibraryCompilation(
|
||||
settings: Settings,
|
||||
freeCompilerArgs: TestCompilerArgs,
|
||||
sourceModules: Collection<TestModule>,
|
||||
dependencies: Collection<TestCompilationDependency<*>>,
|
||||
expectedArtifact: KLIB
|
||||
) : BaseCompilation<KLIB>(
|
||||
targets = settings.get(),
|
||||
home = settings.get(),
|
||||
classLoader = settings.get(),
|
||||
optimizationMode = settings.get(),
|
||||
memoryModel = settings.get(),
|
||||
threadStateChecker = settings.get(),
|
||||
gcType = settings.get(),
|
||||
freeCompilerArgs = freeCompilerArgs,
|
||||
sourceModules = sourceModules,
|
||||
dependencies = dependencies,
|
||||
expectedArtifact = expectedArtifact
|
||||
) {
|
||||
override fun ArgsBuilder.applySpecificCompilerArgs() {
|
||||
add("-produce", "library")
|
||||
}
|
||||
|
||||
fun add(args: Iterable<String>) {
|
||||
this.args += args
|
||||
}
|
||||
|
||||
inline fun <T> add(rawArgs: Iterable<T>, transform: (T) -> String) {
|
||||
rawArgs.mapTo(args) { transform(it) }
|
||||
}
|
||||
|
||||
inline fun <T> addFlattened(rawArgs: Iterable<T>, transform: (T) -> Iterable<String>) {
|
||||
rawArgs.flatMapTo(args) { transform(it) }
|
||||
}
|
||||
|
||||
inline fun <T, R> addFlattenedTwice(rawArgs: Iterable<T>, transform1: (T) -> Iterable<R>, transform2: (R) -> String) {
|
||||
rawArgs.forEach { add(transform1(it), transform2) }
|
||||
}
|
||||
|
||||
fun build(): Array<String> = args.toTypedArray()
|
||||
}
|
||||
|
||||
private inline fun buildArgs(builderAction: ArgsBuilder.() -> Unit): Array<String> {
|
||||
return ArgsBuilder().apply(builderAction).build()
|
||||
}
|
||||
internal class ExecutableCompilation(
|
||||
settings: Settings,
|
||||
freeCompilerArgs: TestCompilerArgs,
|
||||
sourceModules: Collection<TestModule>,
|
||||
private val extras: Extras,
|
||||
dependencies: Collection<TestCompilationDependency<*>>,
|
||||
expectedArtifact: Executable
|
||||
) : BaseCompilation<Executable>(
|
||||
targets = settings.get(),
|
||||
home = settings.get(),
|
||||
classLoader = settings.get(),
|
||||
optimizationMode = settings.get(),
|
||||
memoryModel = settings.get(),
|
||||
threadStateChecker = settings.get(),
|
||||
gcType = settings.get(),
|
||||
freeCompilerArgs = freeCompilerArgs,
|
||||
sourceModules = sourceModules,
|
||||
dependencies = dependencies,
|
||||
expectedArtifact = expectedArtifact
|
||||
) {
|
||||
private val cacheKind: CacheKind = settings.get()
|
||||
|
||||
private fun getLogFile(expectedArtifactFile: File): File = expectedArtifactFile.resolveSibling(expectedArtifactFile.name + ".log")
|
||||
override fun ArgsBuilder.applySpecificCompilerArgs() {
|
||||
add("-produce", "program")
|
||||
when (extras) {
|
||||
is NoTestRunnerExtras -> add("-entry", extras.entryPoint)
|
||||
is WithTestRunnerExtras -> {
|
||||
val testRunnerArg = when (extras.runnerType) {
|
||||
TestRunnerType.DEFAULT -> "-generate-test-runner"
|
||||
TestRunnerType.WORKER -> "-generate-worker-test-runner"
|
||||
TestRunnerType.NO_EXIT -> "-generate-no-exit-test-runner"
|
||||
}
|
||||
add(testRunnerArg)
|
||||
}
|
||||
}
|
||||
cacheKind.rootCacheDir?.let { rootCacheDir -> add("-Xcache-directory=$rootCacheDir") }
|
||||
}
|
||||
}
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.compilation
|
||||
|
||||
import java.io.File
|
||||
|
||||
internal sealed interface TestCompilationArtifact {
|
||||
val file: File
|
||||
val path: String get() = file.path
|
||||
|
||||
data class KLIB(override val file: File) : TestCompilationArtifact
|
||||
data class Executable(override val file: File) : TestCompilationArtifact
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.compilation
|
||||
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationResult.Companion.assertSuccess
|
||||
|
||||
/**
|
||||
* The type of dependency for a particular [TestCompilation].
|
||||
*
|
||||
* [Library] - the [TestCompilation]s (modules) that should yield KLIBs to be consumed as dependency libraries in the current compilation.
|
||||
* [FriendLibrary] - similarly but friend modules (-friend-modules).
|
||||
* [IncludedLibrary] - similarly but included modules (-Xinclude).
|
||||
*/
|
||||
internal sealed interface TestCompilationDependencyType<A : TestCompilationArtifact> {
|
||||
object Library : TestCompilationDependencyType<TestCompilationArtifact.KLIB>
|
||||
object FriendLibrary : TestCompilationDependencyType<TestCompilationArtifact.KLIB>
|
||||
object IncludedLibrary : TestCompilationDependencyType<TestCompilationArtifact.KLIB>
|
||||
}
|
||||
|
||||
internal sealed interface TestCompilationDependency<A : TestCompilationArtifact> {
|
||||
val artifact: A
|
||||
val type: TestCompilationDependencyType<A>
|
||||
}
|
||||
|
||||
internal class CompiledDependency<A : TestCompilationArtifact>(
|
||||
val compilation: TestCompilation<A>,
|
||||
override val type: TestCompilationDependencyType<A>
|
||||
) : TestCompilationDependency<A> {
|
||||
override val artifact: A get() = compilation.result.assertSuccess().resultingArtifact
|
||||
}
|
||||
|
||||
internal class ExistingLibraryDependency(
|
||||
override val artifact: TestCompilationArtifact.KLIB,
|
||||
override val type: TestCompilationDependencyType<TestCompilationArtifact.KLIB>
|
||||
) : TestCompilationDependency<TestCompilationArtifact.KLIB>
|
||||
+38
-23
@@ -11,6 +11,10 @@ import org.jetbrains.kotlin.konan.blackboxtest.support.TestCompilerArgs
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.TestModule
|
||||
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.compilation.TestCompilationArtifact.Executable
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationArtifact.KLIB
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationDependencyType.FriendLibrary
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationDependencyType.Library
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.Binaries
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.KotlinNativeTargets
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.Settings
|
||||
@@ -18,60 +22,71 @@ import org.jetbrains.kotlin.konan.blackboxtest.support.util.*
|
||||
import java.io.File
|
||||
|
||||
internal class TestCompilationFactory {
|
||||
private val cachedCompilations = ThreadSafeCache<TestCompilationCacheKey, TestCompilation>()
|
||||
private val cachedKlibCompilations = ThreadSafeCache<KlibCacheKey, TestCompilation<KLIB>>()
|
||||
private val cachedExecutableCompilations = ThreadSafeCache<ExecutableCacheKey, TestCompilation<Executable>>()
|
||||
|
||||
private sealed interface TestCompilationCacheKey {
|
||||
data class Klib(val sourceModules: Set<TestModule>, val freeCompilerArgs: TestCompilerArgs) : TestCompilationCacheKey
|
||||
data class Executable(val sourceModules: Set<TestModule>) : TestCompilationCacheKey
|
||||
}
|
||||
private data class KlibCacheKey(val sourceModules: Set<TestModule>, val freeCompilerArgs: TestCompilerArgs)
|
||||
private data class ExecutableCacheKey(val sourceModules: Set<TestModule>)
|
||||
|
||||
fun testCasesToExecutable(testCases: Collection<TestCase>, settings: Settings): TestCompilation {
|
||||
fun testCasesToExecutable(testCases: Collection<TestCase>, settings: Settings): TestCompilation<Executable> {
|
||||
val rootModules = testCases.flatMapToSet { testCase -> testCase.rootModules }
|
||||
val cacheKey = TestCompilationCacheKey.Executable(rootModules)
|
||||
val cacheKey = ExecutableCacheKey(rootModules)
|
||||
|
||||
// Fast pass.
|
||||
cachedCompilations[cacheKey]?.let { return it }
|
||||
cachedExecutableCompilations[cacheKey]?.let { return it }
|
||||
|
||||
// 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, settings) }
|
||||
val friends = rootModules.flatMapToSet { it.allFriends }.map { moduleToKlib(it, freeCompilerArgs, settings) }
|
||||
val dependencies = buildList {
|
||||
rootModules.flatMapToSet { it.allDependencies }.mapTo(this) { it.asKlibDependency(freeCompilerArgs, settings, Library) }
|
||||
rootModules.flatMapToSet { it.allFriends }.mapTo(this) { it.asKlibDependency(freeCompilerArgs, settings, FriendLibrary) }
|
||||
}
|
||||
val expectedArtifact = Executable(settings.artifactFileForExecutable(rootModules))
|
||||
|
||||
return cachedCompilations.computeIfAbsent(cacheKey) {
|
||||
TestCompilation.createForExecutable(
|
||||
return cachedExecutableCompilations.computeIfAbsent(cacheKey) {
|
||||
ExecutableCompilation(
|
||||
settings = settings,
|
||||
freeCompilerArgs = freeCompilerArgs,
|
||||
sourceModules = rootModules,
|
||||
extras = extras,
|
||||
dependencies = TestCompilationDependencies(libraries = libraries, friends = friends),
|
||||
expectedExecutableFile = settings.artifactFileForExecutable(rootModules),
|
||||
dependencies = dependencies,
|
||||
expectedArtifact = expectedArtifact
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun moduleToKlib(sourceModule: TestModule, freeCompilerArgs: TestCompilerArgs, settings: Settings): TestCompilation {
|
||||
private fun moduleToKlib(sourceModule: TestModule, freeCompilerArgs: TestCompilerArgs, settings: Settings): TestCompilation<KLIB> {
|
||||
val sourceModules = setOf(sourceModule)
|
||||
val cacheKey = TestCompilationCacheKey.Klib(sourceModules, freeCompilerArgs)
|
||||
val cacheKey = KlibCacheKey(sourceModules, freeCompilerArgs)
|
||||
|
||||
// Fast pass.
|
||||
cachedCompilations[cacheKey]?.let { return it }
|
||||
cachedKlibCompilations[cacheKey]?.let { return it }
|
||||
|
||||
// Long pass.
|
||||
val libraries = sourceModule.allDependencies.map { moduleToKlib(it, freeCompilerArgs, settings) }
|
||||
val friends = sourceModule.allFriends.map { moduleToKlib(it, freeCompilerArgs, settings) }
|
||||
val dependencies = buildList {
|
||||
sourceModule.allDependencies.mapTo(this) { it.asKlibDependency(freeCompilerArgs, settings, Library) }
|
||||
sourceModule.allFriends.mapTo(this) { it.asKlibDependency(freeCompilerArgs, settings, FriendLibrary) }
|
||||
}
|
||||
val expectedArtifact = KLIB(settings.artifactFileForKlib(sourceModule, freeCompilerArgs))
|
||||
|
||||
return cachedCompilations.computeIfAbsent(cacheKey) {
|
||||
TestCompilation.createForKlib(
|
||||
return cachedKlibCompilations.computeIfAbsent(cacheKey) {
|
||||
LibraryCompilation(
|
||||
settings = settings,
|
||||
freeCompilerArgs = freeCompilerArgs,
|
||||
sourceModules = sourceModules,
|
||||
dependencies = TestCompilationDependencies(libraries = libraries, friends = friends),
|
||||
expectedKlibFile = settings.artifactFileForKlib(sourceModule, freeCompilerArgs)
|
||||
dependencies = dependencies,
|
||||
expectedArtifact = expectedArtifact
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T : TestCompilationDependencyType<KLIB>> TestModule.asKlibDependency(
|
||||
freeCompilerArgs: TestCompilerArgs,
|
||||
settings: Settings,
|
||||
type: T
|
||||
) = CompiledDependency(moduleToKlib(this, freeCompilerArgs, settings), type)
|
||||
|
||||
private fun Settings.artifactFileForExecutable(modules: Set<TestModule.Exclusive>) = when (modules.size) {
|
||||
1 -> artifactFileForExecutable(modules.first())
|
||||
else -> multiModuleArtifactFile(modules, get<KotlinNativeTargets>().testTarget.family.exeSuffix)
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.compilation
|
||||
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.LoggedData
|
||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
|
||||
|
||||
internal sealed interface TestCompilationResult<A : TestCompilationArtifact> {
|
||||
sealed interface ImmediateResult<A : TestCompilationArtifact> : TestCompilationResult<A> {
|
||||
val loggedData: LoggedData
|
||||
}
|
||||
|
||||
sealed interface Failure : ImmediateResult<Nothing>
|
||||
|
||||
data class Success<A : TestCompilationArtifact>(val resultingArtifact: A, override val loggedData: LoggedData.CompilerCall) :
|
||||
ImmediateResult<A>
|
||||
|
||||
data class CompilerFailure(override val loggedData: LoggedData.CompilerCall) : Failure
|
||||
data class UnexpectedFailure(override val loggedData: LoggedData.CompilerCallUnexpectedFailure) : Failure
|
||||
data class DependencyFailures(val causes: Set<Failure>) : TestCompilationResult<Nothing>
|
||||
|
||||
companion object {
|
||||
fun <A : TestCompilationArtifact> TestCompilationResult<A>.assertSuccess(): Success<A> = when (this) {
|
||||
is Success -> this
|
||||
is Failure -> fail { describeFailure() }
|
||||
is DependencyFailures -> fail { describeDependencyFailures() }
|
||||
}
|
||||
|
||||
private fun Failure.describeFailure() = loggedData.withErrorMessage(
|
||||
when (this@describeFailure) {
|
||||
is CompilerFailure -> "Compilation failed."
|
||||
is UnexpectedFailure -> "Compilation failed with unexpected exception."
|
||||
}
|
||||
)
|
||||
|
||||
private fun DependencyFailures.describeDependencyFailures() =
|
||||
buildString {
|
||||
appendLine("Compilation aborted due to errors in dependency compilations (${causes.size} items). See details below.")
|
||||
appendLine()
|
||||
causes.forEachIndexed { index, cause ->
|
||||
append("#").append(index + 1).append(". ")
|
||||
appendLine(cause.describeFailure())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-4
@@ -11,8 +11,9 @@ 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.compilation.TestCompilation
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationArtifact.Executable
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationFactory
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationResult.Companion.assertSuccessfullyCompiled
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationResult.Companion.assertSuccess
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.group.TestCaseGroupProvider
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.runner.TestRunners.extractTestNames
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.Settings
|
||||
@@ -32,7 +33,7 @@ internal class TestRunProvider(
|
||||
private val testCaseGroupProvider: TestCaseGroupProvider
|
||||
) : BaseTestRunProvider(), ExtensionContext.Store.CloseableResource {
|
||||
private val compilationFactory = TestCompilationFactory()
|
||||
private val cachedCompilations = ThreadSafeCache<TestCompilationCacheKey, TestCompilation>()
|
||||
private val cachedCompilations = ThreadSafeCache<TestCompilationCacheKey, TestCompilation<Executable>>()
|
||||
private val cachedTestNames = ThreadSafeCache<TestCompilationCacheKey, Collection<TestName>>()
|
||||
|
||||
/**
|
||||
@@ -155,8 +156,8 @@ internal class TestRunProvider(
|
||||
}
|
||||
}
|
||||
|
||||
val (executableFile, loggedCompilerCall) = testCompilation.result.assertSuccessfullyCompiled() // <-- Compilation happens here.
|
||||
val executable = TestExecutable(executableFile, loggedCompilerCall)
|
||||
val (artifact, loggedCompilerCall) = testCompilation.result.assertSuccess() // <-- Compilation happens here.
|
||||
val executable = TestExecutable(artifact.file, loggedCompilerCall)
|
||||
|
||||
return action(testCase, executable, cacheKey)
|
||||
}
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.util
|
||||
|
||||
internal class ArgsBuilder {
|
||||
private val args = mutableListOf<String>()
|
||||
|
||||
fun add(vararg args: String) {
|
||||
this.args += args
|
||||
}
|
||||
|
||||
fun add(args: Iterable<String>) {
|
||||
this.args += args
|
||||
}
|
||||
|
||||
inline fun <T> add(rawArgs: Iterable<T>, transform: (T) -> String) {
|
||||
rawArgs.mapTo(args) { transform(it) }
|
||||
}
|
||||
|
||||
inline fun <T> addFlattened(rawArgs: Iterable<T>, transform: (T) -> Iterable<String>) {
|
||||
rawArgs.flatMapTo(args) { transform(it) }
|
||||
}
|
||||
|
||||
inline fun <T, R> addFlattenedTwice(rawArgs: Iterable<T>, transform1: (T) -> Iterable<R>, transform2: (R) -> String) {
|
||||
rawArgs.forEach { add(transform1(it), transform2) }
|
||||
}
|
||||
|
||||
fun build(): Array<String> = args.toTypedArray()
|
||||
}
|
||||
|
||||
internal inline fun buildArgs(builderAction: ArgsBuilder.() -> Unit): Array<String> {
|
||||
return ArgsBuilder().apply(builderAction).build()
|
||||
}
|
||||
Reference in New Issue
Block a user