Migrated GeneralNativeIT to junit 5 and gradle TestKit

This commit is contained in:
Dmitrii Krasnov
2023-09-01 12:52:44 +02:00
committed by Space Team
parent 1ace71a907
commit 3074b50e86
40 changed files with 1106 additions and 1039 deletions
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.gradle.testbase.*
import org.jetbrains.kotlin.gradle.util.*
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.target.presetName
import org.jetbrains.kotlin.test.RunnerWithMuteInDatabase
import org.junit.After
import org.junit.AfterClass
@@ -273,7 +274,7 @@ abstract class BaseGradleIT {
val useParsableDiagnosticsFormatting: Boolean = true,
val showDiagnosticsStacktrace: Boolean? = false, // false by default to not clutter the testdata + stacktraces change often
val stacktraceMode: String? = StacktraceOption.FULL_STACKTRACE_LONG_OPTION,
val konanDataDir: Path = Paths.get("build/.konan"),
val konanDataDir: Path = konanDir,
) {
val safeAndroidGradlePluginVersion: AGPVersion
get() = androidGradlePluginVersion ?: error("AGP version is expected to be set")
@@ -847,6 +848,68 @@ abstract class BaseGradleIT {
assertEquals(expectedTestResults, actualTestResults)
}
/**
* Filter output for specific task with given [taskPath]
*
* Requires using [LogLevel.DEBUG].
*/
fun CompiledProject.getOutputForTask(taskPath: String): String = getOutputForTask(taskPath, output)
fun CompiledProject.withNativeCommandLineArguments(
vararg taskPaths: String,
toolName: NativeToolKind = NativeToolKind.KONANC,
check: (List<String>) -> Unit,
) = taskPaths.forEach { taskPath -> check(extractNativeCompilerCommandLineArguments(getOutputForTask(taskPath), toolName)) }
internal fun transformNativeTestProject(
projectName: String,
wrapperVersion: GradleVersionRequired = defaultGradleVersion,
directoryPrefix: String? = null,
): BaseGradleIT.Project {
val project = Project(projectName, wrapperVersion, directoryPrefix = directoryPrefix)
project.setupWorkingDir()
project.configureSingleNativeTarget()
project.gradleProperties().apply {
configureJvmMemory()
disableKotlinNativeCaches()
}
return project
}
internal fun transformNativeTestProjectWithPluginDsl(
projectName: String,
wrapperVersion: GradleVersionRequired = defaultGradleVersion,
directoryPrefix: String? = null,
): BaseGradleIT.Project {
val project = transformProjectWithPluginsDsl(projectName, wrapperVersion, directoryPrefix = directoryPrefix)
project.configureSingleNativeTarget()
project.gradleProperties().apply {
configureJvmMemory()
disableKotlinNativeCaches()
}
return project
}
internal fun File.configureJvmMemory() {
appendText("\norg.gradle.jvmargs=-Xmx1g\n")
}
internal fun File.disableKotlinNativeCaches() {
appendText("\nkotlin.native.cacheKind=none\n")
}
private val SINGLE_NATIVE_TARGET_PLACEHOLDER = "<SingleNativeTarget>"
private fun Project.configureSingleNativeTarget(preset: String = HostManager.host.presetName) {
projectDir.walk()
.filter { it.isFile && (it.name == "build.gradle.kts" || it.name == "build.gradle") }
.forEach { file ->
file.modify {
it.replace(SINGLE_NATIVE_TARGET_PLACEHOLDER, preset)
}
}
}
private fun Project.createGradleTailParameters(options: BuildOptions, params: Array<out String> = arrayOf()): List<String> =
params.toMutableList().apply {
when (minLogLevel) {
@@ -939,7 +1002,7 @@ abstract class BaseGradleIT {
add("-Pkotlin.internal.suppressGradlePluginErrors=PreHMPPFlagsError")
}
add("-Pkonan.data.dir=${options.konanDataDir.absolutePathString()}")
add("-Pkonan.data.dir=${options.konanDataDir.absolutePathString().normalize()}")
// Workaround: override a console type set in the user machine gradle.properties (since Gradle 4.3):
add("--console=plain")
@@ -28,7 +28,6 @@ class BuildFusStatisticsIT : KGPDaemonsBaseTest() {
// https://docs.gradle.org/8.0/release-notes.html#kotlin-dsl-updated-to-kotlin-api-level-1.8 ,
// and it registers old service, so we don't need check with re-registering old version service.
if (gradleVersion < GradleVersion.version(TestVersions.Gradle.G_8_0)) {
// TODO(Dmitrii Krasnov): you can remove this check, when min gradle version becomes 8 or greater
//kotlin 1.4 in kotlinDsl does not create jmx service yet
assertOutputContains("Register JMX service for backward compatibility")
}
@@ -223,9 +223,6 @@ internal class CompilerOptionsIT : KGPBaseTest() {
project(
projectName = "new-mpp-lib-and-app/sample-lib",
gradleVersion = gradleVersion,
// We need to get specific task output as commonizer may run first producing
// arguments as well in output
buildOptions = defaultBuildOptions.copy(logLevel = LogLevel.DEBUG)
) {
buildGradle.appendText(
//language=Groovy
@@ -256,7 +253,7 @@ internal class CompilerOptionsIT : KGPBaseTest() {
build("compileNativeMainKotlinMetadata") {
assertTasksExecuted(":compileNativeMainKotlinMetadata")
val taskOutput = getOutputForTask(":compileNativeMainKotlinMetadata")
val taskOutput = getOutputForTask(":compileNativeMainKotlinMetadata", logLevel = LogLevel.INFO)
val arguments = parseCompilerArgumentsFromBuildOutput(K2NativeCompilerArguments::class, taskOutput)
assertEquals(
setOf("another.custom.UnderOptIn", "my.custom.OptInAnnotation"), arguments.optIn?.toSet(),
@@ -266,7 +263,7 @@ internal class CompilerOptionsIT : KGPBaseTest() {
build("compileKotlinLinux64") {
assertTasksExecuted(":compileKotlinLinux64")
val taskOutput = getOutputForTask(":compileKotlinLinux64")
val taskOutput = getOutputForTask(":compileKotlinLinux64", logLevel = LogLevel.INFO)
val arguments = parseCompilerArgumentsFromBuildOutput(K2NativeCompilerArguments::class, taskOutput)
assertEquals(
setOf("another.custom.UnderOptIn", "my.custom.OptInAnnotation"), arguments.optIn?.toSet(),
@@ -293,7 +290,7 @@ internal class CompilerOptionsIT : KGPBaseTest() {
""".trimMargin()
)
build("compileKotlinHost", forceOutput = true) {
build("compileKotlinHost") {
val expectedOptIn = listOf("kotlin.RequiresOptIn", "my.CustomOptIn")
val arguments = parseCompilerArguments<K2NativeCompilerArguments>()
if (arguments.optIn?.toList() != listOf("kotlin.RequiresOptIn", "my.CustomOptIn")) {
@@ -446,9 +443,6 @@ internal class CompilerOptionsIT : KGPBaseTest() {
project(
projectName = "new-mpp-lib-and-app/sample-lib",
gradleVersion = gradleVersion,
// We need to get specific task output as commonizer may run first producing
// arguments as well in output
buildOptions = defaultBuildOptions.copy(logLevel = LogLevel.DEBUG)
) {
buildGradle.appendText(
//language=Groovy
@@ -492,9 +486,6 @@ internal class CompilerOptionsIT : KGPBaseTest() {
project(
projectName = "new-mpp-lib-and-app/sample-lib",
gradleVersion = gradleVersion,
// We need to get specific task output as commonizer may run first producing
// arguments as well in output
buildOptions = defaultBuildOptions.copy(logLevel = LogLevel.DEBUG)
) {
buildGradle.modify {
val buildScript = """
@@ -546,7 +546,7 @@ class CompilerOptionsProjectIT : KGPBaseTest() {
}
build(":compileKotlinLinuxX64") {
extractNativeTasksCommandLineArgumentsFromOutput(":compileKotlinLinuxX64") {
extractNativeTasksCommandLineArgumentsFromOutput(":compileKotlinLinuxX64", logLevel = LogLevel.DEBUG) {
assertCommandLineArgumentsContain("-language-version", "1.7")
assertCommandLineArgumentsContain("-api-version", "1.7")
assertCommandLineArgumentsContain("-progressive")
@@ -143,7 +143,7 @@ class ExplicitApiIT : KGPBaseTest() {
if (nativeTaskName != null) {
build(nativeTaskName) {
assertTasksExecuted(nativeTaskName)
extractNativeTasksCommandLineArgumentsFromOutput(nativeTaskName) {
extractNativeTasksCommandLineArgumentsFromOutput(nativeTaskName, logLevel = LogLevel.DEBUG) {
assertCommandLineArgumentsContain("-Xexplicit-api=warning")
}
}
@@ -7,17 +7,12 @@ package org.jetbrains.kotlin.gradle
import org.gradle.api.logging.LogLevel
import org.jetbrains.kotlin.cli.common.arguments.K2NativeCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments
import org.jetbrains.kotlin.gradle.native.GeneralNativeIT.Companion.containsSequentially
import org.jetbrains.kotlin.gradle.native.GeneralNativeIT.Companion.withNativeCommandLineArguments
import org.jetbrains.kotlin.gradle.native.MPPNativeTargets
import org.jetbrains.kotlin.gradle.native.configureJvmMemory
import org.jetbrains.kotlin.gradle.native.transformNativeTestProject
import org.jetbrains.kotlin.gradle.native.transformNativeTestProjectWithPluginDsl
import org.jetbrains.kotlin.gradle.plugin.ProjectLocalConfigurations
import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPlugin
import org.jetbrains.kotlin.gradle.plugin.sources.METADATA_CONFIGURATION_NAME_SUFFIX
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
import org.jetbrains.kotlin.gradle.testbase.MPPNativeTargets
import org.jetbrains.kotlin.gradle.testbase.TestVersions
import org.jetbrains.kotlin.gradle.testbase.assertHasDiagnostic
import org.jetbrains.kotlin.gradle.testbase.assertNoDiagnostic
@@ -1791,4 +1786,11 @@ open class NewMultiplatformIT : BaseGradleIT() {
HostManager.hostIsMac -> "macosX64"
else -> throw AssertionError("Host ${HostManager.host} is not supported for this test")
}
companion object {
fun List<String>.containsSequentially(vararg elements: String): Boolean {
check(elements.isNotEmpty())
return Collections.indexOfSubList(this, elements.toList()) != -1
}
}
}
@@ -99,7 +99,7 @@ class ResourcesIT : KGPBaseTest() {
""".trimMargin()
)
build("jar", forceOutput = true) {
build("jar") {
assertFileInProjectExists("build/libs/simpleProject.jar")
projectPath.resolve("build/libs/simpleProject.jar").assertZipArchiveContainsFilesOnce(
listOf(mainResFile.name, additionalResFile.name)
@@ -144,7 +144,7 @@ class TryK2IT : KGPBaseTest() {
""".trimMargin()
)
buildAndFail("build", forceOutput = true) {
buildAndFail("build") {
assertOutputContains(
"""
|##### 'kotlin.experimental.tryK2' results #####
@@ -7,10 +7,8 @@ package org.jetbrains.kotlin.gradle.mpp
import org.gradle.testkit.runner.BuildResult
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.native.MPPNativeTargets
import org.jetbrains.kotlin.gradle.testbase.*
import org.jetbrains.kotlin.gradle.util.capitalize
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.DisplayName
@MppGradlePluginTests
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.gradle.mpp
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.native.MPPNativeTargets
import org.jetbrains.kotlin.gradle.testbase.*
import org.junit.jupiter.api.DisplayName
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.gradle.native
import org.gradle.api.JavaVersion
import org.gradle.testkit.runner.BuildResult
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.testbase.*
import org.jetbrains.kotlin.gradle.util.replaceText
@@ -460,32 +459,23 @@ class AppleFrameworkIT : KGPBaseTest() {
":iosApp:dependencyInsight", "--configuration", configuration, "--dependency", "iosLib"
)
fun variant(variantName: String) =
if (gradleVersion >= GradleVersion.version(TestVersions.Gradle.G_7_5)) {
"Variant $variantName"
} else {
"variant \"$variantName\""
}
fun BuildResult.assertContainsVariant(variantName: String) = assertOutputContains(variant(variantName))
subProject("iosApp").buildGradleKts.replaceText("<applePluginTestVersion>", "\"${TestVersions.AppleGradlePlugin.V222_0_21}\"")
build(*dependencyInsight("iosAppIosX64DebugImplementation")) {
assertContainsVariant("mainDynamicDebugFrameworkIos")
assertOutputContainsNativeFrameworkVariant("mainDynamicDebugFrameworkIos", gradleVersion)
}
build(*dependencyInsight("iosAppIosX64ReleaseImplementation")) {
assertContainsVariant("mainDynamicReleaseFrameworkIos")
assertOutputContainsNativeFrameworkVariant("mainDynamicReleaseFrameworkIos", gradleVersion)
}
// NB: '0' is required at the end since dependency is added with custom attribute, and it creates new configuration
build(*dependencyInsight("iosAppIosX64DebugImplementation0"), "-PmultipleFrameworks") {
assertContainsVariant("mainStaticDebugFrameworkIos")
assertOutputContainsNativeFrameworkVariant("mainStaticDebugFrameworkIos", gradleVersion)
}
build(*dependencyInsight("iosAppIosX64ReleaseImplementation0"), "-PmultipleFrameworks") {
assertOutputDoesNotContain(variant("mainStaticReleaseFrameworkIos"))
assertOutputContainsNativeFrameworkVariant("mainStaticReleaseFrameworkIos", gradleVersion)
}
}
}
@@ -705,24 +705,10 @@ class CocoaPodsIT : KGPBaseTest() {
assertTasksExecuted(":cinteropSDWebImageIOS")
// TODO(Dmitrii Krasnov): rewrite it, when GeneralNativeIT will be migrated to new test dsl
assertOutputContains(
"""
| -linker-option
| -framework
| -linker-option
| AFNetworking
""".trimMargin()
)
assertOutputContains(
"""
| -linker-option
| -framework
| -linker-option
| SSZipArchive
""".trimMargin()
)
extractNativeTasksCommandLineArgumentsFromOutput(":linkPodDebugFrameworkIOS") {
assertCommandLineArgumentsContainSequentially("-linker-option", "-framework", "-linker-option", "AFNetworking")
assertCommandLineArgumentsContainSequentially("-linker-option", "-framework", "-linker-option", "SSZipArchive")
}
}
}
}
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.gradle.native
import org.gradle.api.logging.LogLevel
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.dsl.NativeCacheKind
import org.jetbrains.kotlin.gradle.testbase.*
@@ -30,8 +29,7 @@ internal class KotlinNativeLinkIT : KGPBaseTest() {
fun shouldUseCompilationFreeCompilerArgs(gradleVersion: GradleVersion) {
nativeProject(
"native-link-simple",
gradleVersion,
buildOptions = defaultBuildOptions.copy(logLevel = LogLevel.DEBUG)
gradleVersion
) {
buildGradle.appendText(
"""
@@ -70,7 +68,6 @@ internal class KotlinNativeLinkIT : KGPBaseTest() {
"kt-60839-native-link-cache-builder",
gradleVersion = gradleVersion,
buildOptions = defaultBuildOptions.copy(
logLevel = LogLevel.DEBUG,
// KT-60839 only reproduces when the build cache is enabled,
// but we must ignore it when running this test in order to
// ensure we actually try to pass -Xpartial-linkage to konanc.
@@ -5,9 +5,9 @@
package org.jetbrains.kotlin.gradle.native
import org.gradle.api.logging.LogLevel
import org.gradle.testkit.runner.BuildResult
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics
import org.jetbrains.kotlin.gradle.testbase.*
import org.jetbrains.kotlin.gradle.util.replaceFirst
import org.jetbrains.kotlin.gradle.utils.NativeCompilerDownloader
@@ -150,7 +150,7 @@ class NativeDownloadAndPlatformLibsIT : KGPBaseTest() {
""".trimMargin()
)
build("linkDebugSharedLinuxX64", buildOptions = defaultBuildOptions.copy(logLevel = LogLevel.DEBUG)) {
build("linkDebugSharedLinuxX64") {
assertTasksExecuted(
":compileKotlinLinuxX64",
":linkDebugSharedLinuxX64"
@@ -280,6 +280,68 @@ class NativeDownloadAndPlatformLibsIT : KGPBaseTest() {
}
}
@DisplayName("The plugin shouldn't download the K/N compiler if there are no corresponding targets in the project.")
@GradleTest
fun shouldNotDownloadKonanWithoutCorrespondingTargets(gradleVersion: GradleVersion) {
nativeProject("new-mpp-lib-and-app/sample-old-style-app", gradleVersion) {
build("tasks") {
assertOutputDoesNotContain("Kotlin/Native distribution: ")
}
}
}
@DisplayName("The plugin shouldn't download the K/N compiler if there is konan home property override and no konan.data.dir property override.")
@GradleTest
fun testNativeCompilerDownloadingWithDifferentKNHomeOptions(gradleVersion: GradleVersion) {
nativeProject("native-libraries", gradleVersion) {
// This directory actually doesn't contain a K/N distribution
// but we still can run a project configuration and check logs.
val currentDir = projectPath
build("tasks", "-Pkotlin.native.home=$currentDir", buildOptions = defaultBuildOptions.copy(konanDataDir = null)) {
assertOutputContains("User-provided Kotlin/Native distribution: $currentDir")
assertOutputDoesNotContain("Project property 'org.jetbrains.kotlin.native.home' is deprecated")
assertHasDiagnostic(KotlinToolingDiagnostics.NativeStdlibIsMissingDiagnostic, withSubstring = "kotlin.native.home")
}
// Deprecated property.
build(
"tasks",
"-Porg.jetbrains.kotlin.native.home=$currentDir",
"-Pkotlin.native.nostdlib=true",
buildOptions = defaultBuildOptions.copy(konanDataDir = null)
) {
assertOutputContains("User-provided Kotlin/Native distribution: $currentDir")
assertOutputContains("Project property 'org.jetbrains.kotlin.native.home' is deprecated")
assertNoDiagnostic(KotlinToolingDiagnostics.NativeStdlibIsMissingDiagnostic)
}
}
}
@DisplayName("Checks downloading K/N compiler with different version options")
@GradleTest
fun testNativeCompilerDownloadingWithDifferentVersionOptions(gradleVersion: GradleVersion) {
nativeProject("native-libraries", gradleVersion) {
val platform = HostManager.platformName()
build("tasks") {
assertOutputContains("Kotlin/Native distribution:")
}
val version = TestVersions.Kotlin.STABLE_RELEASE
val escapedRegexVersion = Regex.escape(TestVersions.Kotlin.STABLE_RELEASE)
build("tasks", "-Pkotlin.native.version=$version") {
assertOutputContains("Kotlin/Native distribution: .*kotlin-native-prebuilt-$platform-$escapedRegexVersion".toRegex())
assertOutputDoesNotContain("Project property 'org.jetbrains.kotlin.native.version' is deprecated")
}
// Deprecated property
build("tasks", "-Porg.jetbrains.kotlin.native.version=$version") {
assertOutputContains("Kotlin/Native distribution: .*kotlin-native-prebuilt-$platform-$escapedRegexVersion".toRegex())
assertOutputContains("Project property 'org.jetbrains.kotlin.native.version' is deprecated")
}
}
}
private fun platformLibrariesProject(
vararg targets: String,
gradleVersion: GradleVersion,
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.gradle.native
import org.gradle.api.logging.LogLevel
import org.gradle.testkit.runner.BuildResult
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.testbase.*
@@ -15,8 +14,6 @@ import org.junit.jupiter.api.DisplayName
@NativeGradlePluginTests
internal class NativeEmbeddableCompilerJarIT : KGPBaseTest() {
override val defaultBuildOptions = super.defaultBuildOptions.copy(logLevel = LogLevel.DEBUG)
private fun String.isRegularJar() = this.endsWith("/kotlin-native.jar")
private fun String.isEmbeddableJar() = this.endsWith("/kotlin-native-compiler-embeddable.jar")
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.gradle.native
import org.gradle.api.logging.LogLevel
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.testbase.*
import org.junit.jupiter.api.DisplayName
@@ -57,7 +56,7 @@ class NativeLibraryDslIT : KGPBaseTest() {
@GradleTest
fun shouldLinkSharedLibrariesFromSingleModule(gradleVersion: GradleVersion) {
nativeProject("new-kn-library-dsl", gradleVersion) {
build(":shared:assembleMylibDebugSharedLibraryLinuxX64", buildOptions = buildOptions.copy(logLevel = LogLevel.DEBUG)) {
build(":shared:assembleMylibDebugSharedLibraryLinuxX64") {
assertTasksExecuted(
":shared:compileKotlinLinuxX64",
":shared:assembleMylibDebugSharedLibraryLinuxX64"
@@ -80,7 +79,7 @@ class NativeLibraryDslIT : KGPBaseTest() {
fun shouldLinkSharedLibrariesFromSingleModuleWithAdditionalLinkArgs(gradleVersion: GradleVersion) {
nativeProject("new-kn-library-dsl", gradleVersion) {
gradleProperties.appendText("\nkotlin.native.linkArgs=-Xfoo=bar -Xbaz=qux")
build(":shared:assembleMylibDebugSharedLibraryLinuxX64", buildOptions = buildOptions.copy(logLevel = LogLevel.DEBUG)) {
build(":shared:assembleMylibDebugSharedLibraryLinuxX64") {
assertTasksExecuted(
":shared:compileKotlinLinuxX64",
":shared:assembleMylibDebugSharedLibraryLinuxX64"
@@ -185,7 +185,7 @@ class NativeLibraryDslWithCocoapodsIT : KGPBaseTest() {
private fun buildNewKnLibraryDslCocoapodsProjectWithTasks(
gradleVersion: GradleVersion,
buildBlock: TestProject.() -> Unit
buildBlock: TestProject.() -> Unit,
) {
nativeProject("new-kn-library-dsl-cocoapods", gradleVersion, test = buildBlock)
}
@@ -17,7 +17,6 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy
import org.jetbrains.kotlin.gradle.report.BuildReportType
import org.junit.jupiter.api.condition.OS
import java.nio.file.Path
import java.nio.file.Paths
import java.util.*
import kotlin.io.path.absolutePathString
@@ -54,7 +53,7 @@ data class BuildOptions(
val nativeOptions: NativeOptions = NativeOptions(),
val compilerExecutionStrategy: KotlinCompilerExecutionStrategy? = null,
val runViaBuildToolsApi: Boolean? = null,
val konanDataDir: Path? = konanDir,
val konanDataDir: Path? = konanDir, // null can be used only if you are using custom 'kotlin.native.home' or 'org.jetbrains.kotlin.native.home' property instead of konanDir
) {
val isK2ByDefault
get() = KotlinVersion.DEFAULT >= KotlinVersion.KOTLIN_2_0
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.gradle.testbase
import org.gradle.api.logging.LogLevel
import org.gradle.testkit.runner.BuildResult
import org.intellij.lang.annotations.Language
import org.jetbrains.kotlin.build.report.metrics.BuildAttribute
import java.io.File
import java.nio.file.Path
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.gradle.testbase
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.target.presetName
import java.util.*
@@ -35,7 +36,6 @@ fun extractNativeCompilerCommandLineArguments(taskOutput: String, toolName: Nati
enum class NativeToolKind(val title: String) {
KONANC("konanc"),
GENERATE_PLATFORM_LIBRARIES("generatePlatformLibraries"),
C_INTEROP("cinterop")
}
@@ -69,4 +69,23 @@ fun extractNativeToolSettings(
emptySequence() // No parameters.
else
settings.drop(1).map { it.trim() }.takeWhile { it != "]" }
}
internal object MPPNativeTargets {
val current = when (HostManager.host) {
KonanTarget.LINUX_X64 -> "linux64"
KonanTarget.MACOS_X64 -> "macos64"
KonanTarget.MACOS_ARM64 -> "macosArm64"
KonanTarget.MINGW_X64 -> "mingw64"
else -> error("Unsupported host")
}
val unsupported = when {
HostManager.hostIsMingw -> setOf("macos64")
HostManager.hostIsLinux -> setOf("macos64")
HostManager.hostIsMac -> emptySet()
else -> error("Unknown host")
}
val supported = listOf("linux64", "macos64", "mingw64").filter { !unsupported.contains(it) }
}
@@ -5,8 +5,11 @@
package org.jetbrains.kotlin.gradle.testbase
import org.gradle.api.logging.LogLevel
import org.gradle.api.logging.configuration.WarningMode
import org.gradle.testkit.runner.BuildResult
import org.gradle.util.GradleVersion
import java.util.*
/**
* Asserts Gradle output contains [expectedSubString] string.
@@ -259,6 +262,21 @@ fun BuildResult.assertCompilerArgument(
}
}
/**
* Asserts classpath of the given K/N compiler tool for given tasks' paths.
*
* Note: Log level of output must be set to [LogLevel.INFO].
*
* @param tasksPaths tasks' paths, for which classpath should be checked with give assertions
* @param toolName name of build tool
* @param assertions assertions, with will be applied to each classpath of each given task
*/
fun BuildResult.assertNativeTasksClasspath(
vararg tasksPaths: String,
toolName: NativeToolKind = NativeToolKind.KONANC,
assertions: (List<String>) -> Unit,
) = tasksPaths.forEach { taskPath -> assertions(extractNativeCompilerClasspath(getOutputForTask(taskPath, LogLevel.INFO), toolName)) }
fun BuildResult.assertCompilerArguments(
taskPath: String,
vararg expectedArguments: String,
@@ -296,6 +314,21 @@ fun BuildResult.assertNoCompilerArgument(
}
}
/**
* Asserts environment variables of the given K/N compiler for given tasks' paths
*
* Note: Log level of output must be set to [LogLevel.INFO].
*
* @param tasksPaths tasks' paths, for which command line arguments should be checked with give assertions.
* @param toolName name of build tool
* @param assertions assertions, with will be applied to each command line arguments of each given task
*/
fun BuildResult.assertNativeTasksCustomEnvironment(
vararg tasksPaths: String,
toolName: NativeToolKind = NativeToolKind.KONANC,
assertions: (Map<String, String>) -> Unit,
) = tasksPaths.forEach { taskPath -> assertions(extractNativeCustomEnvironment(taskPath, toolName)) }
/**
* Asserts that the given list of command line arguments does not contain any of the expected arguments.
*
@@ -330,6 +363,56 @@ fun CommandLineArguments.assertCommandLineArgumentsContain(
}
}
/**
* Asserts that the given list of command line arguments contains sequentially all the expected arguments.
*
* @param expectedArgs the list of expected arguments
* @throws AssertionError if any of the expected arguments are missing from the actual arguments list
*/
fun CommandLineArguments.assertCommandLineArgumentsContainSequentially(
vararg expectedArgs: String,
) {
expectedArgs.forEach {
assert(expectedArgs.isNotEmpty() && Collections.indexOfSubList(args, expectedArgs.toList()) != -1) {
this.buildResult.printBuildOutput()
"There is no sequential arguments ${it} in actual command line arguments are: ${args}"
}
}
}
/**
* Asserts that the output of a Gradle build contains a variant with the given name.
*
* @param variantName The name of the variant to look for in the output.
* @param gradleVersion The version of Gradle used to build the variant.
* @throws AssertionError if no variant with the given name and Gradle version is found in the output.
*/
fun BuildResult.assertOutputContainsNativeFrameworkVariant(variantName: String, gradleVersion: GradleVersion) {
val isAtLeastGradle75 = gradleVersion >= GradleVersion.version(TestVersions.Gradle.G_7_5)
try {
assertOutputContains(
if (isAtLeastGradle75)
"Variant $variantName"
else "variant \"$variantName\" ["
)
} catch (originalError: AssertionError) {
val regexPattern = if (isAtLeastGradle75) {
"Variant (.*?):"
} else {
"variant \"(.*?)\" \\["
}
val matchedVariants = Regex(regexPattern).findAll(output).toList()
throw AssertionError(
"Expected variant $variantName. " +
if (matchedVariants.isNotEmpty())
"Found instead: " + matchedVariants.joinToString { it.groupValues[1] }
else "No match.",
originalError
)
}
}
/**
* Asserts that the command line arguments do not contain any duplicates.
*/
@@ -339,3 +422,11 @@ fun CommandLineArguments.assertNoDuplicates() {
"Link task has duplicated arguments: ${args.joinToString()}"
}
}
private fun BuildResult.extractNativeCustomEnvironment(taskPath: String, toolName: NativeToolKind): Map<String, String> =
extractNativeToolSettings(getOutputForTask(taskPath, LogLevel.INFO), toolName, NativeToolSettingsKind.CUSTOM_ENV_VARIABLES).map {
val (key, value) = it.split("=")
key.trim() to value.trim()
}.toMap()
@@ -10,7 +10,7 @@ import org.gradle.testkit.runner.BuildResult
import org.intellij.lang.annotations.Language
@Language("RegExp")
private fun taskOutputRegex(
private fun taskOutputRegexForDebugLog(
taskName: String,
) = """
\[org\.gradle\.internal\.operations\.DefaultBuildOperationRunner] Build operation 'Task $taskName' started
@@ -20,22 +20,58 @@ private fun taskOutputRegex(
.replace("\n", "")
.toRegex()
/**
* Filter [BuildResult.getOutput] for specific task with given [taskPath]
*
* Requires using [LogLevel.DEBUG].
*/
fun BuildResult.getOutputForTask(taskPath: String): String = getOutputForTask(taskPath, output)
@Language("RegExp")
private fun taskOutputRegexForInfoLog(
taskName: String,
) = """
^\s*$
^> Task $taskName$
([\s\S]+?)
^\s*$
""".trimIndent()
.toRegex(RegexOption.MULTILINE)
/**
* Filter give output for specific task with given [taskPath]
* Gets the output produced by a specific task during a Gradle build.
*
* Requires using [LogLevel.DEBUG].
* @param taskPath The path of the task whose output should be retrieved.
* @param logLevel The given output contains no more than the [logLevel] logs.
*
* @return The output produced by the specified task during the build.
*
* @throws IllegalStateException if the specified task path does not match any tasks in the build.
*/
fun getOutputForTask(taskPath: String, output: String): String = taskOutputRegex(taskPath)
fun BuildResult.getOutputForTask(taskPath: String, logLevel: LogLevel = LogLevel.DEBUG): String =
getOutputForTask(taskPath, output, logLevel)
/**
* Gets the output produced by a specific task during a Gradle build.
*
* @param taskPath The path of the task whose output should be retrieved.
* @param output The output from which we should extract task's output
* @param logLevel The given output contains no more than the [logLevel] logs.
*
* @return The output produced by the specified task during the build.
*
* @throws IllegalStateException if the specified task path does not match any tasks in the build.
*/
fun getOutputForTask(taskPath: String, output: String, logLevel: LogLevel = LogLevel.DEBUG): String = (
when (logLevel) {
LogLevel.INFO -> taskOutputRegexForInfoLog(taskPath)
LogLevel.DEBUG -> taskOutputRegexForDebugLog(taskPath)
else -> throw throw IllegalStateException("Unsupported log lever for task output was given: $logLevel")
})
.find(output)
?.let { it.groupValues[1] }
?: error("Could not find output for task $taskPath")
?: error(
"""
Could not find output for task $taskPath.
=================
Build output is:
$output
=================
""".trimIndent()
)
class CommandLineArguments(
val args: List<String>,
@@ -49,15 +85,18 @@ class CommandLineArguments(
*
* @param tasksPaths The paths of the tasks for which the command line arguments should be checked against the provided assertions.
* @param toolName The name of the build tool used.
* @param logLevel The given output contains no more than the [logLevel] logs.
* @param assertions The assertions to be applied to each command line argument of each given task.
* These assertions validate the expected properties of the command line arguments.
* These assertions validate the expected properties of the command line arguments.
*/
fun BuildResult.extractNativeTasksCommandLineArgumentsFromOutput(
vararg tasksPaths: String,
toolName: NativeToolKind = NativeToolKind.KONANC,
logLevel: LogLevel = LogLevel.INFO,
assertions: CommandLineArguments.() -> Unit,
) = tasksPaths.forEach { taskPath ->
val taskOutput = getOutputForTask(taskPath)
val taskOutput = getOutputForTask(taskPath, logLevel)
val commandLineArguments = extractNativeCompilerCommandLineArguments(taskOutput, toolName)
assertions(
CommandLineArguments(commandLineArguments, this)
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.gradle.testbase
import java.io.IOException
import java.nio.file.*
import java.nio.file.attribute.BasicFileAttributes
import kotlin.io.path.*
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.gradle.testbase
import org.gradle.api.logging.LogLevel
import org.gradle.testkit.runner.BuildResult
import org.gradle.testkit.runner.TaskOutcome
@@ -150,21 +149,6 @@ fun BuildResult.assertTasksPackedToCache(vararg tasks: String) {
}
}
/**
* Asserts classpath of the given K/N compiler tool for given tasks' paths.
*
* Note: Log level of output must be set to [LogLevel.DEBUG].
*
* @param tasksPaths tasks' paths, for which classpath should be checked with give assertions
* @param toolName name of build tool
* @param assertions assertions, with will be applied to each classpath of each given task
*/
fun BuildResult.assertNativeTasksClasspath(
vararg tasksPaths: String,
toolName: NativeToolKind = NativeToolKind.KONANC,
assertions: (List<String>) -> Unit,
) = tasksPaths.forEach { taskPath -> assertions(extractNativeCompilerClasspath(getOutputForTask(taskPath), toolName)) }
/**
* Builds test project with 'tasks --all' arguments and then
* asserts that [registeredTasks] of the given tasks have been registered
@@ -12,19 +12,18 @@ import org.jdom.input.SAXBuilder
import org.jdom.output.Format
import org.jdom.output.XMLOutputter
import org.jetbrains.kotlin.test.util.trimTrailingWhitespaces
import java.nio.file.Files
import java.nio.file.Path
import kotlin.io.path.*
import kotlin.streams.asSequence
import kotlin.streams.toList
import kotlin.io.path.absolutePathString
import kotlin.io.path.name
import kotlin.io.path.readText
import kotlin.test.assertEquals
fun GradleProject.assertTestResults(expectedTestReport: Path, vararg testReportNames: String) {
fun GradleProject.assertTestResults(expectedTestReport: Path, vararg testReportNames: String, cleanupStdOut: (String) -> String = { it }) {
val testReportDirs = testReportNames.map { projectPath.resolve("build/test-results/$it") }
assertDirectoriesExist(*testReportDirs.toTypedArray())
val actualTestResults = readAndCleanupTestResults(testReportDirs, projectPath)
val actualTestResults = readAndCleanupTestResults(testReportDirs, projectPath, cleanupStdOut)
val expectedTestResults = prettyPrintXml(expectedTestReport.readText())
assertEquals(expectedTestResults, actualTestResults)
@@ -304,12 +304,14 @@ open class GradleProject(
fun classesDir(
sourceSet: String = "main",
language: String = "kotlin",
): Path = projectPath.resolve("build/classes/$language/$sourceSet/")
targetName: String? = null,
language: String = "kotlin"
): Path = projectPath.resolve("build/classes/$language/${targetName.orEmpty()}/$sourceSet/")
fun kotlinClassesDir(
sourceSet: String = "main",
): Path = classesDir(sourceSet, language = "kotlin")
targetName: String? = null
): Path = classesDir(sourceSet, targetName, language = "kotlin")
fun javaClassesDir(
sourceSet: String = "main",
@@ -328,6 +330,9 @@ open class GradleProject(
): List<Path> = files.map { projectPath.relativize(it) }
}
/**
* You need at least [TestVersions.Gradle.G_7_0] for supporting environment variables with gradle runner
*/
@JvmInline
value class EnvironmentalVariables @EnvironmentalVariablesOverride constructor(val environmentalVariables: Map<String, String> = emptyMap()) {
@EnvironmentalVariablesOverride constructor(vararg environmentVariables: Pair<String, String>) : this(mapOf(*environmentVariables))
@@ -579,7 +584,6 @@ internal fun Path.enableAndroidSdk() {
applyAndroidTestFixes()
}
internal fun Path.enableCacheRedirector() {
// Path relative to the current gradle module project dir
val redirectorScript = Paths.get("../../../repo/scripts/cache-redirector.settings.gradle.kts")
@@ -1,5 +1,5 @@
plugins {
kotlin("multiplatform").version("<pluginMarkerVersion>")
kotlin("multiplatform")
`maven-publish`
}
@@ -1,5 +1,5 @@
plugins {
kotlin("multiplatform").version("<pluginMarkerVersion>")
kotlin("multiplatform")
}
repositories {
@@ -1,5 +1,5 @@
plugins {
id("org.jetbrains.kotlin.multiplatform").version("<pluginMarkerVersion>")
id("org.jetbrains.kotlin.multiplatform")
}
repositories {
@@ -1,5 +1,5 @@
plugins {
id("org.jetbrains.kotlin.multiplatform").version("<pluginMarkerVersion>")
id("org.jetbrains.kotlin.multiplatform")
}
repositories {
@@ -1,5 +1,5 @@
plugins {
id("org.jetbrains.kotlin.multiplatform").version("<pluginMarkerVersion>")
id("org.jetbrains.kotlin.multiplatform")
}
repositories {
@@ -1,5 +1,5 @@
plugins {
id("org.jetbrains.kotlin.multiplatform").version("<pluginMarkerVersion>")
id("org.jetbrains.kotlin.multiplatform")
}
repositories {
@@ -1,5 +1,5 @@
plugins {
id("org.jetbrains.kotlin.multiplatform").version("<pluginMarkerVersion>")
id("org.jetbrains.kotlin.multiplatform")
}
repositories {
@@ -1,5 +1,5 @@
plugins {
id("org.jetbrains.kotlin.multiplatform").version("<pluginMarkerVersion>")
id("org.jetbrains.kotlin.multiplatform")
}
repositories {
@@ -1,5 +1,6 @@
import java.util.concurrent.CountDownLatch
plugins {
id("org.jetbrains.kotlin.multiplatform").version("<pluginMarkerVersion>").apply(false)
id("org.jetbrains.kotlin.multiplatform").apply(false)
}
allprojects {
@@ -7,4 +8,4 @@ allprojects {
mavenLocal()
mavenCentral()
}
}
}
@@ -1,3 +1,4 @@
import java.util.concurrent.CountDownLatch
plugins {
id("org.jetbrains.kotlin.multiplatform")
}
@@ -8,4 +9,4 @@ kotlin {
sourceSets["commonMain"].dependencies {
implementation(kotlin("stdlib"))
}
}
}
@@ -1,3 +1,4 @@
import java.util.concurrent.CountDownLatch
plugins {
id("org.jetbrains.kotlin.multiplatform")
}
@@ -8,4 +9,4 @@ kotlin {
sourceSets["commonMain"].dependencies {
implementation(kotlin("stdlib"))
}
}
}
@@ -1,5 +1,5 @@
plugins {
kotlin("multiplatform").version("<pluginMarkerVersion>").apply(false)
kotlin("multiplatform").apply(false)
}
allprojects {
@@ -558,7 +558,7 @@ internal class PropertiesProvider private constructor(private val project: Proje
private fun propertyWithDeprecatedVariant(propName: String, deprecatedPropName: String): String? {
val deprecatedProperty = property(deprecatedPropName).orNull
if (deprecatedProperty != null) {
project.reportDiagnosticOncePerBuild(KotlinToolingDiagnostics.DeprecatedPropertyWithReplacement(deprecatedProperty, propName))
project.reportDiagnosticOncePerBuild(KotlinToolingDiagnostics.DeprecatedPropertyWithReplacement(deprecatedPropName, propName))
}
return property(propName).orNull ?: deprecatedProperty
}