Migrate KotlinSpecificDependenciesIT to new test dsl

^KT-45745 In Progress
This commit is contained in:
Yahor Berdnikau
2022-03-15 11:17:23 +01:00
committed by teamcity
parent 5810ac01d2
commit c4149c2404
3 changed files with 586 additions and 256 deletions
@@ -312,4 +312,46 @@ class KlibBasedMppIT : BaseGradleIT() {
} }
} }
} }
private var testBuildRunId = 0
private fun BaseGradleIT.Project.checkTaskCompileClasspath(
taskPath: String,
checkModulesInClasspath: List<String> = emptyList(),
checkModulesNotInClasspath: List<String> = emptyList(),
isNative: Boolean = false
) {
val subproject = taskPath.substringBeforeLast(":").takeIf { it.isNotEmpty() && it != taskPath }
val taskName = taskPath.removePrefix(subproject.orEmpty())
val taskClass = if (isNative) "org.jetbrains.kotlin.gradle.tasks.AbstractKotlinNativeCompile<*, *>" else "AbstractCompile"
val expression = """(tasks.getByName("$taskName") as $taskClass).${if (isNative) "libraries" else "classpath"}.toList()"""
checkPrintedItems(subproject, expression, checkModulesInClasspath, checkModulesNotInClasspath)
}
private fun BaseGradleIT.Project.checkPrintedItems(
subproject: String?,
itemsExpression: String,
checkAnyItemsContains: List<String>,
checkNoItemContains: List<String>
) = with(testCase) {
setupWorkingDir()
val printingTaskName = "printItems${testBuildRunId++}"
gradleBuildScript(subproject).appendText(
"""
${'\n'}
tasks.create("$printingTaskName") {
doLast {
println("###$printingTaskName" + $itemsExpression)
}
}
""".trimIndent()
)
build("${subproject?.prependIndent(":").orEmpty()}:$printingTaskName") {
assertSuccessful()
val itemsLine = output.lines().single { "###$printingTaskName" in it }.substringAfter(printingTaskName)
val items = itemsLine.removeSurrounding("[", "]").split(", ").toSet()
checkAnyItemsContains.forEach { pattern -> assertTrue { items.any { pattern in it } } }
checkNoItemContains.forEach { pattern -> assertFalse { items.any { pattern in it } } }
}
}
} }
@@ -5,141 +5,292 @@
package org.jetbrains.kotlin.gradle package org.jetbrains.kotlin.gradle
import org.jetbrains.kotlin.gradle.util.AGPVersion import org.gradle.api.logging.configuration.WarningMode
import org.jetbrains.kotlin.gradle.util.modify import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.test.util.KtTestUtil import org.jetbrains.kotlin.gradle.testbase.*
import kotlin.test.Test import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.extension.ExtensionContext
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.*
import java.nio.file.Path
import java.util.UUID
import java.util.stream.Stream
import kotlin.io.path.appendText
import kotlin.streams.asStream
import kotlin.streams.toList
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertTrue import kotlin.test.assertTrue
class KotlinSpecificDependenciesIT : BaseGradleIT() { @DisplayName("Kotlin default dependencies")
class KotlinSpecificDependenciesIT : KGPBaseTest() {
override val defaultGradleVersion: GradleVersionRequired @JvmGradlePluginTests
get() = GradleVersionRequired.FOR_MPP_SUPPORT @DisplayName("JVM: kotlin-stdlib dependency is added by default")
@GradleTest
override fun defaultBuildOptions(): BuildOptions = fun testStdlibByDefaultJvm(gradleVersion: GradleVersion) {
super.defaultBuildOptions().copy(androidGradlePluginVersion = AGPVersion.v3_6_0, androidHome = KtTestUtil.findAndroidSdk()) project("simpleProject", gradleVersion) {
removeDependencies(buildGradle)
private fun Project.prepare() { // call this when reusing a project after a test, too, in order to remove any added dependencies checkTaskCompileClasspath("compileKotlin", listOf("kotlin-stdlib" /*any of them*/))
setupWorkingDir()
gradleSettingsScript().takeIf { it.exists() }?.modify(::transformBuildScriptWithPluginsDsl)
gradleBuildScript().modify {
transformBuildScriptWithPluginsDsl(it).lines().filter { line ->
"stdlib" !in line && "kotlin(\"test" !in line && "kotlin-test" !in line
}.joinToString("\n")
} }
} }
private fun jsProject() = Project("kotlin-js-plugin-project").apply { @JvmGradlePluginTests
// TODO: remove settings.gradle from test project after migrating to the new test DSL @DisplayName("JVM: kotlin-stdlib dependency is not added when disabled via properties")
setupWorkingDir() @GradleTest
gradleBuildScript().modify { it.lines().filter { "html" !in it }.joinToString("\n") } fun testStdlibNotAddedJvm(gradleVersion: GradleVersion) {
projectFile("Main.kt").modify { "fun f() = listOf(1, 2, 3).joinToString()" } project("simpleProject", gradleVersion) {
prepare() removeDependencies(buildGradle)
gradleProperties.appendText(
"\nkotlin.stdlib.default.dependency=false"
)
checkTaskCompileClasspath(
"compileKotlin",
listOf(),
checkModulesNotInClasspath = listOf("kotlin-stdlib" /*any of them*/)
)
}
} }
private fun androidProject() = Project("AndroidLibraryKotlinProject").apply { prepare() } @JsGradlePluginTests
@DisplayName("JS: kotlin-stdlib dependency is added by default")
@GradleTest
fun testStdlibByDefaultJs(gradleVersion: GradleVersion) {
project(
"kotlin-js-plugin-project",
gradleVersion,
buildOptions = defaultBuildOptions.copy(warningMode = WarningMode.Summary)
) {
buildGradleKts.modify { it.lines().filter { "html" !in it }.joinToString("\n") }
kotlinSourcesDir().resolve("Main.kt").modify { "fun f() = listOf(1, 2, 3).joinToString()" }
removeDependencies(buildGradleKts)
private fun mppProject() = Project("jvm-and-js-hmpp").apply { checkTaskCompileClasspath(
setupWorkingDir() "compileKotlinJs",
prepare() listOf("kotlin-stdlib"),
isBuildGradleKts = true
)
}
} }
private fun jvmProject() = Project("simpleProject").apply { @JsGradlePluginTests
prepare() @DisplayName("JS: kotlin-stdlib dependency is not added when disabled via properties")
gradleBuildScript().modify { it.lines().filter { "testng" !in it }.joinToString("\n") } @GradleTest
fun testStdlibDisabledJs(gradleVersion: GradleVersion) {
project(
"kotlin-js-plugin-project",
gradleVersion,
buildOptions = defaultBuildOptions.copy(warningMode = WarningMode.Summary)
) {
buildGradleKts.modify { it.lines().filter { "html" !in it }.joinToString("\n") }
kotlinSourcesDir().resolve("Main.kt").modify { "fun f() = listOf(1, 2, 3).joinToString()" }
removeDependencies(buildGradleKts)
gradleProperties.appendText(
"\nkotlin.stdlib.default.dependency=false"
)
checkTaskCompileClasspath(
"compileKotlinJs",
listOf(),
checkModulesNotInClasspath = listOf("kotlin-stdlib" /*any of them*/),
isBuildGradleKts = true
)
}
} }
@Test @JvmGradlePluginTests
fun testStdlibByDefault() { @DisplayName("Android: kotlin-stdlib dependency is added by default")
listOf( // projects and tasks: @GradleTest
androidProject() to listOf("compileDebugKotlin"), fun testStdlibDefaultAndroid(gradleVersion: GradleVersion) {
jvmProject() to listOf("compileKotlin"), project(
jsProject() to listOf("compileKotlinJs"), "AndroidLibraryKotlinProject",
mppProject() to listOf( gradleVersion,
buildOptions = defaultBuildOptions.copy(androidVersion = TestVersions.AGP.AGP_42)
) {
removeDependencies(buildGradle)
checkTaskCompileClasspath("compileDebugKotlin", listOf("kotlin-stdlib" /*any of them*/))
}
}
@JvmGradlePluginTests
@DisplayName("Android: kotlin-stdlib dependency is not added when disabled via properties")
@GradleTest
fun testStdlibDisabledAndroid(gradleVersion: GradleVersion) {
project(
"AndroidLibraryKotlinProject",
gradleVersion,
buildOptions = defaultBuildOptions.copy(androidVersion = TestVersions.AGP.AGP_42)
) {
removeDependencies(buildGradle)
gradleProperties.appendText(
"\nkotlin.stdlib.default.dependency=false"
)
checkTaskCompileClasspath(
"compileDebugKotlin",
emptyList(),
checkModulesNotInClasspath = listOf("kotlin-stdlib" /*any of them*/),
)
}
}
@MppGradlePluginTests
@DisplayName("MPP: kotlin-stdlib is added by default")
@GradleTest
fun kotlinStdlibDefaultMpp(gradleVersion: GradleVersion) {
project("jvm-and-js-hmpp", gradleVersion) {
removeDependencies(buildGradleKts)
listOf(
"compileKotlinJvm", "compileKotlinJvm",
"compileKotlinJs", "compileKotlinJs",
"compileCommonMainKotlinMetadata", "compileCommonMainKotlinMetadata",
"compileJvmAndJsMainKotlinMetadata" "compileJvmAndJsMainKotlinMetadata"
) ).forEach { task ->
).forEach { (project, tasks) -> checkTaskCompileClasspath(task, listOf("kotlin-stdlib"), isBuildGradleKts = true)
with(project) {
prepare()
tasks.forEach { task ->
project.checkTaskCompileClasspath(task, listOf("kotlin-stdlib" /*any of them*/))
}
projectDir.resolve("gradle.properties").appendText(
"\nkotlin.stdlib.default.dependency=false"
)
tasks.forEach { task ->
project.checkTaskCompileClasspath(task, listOf(), checkModulesNotInClasspath = listOf("kotlin-stdlib" /*any of them*/))
}
} }
} }
} }
@Test @MppGradlePluginTests
fun testStdlibBasedOnJdk() = with(jvmProject()) { @DisplayName("MPP: kotlin-stdlib is not added when disabled in properties")
prepare() @GradleTest
gradleBuildScript().modify { "$it\nkotlin.target.compilations[\"main\"].kotlinOptions { jvmTarget = \"1.6\" }" } fun kotlinStdlibDisabledMpp(gradleVersion: GradleVersion) {
val version = defaultBuildOptions().kotlinVersion project("jvm-and-js-hmpp", gradleVersion) {
checkTaskCompileClasspath( removeDependencies(buildGradleKts)
"compileKotlin", gradleProperties.appendText(
listOf("kotlin-stdlib-$version"), "\nkotlin.stdlib.default.dependency=false"
listOf("kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8") )
)
gradleBuildScript().modify { "$it\nkotlin.target.compilations[\"main\"].kotlinOptions { jvmTarget = \"11\" }" } listOf(
checkTaskCompileClasspath( "compileKotlinJvm",
"compileKotlin", "compileKotlinJs",
listOf("kotlin-stdlib", "kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8"), "compileCommonMainKotlinMetadata",
) "compileJvmAndJsMainKotlinMetadata"
).forEach { task ->
checkTaskCompileClasspath(
task,
emptyList(),
checkModulesNotInClasspath = listOf("kotlin-stdlib"),
isBuildGradleKts = true
)
}
}
} }
@Test @DisplayName("Adds kotlin-stdlib variant based on jvmTarget value")
fun testOverrideStdlib() = with(jvmProject().apply { prepare() }) { @JvmGradlePluginTests
gradleBuildScript().appendText( @GradleTest
"\n" + """ fun testStdlibBasedOnJdk(gradleVersion: GradleVersion) {
kotlin.target.compilations["main"].kotlinOptions.jvmTarget = "1.8" project("simpleProject", gradleVersion) {
dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib") } removeDependencies(buildGradle)
""".trimIndent() buildGradle.modify { "$it\nkotlin.target.compilations[\"main\"].kotlinOptions { jvmTarget = \"1.6\" }" }
) val version = defaultBuildOptions.kotlinVersion
// Check that the explicit stdlib overrides the plugin's choice of stdlib-jdk8 checkTaskCompileClasspath(
checkTaskCompileClasspath( "compileKotlin",
"compileKotlin", listOf("kotlin-stdlib-$version"),
listOf("kotlin-stdlib-${defaultBuildOptions().kotlinVersion}"), listOf("kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8")
listOf("kotlin-stdlib-jdk8") )
)
buildGradle.modify { "$it\nkotlin.target.compilations[\"main\"].kotlinOptions { jvmTarget = \"11\" }" }
checkTaskCompileClasspath(
"compileKotlin",
listOf("kotlin-stdlib", "kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8"),
)
}
} }
private val kotlinTestMultiplatformDependency = "org.jetbrains.kotlin:kotlin-test" @JvmGradlePluginTests
@DisplayName("Explicit kotlin-stdlib version overrides default one")
@GradleTest
fun testOverrideStdlib(gradleVersion: GradleVersion) {
project("simpleProject", gradleVersion) {
removeDependencies(buildGradle)
buildGradle.appendText(
"""
kotlin.target.compilations["main"].kotlinOptions.jvmTarget = "1.8"
dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib") }
""".trimIndent()
)
@Test // Check that the explicit stdlib overrides the plugin's choice of stdlib-jdk8
fun testKotlinTestSingleDependency() { checkTaskCompileClasspath(
data class TestCase( "compileKotlin",
val project: Project, listOf("kotlin-stdlib-${defaultBuildOptions.kotlinVersion}"),
val configurationsToAddDependency: List<String>, listOf("kotlin-stdlib-jdk8")
val classpathElementsExpectedByTask: Map<String, List<String>>, )
val filesExpectedByConfiguration: Map<String, List<String>> = emptyMap() }
) }
listOf( @JvmGradlePluginTests
TestCase( @DisplayName("Android: Kotlin test single dependency in unit tests")
androidProject(), @GradleTest
fun kotlinTestSingleDependencyAndroidUnitTests(gradleVersion: GradleVersion) {
project(
"AndroidLibraryKotlinProject",
gradleVersion,
buildOptions = defaultBuildOptions.copy(androidVersion = TestVersions.AGP.AGP_42)
) {
assertKotlinTestDependency(
listOf("testImplementation"), listOf("testImplementation"),
mapOf( mapOf(
"compileDebugUnitTestKotlin" to listOf("kotlin-test-junit"), "compileDebugUnitTestKotlin" to listOf("kotlin-test-junit"),
"compileReleaseUnitTestKotlin" to listOf("kotlin-test-junit") "compileReleaseUnitTestKotlin" to listOf("kotlin-test-junit")
) )
), )
TestCase( }
androidProject(), }
@JvmGradlePluginTests
@DisplayName("Android: Kotlin test single dependency in ui tests")
@GradleTest
fun kotlinTestSingleDependencyAndroidUiTests(gradleVersion: GradleVersion) {
project(
"AndroidLibraryKotlinProject",
gradleVersion,
buildOptions = defaultBuildOptions.copy(androidVersion = TestVersions.AGP.AGP_42)
) {
assertKotlinTestDependency(
listOf("androidTestImplementation"), listOf("androidTestImplementation"),
mapOf("compileDebugAndroidTestKotlin" to listOf("kotlin-test-junit")) mapOf("compileDebugAndroidTestKotlin" to listOf("kotlin-test-junit"))
), )
TestCase(jvmProject(), listOf("testImplementation"), mapOf("compileTestKotlin" to listOf("kotlin-test-testng"))), }
TestCase(jsProject(), listOf("testImplementation"), mapOf("compileTestKotlinJs" to listOf("kotlin-test-js"))), }
TestCase(
mppProject(), @JvmGradlePluginTests
@DisplayName("JVM: Kotlin test single dependency")
@GradleTest
fun kotlinTestSingleDependencyJvm(gradleVersion: GradleVersion) {
project("simpleProject", gradleVersion) {
assertKotlinTestDependency(
listOf("testImplementation"),
mapOf("compileTestKotlin" to listOf("kotlin-test-testng"))
)
}
}
@JsGradlePluginTests
@DisplayName("JS: Kotlin test single dependency")
@GradleTest
fun kotlinTestSingleDependencyJs(gradleVersion: GradleVersion) {
project(
"kotlin-js-plugin-project",
gradleVersion,
buildOptions = defaultBuildOptions.copy(warningMode = WarningMode.Summary)
) {
assertKotlinTestDependency(
listOf("testImplementation"),
mapOf("compileTestKotlinJs" to listOf("kotlin-test-js")),
isBuildGradleKts = true
)
}
}
@MppGradlePluginTests
@DisplayName("MPP: Kotlin test single dependency in common")
@GradleTest
fun kotlinTestSingleDependencyMppCommon(gradleVersion: GradleVersion) {
project("jvm-and-js-hmpp", gradleVersion) {
assertKotlinTestDependency(
listOf("commonTestImplementation"), listOf("commonTestImplementation"),
mapOf( mapOf(
"compileTestKotlinJvm" to listOf("kotlin-test-junit"), "compileTestKotlinJvm" to listOf("kotlin-test-junit"),
@@ -148,10 +299,18 @@ class KotlinSpecificDependenciesIT : BaseGradleIT() {
mapOf( mapOf(
"commonTestImplementationDependenciesMetadata" to listOf("kotlin-test-common", "kotlin-test-annotations-common"), "commonTestImplementationDependenciesMetadata" to listOf("kotlin-test-common", "kotlin-test-annotations-common"),
"commonTestApiDependenciesMetadata" to listOf("!kotlin-test-common", "!kotlin-test-annotations-common"), "commonTestApiDependenciesMetadata" to listOf("!kotlin-test-common", "!kotlin-test-annotations-common"),
) ),
), isBuildGradleKts = true
TestCase( )
mppProject(), }
}
@MppGradlePluginTests
@DisplayName("MPP: Kotlin test single dependency in JVM and JS variants")
@GradleTest
fun kotlinTestSingleDependencyMppJvmJs(gradleVersion: GradleVersion) {
project("jvm-and-js-hmpp", gradleVersion) {
assertKotlinTestDependency(
listOf("jvmAndJsTestApi", "jvmAndJsTestCompileOnly"), // add to the intermediate source set, and to two scopes listOf("jvmAndJsTestApi", "jvmAndJsTestCompileOnly"), // add to the intermediate source set, and to two scopes
mapOf( mapOf(
"compileTestKotlinJvm" to listOf("kotlin-test-junit"), "compileTestKotlinJvm" to listOf("kotlin-test-junit"),
@@ -163,184 +322,313 @@ class KotlinSpecificDependenciesIT : BaseGradleIT() {
"jvmAndJsTestApiDependenciesMetadata" to listOf("kotlin-test-common"), "jvmAndJsTestApiDependenciesMetadata" to listOf("kotlin-test-common"),
"jvmAndJsTestCompileOnlyDependenciesMetadata" to listOf("kotlin-test-common"), "jvmAndJsTestCompileOnlyDependenciesMetadata" to listOf("kotlin-test-common"),
"jvmAndJsTestImplementationDependenciesMetadata" to listOf("!kotlin-test-common"), "jvmAndJsTestImplementationDependenciesMetadata" to listOf("!kotlin-test-common"),
) ),
), isBuildGradleKts = true
TestCase( )
/** }
* Check that in a single-platform project the common metadata configurations resolve the }
* framework-specific dependency (inferred as JUnit) correctly to the common artifact
* KTIJ-6098 @MppGradlePluginTests
*/ @DisplayName("MPP (KTIJ-6098): in single platform project common metadata configurations resolve the framework-specific dependency ")
Project("mpp-single-jvm-target"), @GradleTest
fun kotlinTestSingleDependencyMppCommonSinglePlatform(gradleVersion: GradleVersion) {
project("jvm-and-js-hmpp", gradleVersion) {
assertKotlinTestDependency(
listOf("commonTestImplementation"), listOf("commonTestImplementation"),
mapOf( mapOf(
"compileTestKotlinJvm" to listOf("kotlin-test-junit"), "compileTestKotlinJvm" to listOf("kotlin-test-junit"),
), ),
mapOf( mapOf(
"commonTestImplementationDependenciesMetadata" to listOf("kotlin-test-common", "kotlin-test-annotations-common") "commonTestImplementationDependenciesMetadata" to listOf("kotlin-test-common", "kotlin-test-annotations-common")
) ),
isBuildGradleKts = true
) )
).forEach { testCase ->
with(testCase) {
project.prepare()
project.gradleBuildScript().appendText(
configurationsToAddDependency.joinToString("\n", "\n") { configuration ->
"\ndependencies { \"$configuration\"(\"$kotlinTestMultiplatformDependency\") }"
}
)
classpathElementsExpectedByTask.forEach { (task, expected) ->
val (notInClasspath, inClasspath) = expected.partition { it.startsWith("!") }
project.checkTaskCompileClasspath(task, inClasspath, notInClasspath.map { it.removePrefix("!") })
}
filesExpectedByConfiguration.forEach { (configuration, expected) ->
val (notInItems, inItems) = expected.partition { it.startsWith("!") }
project.checkConfigurationContent(configuration, subproject = null, inItems, notInItems.map { it.removePrefix("!") })
}
}
} }
} }
@Test @JvmGradlePluginTests
fun testFrameworkSelection() { @DisplayName("JVM: test framework variant proper selection")
data class TestCase( @GradleTestVersions
val project: Project, @ParameterizedTest(name = "{1} with {0}: {displayName}")
val testTaskName: String, @ArgumentsSource(GradleAndTestFrameworksArgumentsProvider::class)
val compileTaskName: String, fun testFrameworkSelectionJvm(
val configurationToAddDependency: String gradleVersion: GradleVersion,
) testFramework: Pair<String, String>
) {
project("simpleProject", gradleVersion) {
removeDependencies(buildGradle)
buildGradle.appendText("""${'\n'}dependencies { "testImplementation"("$kotlinTestMultiplatformDependency") }""")
buildGradle.appendText("\n(tasks.getByName(\"test\") as Test).${testFramework.first}")
val frameworks = listOf("useJUnit()" to "junit", "useTestNG()" to "testng", "useJUnitPlatform()" to "junit5") val expectedModule = "kotlin-test-${testFramework.second}-"
listOf( checkTaskCompileClasspath(
TestCase(jvmProject(), "test", "compileTestKotlin", "testImplementation"), "compileTestKotlin",
TestCase(mppProject(), "jvmTest", "compileTestKotlinJvm", "commonTestImplementation"), listOf(expectedModule),
TestCase(mppProject(), "jvmTest", "compileTestKotlinJvm", "jvmAndJsTestImplementation") testFrameworks.map { "kotlin-test-" + it.second + "-" } - expectedModule
).forEach { (project, testTaskName, compileTaskName, configuration) -> )
project.prepare() }
project.gradleBuildScript().appendText("""${'\n'}dependencies { "$configuration"("$kotlinTestMultiplatformDependency") }""") }
frameworks.forEach { (setup, frameworkName) -> @MppGradlePluginTests
with(project) { @DisplayName("MPP common: test framework variant proper selection")
gradleBuildScript().appendText("\n(tasks.getByName(\"$testTaskName\") as Test).$setup") @GradleTestVersions
val expectedModule = "kotlin-test-$frameworkName-" @ParameterizedTest(name = "{1} with {0}: {displayName}")
checkTaskCompileClasspath( @ArgumentsSource(GradleAndTestFrameworksArgumentsProvider::class)
compileTaskName, fun testFrameworkSelectionMppJvm(
listOf(expectedModule), gradleVersion: GradleVersion,
frameworks.map { "kotlin-test-" + it.second + "-" } - expectedModule testFramework: Pair<String, String>
) ) {
project("jvm-and-js-hmpp", gradleVersion) {
removeDependencies(buildGradleKts)
buildGradleKts.appendText("""${'\n'}dependencies { "jvmAndJsTestImplementation"("$kotlinTestMultiplatformDependency") }""")
buildGradleKts.appendText("\n(tasks.getByName(\"jvmTest\") as Test).${testFramework.first}")
val expectedModule = "kotlin-test-${testFramework.second}-"
checkTaskCompileClasspath(
"compileTestKotlinJvm",
listOf(expectedModule),
testFrameworks.map { "kotlin-test-" + it.second + "-" } - expectedModule,
isBuildGradleKts = true
)
}
}
@MppGradlePluginTests
@DisplayName("MPP jvm: test framework variant proper selection")
@GradleTestVersions
@ParameterizedTest(name = "{1} with {0}: {displayName}")
@ArgumentsSource(GradleAndTestFrameworksArgumentsProvider::class)
fun testFrameworkSelectionMppCommon(
gradleVersion: GradleVersion,
testFramework: Pair<String, String>
) {
project("jvm-and-js-hmpp", gradleVersion) {
removeDependencies(buildGradleKts)
buildGradleKts.appendText("""${'\n'}dependencies { "commonTestImplementation"("$kotlinTestMultiplatformDependency") }""")
buildGradleKts.appendText("\n(tasks.getByName(\"jvmTest\") as Test).${testFramework.first}")
val expectedModule = "kotlin-test-${testFramework.second}-"
checkTaskCompileClasspath(
"compileTestKotlinJvm",
listOf(expectedModule),
testFrameworks.map { "kotlin-test-" + it.second + "-" } - expectedModule,
isBuildGradleKts = true
)
}
}
@JvmGradlePluginTests
@DisplayName("Possible to remove 'kotlin-test' dependency from configuration")
@GradleTest
fun testRemoveKotlinTestDependency(gradleVersion: GradleVersion) {
project("simpleProject", gradleVersion) {
removeDependencies(buildGradle)
buildGradle.appendText(
"""
dependencies { testImplementation("$kotlinTestMultiplatformDependency") }
configurations.getByName("testImplementation").dependencies.removeAll { it.name == "kotlin-test" }
""".trimIndent()
)
checkTaskCompileClasspath("compileTestKotlin", checkModulesNotInClasspath = listOf("kotlin-test"))
// Add it back after removal:
buildGradle.appendText(
"""
dependencies { testImplementation("$kotlinTestMultiplatformDependency") }
""".trimIndent()
)
checkTaskCompileClasspath("compileTestKotlin", checkModulesInClasspath = listOf("kotlin-test"))
}
}
@JvmGradlePluginTests
@DisplayName("coreLibrariesVersion override default version")
@GradleTest
fun testCoreLibraryVersionsDsl(gradleVersion: GradleVersion) {
project("simpleProject", gradleVersion) {
removeDependencies(buildGradle)
val customVersion = TestVersions.Kotlin.STABLE_RELEASE
buildGradle.appendText(
"""
kotlin.coreLibrariesVersion = "$customVersion"
dependencies {
testImplementation("org.jetbrains.kotlin:kotlin-reflect")
testImplementation("org.jetbrains.kotlin:kotlin-test")
} }
} test.useJUnit()
""".trimIndent()
)
checkTaskCompileClasspath(
"compileTestKotlin",
listOf("kotlin-stdlib-", "kotlin-reflect-", "kotlin-test-").map { it + customVersion }
)
} }
} }
@Test @JvmGradlePluginTests
fun testRemoveKotlinTestDependency() = with(jvmProject().apply { prepare() }) { @DisplayName("No failure if configuration is observed")
gradleBuildScript().appendText( @GradleTest
"\n" + """ fun testNoFailureIfConfigurationIsObserved(gradleVersion: GradleVersion) {
dependencies { testImplementation("$kotlinTestMultiplatformDependency") } project("simpleProject", gradleVersion) {
configurations.getByName("testImplementation").dependencies.removeAll { it.name == "kotlin-test" } removeDependencies(buildGradle)
""".trimIndent() buildGradle.modify {
) //language=Groovy
checkTaskCompileClasspath("compileTestKotlin", checkModulesNotInClasspath = listOf("kotlin-test")) """
$it
// Add it back after removal:
gradleBuildScript().appendText( configurations {
"\n" + """ apiTest
dependencies { testImplementation("$kotlinTestMultiplatformDependency") } api.extendsFrom(apiTest)
""" }
)
checkTaskCompileClasspath("compileTestKotlin", checkModulesInClasspath = listOf("kotlin-test")) dependencies {
} apiTest("org.jetbrains.kotlin:kotlin-reflect")
}
@Test println(configurations.apiTest.incoming.resolutionResult.allDependencies)
fun testCoreLibraryVersionsDsl() = with(jvmProject().apply { prepare() }) { println(configurations.apiTest.incoming.dependencies.toList())
val customVersion = "1.3.70" """.trimIndent()
gradleBuildScript().appendText(
"\n" + """
kotlin.coreLibrariesVersion = "$customVersion"
dependencies {
testImplementation("org.jetbrains.kotlin:kotlin-reflect")
testImplementation("org.jetbrains.kotlin:kotlin-test")
} }
test.useJUnit()
""".trimIndent()
)
checkTaskCompileClasspath(
"compileTestKotlin",
listOf("kotlin-stdlib-", "kotlin-reflect-", "kotlin-test-").map { it + customVersion }
)
}
@Test checkTaskCompileClasspath("compileKotlin", listOf("kotlin-reflect"))
fun testNoFailureIfConfigurationIsObserved() = with(jvmProject()) {
gradleBuildScript().modify {
//language=Groovy
"""
$it
configurations {
apiTest
api.extendsFrom(apiTest)
}
dependencies {
apiTest("org.jetbrains.kotlin:kotlin-reflect")
}
println(configurations.apiTest.incoming.resolutionResult.allDependencies)
println(configurations.apiTest.incoming.dependencies.toList())
""".trimIndent()
} }
checkTaskCompileClasspath("compileKotlin", listOf("kotlin-reflect"))
} }
private fun Project.checkConfigurationContent( private fun TestProject.assertKotlinTestDependency(
configurationsToAddDependency: List<String>,
classpathElementsExpectedByTask: Map<String, List<String>>,
filesExpectedByConfiguration: Map<String, List<String>> = emptyMap(),
isBuildGradleKts: Boolean = false
) {
val buildFile = if (isBuildGradleKts) buildGradleKts else buildGradle
removeDependencies(buildFile)
buildFile.appendText(
configurationsToAddDependency.joinToString("\n", "\n") { configuration ->
"\ndependencies { \"$configuration\"(\"$kotlinTestMultiplatformDependency\") }"
}
)
classpathElementsExpectedByTask.forEach { (task, expected) ->
val (notInClasspath, inClasspath) = expected.partition { it.startsWith("!") }
checkTaskCompileClasspath(
task,
inClasspath,
notInClasspath.map { it.removePrefix("!") },
isBuildGradleKts = isBuildGradleKts
)
}
filesExpectedByConfiguration.forEach { (configuration, expected) ->
val (notInItems, inItems) = expected.partition { it.startsWith("!") }
checkConfigurationContent(
configuration,
inItems,
notInItems.map { it.removePrefix("!") },
isBuildGradleKts
)
}
}
private fun TestProject.checkConfigurationContent(
configurationName: String, configurationName: String,
subproject: String? = null,
checkModulesInResolutionResult: List<String> = emptyList(), checkModulesInResolutionResult: List<String> = emptyList(),
checkModulesNotInResolutionResult: List<String> = emptyList() checkModulesNotInResolutionResult: List<String> = emptyList(),
isBuildGradleKts: Boolean
) { ) {
val expression = """configurations["$configurationName"].toList()""" val expression = """configurations["$configurationName"].toList()"""
checkPrintedItems(subproject, expression, checkModulesInResolutionResult, checkModulesNotInResolutionResult) checkPrintedItems(
null,
expression,
checkModulesInResolutionResult,
checkModulesNotInResolutionResult,
isBuildGradleKts
)
} }
}
private var testBuildRunId = 0 private fun TestProject.checkTaskCompileClasspath(
taskPath: String,
checkModulesInClasspath: List<String> = emptyList(),
checkModulesNotInClasspath: List<String> = emptyList(),
isNative: Boolean = false,
isBuildGradleKts: Boolean = false
) {
val subproject = taskPath.substringBeforeLast(":").takeIf { it.isNotEmpty() && it != taskPath }
val taskName = taskPath.removePrefix(subproject.orEmpty())
val taskClass = if (isNative) "org.jetbrains.kotlin.gradle.tasks.AbstractKotlinNativeCompile<*, *>" else "AbstractCompile"
val expression = """(tasks.getByName("$taskName") as $taskClass).${if (isNative) "libraries" else "classpath"}.toList()"""
checkPrintedItems(subproject, expression, checkModulesInClasspath, checkModulesNotInClasspath, isBuildGradleKts)
}
fun BaseGradleIT.Project.checkTaskCompileClasspath( private fun TestProject.checkPrintedItems(
taskPath: String, subproject: String?,
checkModulesInClasspath: List<String> = emptyList(), itemsExpression: String,
checkModulesNotInClasspath: List<String> = emptyList(), checkAnyItemsContains: List<String>,
isNative: Boolean = false checkNoItemContains: List<String>,
) { isBuildGradleKts: Boolean
val subproject = taskPath.substringBeforeLast(":").takeIf { it.isNotEmpty() && it != taskPath } ) {
val taskName = taskPath.removePrefix(subproject.orEmpty()) val printingTaskName = "printItems${UUID.randomUUID()}"
val taskClass = if (isNative) "org.jetbrains.kotlin.gradle.tasks.AbstractKotlinNativeCompile<*, *>" else "AbstractCompile" val buildFile = if (subproject != null) {
val expression = """(tasks.getByName("$taskName") as $taskClass).${if (isNative) "libraries" else "classpath"}.toList()""" subProject(subproject).run { if (isBuildGradleKts) buildGradleKts else buildGradle }
checkPrintedItems(subproject, expression, checkModulesInClasspath, checkModulesNotInClasspath) } else {
} if (isBuildGradleKts) buildGradleKts else buildGradle
}
private fun BaseGradleIT.Project.checkPrintedItems( buildFile.appendText(
subproject: String?, """
itemsExpression: String,
checkAnyItemsContains: List<String>, tasks.create("$printingTaskName") {
checkNoItemContains: List<String> doLast {
) = with(testCase) { println("###$printingTaskName " + $itemsExpression)
setupWorkingDir() }
val printingTaskName = "printItems${testBuildRunId++}" }
gradleBuildScript(subproject).appendText( """.trimIndent()
""" )
${'\n'}
tasks.create("$printingTaskName") { build("${subproject?.prependIndent(":").orEmpty()}:$printingTaskName") {
doLast { val itemsLine = output.lines().single { "###$printingTaskName" in it }.substringAfter(printingTaskName)
println("###$printingTaskName" + $itemsExpression) val items = itemsLine.removeSurrounding("[", "]").split(", ").toSet()
} checkAnyItemsContains.forEach { pattern -> assertTrue { items.any { pattern in it } } }
checkNoItemContains.forEach { pattern -> assertFalse { items.any { pattern in it } } }
} }
""".trimIndent()
)
build("${subproject?.prependIndent(":").orEmpty()}:$printingTaskName") {
assertSuccessful()
val itemsLine = output.lines().single { "###$printingTaskName" in it }.substringAfter(printingTaskName)
val items = itemsLine.removeSurrounding("[", "]").split(", ").toSet()
checkAnyItemsContains.forEach { pattern -> assertTrue { items.any { pattern in it } } }
checkNoItemContains.forEach { pattern -> assertFalse { items.any { pattern in it } } }
} }
}
private fun removeDependencies(
buildGradleFile: Path
) {
buildGradleFile.modify {
it.lines()
.filter { line ->
"stdlib" !in line && "kotlin(\"test" !in line && "kotlin-test" !in line
}
.joinToString("\n")
}
}
internal class GradleAndTestFrameworksArgumentsProvider : GradleArgumentsProvider() {
override fun provideArguments(
context: ExtensionContext
): Stream<out Arguments> {
val gradleVersions = super.provideArguments(context).map { it.get().first() as GradleVersion }.toList()
return testFrameworks
.flatMap { testFramework ->
gradleVersions.map { it to testFramework }
}
.asSequence()
.map {
Arguments.of(it.first, it.second)
}
.asStream()
}
}
companion object {
private const val kotlinTestMultiplatformDependency = "org.jetbrains.kotlin:kotlin-test"
private val testFrameworks = listOf(
"useJUnit()" to "junit",
"useTestNG()" to "testng",
"useJUnitPlatform()" to "junit5"
)
}
}
@@ -1,5 +1,5 @@
plugins { plugins {
kotlin("multiplatform").version("<pluginMarkerVersion>") kotlin("multiplatform")
`maven-publish` `maven-publish`
} }