Migrated CommonizerIT to new test dsl (junit 5 and gradle test kit)

This commit is contained in:
Dmitrii Krasnov
2023-06-13 16:00:59 +02:00
committed by Space Team
parent b22769919e
commit 0e51a00871
7 changed files with 424 additions and 408 deletions
@@ -1,71 +1,76 @@
/* /*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2023 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. * 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.gradle package org.jetbrains.kotlin.gradle
import groovy.json.StringEscapeUtils import org.gradle.api.logging.LogLevel
import org.gradle.api.logging.LogLevel.INFO
import org.gradle.internal.os.OperatingSystem import org.gradle.internal.os.OperatingSystem
import org.gradle.testkit.runner.BuildResult
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.commonizer.CommonizerTarget import org.jetbrains.kotlin.commonizer.CommonizerTarget
import org.jetbrains.kotlin.gradle.testbase.*
import org.jetbrains.kotlin.gradle.util.replaceText
import org.jetbrains.kotlin.gradle.util.reportSourceSetCommonizerDependencies import org.jetbrains.kotlin.gradle.util.reportSourceSetCommonizerDependencies
import org.jetbrains.kotlin.incremental.testingUtils.assertEqualDirectories import org.jetbrains.kotlin.incremental.testingUtils.assertEqualDirectories
import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget.* import org.jetbrains.kotlin.konan.target.KonanTarget.*
import kotlin.test.Test import org.junit.jupiter.api.DisplayName
import kotlin.test.assertFalse import kotlin.io.path.*
import kotlin.test.assertTrue import kotlin.test.assertTrue
import kotlin.test.fail import kotlin.test.fail
open class CommonizerIT : BaseGradleIT() {
override val defaultGradleVersion: GradleVersionRequired = GradleVersionRequired.FOR_MPP_SUPPORT @DisplayName("K/N tests for commonizer")
@NativeGradlePluginTests
open class CommonizerIT : KGPBaseTest() {
companion object { companion object {
private const val commonizerOutput = "Preparing commonized Kotlin/Native libraries" private const val commonizerOutput = "Preparing commonized Kotlin/Native libraries"
} }
@Test @DisplayName("Commonize native distribution with Ios Linux and Windows")
fun `test commonizeNativeDistributionWithIosLinuxWindows`() { @GradleTest
with(Project("commonizeNativeDistributionWithIosLinuxWindows")) { fun testCommonizeNativeDistributionWithIosLinuxWindows(gradleVersion: GradleVersion) {
build(":cleanNativeDistributionCommonization") { nativeProject("commonizeNativeDistributionWithIosLinuxWindows", gradleVersion) {
assertSuccessful()
} build(":cleanNativeDistributionCommonization")
build("commonize", "-Pkotlin.mpp.enableNativeDistributionCommonizationCache=false") { build("commonize", "-Pkotlin.mpp.enableNativeDistributionCommonizationCache=false") {
assertTasksExecuted(":commonizeNativeDistribution") assertTasksExecuted(":commonizeNativeDistribution")
assertContains(commonizerOutput) assertOutputContains(commonizerOutput)
assertSuccessful()
} }
build("commonize", "-Pkotlin.mpp.enableNativeDistributionCommonizationCache=true") { build("commonize", "-Pkotlin.mpp.enableNativeDistributionCommonizationCache=true") {
assertTasksUpToDate(":commonizeNativeDistribution") assertTasksUpToDate(":commonizeNativeDistribution")
assertNativeDistributionCommonizationCacheHit() assertNativeDistributionCommonizationCacheHit()
assertContains("Native Distribution Commonization: All available targets are commonized already") assertOutputContains("Native Distribution Commonization: All available targets are commonized already")
assertContains("Native Distribution Commonization: Lock acquired") assertOutputContains("Native Distribution Commonization: Lock acquired")
assertContains("Native Distribution Commonization: Lock released") assertOutputContains("Native Distribution Commonization: Lock released")
assertNotContains(commonizerOutput) assertOutputDoesNotContain(commonizerOutput)
assertSuccessful()
} }
build("commonize", "-Pkotlin.mpp.enableNativeDistributionCommonizationCache=false") { build("commonize", "-Pkotlin.mpp.enableNativeDistributionCommonizationCache=false") {
assertTasksExecuted(":commonizeNativeDistribution") assertTasksExecuted(":commonizeNativeDistribution")
assertContains("Native Distribution Commonization: Cache disabled") assertOutputContains("Native Distribution Commonization: Cache disabled")
assertContains(commonizerOutput) assertOutputContains(commonizerOutput)
assertSuccessful()
} }
} }
} }
@Test @DisplayName("Commonize Curl Interop UP-TO-DATE check")
fun `test commonizeCurlInterop UP-TO-DATE check`() { @GradleTest
with(preparedProject("commonizeCurlInterop")) { fun testCommonizeCurlInteropUTDCheck(gradleVersion: GradleVersion) {
nativeProject("commonizeCurlInterop", gradleVersion) {
configureCommonizerTargets()
build(":commonize") { build(":commonize") {
assertTasksExecuted(":commonizeNativeDistribution") assertTasksExecuted(":commonizeNativeDistribution")
assertTasksExecuted(":cinteropCurlTargetA") assertTasksExecuted(":cinteropCurlTargetA")
assertTasksExecuted(":cinteropCurlTargetB") assertTasksExecuted(":cinteropCurlTargetB")
assertTasksExecuted(":commonizeCInterop") assertTasksExecuted(":commonizeCInterop")
assertSuccessful()
} }
build(":commonize") { build(":commonize") {
@@ -73,38 +78,37 @@ open class CommonizerIT : BaseGradleIT() {
assertTasksUpToDate(":cinteropCurlTargetA") assertTasksUpToDate(":cinteropCurlTargetA")
assertTasksUpToDate(":cinteropCurlTargetB") assertTasksUpToDate(":cinteropCurlTargetB")
assertTasksUpToDate(":commonizeCInterop") assertTasksUpToDate(":commonizeCInterop")
assertSuccessful()
} }
val buildGradleKts = projectFile("build.gradle.kts") buildGradleKts.replaceText("curl", "curl2")
val originalBuildGradleKtsContent = buildGradleKts.readText()
buildGradleKts.writeText(originalBuildGradleKtsContent.replace("curl", "curl2"))
build(":commonize") { build(":commonize") {
assertNativeDistributionCommonizationCacheHit() assertNativeDistributionCommonizationCacheHit()
assertTasksExecuted(":cinteropCurl2TargetA") assertTasksExecuted(":cinteropCurl2TargetA")
assertTasksExecuted(":cinteropCurl2TargetB") assertTasksExecuted(":cinteropCurl2TargetB")
assertTasksExecuted(":commonizeCInterop") assertTasksExecuted(":commonizeCInterop")
assertSuccessful()
} }
buildGradleKts.writeText(originalBuildGradleKtsContent.lineSequence().filter { "curl" !in it }.joinToString("\n")) buildGradleKts.modify {
it.lineSequence().filter { "curl" !in it }.joinToString("\n")
}
build(":commonize") { build(":commonize") {
assertNativeDistributionCommonizationCacheHit() assertNativeDistributionCommonizationCacheHit()
assertTasksNotExecuted(":cinteropCurlTargetA") assertTasksNotExecuted(":cinteropCurlTargetA")
assertTasksNotExecuted(":cinteropCurlTargetB") assertTasksNotExecuted(":cinteropCurlTargetB")
assertSuccessful()
} }
} }
} }
@Test @DisplayName("Commonize Curl Interop feature flag")
fun `test commonizeCurlInterop feature flag`() { @GradleTest
with(preparedProject("commonizeCurlInterop")) { fun testCommonizeCurlInteropFeatureFlag(gradleVersion: GradleVersion) {
setupWorkingDir() nativeProject("commonizeCurlInterop", gradleVersion) {
configureCommonizerTargets()
// Remove feature flag from gradle.properties // Remove feature flag from gradle.properties
projectFile("gradle.properties").apply { gradleProperties.modify {
writeText(readText().lineSequence().filter { "enableCInteropCommonization" !in it }.joinToString("\n")) it.lineSequence().filter { "enableCInteropCommonization" !in it }.joinToString("\n")
} }
build(":commonize") { build(":commonize") {
@@ -112,7 +116,6 @@ open class CommonizerIT : BaseGradleIT() {
assertTasksNotExecuted(":cinteropCurl2TargetA") assertTasksNotExecuted(":cinteropCurl2TargetA")
assertTasksNotExecuted(":cinteropCurl2TargetB") assertTasksNotExecuted(":cinteropCurl2TargetB")
assertTasksNotExecuted(":commonizeCInterop") assertTasksNotExecuted(":commonizeCInterop")
assertSuccessful()
} }
build(":commonize", "-Pkotlin.mpp.enableCInteropCommonization=true") { build(":commonize", "-Pkotlin.mpp.enableCInteropCommonization=true") {
@@ -120,7 +123,6 @@ open class CommonizerIT : BaseGradleIT() {
assertTasksExecuted(":cinteropCurlTargetA") assertTasksExecuted(":cinteropCurlTargetA")
assertTasksExecuted(":cinteropCurlTargetB") assertTasksExecuted(":cinteropCurlTargetB")
assertTasksExecuted(":commonizeCInterop") assertTasksExecuted(":commonizeCInterop")
assertSuccessful()
} }
build(":commonize", "-Pkotlin.mpp.enableCInteropCommonization=false") { build(":commonize", "-Pkotlin.mpp.enableCInteropCommonization=false") {
@@ -128,64 +130,67 @@ open class CommonizerIT : BaseGradleIT() {
assertTasksNotExecuted(":cinteropCurlTargetA") assertTasksNotExecuted(":cinteropCurlTargetA")
assertTasksNotExecuted(":cinteropCurlTargetB") assertTasksNotExecuted(":cinteropCurlTargetB")
assertTasksNotExecuted(":commonizeCInterop") assertTasksNotExecuted(":commonizeCInterop")
assertSuccessful()
} }
} }
} }
@Test
fun `test commonizeCurlInterop copyCommonizeCInteropForIde`() { @DisplayName("Commonize Curl Interop copy CommonizeCInterop for Ide")
with(preparedProject("commonizeCurlInterop")) { @GradleTest
setupWorkingDir() fun testCommonizeCurlInteropcopyCommonizeCInteropForIde(gradleVersion: GradleVersion) {
val expectedOutputDirectoryForIde = projectDir.resolve(".gradle/kotlin/commonizer") nativeProject("commonizeCurlInterop", gradleVersion) {
val expectedOutputDirectoryForBuild = projectDir.resolve("build/classes/kotlin/commonizer")
configureCommonizerTargets()
val expectedOutputDirectoryForIde = projectPath.resolve(".gradle/kotlin/commonizer")
val expectedOutputDirectoryForBuild = projectPath.resolve("build/classes/kotlin/commonizer")
build(":copyCommonizeCInteropForIde") { build(":copyCommonizeCInteropForIde") {
assertSuccessful()
assertTasksExecuted(":cinteropCurlTargetA")
assertTasksExecuted(":cinteropCurlTargetB") assertTasksExecuted(":cinteropCurlTargetB")
assertTasksExecuted(":commonizeCInterop") assertTasksExecuted(":commonizeCInterop")
assertTrue(expectedOutputDirectoryForIde.isDirectory, "Missing output directory for IDE") assertDirectoryExists(expectedOutputDirectoryForIde, "Missing output directory for IDE")
assertTrue(expectedOutputDirectoryForBuild.isDirectory, "Missing output directory for build") assertDirectoryExists(expectedOutputDirectoryForBuild, "Missing output directory for build")
assertEqualDirectories(expectedOutputDirectoryForBuild, expectedOutputDirectoryForIde, false) assertEqualDirectories(expectedOutputDirectoryForBuild.toFile(), expectedOutputDirectoryForIde.toFile(), false)
} }
build(":clean") { build(":clean") {
assertSuccessful() assertDirectoryExists(expectedOutputDirectoryForIde, "Expected ide output directory to survive cleaning")
assertTrue(expectedOutputDirectoryForIde.isDirectory, "Expected ide output directory to survive cleaning") assertFileNotExists(expectedOutputDirectoryForBuild, "Expected output directory for build to be cleaned")
assertFalse(expectedOutputDirectoryForBuild.exists(), "Expected output directory for build to be cleaned")
} }
} }
} }
@Test @DisplayName("Commonize Curl Interop compilation")
fun `test commonizeCurlInterop compilation`() { @GradleTest
with(preparedProject("commonizeCurlInterop")) { fun testCommonizeCurlInteropCopyCompilation(gradleVersion: GradleVersion) {
nativeProject("commonizeCurlInterop", gradleVersion) {
configureCommonizerTargets()
build(":compileNativeMainKotlinMetadata") { build(":compileNativeMainKotlinMetadata") {
assertTasksExecuted(":commonizeNativeDistribution") assertTasksExecuted(":commonizeNativeDistribution")
assertTasksExecuted(":cinteropCurlTargetA") assertTasksExecuted(":cinteropCurlTargetA")
assertTasksExecuted(":cinteropCurlTargetB") assertTasksExecuted(":cinteropCurlTargetB")
assertTasksExecuted(":commonizeCInterop") assertTasksExecuted(":commonizeCInterop")
assertSuccessful()
} }
// targetA will be macos // targetA will be macos
build(":compileTestKotlinTargetA") { build(":compileTestKotlinTargetA")
assertSuccessful()
}
//targetB will be linuxArm64 //targetB will be linuxArm64
build(":compileTestKotlinTargetB") { build(":compileTestKotlinTargetB")
assertSuccessful()
}
} }
} }
@Test @DisplayName("Commonize SQL Lite Interop")
fun `test commonizeSQLiteInterop`() { @GradleTest
with(preparedProject("commonizeSQLiteInterop")) { fun testCommonizeSQLiteInterop(gradleVersion: GradleVersion) {
nativeProject("commonizeSQLiteInterop", gradleVersion) {
configureCommonizerTargets()
build(":commonize") { build(":commonize") {
assertSuccessful()
assertTasksExecuted(":cinteropSqliteTargetA") assertTasksExecuted(":cinteropSqliteTargetA")
assertTasksExecuted(":cinteropSqliteTargetB") assertTasksExecuted(":cinteropSqliteTargetB")
assertTasksExecuted(":commonizeCInterop") assertTasksExecuted(":commonizeCInterop")
@@ -193,11 +198,14 @@ open class CommonizerIT : BaseGradleIT() {
} }
} }
@Test @DisplayName("Commonize SQL Lite and Curl Interop")
fun `test commonizeSQLiteAndCurlInterop`() { @GradleTest
with(preparedProject("commonizeSQLiteAndCurlInterop")) { fun testCommonizeSQLiteAndCurlInterop(gradleVersion: GradleVersion) {
nativeProject("commonizeSQLiteAndCurlInterop", gradleVersion) {
configureCommonizerTargets()
build(":commonize") { build(":commonize") {
assertSuccessful()
assertTasksExecuted(":cinteropSqliteTargetA") assertTasksExecuted(":cinteropSqliteTargetA")
assertTasksExecuted(":cinteropSqliteTargetB") assertTasksExecuted(":cinteropSqliteTargetB")
assertTasksExecuted(":cinteropCurlTargetA") assertTasksExecuted(":cinteropCurlTargetA")
@@ -206,7 +214,6 @@ open class CommonizerIT : BaseGradleIT() {
} }
build(":compileNativeMainKotlinMetadata") { build(":compileNativeMainKotlinMetadata") {
assertSuccessful()
assertTasksUpToDate(":cinteropSqliteTargetA") assertTasksUpToDate(":cinteropSqliteTargetA")
assertTasksUpToDate(":cinteropSqliteTargetB") assertTasksUpToDate(":cinteropSqliteTargetB")
assertTasksUpToDate(":cinteropCurlTargetA") assertTasksUpToDate(":cinteropCurlTargetA")
@@ -216,11 +223,14 @@ open class CommonizerIT : BaseGradleIT() {
} }
} }
@Test @DisplayName("Commonize Interop using posix APIs")
fun `test commonizeInterop using posix APIs`() { @GradleTest
with(preparedProject("commonizeInteropUsingPosixApis")) { fun testCommonizeInteropUsingPosixAPIs(gradleVersion: GradleVersion) {
nativeProject("commonizeInteropUsingPosixApis", gradleVersion) {
configureCommonizerTargets()
build(":commonizeCInterop") { build(":commonizeCInterop") {
assertSuccessful()
assertTasksExecuted(":cinteropWithPosixTargetA") assertTasksExecuted(":cinteropWithPosixTargetA")
assertTasksExecuted(":cinteropWithPosixTargetB") assertTasksExecuted(":cinteropWithPosixTargetB")
assertTasksExecuted(":commonizeNativeDistribution") assertTasksExecuted(":commonizeNativeDistribution")
@@ -228,7 +238,6 @@ open class CommonizerIT : BaseGradleIT() {
} }
build(":compileNativeMainKotlinMetadata") { build(":compileNativeMainKotlinMetadata") {
assertSuccessful()
assertTasksUpToDate(":cinteropWithPosixTargetA") assertTasksUpToDate(":cinteropWithPosixTargetA")
assertTasksUpToDate(":cinteropWithPosixTargetB") assertTasksUpToDate(":cinteropWithPosixTargetB")
assertNativeDistributionCommonizationCacheHit() assertNativeDistributionCommonizationCacheHit()
@@ -237,184 +246,277 @@ open class CommonizerIT : BaseGradleIT() {
} }
} }
@Test @DisplayName("KT-46234 intermediate source set with only one native target")
fun `test KT-46234 intermediate source set with only one native target`() { @GradleTest
`test single native platform`("commonize-kt-46234-singleNativeTarget") fun testIntermediateSourceSetWithOnlyOneNativeTarget(gradleVersion: GradleVersion) {
testSingleNativePlatform("commonize-kt-46234-singleNativeTarget", gradleVersion)
} }
@Test @DisplayName("KT-46142 standalone native source set")
fun `test KT-46142 standalone native source set`() { @GradleTest
`test single native platform`("commonize-kt-46142-singleNativeTarget") fun testStandaloneNativeSourceSet(gradleVersion: GradleVersion) {
testSingleNativePlatform("commonize-kt-46142-singleNativeTarget", gradleVersion)
} }
private fun `test single native platform`(project: String) { @DisplayName("KT-46248 single supported native target dependency propagation")
val posixInIntransitiveMetadataConfigurationRegex = Regex(""".*intransitiveMetadataConfiguration:.*([pP])osix""") @GradleTest
fun testSingleSupportedNativeTargetDependencyPropagation(gradleVersion: GradleVersion) {
fun CompiledProject.containsPosixInIntransitiveMetadataConfiguration(): Boolean = val posixDependencyRegex = Regex(""".*Dependency:.*[pP]osix""")
output.lineSequence().any { line -> val dummyCInteropDependencyRegex = Regex(""".*Dependency:.*cinterop-dummy.*""")
line.matches(posixInIntransitiveMetadataConfigurationRegex) nativeProject("commonize-kt-46248-singleNativeTargetPropagation", gradleVersion) {
}
with(Project(project)) {
build(":p1:listNativePlatformMainDependencies", "-Pkotlin.mpp.enableIntransitiveMetadataConfiguration=false") {
assertSuccessful()
assertFalse(
containsPosixInIntransitiveMetadataConfiguration(),
"Expected **no** dependency on posix in intransitiveMetadataConfiguration"
)
}
build(":p1:listNativePlatformMainDependencies", "-Pkotlin.mpp.enableIntransitiveMetadataConfiguration=true") {
assertSuccessful()
assertTrue(
containsPosixInIntransitiveMetadataConfiguration(),
"Expected dependency on posix in intransitiveMetadataConfiguration"
)
}
build("assemble") {
assertSuccessful()
}
}
}
@Test
fun `test KT-46248 single supported native target dependency propagation`() {
fun CompiledProject.containsPosixDependency(): Boolean = output.lineSequence().any { line ->
line.matches(Regex(""".*Dependency:.*[pP]osix"""))
}
fun CompiledProject.containsDummyCInteropDependency(): Boolean = output.lineSequence().any { line ->
line.matches(Regex(""".*Dependency:.*cinterop-dummy.*"""))
}
with(Project("commonize-kt-46248-singleNativeTargetPropagation")) {
build(":p1:listNativeMainDependencies") { build(":p1:listNativeMainDependencies") {
assertSuccessful() assertOutputContains(posixDependencyRegex)
assertTrue(containsPosixDependency(), "Expected dependency on posix in nativeMain") assertOutputContains(dummyCInteropDependencyRegex)
assertTrue(containsDummyCInteropDependency(), "Expected dependency on dummy cinterop in nativeMain")
} }
build(":p1:listNativeMainParentDependencies") { build(":p1:listNativeMainParentDependencies") {
assertSuccessful() assertOutputContains(posixDependencyRegex)
assertTrue(containsPosixDependency(), "Expected dependency on posix in nativeMainParent") assertOutputContains(dummyCInteropDependencyRegex)
assertTrue(containsDummyCInteropDependency(), "Expected dependency on dummy cinterop in nativeMain")
} }
build(":p1:listCommonMainDependencies") { build(":p1:listCommonMainDependencies") {
assertSuccessful() assertOutputDoesNotContain(posixDependencyRegex)
assertFalse(containsPosixDependency(), "Expected **no** dependency on posix in commonMain (because of jvm target)") assertOutputDoesNotContain(dummyCInteropDependencyRegex)
assertFalse(containsDummyCInteropDependency(), "Expected **no** dependency on dummy cinterop in nativeMain")
} }
build("assemble") { build("assemble") {
assertSuccessful()
assertTasksExecuted(":p1:compileCommonMainKotlinMetadata") assertTasksExecuted(":p1:compileCommonMainKotlinMetadata")
assertTasksExecuted(":p1:compileKotlinNativePlatform") assertTasksExecuted(":p1:compileKotlinNativePlatform")
} }
} }
} }
@Test @DisplayName("KT-46248 single supported native target dependency propagation - cinterop")
fun `test KT-46248 single supported native target dependency propagation - cinterop`() { @GradleTest
fun CompiledProject.containsCinteropDependency(): Boolean { fun testSingleSupportedNativeTargetDependencyPropagationCInterop(gradleVersion: GradleVersion) {
val nativeMainContainsCInteropDependencyRegex = Regex(""".*Dependency:.*cinterop-dummy.*""") val nativeMainContainsCInteropDependencyRegex = Regex(""".*Dependency:.*cinterop-dummy.*""")
return output.lineSequence().any { line -> nativeProject("commonize-kt-47523-singleNativeTargetPropagation-cinterop", gradleVersion) {
line.matches(nativeMainContainsCInteropDependencyRegex)
}
}
with(Project("commonize-kt-47523-singleNativeTargetPropagation-cinterop")) {
build("listNativePlatformMainDependencies") { build("listNativePlatformMainDependencies") {
assertSuccessful() assertOutputDoesNotContain(nativeMainContainsCInteropDependencyRegex)
assertFalse(
containsCinteropDependency(),
"Expected sourceSet 'nativeMain' to list cinterop dependency (not necessary, since included in compilation)"
)
} }
build("listNativeMainDependencies") { build("listNativeMainDependencies") {
assertSuccessful() assertOutputContains(nativeMainContainsCInteropDependencyRegex)
assertTrue(containsCinteropDependency(), "Expected sourceSet 'nativeMain' to list cinterop dependency")
} }
build("listCommonMainDependencies") { build("listCommonMainDependencies") {
assertSuccessful() assertOutputContains(nativeMainContainsCInteropDependencyRegex)
assertTrue(containsCinteropDependency(), "Expected sourceSet 'commonMain' to list cinterop dependency")
} }
build("assemble") { build("assemble")
assertSuccessful()
}
} }
} }
@Test @DisplayName("KT-48856 single native target dependency propagation - test source set - cinterop")
fun `test KT-48856 single native target dependency propagation - test source set - cinterop`() { @GradleTest
fun CompiledProject.containsCinteropDependency(): Boolean { fun testSingleSupportedNativeTargetDependencyPropagationTestSourceSetCInterop(gradleVersion: GradleVersion) {
val nativeMainContainsCInteropDependencyRegex = Regex(""".*Dependency:.*cinterop-sampleInterop.*""") val nativeMainContainsCInteropDependencyRegex = Regex(""".*Dependency:.*cinterop-sampleInterop.*""")
return output.lineSequence().any { line -> nativeProject("commonize-kt-48856-singleNativeTargetPropagation-testSourceSet", gradleVersion) {
line.matches(nativeMainContainsCInteropDependencyRegex)
}
}
with(Project("commonize-kt-48856-singleNativeTargetPropagation-testSourceSet")) {
build("listNativeTestDependencies") { build("listNativeTestDependencies") {
assertSuccessful() assertOutputContains(
assertTrue(containsCinteropDependency(), "Expected sourceSet 'nativeTest' to list cinterop dependency") nativeMainContainsCInteropDependencyRegex,
"Expected sourceSet 'nativeTest' to list cinterop dependency"
)
} }
build("assemble") { build("assemble")
assertSuccessful()
}
} }
} }
@Test @DisplayName("KT-46856 filename too long - all native targets configured")
fun `test KT-46856 filename too long - all native targets configured`() { @GradleTest
with(Project("commonize-kt-46856-all-targets")) { fun testFilenameTooLongAllNativeTargetsConfigured(gradleVersion: GradleVersion) {
build(":commonize", options = BuildOptions(forceOutputToStdout = true)) { nativeProject("commonize-kt-46856-all-targets", gradleVersion) {
assertSuccessful() build(":commonize")
}
} }
} }
@Test @DisplayName("Multiple cinterops with test source sets and compilations - test source sets depending on main")
fun `test multiple cinterops with test source sets and compilations - test source sets depending on main`() { @GradleTest
`test multiple cinterops with test source sets and compilations`(true) fun testMultipleCinteropsWithTestSourceSetsAndCompilationsTestSourceSetsDependingOnMain(gradleVersion: GradleVersion) {
`test multiple cinterops with test source sets and compilations`(gradleVersion, true)
} }
@Test @DisplayName("Multiple cinterops with test source sets and compilations")
fun `test multiple cinterops with test source sets and compilations`() { @GradleTest
`test multiple cinterops with test source sets and compilations`(false) fun testMultipleCinteropsWithTestSourceSetsAndCompilations(gradleVersion: GradleVersion) {
`test multiple cinterops with test source sets and compilations`(gradleVersion, false)
} }
@Test @DisplayName("KT-49735 two kotlin targets with same konanTarget")
fun `test KT-49735 two kotlin targets with same konanTarget`() { @GradleTest
with(Project("commonize-kt-49735-twoKotlinTargets-oneKonanTarget")) { fun testTwoKotlinTargetsWithSameKonanTarget(gradleVersion: GradleVersion) {
nativeProject("commonize-kt-49735-twoKotlinTargets-oneKonanTarget", gradleVersion) {
build(":assemble") { build(":assemble") {
assertTasksExecuted(":compileCommonMainKotlinMetadata") assertTasksExecuted(":compileCommonMainKotlinMetadata")
assertSuccessful()
} }
} }
} }
private fun `test multiple cinterops with test source sets and compilations`(testSourceSetsDependingOnMain: Boolean) { @DisplayName("KT-48118 c-interops available in commonMain")
with(Project("commonizeMultipleCInteropsWithTests", minLogLevel = INFO)) { @GradleTest
fun testCInteropsAvailableInCommonMain(gradleVersion: GradleVersion) {
nativeProject("commonize-kt-48118-c-interop-in-common-main", gradleVersion) {
reportSourceSetCommonizerDependencies {
val upperMain = getCommonizerDependencies("upperMain")
upperMain.withoutNativeDistributionDependencies().assertDependencyFilesMatches(".*cinterop-dummy")
upperMain.onlyNativeDistributionDependencies().assertNotEmpty()
val commonMain = getCommonizerDependencies("commonMain")
commonMain.withoutNativeDistributionDependencies().assertDependencyFilesMatches(".*cinterop-dummy")
commonMain.onlyNativeDistributionDependencies().assertNotEmpty()
}
build(":compileCommonMainKotlinMetadata")
build(":compileUpperMainKotlinMetadata")
}
}
@DisplayName("KT-47641 commonizing c-interops does not depend on any source compilation")
@GradleTest
fun testCInteropsDoesNotDependOnAnySourceCompilation(gradleVersion: GradleVersion) {
nativeProject(
"commonize-kt-47641-cinterops-compilation-dependency",
gradleVersion,
buildOptions = defaultBuildOptions.copy(logLevel = LogLevel.DEBUG)
) {
build("commonizeCInterop") {
assertTasksExecuted(":p1:commonizeCInterop")
assertTasksExecuted(":p2:commonizeCInterop")
assertAnyTaskHasBeenExecuted(findTasksByPattern(":p1:cinteropTestWithPosix.*".toRegex()))
assertAnyTaskHasBeenExecuted(findTasksByPattern(":p2:cinteropTestWithPosix.*".toRegex()))
assertAnyTaskHasBeenExecuted(findTasksByPattern(":p2:cinteropTestWithPosixP2.*".toRegex()))
/* Make sure that we correctly reference any compile tasks in this test (test is useless otherwise) */
assertOutputContains("Register task :p1.*compile.*".toRegex())
assertOutputContains("Register task :p2.*compile.*".toRegex())
/* CInterops *shall not* require any compilation */
assertOutputDoesNotContain("(Executing actions for task|Executing task) ':p0.*compile.*'".toRegex())
assertOutputDoesNotContain("(Executing actions for task|Executing task) ':p1.*compile.*'".toRegex())
assertOutputDoesNotContain("(Executing actions for task|Executing task) ':p2.*compile.*'".toRegex())
}
}
}
@DisplayName("KT-48138 commonizing c-interops when nativeTest and nativeMain have different targets")
@GradleTest
fun testCommonizingCInteropsWhenNativeTestAndNativeMainHaveDifferentTargets(gradleVersion: GradleVersion) {
nativeProject("commonize-kt-48138-nativeMain-nativeTest-different-targets", gradleVersion) {
reportSourceSetCommonizerDependencies {
val nativeMain = getCommonizerDependencies("nativeMain")
nativeMain.withoutNativeDistributionDependencies().assertDependencyFilesMatches(".*cinterop-dummy")
nativeMain.onlyNativeDistributionDependencies().assertNotEmpty()
nativeMain.assertTargetOnAllDependencies(CommonizerTarget(LINUX_X64, LINUX_ARM64, MINGW_X64))
val nativeTest = getCommonizerDependencies("nativeTest")
nativeTest.onlyNativeDistributionDependencies().assertNotEmpty()
nativeTest.withoutNativeDistributionDependencies().assertDependencyFilesMatches(".*cinterop-dummy")
nativeTest.assertTargetOnAllDependencies(CommonizerTarget(LINUX_X64, LINUX_ARM64))
}
}
}
@DisplayName("KT-50847 missing cinterop in supported target")
@GradleTest
fun testMissingCinteropInSupportedTarget(gradleVersion: GradleVersion) {
nativeProject("commonize-kt-50847-cinterop-missing-in-supported-target", gradleVersion) {
build(":compileCommonMainKotlinMetadata", "-PdisableTargetNumber=1") {
assertTasksSkipped(":cinteropSimpleTarget1")
assertTasksExecuted(":cinteropSimpleTarget2")
assertTasksExecuted(":commonizeCInterop")
}
build(":compileCommonMainKotlinMetadata", "-PdisableTargetNumber=2") {
assertTasksSkipped(":cinteropSimpleTarget2")
assertTasksExecuted(":cinteropSimpleTarget1")
assertTasksExecuted(":commonizeCInterop")
}
}
}
@DisplayName("KT-52243 cinterop caching")
@GradleTest
fun testCInteropCaching(gradleVersion: GradleVersion) {
nativeProject("commonizeCurlInterop", gradleVersion) {
configureCommonizerTargets()
val localBuildCacheDir = projectPath.resolve("local-build-cache-dir").also { assertTrue(it.toFile().mkdirs()) }
enableLocalBuildCache(localBuildCacheDir)
build(":commonize", buildOptions = defaultBuildOptions.copy(buildCacheEnabled = true)) {
assertTasksExecuted(":cinteropCurlTargetA", ":cinteropCurlTargetB")
}
build(":clean") {}
build(":commonize", buildOptions = defaultBuildOptions.copy(buildCacheEnabled = true)) {
assertTasksFromCache(":cinteropCurlTargetA", ":cinteropCurlTargetB")
}
}
}
@DisplayName("KT-51517 commonization with transitive cinterop")
@GradleTest
fun testCommonizationWithTransitiveCinterop(gradleVersion: GradleVersion) {
nativeProject("commonize-kt-51517-transitive-cinterop", gradleVersion) {
build(":app:assemble") {
assertTasksExecuted(":lib:transformCommonMainCInteropDependenciesMetadata")
assertTasksExecuted(":lib:commonizeCInterop")
assertTasksExecuted(":lib:compileCommonMainKotlinMetadata")
assertTasksExecuted(":app:commonizeCInterop")
assertTasksExecuted(":app:compileNativeMainKotlinMetadata")
}
}
}
@DisplayName("KT-57796 commonization with two cinterop commonizer groups`")
@GradleTest
fun testCommonizationWithTwoCInteropCommonizerGroups(gradleVersion: GradleVersion) {
nativeProject("commonize-kt-57796-twoCInteropCommonizerGroups", gradleVersion) {
build(":app:commonizeCIntero") {
assertTasksExecuted(":lib:transformCommonMainCInteropDependenciesMetadata")
assertTasksExecuted(":lib:commonizeCInterop")
assertTasksExecuted(":app:transformCommonMainCInteropDependenciesMetadata")
assertTasksExecuted(":app:commonizeCInterop")
}
}
}
@DisplayName("KT-57796 commonization with two cinterop commonizer groups`")
@GradleTest
fun testCommonizationWithLibraryContainingTwoRoots(gradleVersion: GradleVersion) {
project("commonize-kt-56729-consume-library-with-two-roots", gradleVersion) {
build("publish")
build(":consumer:assemble") {
assertTasksExecuted(":consumer:compileCommonMainKotlinMetadata")
assertOutputDoesNotContain("Duplicated libraries:")
assertOutputDoesNotContain("w: duplicate library name")
}
}
}
private fun `test multiple cinterops with test source sets and compilations`(
gradleVersion: GradleVersion,
testSourceSetsDependingOnMain: Boolean,
) {
nativeProject("commonizeMultipleCInteropsWithTests", gradleVersion) {
val isMac = HostManager.hostIsMac val isMac = HostManager.hostIsMac
fun CompiledProject.assertTestSourceSetsDependingOnMainParameter() { fun BuildResult.assertTestSourceSetsDependingOnMainParameter() {
val message = "testSourceSetsDependingOnMain is set" val message = "testSourceSetsDependingOnMain is set"
if (testSourceSetsDependingOnMain) assertContains(message) else assertNotContains(message) if (testSourceSetsDependingOnMain) assertOutputContains(message) else assertOutputDoesNotContain(message)
} }
val testSourceSetsDependingOnMainParameterOption = defaultBuildOptions() val testSourceSetsDependingOnMainParameterOption = defaultBuildOptions.copy(
.withFreeCommandLineArgument("-PtestSourceSetsDependingOnMain=$testSourceSetsDependingOnMain") freeArgs = listOf("-PtestSourceSetsDependingOnMain=$testSourceSetsDependingOnMain")
)
reportSourceSetCommonizerDependencies(this, options = testSourceSetsDependingOnMainParameterOption) { reportSourceSetCommonizerDependencies(options = testSourceSetsDependingOnMainParameterOption) {
it.assertTestSourceSetsDependingOnMainParameter() it.assertTestSourceSetsDependingOnMainParameter()
/* this source sets are also shared with a jvm target */ /* this source sets are also shared with a jvm target */
@@ -495,208 +597,62 @@ open class CommonizerIT : BaseGradleIT() {
getCommonizerDependencies("windowsX64Test").assertEmpty() getCommonizerDependencies("windowsX64Test").assertEmpty()
} }
build(":assemble", options = testSourceSetsDependingOnMainParameterOption) { build(":assemble", buildOptions = testSourceSetsDependingOnMainParameterOption) {
assertTestSourceSetsDependingOnMainParameter() assertTestSourceSetsDependingOnMainParameter()
assertSuccessful()
assertTasksUpToDate(":commonizeNativeDistribution") assertTasksUpToDate(":commonizeNativeDistribution")
assertContains("Native Distribution Commonization: Cache hit") assertOutputContains("Native Distribution Commonization: Cache hit")
assertTasksUpToDate(":commonizeCInterop") assertTasksUpToDate(":commonizeCInterop")
} }
build(":compileNativeMainKotlinMetadata", options = testSourceSetsDependingOnMainParameterOption) { build(":compileNativeMainKotlinMetadata", buildOptions = testSourceSetsDependingOnMainParameterOption) {
assertTestSourceSetsDependingOnMainParameter() assertTestSourceSetsDependingOnMainParameter()
assertSuccessful()
} }
build(":compileUnixMainKotlinMetadata", options = testSourceSetsDependingOnMainParameterOption) { build(":compileUnixMainKotlinMetadata", buildOptions = testSourceSetsDependingOnMainParameterOption) {
assertTestSourceSetsDependingOnMainParameter() assertTestSourceSetsDependingOnMainParameter()
assertSuccessful()
} }
build(":compileLinuxMainKotlinMetadata", options = testSourceSetsDependingOnMainParameterOption) { build(":compileLinuxMainKotlinMetadata", buildOptions = testSourceSetsDependingOnMainParameterOption) {
assertTestSourceSetsDependingOnMainParameter() assertTestSourceSetsDependingOnMainParameter()
assertSuccessful()
} }
if (isMac) { if (isMac) {
build(":compileAppleMainKotlinMetadata", options = testSourceSetsDependingOnMainParameterOption) { build(":compileAppleMainKotlinMetadata", buildOptions = testSourceSetsDependingOnMainParameterOption) {
assertTestSourceSetsDependingOnMainParameter() assertTestSourceSetsDependingOnMainParameter()
assertSuccessful()
} }
build(":compileIosMainKotlinMetadata", options = testSourceSetsDependingOnMainParameterOption) { build(":compileIosMainKotlinMetadata", buildOptions = testSourceSetsDependingOnMainParameterOption) {
assertTestSourceSetsDependingOnMainParameter() assertTestSourceSetsDependingOnMainParameter()
assertSuccessful()
} }
} }
} }
} }
@Test private fun testSingleNativePlatform(projectName: String, gradleVersion: GradleVersion) {
fun `test KT-48118 c-interops available in commonMain`() {
with(Project("commonize-kt-48118-c-interop-in-common-main")) {
reportSourceSetCommonizerDependencies(this) {
val upperMain = getCommonizerDependencies("upperMain")
upperMain.withoutNativeDistributionDependencies().assertDependencyFilesMatches(".*cinterop-dummy")
upperMain.onlyNativeDistributionDependencies().assertNotEmpty()
val commonMain = getCommonizerDependencies("commonMain") val posixInIntransitiveMetadataConfigurationRegex = Regex(""".*intransitiveMetadataConfiguration:.*([pP])osix""")
commonMain.withoutNativeDistributionDependencies().assertDependencyFilesMatches(".*cinterop-dummy")
commonMain.onlyNativeDistributionDependencies().assertNotEmpty() nativeProject(projectName, gradleVersion) {
build(":p1:listNativePlatformMainDependencies", "-Pkotlin.mpp.enableIntransitiveMetadataConfiguration=false") {
assertOutputDoesNotContain(posixInIntransitiveMetadataConfigurationRegex)
} }
build(":compileCommonMainKotlinMetadata") { build(":p1:listNativePlatformMainDependencies", "-Pkotlin.mpp.enableIntransitiveMetadataConfiguration=true") {
assertSuccessful() assertOutputContains(posixInIntransitiveMetadataConfigurationRegex)
} }
build(":compileUpperMainKotlinMetadata") { build("assemble")
assertSuccessful()
}
} }
} }
@Test private fun TestProject.configureCommonizerTargets() {
fun `test KT-47641 commonizing c-interops does not depend on any source compilation`() { buildGradleKts.replaceText("<targetA>", CommonizableTargets.targetA.value)
with(Project("commonize-kt-47641-cinterops-compilation-dependency")) { buildGradleKts.replaceText("<targetB>", CommonizableTargets.targetB.value)
build("commonizeCInterop", options = BuildOptions(forceOutputToStdout = true)) {
assertTasksExecuted(":p1:commonizeCInterop")
assertTasksExecuted(":p2:commonizeCInterop")
assertTasksExecuted(":p1:cinteropTestWithPosix.*")
assertTasksExecuted(":p2:cinteropTestWithPosix.*")
assertTasksExecuted(":p2:cinteropTestWithPosixP2.*")
/* Make sure that we correctly reference any compile tasks in this test (test is useless otherwise) */
assertTasksRegisteredRegex(":p1.*compile.*")
assertTasksRegisteredRegex(":p2.*compile.*")
/* CInterops *shall not* require any compilation */
assertTasksNotExecuted(":p0.*compile.*")
assertTasksNotExecuted(":p1.*compile.*")
assertTasksNotExecuted(":p2.*compile.*")
}
}
} }
@Test private fun BuildResult.assertNativeDistributionCommonizationCacheHit() {
fun `test KT-48138 commonizing c-interops when nativeTest and nativeMain have different targets`() { assertOutputContains("Native Distribution Commonization: Cache hit")
with(Project("commonize-kt-48138-nativeMain-nativeTest-different-targets")) {
reportSourceSetCommonizerDependencies(this) {
val nativeMain = getCommonizerDependencies("nativeMain")
nativeMain.withoutNativeDistributionDependencies().assertDependencyFilesMatches(".*cinterop-dummy")
nativeMain.onlyNativeDistributionDependencies().assertNotEmpty()
nativeMain.assertTargetOnAllDependencies(CommonizerTarget(LINUX_X64, LINUX_ARM64, MINGW_X64))
val nativeTest = getCommonizerDependencies("nativeTest")
nativeTest.onlyNativeDistributionDependencies().assertNotEmpty()
nativeTest.withoutNativeDistributionDependencies().assertDependencyFilesMatches(".*cinterop-dummy")
nativeTest.assertTargetOnAllDependencies(CommonizerTarget(LINUX_X64, LINUX_ARM64))
}
}
}
@Test
fun `test KT-50847 missing cinterop in supported target`() {
with(Project("commonize-kt-50847-cinterop-missing-in-supported-target")) {
build(":compileCommonMainKotlinMetadata", "-PdisableTargetNumber=1") {
assertSuccessful()
assertTasksSkipped(":cinteropSimpleTarget1")
assertTasksExecuted(":cinteropSimpleTarget2")
assertTasksExecuted(":commonizeCInterop")
}
build(":compileCommonMainKotlinMetadata", "-PdisableTargetNumber=2") {
assertSuccessful()
assertTasksSkipped(":cinteropSimpleTarget2")
assertTasksExecuted(":cinteropSimpleTarget1")
assertTasksExecuted(":commonizeCInterop")
}
}
}
@Test
fun `test KT-52243 cinterop caching`() {
with(preparedProject("commonizeCurlInterop")) {
val localBuildCacheDir = projectDir.resolve("local-build-cache-dir").also { assertTrue(it.mkdirs()) }
gradleSettingsScript().appendText(
"""
buildCache {
local {
directory = "${StringEscapeUtils.escapeJava(localBuildCacheDir.absolutePath)}"
}
}
""".trimIndent()
)
build(":commonize", options = defaultBuildOptions().copy(withBuildCache = true)) {
assertTasksExecuted(":cinteropCurlTargetA", ":cinteropCurlTargetB")
}
build(":clean") {}
build(":commonize", options = defaultBuildOptions().copy(withBuildCache = true)) {
assertTasksRetrievedFromCache(":cinteropCurlTargetA", ":cinteropCurlTargetB")
}
}
}
@Test
fun `test KT-51517 commonization with transitive cinterop`() {
with(Project("commonize-kt-51517-transitive-cinterop")) {
build(":app:assemble") {
assertSuccessful()
assertTasksExecuted(":lib:transformCommonMainCInteropDependenciesMetadata")
assertTasksExecuted(":lib:commonizeCInterop")
assertTasksExecuted(":lib:compileCommonMainKotlinMetadata")
assertTasksExecuted(":app:commonizeCInterop")
assertTasksExecuted(":app:compileNativeMainKotlinMetadata")
}
}
}
@Test
fun `test KT-57796 commonization with two cinterop commonizer groups`() {
with(Project("commonize-kt-57796-twoCInteropCommonizerGroups")) {
build(":app:commonizeCIntero") {
assertSuccessful()
assertTasksExecuted(":lib:transformCommonMainCInteropDependenciesMetadata")
assertTasksExecuted(":lib:commonizeCInterop")
assertTasksExecuted(":app:transformCommonMainCInteropDependenciesMetadata")
assertTasksExecuted(":app:commonizeCInterop")
}
}
}
@Test
fun `test KT-56729 commonization with library containing two roots`() {
with(Project("commonize-kt-56729-consume-library-with-two-roots")) {
build("publish") {
assertSuccessful()
}
build(":consumer:assemble") {
assertSuccessful()
assertTasksExecuted(":consumer:compileCommonMainKotlinMetadata")
assertNotContains("Duplicated libraries:")
assertNotContains("w: duplicate library name")
}
}
}
private fun preparedProject(name: String): Project {
return Project(name).apply {
setupWorkingDir()
projectDir.walkTopDown().filter { it.name.startsWith("build.gradle") }.forEach { buildFile ->
val originalText = buildFile.readText()
val preparedText = originalText
.replace("<targetA>", CommonizableTargets.targetA.value)
.replace("<targetB>", CommonizableTargets.targetB.value)
buildFile.writeText(preparedText)
}
}
}
private fun CompiledProject.assertNativeDistributionCommonizationCacheHit() {
assertContains("Native Distribution Commonization: Cache hit")
} }
} }
@@ -28,9 +28,9 @@ class K2NewMultiplatformIT : NewMultiplatformIT() {
override fun defaultBuildOptions(): BuildOptions = super.defaultBuildOptions().copy(languageVersion = "2.0") override fun defaultBuildOptions(): BuildOptions = super.defaultBuildOptions().copy(languageVersion = "2.0")
} }
@Ignore @Disabled("Used for local testing only")
class K2CommonizerIT : CommonizerIT() { class K2CommonizerIT : CommonizerIT() {
override fun defaultBuildOptions(): BuildOptions = super.defaultBuildOptions().copy(languageVersion = "2.0") override val defaultBuildOptions: BuildOptions get() = super.defaultBuildOptions.copy(languageVersion = "2.0")
} }
@Ignore @Ignore
@@ -89,7 +89,13 @@ class CustomK2Tests : KGPBaseTest() {
@GradleTest @GradleTest
@DisplayName("Compiling shared native source with FirFakeOverrideGenerator referencing a common entity. KT-58145") @DisplayName("Compiling shared native source with FirFakeOverrideGenerator referencing a common entity. KT-58145")
fun kt581450MppNativeSharedCrash(gradleVersion: GradleVersion) { fun kt581450MppNativeSharedCrash(gradleVersion: GradleVersion) {
with(project("kt-581450-mpp-native-shared-crash", gradleVersion, buildOptions = defaultBuildOptions.copy(languageVersion = "2.0"))) { with(
project(
"kt-581450-mpp-native-shared-crash",
gradleVersion,
buildOptions = defaultBuildOptions.copy(languageVersion = "2.0")
)
) {
val taskToExecute = ":compileNativeMainKotlinMetadata" val taskToExecute = ":compileNativeMainKotlinMetadata"
build(taskToExecute) { build(taskToExecute) {
assertTasksExecuted(taskToExecute) assertTasksExecuted(taskToExecute)
@@ -21,7 +21,7 @@ import kotlin.test.asserter
* Asserts file under [file] path exists and is a regular file. * Asserts file under [file] path exists and is a regular file.
*/ */
fun assertFileExists( fun assertFileExists(
file: Path file: Path,
) { ) {
assert(Files.exists(file)) { assert(Files.exists(file)) {
"File '${file}' does not exist!" "File '${file}' does not exist!"
@@ -36,14 +36,14 @@ fun assertFileExists(
* Asserts file under [pathToFile] relative to the test project exists and is a regular file. * Asserts file under [pathToFile] relative to the test project exists and is a regular file.
*/ */
fun GradleProject.assertFileInProjectExists( fun GradleProject.assertFileInProjectExists(
pathToFile: String pathToFile: String,
) { ) {
assertFileExists(projectPath.resolve(pathToFile)) assertFileExists(projectPath.resolve(pathToFile))
} }
fun assertFileExistsInTree( fun assertFileExistsInTree(
pathToTreeRoot: Path, pathToTreeRoot: Path,
fileName: String fileName: String,
) { ) {
val foundFile = pathToTreeRoot val foundFile = pathToTreeRoot
.toFile() .toFile()
@@ -61,22 +61,23 @@ fun assertFileExistsInTree(
* Asserts file under [pathToFile] relative to the test project does not exist. * Asserts file under [pathToFile] relative to the test project does not exist.
*/ */
fun GradleProject.assertFileInProjectNotExists( fun GradleProject.assertFileInProjectNotExists(
pathToFile: String pathToFile: String,
) { ) {
assertFileNotExists(projectPath.resolve(pathToFile)) assertFileNotExists(projectPath.resolve(pathToFile))
} }
fun assertFileNotExists( fun assertFileNotExists(
pathToFile: Path pathToFile: Path,
message: String = "File '${pathToFile}' exists!"
) { ) {
assert(!Files.exists(pathToFile)) { assert(!Files.exists(pathToFile)) {
"File '${pathToFile}' exists!" message
} }
} }
fun assertFileNotExistsInTree( fun assertFileNotExistsInTree(
pathToTreeRoot: Path, pathToTreeRoot: Path,
fileName: String fileName: String,
) { ) {
val foundFile = pathToTreeRoot val foundFile = pathToTreeRoot
.toFile() .toFile()
@@ -92,7 +93,7 @@ fun assertFileNotExistsInTree(
fun GradleProject.assertFileNotExistsInTree( fun GradleProject.assertFileNotExistsInTree(
pathToTreeRoot: String, pathToTreeRoot: String,
fileName: String fileName: String,
) { ) {
assertFileNotExistsInTree(projectPath.resolve(pathToTreeRoot), fileName) assertFileNotExistsInTree(projectPath.resolve(pathToTreeRoot), fileName)
} }
@@ -101,7 +102,7 @@ fun GradleProject.assertFileNotExistsInTree(
* Asserts symlink under [path] exists and is a symlink * Asserts symlink under [path] exists and is a symlink
*/ */
fun assertSymlinkExists( fun assertSymlinkExists(
path: Path path: Path,
) { ) {
assert(Files.exists(path)) { assert(Files.exists(path)) {
"Symlink '${path}' does not exist!" "Symlink '${path}' does not exist!"
@@ -116,7 +117,7 @@ fun assertSymlinkExists(
* Asserts symlink under [pathToFile] relative to the test project exists and is a symlink. * Asserts symlink under [pathToFile] relative to the test project exists and is a symlink.
*/ */
fun TestProject.assertSymlinkInProjectExists( fun TestProject.assertSymlinkInProjectExists(
pathToFile: String pathToFile: String,
) { ) {
assertSymlinkExists(projectPath.resolve(pathToFile)) assertSymlinkExists(projectPath.resolve(pathToFile))
} }
@@ -125,24 +126,26 @@ fun TestProject.assertSymlinkInProjectExists(
* Asserts directory under [pathToDir] relative to the test project exists and is a directory. * Asserts directory under [pathToDir] relative to the test project exists and is a directory.
*/ */
fun GradleProject.assertDirectoryInProjectExists( fun GradleProject.assertDirectoryInProjectExists(
pathToDir: String pathToDir: String,
) = assertDirectoryExists(projectPath.resolve(pathToDir)) ) = assertDirectoryExists(projectPath.resolve(pathToDir))
/** /**
* Asserts directory under [dirPath] exists and is a directory. * Asserts directory under [dirPath] exists and is a directory.
*/ */
fun assertDirectoryExists( fun assertDirectoryExists(
dirPath: Path dirPath: Path,
) = assertDirectoriesExist(dirPath) message: String? = null,
) = assertDirectoriesExist(dirPath, message = message)
fun assertDirectoriesExist( fun assertDirectoriesExist(
vararg dirPaths: Path vararg dirPaths: Path,
message: String? = null,
) { ) {
val (exist, notExist) = dirPaths.partition { it.exists() } val (exist, notExist) = dirPaths.partition { it.exists() }
val notDirectories = exist.filterNot { it.isDirectory() } val notDirectories = exist.filterNot { it.isDirectory() }
assert(notExist.isEmpty() && notDirectories.isEmpty()) { assert(notExist.isEmpty() && notDirectories.isEmpty()) {
buildString { message ?: buildString {
if (notExist.isNotEmpty()) { if (notExist.isNotEmpty()) {
appendLine("Following directories does not exist:") appendLine("Following directories does not exist:")
appendLine(notExist.joinToString(separator = "\n")) appendLine(notExist.joinToString(separator = "\n"))
@@ -160,7 +163,7 @@ fun assertDirectoriesExist(
*/ */
fun GradleProject.assertFileInProjectContains( fun GradleProject.assertFileInProjectContains(
pathToFile: String, pathToFile: String,
vararg expectedText: String vararg expectedText: String,
) { ) {
assertFileContains(projectPath.resolve(pathToFile), *expectedText) assertFileContains(projectPath.resolve(pathToFile), *expectedText)
} }
@@ -170,7 +173,7 @@ fun GradleProject.assertFileInProjectContains(
*/ */
fun GradleProject.assertFileInProjectDoesNotContain( fun GradleProject.assertFileInProjectDoesNotContain(
pathToFile: String, pathToFile: String,
vararg unexpectedText: String vararg unexpectedText: String,
) { ) {
assertFileDoesNotContain(projectPath.resolve(pathToFile), *unexpectedText) assertFileDoesNotContain(projectPath.resolve(pathToFile), *unexpectedText)
} }
@@ -180,7 +183,7 @@ fun GradleProject.assertFileInProjectDoesNotContain(
*/ */
fun assertFileContains( fun assertFileContains(
file: Path, file: Path,
vararg expectedText: String vararg expectedText: String,
) { ) {
assertFileExists(file) assertFileExists(file)
val text = file.readText() val text = file.readText()
@@ -202,7 +205,7 @@ fun assertFileContains(
*/ */
fun assertFileDoesNotContain( fun assertFileDoesNotContain(
file: Path, file: Path,
vararg unexpectedText: String vararg unexpectedText: String,
) { ) {
assertFileExists(file) assertFileExists(file)
val text = file.readText() val text = file.readText()
@@ -258,7 +261,7 @@ fun assertFilesContentEquals(expected: Path, actual: Path) {
} }
class GradleVariantAssertions( class GradleVariantAssertions(
val variantJson: JsonObject val variantJson: JsonObject,
) { ) {
fun assertAttributesEquals(expected: Map<String, String>) { fun assertAttributesEquals(expected: Map<String, String>) {
val attributesJson = variantJson.getAsJsonObject("attributes") val attributesJson = variantJson.getAsJsonObject("attributes")
@@ -22,7 +22,7 @@ fun BuildResult.assertOutputContains(
} }
/** /**
* Asserts Gradle output contains any of [expectedSubString] strings. * Asserts Gradle output contains any of [expectedSubStrings] strings.
*/ */
fun BuildResult.assertOutputContainsAny( fun BuildResult.assertOutputContainsAny(
vararg expectedSubStrings: String, vararg expectedSubStrings: String,
@@ -103,11 +103,12 @@ fun BuildResult.assertOutputDoesNotContain(
*/ */
fun BuildResult.assertOutputContains( fun BuildResult.assertOutputContains(
expected: Regex, expected: Regex,
message: String = "Build output does not contain any line matching '$expected' regex.",
) { ) {
assert(output.contains(expected)) { assert(output.contains(expected)) {
printBuildOutput() printBuildOutput()
"Build output does not contain any line matching '$expected' regex." message
} }
} }
@@ -41,6 +41,20 @@ fun BuildResult.assertTasksExecuted(vararg tasks: String) {
} }
} }
/**
* Asserts any of [tasks] has 'SUCCESS' execution state.
*/
fun BuildResult.assertAnyTaskHasBeenExecuted(tasks: Set<String>) {
assert(
tasks.any { task ->
task(task)?.outcome == TaskOutcome.SUCCESS
}
) {
printBuildOutput()
"There are no 'SUCCESS' tasks in the $tasks"
}
}
/** /**
* Asserts given [tasks] have not been executed. * Asserts given [tasks] have not been executed.
*/ */
@@ -148,7 +162,7 @@ fun BuildResult.assertTasksPackedToCache(vararg tasks: String) {
fun BuildResult.assertNativeTasksClasspath( fun BuildResult.assertNativeTasksClasspath(
vararg tasksPaths: String, vararg tasksPaths: String,
toolName: NativeToolKind = NativeToolKind.KONANC, toolName: NativeToolKind = NativeToolKind.KONANC,
assertions: (List<String>) -> Unit assertions: (List<String>) -> Unit,
) = tasksPaths.forEach { taskPath -> assertions(extractNativeCompilerClasspath(getOutputForTask(taskPath), toolName)) } ) = tasksPaths.forEach { taskPath -> assertions(extractNativeCompilerClasspath(getOutputForTask(taskPath), toolName)) }
/** /**
@@ -189,7 +203,7 @@ fun TestProject.buildAndAssertAllTasks(
*/ */
fun BuildResult.assertTasksInBuildOutput( fun BuildResult.assertTasksInBuildOutput(
expectedPresentTasks: List<String> = emptyList(), expectedPresentTasks: List<String> = emptyList(),
expectedAbsentTasks: List<String> = emptyList() expectedAbsentTasks: List<String> = emptyList(),
) { ) {
val registeredTasks = getAllTasksFromTheOutput() val registeredTasks = getAllTasksFromTheOutput()
expectedPresentTasks.forEach { expectedPresentTasks.forEach {
@@ -240,12 +240,14 @@ internal inline fun <reified T> TestProject.getModels(
fun TestProject.enableLocalBuildCache( fun TestProject.enableLocalBuildCache(
buildCacheLocation: Path, buildCacheLocation: Path,
) { ) {
// language=Groovy
settingsGradle.append( val settingsFile = if (Files.exists(settingsGradle)) settingsGradle else settingsGradleKts
settingsFile.append(
""" """
buildCache { buildCache {
local { local {
directory = '${buildCacheLocation.toUri()}' directory = "${buildCacheLocation.toUri()}"
} }
} }
""".trimIndent() """.trimIndent()
@@ -5,30 +5,34 @@
package org.jetbrains.kotlin.gradle.util package org.jetbrains.kotlin.gradle.util
import org.gradle.testkit.runner.BuildResult
import org.intellij.lang.annotations.Language import org.intellij.lang.annotations.Language
import org.intellij.lang.annotations.RegExp import org.intellij.lang.annotations.RegExp
import org.jetbrains.kotlin.commonizer.CommonizerTarget import org.jetbrains.kotlin.commonizer.CommonizerTarget
import org.jetbrains.kotlin.commonizer.SharedCommonizerTarget import org.jetbrains.kotlin.commonizer.SharedCommonizerTarget
import org.jetbrains.kotlin.commonizer.parseCommonizerTarget import org.jetbrains.kotlin.commonizer.parseCommonizerTarget
import org.jetbrains.kotlin.gradle.BaseGradleIT import org.jetbrains.kotlin.gradle.BaseGradleIT
import org.jetbrains.kotlin.gradle.BaseGradleIT.CompiledProject import org.jetbrains.kotlin.gradle.testbase.BuildOptions
import org.jetbrains.kotlin.gradle.testbase.TestProject
import org.jetbrains.kotlin.gradle.testbase.build
import org.jetbrains.kotlin.library.ToolingSingleFileKlibResolveStrategy import org.jetbrains.kotlin.library.ToolingSingleFileKlibResolveStrategy
import org.jetbrains.kotlin.library.commonizerTarget import org.jetbrains.kotlin.library.commonizerTarget
import org.jetbrains.kotlin.library.resolveSingleFileKlib import org.jetbrains.kotlin.library.resolveSingleFileKlib
import org.jetbrains.kotlin.tooling.core.linearClosure import org.jetbrains.kotlin.tooling.core.linearClosure
import java.io.File import java.io.File
import javax.annotation.RegEx import javax.annotation.RegEx
import kotlin.io.path.appendText
import kotlin.test.fail import kotlin.test.fail
data class SourceSetCommonizerDependency( data class SourceSetCommonizerDependency(
val sourceSetName: String, val sourceSetName: String,
val target: CommonizerTarget, val target: CommonizerTarget,
val file: File val file: File,
) )
data class SourceSetCommonizerDependencies( data class SourceSetCommonizerDependencies(
val sourceSetName: String, val sourceSetName: String,
val dependencies: Set<SourceSetCommonizerDependency> val dependencies: Set<SourceSetCommonizerDependency>,
) { ) {
fun withoutNativeDistributionDependencies(): SourceSetCommonizerDependencies { fun withoutNativeDistributionDependencies(): SourceSetCommonizerDependencies {
@@ -77,10 +81,6 @@ data class SourceSetCommonizerDependencies(
assertDependencyFilesMatches(fileMatchers.filterNotNull().map(::Regex).toSet()) assertDependencyFilesMatches(fileMatchers.filterNotNull().map(::Regex).toSet())
} }
fun assertDependencyFilesMatches(vararg fileMatchers: Regex?) = apply {
assertDependencyFilesMatches(fileMatchers.filterNotNull().toSet())
}
fun assertDependencyFilesMatches(fileMatchers: Set<Regex>) = apply { fun assertDependencyFilesMatches(fileMatchers: Set<Regex>) = apply {
val unmatchedDependencies = dependencies.filter { dependency -> val unmatchedDependencies = dependencies.filter { dependency ->
fileMatchers.none { matcher -> dependency.file.absolutePath.matches(matcher) } fileMatchers.none { matcher -> dependency.file.absolutePath.matches(matcher) }
@@ -120,7 +120,7 @@ fun BaseGradleIT.reportSourceSetCommonizerDependencies(
project: BaseGradleIT.Project, project: BaseGradleIT.Project,
subproject: String? = null, subproject: String? = null,
options: BaseGradleIT.BuildOptions = defaultBuildOptions(), options: BaseGradleIT.BuildOptions = defaultBuildOptions(),
test: WithSourceSetCommonizerDependencies.(compiledProject: CompiledProject) -> Unit test: WithSourceSetCommonizerDependencies.(compiledProject: BaseGradleIT.CompiledProject) -> Unit
) = with(project) { ) = with(project) {
if (!projectDir.exists()) { if (!projectDir.exists()) {
@@ -160,6 +160,40 @@ fun BaseGradleIT.reportSourceSetCommonizerDependencies(
} }
} }
fun TestProject.reportSourceSetCommonizerDependencies(
subproject: String? = null,
options: BuildOptions = this.buildOptions,
test: WithSourceSetCommonizerDependencies.(compiledProject: BuildResult) -> Unit,
) {
buildGradleKts.appendText("\n\n$taskSourceCode\n\n")
val taskName = buildString {
if (subproject != null) append(":$subproject")
append(":reportCommonizerSourceSetDependencies")
}
build(taskName, buildOptions = options) {
val dependencyReports = output.lineSequence().filter { line -> line.contains("SourceSetCommonizerDependencyReport") }.toList()
val withSourceSetCommonizerDependencies = WithSourceSetCommonizerDependencies { sourceSetName ->
val reportMarker = "Report[$sourceSetName]"
val reportForSourceSet = dependencyReports.firstOrNull { line -> line.contains(reportMarker) }
?: fail("Missing dependency report for $sourceSetName")
val files = reportForSourceSet.split(reportMarker, limit = 2).last().split("|#+#|")
.map(String::trim).filter(String::isNotEmpty).map(::File)
val dependencies = files.mapNotNull { file -> createSourceSetCommonizerDependencyOrNull(sourceSetName, file) }.toSet()
SourceSetCommonizerDependencies(sourceSetName, dependencies)
}
withSourceSetCommonizerDependencies.test(this)
}
}
private fun createSourceSetCommonizerDependencyOrNull(sourceSetName: String, libraryFile: File): SourceSetCommonizerDependency? { private fun createSourceSetCommonizerDependencyOrNull(sourceSetName: String, libraryFile: File): SourceSetCommonizerDependency? {
return SourceSetCommonizerDependency( return SourceSetCommonizerDependency(
sourceSetName, sourceSetName,