diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/AbstractNativeKlibABITest.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/AbstractNativeKlibABITest.kt index 4889f865f5d..88792bbfc87 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/AbstractNativeKlibABITest.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/AbstractNativeKlibABITest.kt @@ -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 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) { @@ -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) = TestCompilationDependencies( - libraries = dependencies.map(TestCompilation.Companion::createForExistingArtifact) - ) + private fun createExistingDependencies(dependencies: Collection) = + dependencies.map { ExistingLibraryDependency(TestCompilationArtifact.KLIB(it), Library) } private val buildDir: File get() = testRunSettings.get().testBuildDir private val stdlibFile: File get() = testRunSettings.get().dir.resolve("klib/common/stdlib") diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/compilation/TestCompilation.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/compilation/TestCompilation.kt index 586f1a7aba8..7c2caa9b0b0 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/compilation/TestCompilation.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/compilation/TestCompilation.kt @@ -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, - 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, - 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 { + abstract val result: TestCompilationResult } -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) : 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 = emptyList(), - val friends: Collection = emptyList(), - val includedLibraries: Collection = emptyList() -) { - fun collectFailures(): Set = 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( 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, - private val dependencies: TestCompilationDependencies, - private val expectedArtifactFile: File, - private val specificCompilerArgs: ArgsBuilder.() -> Unit -) : TestCompilation { + private val dependencies: Collection>, + private val expectedArtifact: A +) : TestCompilation() { // Runs the compiler and memorizes the result on property access. - override val result: TestCompilationResult by lazy { + final override val result: TestCompilationResult 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 { 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) = 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>.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, - 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 > Collection>.collectArtifacts() = + mapNotNull { dependency -> if (dependency.type is T) dependency.artifact as A else null } + + private val Collection>.libraries get() = collectArtifacts() + private val Collection>.friends get() = collectArtifacts() + private val Collection>.includedLibraries get() = collectArtifacts() + + private fun getLogFile(expectedArtifactFile: File): File = expectedArtifactFile.resolveSibling(expectedArtifactFile.name + ".log") } } -private class ArgsBuilder { - private val args = mutableListOf() - - fun add(vararg args: String) { - this.args += args +internal class LibraryCompilation( + settings: Settings, + freeCompilerArgs: TestCompilerArgs, + sourceModules: Collection, + dependencies: Collection>, + expectedArtifact: KLIB +) : BaseCompilation( + 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) { - this.args += args - } - - inline fun add(rawArgs: Iterable, transform: (T) -> String) { - rawArgs.mapTo(args) { transform(it) } - } - - inline fun addFlattened(rawArgs: Iterable, transform: (T) -> Iterable) { - rawArgs.flatMapTo(args) { transform(it) } - } - - inline fun addFlattenedTwice(rawArgs: Iterable, transform1: (T) -> Iterable, transform2: (R) -> String) { - rawArgs.forEach { add(transform1(it), transform2) } - } - - fun build(): Array = args.toTypedArray() } -private inline fun buildArgs(builderAction: ArgsBuilder.() -> Unit): Array { - return ArgsBuilder().apply(builderAction).build() -} +internal class ExecutableCompilation( + settings: Settings, + freeCompilerArgs: TestCompilerArgs, + sourceModules: Collection, + private val extras: Extras, + dependencies: Collection>, + expectedArtifact: Executable +) : BaseCompilation( + 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") } + } +} diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/compilation/TestCompilationArtifact.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/compilation/TestCompilationArtifact.kt new file mode 100644 index 00000000000..12e312fbcc9 --- /dev/null +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/compilation/TestCompilationArtifact.kt @@ -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 +} diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/compilation/TestCompilationDependency.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/compilation/TestCompilationDependency.kt new file mode 100644 index 00000000000..903ecbbb4ec --- /dev/null +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/compilation/TestCompilationDependency.kt @@ -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 { + object Library : TestCompilationDependencyType + object FriendLibrary : TestCompilationDependencyType + object IncludedLibrary : TestCompilationDependencyType +} + +internal sealed interface TestCompilationDependency { + val artifact: A + val type: TestCompilationDependencyType +} + +internal class CompiledDependency( + val compilation: TestCompilation, + override val type: TestCompilationDependencyType +) : TestCompilationDependency { + override val artifact: A get() = compilation.result.assertSuccess().resultingArtifact +} + +internal class ExistingLibraryDependency( + override val artifact: TestCompilationArtifact.KLIB, + override val type: TestCompilationDependencyType +) : TestCompilationDependency diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/compilation/TestCompilationFactory.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/compilation/TestCompilationFactory.kt index 1da23884ce9..2ad7d36efec 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/compilation/TestCompilationFactory.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/compilation/TestCompilationFactory.kt @@ -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() + private val cachedKlibCompilations = ThreadSafeCache>() + private val cachedExecutableCompilations = ThreadSafeCache>() - private sealed interface TestCompilationCacheKey { - data class Klib(val sourceModules: Set, val freeCompilerArgs: TestCompilerArgs) : TestCompilationCacheKey - data class Executable(val sourceModules: Set) : TestCompilationCacheKey - } + private data class KlibCacheKey(val sourceModules: Set, val freeCompilerArgs: TestCompilerArgs) + private data class ExecutableCacheKey(val sourceModules: Set) - fun testCasesToExecutable(testCases: Collection, settings: Settings): TestCompilation { + fun testCasesToExecutable(testCases: Collection, settings: Settings): TestCompilation { 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 { 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 > TestModule.asKlibDependency( + freeCompilerArgs: TestCompilerArgs, + settings: Settings, + type: T + ) = CompiledDependency(moduleToKlib(this, freeCompilerArgs, settings), type) + private fun Settings.artifactFileForExecutable(modules: Set) = when (modules.size) { 1 -> artifactFileForExecutable(modules.first()) else -> multiModuleArtifactFile(modules, get().testTarget.family.exeSuffix) diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/compilation/TestCompilationResult.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/compilation/TestCompilationResult.kt new file mode 100644 index 00000000000..a5715311388 --- /dev/null +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/compilation/TestCompilationResult.kt @@ -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 { + sealed interface ImmediateResult : TestCompilationResult { + val loggedData: LoggedData + } + + sealed interface Failure : ImmediateResult + + data class Success(val resultingArtifact: A, override val loggedData: LoggedData.CompilerCall) : + ImmediateResult + + data class CompilerFailure(override val loggedData: LoggedData.CompilerCall) : Failure + data class UnexpectedFailure(override val loggedData: LoggedData.CompilerCallUnexpectedFailure) : Failure + data class DependencyFailures(val causes: Set) : TestCompilationResult + + companion object { + fun TestCompilationResult.assertSuccess(): Success = 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()) + } + } + } +} diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/runner/TestRunProvider.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/runner/TestRunProvider.kt index 948062fe1e9..debf638e8ae 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/runner/TestRunProvider.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/runner/TestRunProvider.kt @@ -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() + private val cachedCompilations = ThreadSafeCache>() private val cachedTestNames = ThreadSafeCache>() /** @@ -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) } diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/util/ArgsBuilder.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/util/ArgsBuilder.kt new file mode 100644 index 00000000000..e2aadc78ecb --- /dev/null +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/util/ArgsBuilder.kt @@ -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() + + fun add(vararg args: String) { + this.args += args + } + + fun add(args: Iterable) { + this.args += args + } + + inline fun add(rawArgs: Iterable, transform: (T) -> String) { + rawArgs.mapTo(args) { transform(it) } + } + + inline fun addFlattened(rawArgs: Iterable, transform: (T) -> Iterable) { + rawArgs.flatMapTo(args) { transform(it) } + } + + inline fun addFlattenedTwice(rawArgs: Iterable, transform1: (T) -> Iterable, transform2: (R) -> String) { + rawArgs.forEach { add(transform1(it), transform2) } + } + + fun build(): Array = args.toTypedArray() +} + +internal inline fun buildArgs(builderAction: ArgsBuilder.() -> Unit): Array { + return ArgsBuilder().apply(builderAction).build() +}