[Gradle] Add BuildKotlinToolingMetadataTest for KPM

This commit is contained in:
Anton Lakotka
2022-03-22 14:42:21 +01:00
parent 2e327f1367
commit 1aac53d544
2 changed files with 235 additions and 91 deletions
@@ -25,33 +25,21 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetWithHostTests
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.*
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
import org.jetbrains.kotlin.gradle.tooling.BuildKotlinToolingMetadataTask
import org.jetbrains.kotlin.gradle.tooling.buildKotlinToolingMetadataForMainKpmModuleTask
import org.jetbrains.kotlin.gradle.tooling.buildKotlinToolingMetadataTask
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.library.KotlinAbiVersion
import org.jetbrains.kotlin.tooling.KotlinToolingMetadata
import org.jetbrains.kotlin.tooling.toJsonString
import org.junit.Assume
import org.junit.AssumptionViolatedException
import org.junit.Before
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
@RunWith(Parameterized::class)
class BuildKotlinToolingMetadataTest {
@Parameterized.Parameter(0)
@JvmField
val kpmModelMappingEnabled: Boolean = false
private val project = ProjectBuilder.builder().build().also{ addBuildEventsListenerRegistryMock(it) } as ProjectInternal
private val multiplatformExtension get() = project.extensions.getByType(KotlinMultiplatformExtension::class.java)
private val jsExtension get() = project.extensions.getByType(KotlinJsProjectExtension::class.java)
@@ -60,11 +48,6 @@ class BuildKotlinToolingMetadataTest {
project.extensions.getByType(ExtraPropertiesExtension::class.java).set("kotlin.mpp.enableKotlinToolingMetadataArtifact", "true")
}
@Before
fun setup() {
project.enableKpmModelMapping(kpmModelMappingEnabled)
}
@Test
fun `multiplatform empty setup`() {
project.plugins.apply("kotlin-multiplatform")
@@ -75,18 +58,12 @@ class BuildKotlinToolingMetadataTest {
assertEquals(project.gradle.gradleVersion, metadata.buildSystemVersion)
assertEquals(KotlinMultiplatformPluginWrapper::class.java.canonicalName, metadata.buildPlugin)
assertEquals(project.getKotlinPluginVersion(), metadata.buildPluginVersion)
if (kpmModelMappingEnabled) {
assertEquals(0, metadata.projectTargets.size, "Expected no targets in KPM")
} else {
assertEquals(1, metadata.projectTargets.size, "Expected one target (metadata)")
val targetMetadata = metadata.projectTargets.single()
val targetClass = KotlinMetadataTarget::class
assertTrue(
targetClass.java.isAssignableFrom(Class.forName(targetMetadata.target)),
"Expect target to implement ${targetClass.simpleName}"
)
assertEquals(common.name, targetMetadata.platformType)
}
assertEquals(1, metadata.projectTargets.size, "Expected one target (metadata)")
assertTrue(
KotlinMetadataTarget::class.java.isAssignableFrom(Class.forName(metadata.projectTargets.single().target)),
"Expect target to be implement ${KotlinMetadataTarget::class.simpleName}"
)
assertEquals(common.name, metadata.projectTargets.single().platformType)
assertTrue(metadata.toJsonString().isNotBlank(), "Expected non blank json representation")
}
@@ -112,28 +89,13 @@ class BuildKotlinToolingMetadataTest {
val metadata = getKotlinToolingMetadata()
assertEquals(KotlinMultiplatformPluginWrapper::class.java.canonicalName, metadata.buildPlugin)
if (kpmModelMappingEnabled) {
assertFalse("Kotlin Tooling Metadata generated from KPM should not contain target of 'common' platform") {
metadata.projectTargets.any { it.platformType == common.name }
}
}
val expectedTargets = if (kpmModelMappingEnabled) {
mapOf(
androidJvm to LegacyMappedVariantWithRuntime::class,
jvm to KotlinJvmVariant::class,
js to LegacyMappedVariantWithRuntime::class,
native to KotlinLinuxX64Variant::class
)
} else {
mapOf(
common to KotlinMetadataTarget::class,
androidJvm to KotlinAndroidTarget::class,
jvm to KotlinJvmTarget::class,
js to KotlinJsTarget::class,
native to KotlinNativeTargetWithHostTests::class
)
}
val expectedTargets = mapOf(
common to KotlinMetadataTarget::class,
androidJvm to KotlinAndroidTarget::class,
jvm to KotlinJvmTarget::class,
js to KotlinJsTarget::class,
native to KotlinNativeTargetWithHostTests::class
)
assertEquals(
expectedTargets.keys.map { it.name }.sorted(),
@@ -148,7 +110,7 @@ class BuildKotlinToolingMetadataTest {
)
}
assertEquals(kpmModelMappingEnabled, metadata.projectSettings.isKPMEnabled, "projectSettings.isKPMEnabled must be set")
assertFalse(metadata.projectSettings.isKPMEnabled, "projectSettings.isKPMEnabled must not be set")
}
@Test
@@ -200,7 +162,6 @@ class BuildKotlinToolingMetadataTest {
@Test
fun js() {
Assume.assumeFalse("KPM model mapping is not yet supported in single-platform projects", kpmModelMappingEnabled)
project.plugins.apply("org.jetbrains.kotlin.js")
val kotlin = jsExtension
kotlin.js { nodejs() }
@@ -215,7 +176,6 @@ class BuildKotlinToolingMetadataTest {
@Test
fun jvm() {
Assume.assumeFalse("KPM model mapping is not yet supported in single-platform projects", kpmModelMappingEnabled)
project.plugins.apply("org.jetbrains.kotlin.jvm")
val metadata = getKotlinToolingMetadata()
assertEquals(org.jetbrains.kotlin.gradle.plugin.KotlinPluginWrapper::class.java.canonicalName, metadata.buildPlugin)
@@ -242,45 +202,12 @@ class BuildKotlinToolingMetadataTest {
linuxX64.extras.native?.konanTarget
)
if (kpmModelMappingEnabled) {
assertEquals(
KotlinLinuxArm64Variant::class.java.canonicalName,
linuxArm64.target
)
assertEquals(
KotlinLinuxX64Variant::class.java.canonicalName,
linuxX64.target
)
} else {
assertEquals(
KotlinNativeTarget::class.java.canonicalName,
linuxArm64.target
)
assertEquals(
KotlinNativeTargetWithHostTests::class.java.canonicalName,
linuxX64.target
)
}
assertEquals(KotlinNativeTarget::class.java.canonicalName, linuxArm64.target)
assertEquals(KotlinNativeTargetWithHostTests::class.java.canonicalName, linuxX64.target)
}
private fun getKotlinToolingMetadata(): KotlinToolingMetadata {
if (!kpmModelMappingEnabled) {
val task = project.buildKotlinToolingMetadataTask?.get() ?: error("No ${BuildKotlinToolingMetadataTask.defaultTaskName} task")
return task.kotlinToolingMetadata
} else {
val task = project.buildKotlinToolingMetadataForMainKpmModuleTask
?.get()
?: error("No ${BuildKotlinToolingMetadataTask.defaultTaskName} for Main module task")
return task.kotlinToolingMetadata
}
}
companion object {
@JvmStatic
@Parameterized.Parameters(name = "KPM-Model-Mapping:{0}")
fun parameters() = listOf(
arrayOf(false),
arrayOf(true),
)
val task = project.buildKotlinToolingMetadataTask?.get() ?: error("No ${BuildKotlinToolingMetadataTask.defaultTaskName} task")
return task.kotlinToolingMetadata
}
}
@@ -0,0 +1,217 @@
/*
* 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.kpm
import com.android.build.gradle.BaseExtension
import org.gradle.api.JavaVersion
import org.gradle.api.Project
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.testfixtures.ProjectBuilder
import org.jetbrains.kotlin.compilerRunner.konanVersion
import org.jetbrains.kotlin.gradle.addBuildEventsListenerRegistryMock
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.enableKpmModelMapping
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType.*
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.*
import org.jetbrains.kotlin.gradle.tooling.buildKotlinToolingMetadataTask
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.library.KotlinAbiVersion
import org.jetbrains.kotlin.tooling.KotlinToolingMetadata
import org.jetbrains.kotlin.tooling.toJsonString
import org.gradle.kotlin.dsl.create
import org.jetbrains.kotlin.gradle.plugin.*
import kotlin.test.*
private fun Project.kotlinToolingMetadataOfModule(moduleName: String = KotlinGradleModule.MAIN_MODULE_NAME): KotlinToolingMetadata {
val module = kpmModules.getByName(moduleName)
return module.buildKotlinToolingMetadataTask!!.get().kotlinToolingMetadata
}
class BuildKotlinToolingMetadataTest : AbstractKpmExtensionTest() {
@Test
fun `multiple targets`() {
// Given
with(kotlin) {
mainAndTest {
jvm
val linux = fragments.create("linux")
fragments.create<KotlinLinuxX64Variant>("linuxX64").apply { refines(linux) }
fragments.create<KotlinLinuxArm64Variant>("linuxArm64").apply { refines(linux) }
// No JS & Android variants available at the moment, only through [LegacyMappedVariant] which is tested below
}
}
// When
val metadata = project.kotlinToolingMetadataOfModule()
// Then
assertEquals("Gradle", metadata.buildSystem)
assertEquals(project.gradle.gradleVersion, metadata.buildSystemVersion)
assertEquals(KotlinPm20PluginWrapper::class.java.canonicalName, metadata.buildPlugin)
assertEquals(project.getKotlinPluginVersion(), metadata.buildPluginVersion)
assertEquals(3, metadata.projectTargets.size, "Expected 3 targets in KPM")
val jvmTarget = metadata.projectTargets.single { it.platformType == jvm.name }
assertEquals(KotlinJvmVariant::class.java.canonicalName, jvmTarget.target)
val nativeTargets = metadata.projectTargets.filter { it.platformType == native.name }.sortedBy { it.platformType }
assertEquals(KotlinLinuxArm64Variant::class.java.canonicalName, nativeTargets[0].target)
assertEquals(KotlinLinuxX64Variant::class.java.canonicalName, nativeTargets[1].target)
}
}
class KotlinToolingMetadataWithModelMappingTest {
private val project = ProjectBuilder.builder().build().also { addBuildEventsListenerRegistryMock(it) } as ProjectInternal
private val multiplatformExtension get() = project.extensions.getByType(KotlinMultiplatformExtension::class.java)
private fun getKotlinToolingMetadata() = project.kotlinToolingMetadataOfModule()
@BeforeTest
fun setup() {
project.enableKpmModelMapping()
}
@Test
fun `multiplatform empty setup`() {
project.plugins.apply("kotlin-multiplatform")
val metadata = getKotlinToolingMetadata()
assertEquals("Gradle", metadata.buildSystem)
assertEquals(project.gradle.gradleVersion, metadata.buildSystemVersion)
assertEquals(KotlinMultiplatformPluginWrapper::class.java.canonicalName, metadata.buildPlugin)
assertEquals(project.getKotlinPluginVersion(), metadata.buildPluginVersion)
assertEquals(0, metadata.projectTargets.size, "Expected no targets in KPM")
assertTrue(metadata.toJsonString().isNotBlank(), "Expected non blank json representation")
}
@Test
fun `multiplatform JS JVM Android linuxX64 setup`() {
project.plugins.apply("com.android.application")
project.plugins.apply("kotlin-multiplatform")
val android = project.extensions.getByType(BaseExtension::class.java)
val kotlin = multiplatformExtension
android.compileSdkVersion(28)
kotlin.android()
kotlin.jvm()
kotlin.js {
nodejs()
browser()
}
kotlin.linuxX64()
project.evaluate()
val metadata = getKotlinToolingMetadata()
assertEquals(KotlinMultiplatformPluginWrapper::class.java.canonicalName, metadata.buildPlugin)
assertFalse("Kotlin Tooling Metadata generated from KPM should not contain target of 'common' platform") {
metadata.projectTargets.any { it.platformType == common.name }
}
val expectedTargets = mapOf(
androidJvm to LegacyMappedVariantWithRuntime::class,
jvm to KotlinJvmVariant::class,
js to LegacyMappedVariantWithRuntime::class,
native to KotlinLinuxX64Variant::class
)
assertEquals(
expectedTargets.keys.map { it.name }.sorted(),
metadata.projectTargets.map { it.platformType }.sorted()
)
expectedTargets.forEach { (platformType, targetClass) ->
assertEquals(
targetClass.java.canonicalName,
metadata.projectTargets.single { it.platformType == platformType.name }.target,
"Platform '$platformType' has different target class"
)
}
assertTrue(metadata.projectSettings.isKPMEnabled, "projectSettings.isKPMEnabled must be set")
}
@Test
fun `multiplatform Android target with different source and target compatibility`() {
project.plugins.apply("kotlin-multiplatform")
project.plugins.apply("com.android.application")
val android = project.extensions.getByType(BaseExtension::class.java)
val kotlin = multiplatformExtension
android.compileSdkVersion(28)
kotlin.android()
android.compileOptions.sourceCompatibility = JavaVersion.VERSION_1_6
android.compileOptions.targetCompatibility = JavaVersion.VERSION_1_8
project.evaluate()
val androidTargetMetadata = getKotlinToolingMetadata()
.projectTargets.single { it.platformType == androidJvm.name }
assertEquals("1.6", androidTargetMetadata.extras.android?.sourceCompatibility)
assertEquals("1.8", androidTargetMetadata.extras.android?.targetCompatibility)
}
@Test
fun `multiplatform JVM with different targets`() {
project.plugins.apply("kotlin-multiplatform")
val kotlin = multiplatformExtension
val jvm = kotlin.jvm()
jvm.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME).kotlinOptions.jvmTarget = "12"
jvm.compilations.getByName(KotlinCompilation.TEST_COMPILATION_NAME).kotlinOptions.jvmTarget = "10"
assertEquals(
"12", getKotlinToolingMetadata().projectTargets
.single { it.platformType == KotlinPlatformType.jvm.name }.extras.jvm?.jvmTarget,
"Expected jvmTarget of main compilation"
)
}
@Test
fun `multiplatform with native target`() {
project.plugins.apply("kotlin-multiplatform")
val kotlin = multiplatformExtension
kotlin.linuxX64()
val metadata = getKotlinToolingMetadata()
val linuxTarget = metadata.projectTargets.single { it.platformType == native.name }
assertEquals(KonanTarget.LINUX_X64.name, linuxTarget.extras.native?.konanTarget)
assertEquals(project.konanVersion.toString(), linuxTarget.extras.native?.konanVersion)
assertEquals(KotlinAbiVersion.CURRENT.toString(), linuxTarget.extras.native?.konanAbiVersion)
}
@Test
fun `multiple native targets`() {
project.plugins.apply("kotlin-multiplatform")
val kotlin = multiplatformExtension
kotlin.linuxX64()
kotlin.linuxArm64()
val metadata = getKotlinToolingMetadata()
val nativeTargets = metadata.projectTargets.filter { it.platformType == native.name }.sortedBy { it.extras.native?.konanTarget }
assertEquals(2, nativeTargets.size, "Expected only two native targets")
val (linuxArm64, linuxX64) = nativeTargets
assertEquals(
"linux_arm64",
linuxArm64.extras.native?.konanTarget
)
assertEquals(
"linux_x64",
linuxX64.extras.native?.konanTarget
)
assertEquals(
KotlinLinuxArm64Variant::class.java.canonicalName,
linuxArm64.target
)
assertEquals(
KotlinLinuxX64Variant::class.java.canonicalName,
linuxX64.target
)
}
}