Infer required kotlin-test jvm capability based on used test framework

Except when:
- the Gradle version is less than 6.0, it doesn't read the metadata
published by a newer Gradle by default.
- the requested dependency version is less than 1.5, because it doesn't
have metadata published.

Update integration tests to use kotlin-test common dependency.

KT-40225
This commit is contained in:
Ilya Gorbunov
2021-01-27 03:01:32 +03:00
parent cbeadba15d
commit 4a17228621
4 changed files with 120 additions and 10 deletions
@@ -1,11 +1,10 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle
import org.jetbrains.kotlin.gradle.internals.KOTLIN_TEST_MULTIPLATFORM_MODULE_NAME
import org.jetbrains.kotlin.gradle.util.AGPVersion
import org.jetbrains.kotlin.gradle.util.modify
import org.jetbrains.kotlin.test.util.KtTestUtil
@@ -111,7 +110,7 @@ class KotlinSpecificDependenciesIT : BaseGradleIT() {
)
}
private val kotlinTestMultiplatformDependency = "org.jetbrains.kotlin:$KOTLIN_TEST_MULTIPLATFORM_MODULE_NAME"
private val kotlinTestMultiplatformDependency = "org.jetbrains.kotlin:kotlin-test"
@Test
fun testKotlinTestSingleDependency() {
@@ -222,7 +221,7 @@ class KotlinSpecificDependenciesIT : BaseGradleIT() {
gradleBuildScript().appendText(
"\n" + """
dependencies { testImplementation("$kotlinTestMultiplatformDependency") }
configurations.getByName("testImplementation").dependencies.removeAll { it.name == "$KOTLIN_TEST_MULTIPLATFORM_MODULE_NAME" }
configurations.getByName("testImplementation").dependencies.removeAll { it.name == "kotlin-test" }
""".trimIndent()
)
checkTaskCompileClasspath("compileTestKotlin", checkModulesNotInClasspath = listOf("kotlin-test"))
@@ -244,14 +243,14 @@ class KotlinSpecificDependenciesIT : BaseGradleIT() {
kotlin.coreLibrariesVersion = "$customVersion"
dependencies {
testImplementation("org.jetbrains.kotlin:kotlin-reflect")
testImplementation("org.jetbrains.kotlin:kotlin-test-multiplatform")
testImplementation("org.jetbrains.kotlin:kotlin-test")
}
test.useJUnit()
""".trimIndent()
)
checkTaskCompileClasspath(
"compileTestKotlin",
listOf("kotlin-stdlib-", "kotlin-reflect-", "kotlin-test-junit-").map { it + customVersion }
listOf("kotlin-stdlib-", "kotlin-reflect-", "kotlin-test-").map { it + customVersion }
)
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2021 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.
*/
@@ -36,11 +36,13 @@ import org.jetbrains.kotlin.gradle.targets.jvm.JvmCompilationsTestRunSource
import org.jetbrains.kotlin.gradle.tasks.KOTLIN_MODULE_GROUP
import org.jetbrains.kotlin.gradle.tasks.locateTask
import org.jetbrains.kotlin.gradle.testing.KotlinTaskTestRun
import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
internal fun customizeKotlinDependencies(project: Project) {
configureStdlibDefaultDependency(project)
configureKotlinTestDependencies(project)
configureKotlinTestDependency(project)
configureDefaultVersionsResolutionStrategy(project)
}
@@ -179,6 +181,113 @@ private fun stdlibModuleForJvmCompilations(compilations: Iterable<KotlinCompilat
//endregion
//region kotlin-test
internal fun configureKotlinTestDependency(project: Project) {
if (!isGradleVersionAtLeast(6, 0))
return
if (!PropertiesProvider(project).kotlinTestInferJvmVariant)
return
fun isKotlinTestRootDependency(dependency: Dependency) =
dependency.group == KOTLIN_MODULE_GROUP && dependency.name == KOTLIN_TEST_ROOT_MODULE_NAME
val versionPrefixRegex = Regex("""^(\d+)\.(\d+)""")
fun isAtLeast1_5(version: String) = versionPrefixRegex.find(version)?.let {
val c1 = it.groupValues[1].toInt()
val c2 = it.groupValues[2].toInt()
c1 > 1 || c1 == 1 && c2 >= 5
} ?: false
KotlinDependencyScope.values().forEach { scope ->
val versionOrNullBySourceSet = mutableMapOf<KotlinSourceSet, String?>()
project.kotlinExtension.sourceSets.all { kotlinSourceSet ->
val configuration = project.sourceSetDependencyConfigurationByScope(kotlinSourceSet, scope)
var finalizingDependencies = false
configuration.dependencies.matching(::isKotlinTestRootDependency).apply {
firstOrNull()?.let { versionOrNullBySourceSet[kotlinSourceSet] = it.version }
whenObjectRemoved {
if (!finalizingDependencies && !any())
versionOrNullBySourceSet.remove(kotlinSourceSet)
}
whenObjectAdded { item ->
versionOrNullBySourceSet[kotlinSourceSet] = item.version
}
}
project.tryWithDependenciesIfUnresolved(configuration) { dependencies ->
val parentOrOwnVersions: List<String?> =
kotlinSourceSet.getSourceSetHierarchy().filter(versionOrNullBySourceSet::contains).map(versionOrNullBySourceSet::get)
finalizingDependencies = true
for (version in parentOrOwnVersions.distinct()) { // add dependencies with each version and let Gradle disambiguate them
val effectiveVersion = version ?: project.kotlinExtension.coreLibrariesVersion
if (!isAtLeast1_5(effectiveVersion)) continue
val clarifyCapability = kotlinTestCapabilityForJvmSourceSet(project, kotlinSourceSet) ?: continue
val clarifiedDependency =
(project.kotlinDependency(KOTLIN_TEST_ROOT_MODULE_NAME, version) as ExternalDependency).apply {
if (version == null) {
version { constraint -> constraint.require(project.kotlinExtension.coreLibrariesVersion) }
}
capabilities {
it.requireCapability(clarifyCapability)
}
}
dependencies.add(clarifiedDependency)
}
}
}
}
}
private fun kotlinTestCapabilityForJvmSourceSet(project: Project, kotlinSourceSet: KotlinSourceSet): String? {
val compilations = CompilationSourceSetUtil.compilationsBySourceSets(project).getValue(kotlinSourceSet)
.filter { it.target !is KotlinMetadataTarget }
val platformTypes = compilations.map { it.platformType }
// TODO: Extract jvmPlatformTypes to public constant?
val jvmPlatforms = setOf(KotlinPlatformType.jvm, KotlinPlatformType.androidJvm)
if (!jvmPlatforms.containsAll(platformTypes)) return null
val testTaskLists: List<List<Task>?> = compilations.map { compilation ->
val target = compilation.target
when {
target is KotlinTargetWithTests<*, *> ->
target.findTestRunsByCompilation(compilation)?.filterIsInstance<KotlinTaskTestRun<*, *>>()?.map { it.executionTask.get() }
target is KotlinWithJavaTarget<*> ->
if (compilation.name == KotlinCompilation.TEST_COMPILATION_NAME)
project.locateTask<AbstractTestTask>(target.testTaskName)?.get()?.let(::listOf)
else null
compilation is KotlinJvmAndroidCompilation -> when (compilation.androidVariant) {
is UnitTestVariant ->
project.locateTask<AbstractTestTask>(lowerCamelCaseName("test", compilation.androidVariant.name))?.get()?.let(::listOf)
is TestVariant -> (compilation.androidVariant as TestVariant).connectedInstrumentTest?.let(::listOf)
else -> null
}
else -> null
}
}
if (null in testTaskLists) {
return null
}
val testTasks = testTaskLists.flatMap { checkNotNull(it) }
val frameworks = testTasks.mapTo(mutableSetOf()) { testTask ->
when (testTask) {
is Test -> testFrameworkOf(testTask)
else -> // Android connected test tasks don't inherit from Test, but we use JUnit for them
KotlinTestJvmFramework.junit
}
}
return when {
frameworks.size > 1 -> null
else -> "$KOTLIN_MODULE_GROUP:$KOTLIN_TEST_ROOT_MODULE_NAME-framework-${frameworks.single()}"
}
}
internal const val KOTLIN_TEST_ROOT_MODULE_NAME = "kotlin-test"
internal fun configureKotlinTestDependencies(project: Project) {
fun isKotlinTestMultiplatformDependency(dependency: Dependency) =
dependency.group == KOTLIN_MODULE_GROUP && dependency.name == KOTLIN_TEST_MULTIPLATFORM_MODULE_NAME
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2021 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.
*/
@@ -253,6 +253,9 @@ internal class PropertiesProvider private constructor(private val project: Proje
val stdlibDefaultDependency: Boolean
get() = booleanProperty("kotlin.stdlib.default.dependency") ?: true
val kotlinTestInferJvmVariant: Boolean
get() = booleanProperty("kotlin.test.infer.jvm.variant") ?: true
private fun propertyWithDeprecatedVariant(propName: String, deprecatedPropName: String): String? {
val deprecatedProperty = property(deprecatedPropName)
if (deprecatedProperty != null) {
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2021 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.
*/
@@ -26,4 +26,3 @@ const val NO_NATIVE_STDLIB_PROPERTY_WARNING = NO_NATIVE_STDLIB_PROPERTY_WARNING
val KOTLIN_12X_MPP_DEPRECATION_WARNING = KOTLIN_12X_MPP_DEPRECATION_WARNING
const val KOTLIN_TEST_MULTIPLATFORM_MODULE_NAME = org.jetbrains.kotlin.gradle.internal.KOTLIN_TEST_MULTIPLATFORM_MODULE_NAME