[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.KotlinMetadataTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetWithHostTests 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.js.KotlinJsTarget
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
import org.jetbrains.kotlin.gradle.tooling.BuildKotlinToolingMetadataTask 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.gradle.tooling.buildKotlinToolingMetadataTask
import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.library.KotlinAbiVersion import org.jetbrains.kotlin.library.KotlinAbiVersion
import org.jetbrains.kotlin.tooling.KotlinToolingMetadata import org.jetbrains.kotlin.tooling.KotlinToolingMetadata
import org.jetbrains.kotlin.tooling.toJsonString 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.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertTrue import kotlin.test.assertTrue
@RunWith(Parameterized::class)
class BuildKotlinToolingMetadataTest { class BuildKotlinToolingMetadataTest {
@Parameterized.Parameter(0)
@JvmField
val kpmModelMappingEnabled: Boolean = false
private val project = ProjectBuilder.builder().build().also{ addBuildEventsListenerRegistryMock(it) } as ProjectInternal private val project = ProjectBuilder.builder().build().also{ addBuildEventsListenerRegistryMock(it) } as ProjectInternal
private val multiplatformExtension get() = project.extensions.getByType(KotlinMultiplatformExtension::class.java) private val multiplatformExtension get() = project.extensions.getByType(KotlinMultiplatformExtension::class.java)
private val jsExtension get() = project.extensions.getByType(KotlinJsProjectExtension::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") project.extensions.getByType(ExtraPropertiesExtension::class.java).set("kotlin.mpp.enableKotlinToolingMetadataArtifact", "true")
} }
@Before
fun setup() {
project.enableKpmModelMapping(kpmModelMappingEnabled)
}
@Test @Test
fun `multiplatform empty setup`() { fun `multiplatform empty setup`() {
project.plugins.apply("kotlin-multiplatform") project.plugins.apply("kotlin-multiplatform")
@@ -75,18 +58,12 @@ class BuildKotlinToolingMetadataTest {
assertEquals(project.gradle.gradleVersion, metadata.buildSystemVersion) assertEquals(project.gradle.gradleVersion, metadata.buildSystemVersion)
assertEquals(KotlinMultiplatformPluginWrapper::class.java.canonicalName, metadata.buildPlugin) assertEquals(KotlinMultiplatformPluginWrapper::class.java.canonicalName, metadata.buildPlugin)
assertEquals(project.getKotlinPluginVersion(), metadata.buildPluginVersion) assertEquals(project.getKotlinPluginVersion(), metadata.buildPluginVersion)
if (kpmModelMappingEnabled) { assertEquals(1, metadata.projectTargets.size, "Expected one target (metadata)")
assertEquals(0, metadata.projectTargets.size, "Expected no targets in KPM") assertTrue(
} else { KotlinMetadataTarget::class.java.isAssignableFrom(Class.forName(metadata.projectTargets.single().target)),
assertEquals(1, metadata.projectTargets.size, "Expected one target (metadata)") "Expect target to be implement ${KotlinMetadataTarget::class.simpleName}"
val targetMetadata = metadata.projectTargets.single() )
val targetClass = KotlinMetadataTarget::class assertEquals(common.name, metadata.projectTargets.single().platformType)
assertTrue(
targetClass.java.isAssignableFrom(Class.forName(targetMetadata.target)),
"Expect target to implement ${targetClass.simpleName}"
)
assertEquals(common.name, targetMetadata.platformType)
}
assertTrue(metadata.toJsonString().isNotBlank(), "Expected non blank json representation") assertTrue(metadata.toJsonString().isNotBlank(), "Expected non blank json representation")
} }
@@ -112,28 +89,13 @@ class BuildKotlinToolingMetadataTest {
val metadata = getKotlinToolingMetadata() val metadata = getKotlinToolingMetadata()
assertEquals(KotlinMultiplatformPluginWrapper::class.java.canonicalName, metadata.buildPlugin) assertEquals(KotlinMultiplatformPluginWrapper::class.java.canonicalName, metadata.buildPlugin)
if (kpmModelMappingEnabled) { val expectedTargets = mapOf(
assertFalse("Kotlin Tooling Metadata generated from KPM should not contain target of 'common' platform") { common to KotlinMetadataTarget::class,
metadata.projectTargets.any { it.platformType == common.name } androidJvm to KotlinAndroidTarget::class,
} jvm to KotlinJvmTarget::class,
} js to KotlinJsTarget::class,
native to KotlinNativeTargetWithHostTests::class
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
)
}
assertEquals( assertEquals(
expectedTargets.keys.map { it.name }.sorted(), 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 @Test
@@ -200,7 +162,6 @@ class BuildKotlinToolingMetadataTest {
@Test @Test
fun js() { fun js() {
Assume.assumeFalse("KPM model mapping is not yet supported in single-platform projects", kpmModelMappingEnabled)
project.plugins.apply("org.jetbrains.kotlin.js") project.plugins.apply("org.jetbrains.kotlin.js")
val kotlin = jsExtension val kotlin = jsExtension
kotlin.js { nodejs() } kotlin.js { nodejs() }
@@ -215,7 +176,6 @@ class BuildKotlinToolingMetadataTest {
@Test @Test
fun jvm() { fun jvm() {
Assume.assumeFalse("KPM model mapping is not yet supported in single-platform projects", kpmModelMappingEnabled)
project.plugins.apply("org.jetbrains.kotlin.jvm") project.plugins.apply("org.jetbrains.kotlin.jvm")
val metadata = getKotlinToolingMetadata() val metadata = getKotlinToolingMetadata()
assertEquals(org.jetbrains.kotlin.gradle.plugin.KotlinPluginWrapper::class.java.canonicalName, metadata.buildPlugin) assertEquals(org.jetbrains.kotlin.gradle.plugin.KotlinPluginWrapper::class.java.canonicalName, metadata.buildPlugin)
@@ -242,45 +202,12 @@ class BuildKotlinToolingMetadataTest {
linuxX64.extras.native?.konanTarget linuxX64.extras.native?.konanTarget
) )
if (kpmModelMappingEnabled) { assertEquals(KotlinNativeTarget::class.java.canonicalName, linuxArm64.target)
assertEquals( assertEquals(KotlinNativeTargetWithHostTests::class.java.canonicalName, linuxX64.target)
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
)
}
} }
private fun getKotlinToolingMetadata(): KotlinToolingMetadata { private fun getKotlinToolingMetadata(): KotlinToolingMetadata {
if (!kpmModelMappingEnabled) { val task = project.buildKotlinToolingMetadataTask?.get() ?: error("No ${BuildKotlinToolingMetadataTask.defaultTaskName} task")
val task = project.buildKotlinToolingMetadataTask?.get() ?: error("No ${BuildKotlinToolingMetadataTask.defaultTaskName} task") return task.kotlinToolingMetadata
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),
)
} }
} }
@@ -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
)
}
}