[Gradle] Implement tests for External target publications
^KT-60159 Verification Pending
This commit is contained in:
committed by
Space Team
parent
06d5d2522c
commit
9f2c2b7265
+40
-2
@@ -6,12 +6,18 @@
|
||||
package org.jetbrains.kotlin.gradle.android
|
||||
|
||||
import org.gradle.util.GradleVersion
|
||||
import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinSourceDependency.Type.Regular
|
||||
import org.jetbrains.kotlin.gradle.idea.testFixtures.tcs.*
|
||||
import org.jetbrains.kotlin.gradle.idea.testFixtures.tcs.assertMatches
|
||||
import org.jetbrains.kotlin.gradle.idea.testFixtures.tcs.binaryCoordinates
|
||||
import org.jetbrains.kotlin.gradle.idea.testFixtures.tcs.dependsOnDependency
|
||||
import org.jetbrains.kotlin.gradle.idea.testFixtures.tcs.friendSourceDependency
|
||||
import org.jetbrains.kotlin.gradle.testbase.*
|
||||
import org.jetbrains.kotlin.gradle.util.jetbrainsAnnotationDependencies
|
||||
import org.jetbrains.kotlin.gradle.util.kotlinStdlibDependencies
|
||||
import org.jetbrains.kotlin.gradle.util.resolveIdeDependencies
|
||||
import org.junit.jupiter.api.io.TempDir
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.readText
|
||||
import kotlin.test.fail
|
||||
|
||||
@GradleTestVersions(minVersion = TestVersions.Gradle.G_8_1)
|
||||
@AndroidTestVersions(minVersion = TestVersions.AGP.AGP_82)
|
||||
@@ -81,4 +87,36 @@ class ExternalAndroidTargetIT : KGPBaseTest() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@GradleAndroidTest
|
||||
fun `test - simple project - pom dependencies rewritten`(
|
||||
gradleVersion: GradleVersion, androidVersion: String, jdkVersion: JdkVersions.ProvidedJdk, @TempDir localRepoDir: Path,
|
||||
) {
|
||||
project(
|
||||
"externalAndroidTarget-project2project",
|
||||
gradleVersion,
|
||||
buildOptions = defaultBuildOptions.copy(androidVersion = androidVersion),
|
||||
buildJdk = jdkVersion.location,
|
||||
localRepoDir = localRepoDir
|
||||
) {
|
||||
build("publish", buildOptions = buildOptions.copy(configurationCache = true)) {
|
||||
val pomFile = localRepoDir.resolve("app/app-android/1.0/app-android-1.0.pom")
|
||||
assertFileExists(pomFile)
|
||||
|
||||
fun String.removeWhiteSpaces() = replace("\\s+".toRegex(), "")
|
||||
val pomText = pomFile.readText()
|
||||
val expectedDependency = """
|
||||
<dependency>
|
||||
<groupId>sample</groupId>
|
||||
<artifactId>tcs-android</artifactId>
|
||||
<version>2.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
""".trimIndent()
|
||||
|
||||
if (expectedDependency.removeWhiteSpaces() !in pomText.removeWhiteSpaces())
|
||||
fail("Expected to find\n$expectedDependency\nIn POM file\n$pomText")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
plugins {
|
||||
kotlin("multiplatform")
|
||||
id("com.android.kotlin.multiplatform.library")
|
||||
`maven-publish`
|
||||
}
|
||||
|
||||
group = "app"
|
||||
version = "1.0"
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
linuxX64()
|
||||
linuxArm64()
|
||||
|
||||
androidLibrary {
|
||||
compileSdk = 33
|
||||
namespace = "org.jetbrains.sample"
|
||||
|
||||
withAndroidTestOnJvm()
|
||||
sourceSets.getByName("androidTestOnJvm").dependencies {
|
||||
implementation("junit:junit:4.13.2")
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
api(project(":lib"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
maven("<localRepo>")
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
plugins {
|
||||
kotlin("multiplatform")
|
||||
id("com.android.kotlin.multiplatform.library")
|
||||
`maven-publish`
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
linuxX64()
|
||||
linuxArm64()
|
||||
|
||||
androidLibrary {
|
||||
compileSdk = 33
|
||||
namespace = "org.jetbrains.sample"
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
maven("<localRepo>")
|
||||
}
|
||||
|
||||
publications {
|
||||
val android by getting {
|
||||
this as MavenPublication
|
||||
artifactId = "tcs-android"
|
||||
version = "2.0"
|
||||
groupId = "sample"
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package lib
|
||||
|
||||
fun commonMain() = 42
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
rootProject.name = "app"
|
||||
include(":lib")
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
|
||||
class AndroidMain(val context: Context) {
|
||||
fun useContext() {
|
||||
context.getSystemService(Context.LOCATION_SERVICE)
|
||||
}
|
||||
|
||||
fun useLog() {
|
||||
Log.d("test", CommonMain.toString())
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun useCommonMain() {
|
||||
println("useCommonMain: ${CommonMain}")
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import org.junit.Test
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
class AndroidTestOnJvm {
|
||||
@Test
|
||||
fun run() {
|
||||
AndroidMain.useCommonMain()
|
||||
org.junit.Assert.assertEquals("CommonMain", CommonMain.toString())
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
object CommonMain {
|
||||
override fun toString(): String {
|
||||
return "CommonMain"
|
||||
}
|
||||
}
|
||||
+127
-15
@@ -10,18 +10,18 @@ package org.jetbrains.kotlin.gradle.unitTests
|
||||
import org.gradle.api.attributes.Attribute
|
||||
import org.gradle.api.attributes.Category
|
||||
import org.gradle.api.attributes.DocsType
|
||||
import org.gradle.api.internal.component.SoftwareComponentInternal
|
||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.hierarchy.KotlinSourceSetTreeClassifier
|
||||
import org.jetbrains.kotlin.gradle.plugin.hierarchy.orNull
|
||||
import org.jetbrains.kotlin.gradle.plugin.launchInStage
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.external.ExternalKotlinCompilationDescriptor.CompilationAssociator
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.external.ExternalKotlinCompilationDescriptor.CompilationFactory
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.external.ExternalKotlinCompilationDescriptorBuilder
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.external.createCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.external.createExternalKotlinTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.internal
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.kotlinProjectStructureMetadata
|
||||
import org.jetbrains.kotlin.gradle.util.*
|
||||
import org.jetbrains.kotlin.gradle.utils.property
|
||||
import org.jetbrains.kotlin.gradle.utils.toMap
|
||||
@@ -35,15 +35,21 @@ class ExternalKotlinTargetApiTests {
|
||||
|
||||
|
||||
@Test
|
||||
fun `test - sourceSetClassifier - default`() = buildProjectWithMPP().runLifecycleAwareTest {
|
||||
fun `test - sourceSetClassifier - default`() = project.runLifecycleAwareTest {
|
||||
val target = kotlin.createExternalKotlinTarget<FakeTarget> { defaults() }
|
||||
val compilation = target.createCompilation<FakeCompilation> { defaults(kotlin) }
|
||||
val mainCompilation = target.createCompilation<FakeCompilation> { defaults(kotlin) }
|
||||
val fakeCompilation = target.createCompilation<FakeCompilation> {
|
||||
defaults(kotlin)
|
||||
compilationName = "fake"
|
||||
defaultSourceSet = kotlin.sourceSets.create("fake")
|
||||
}
|
||||
|
||||
assertEquals(KotlinSourceSetTree("fake"), KotlinSourceSetTree.orNull(compilation))
|
||||
assertEquals(KotlinSourceSetTree.main, KotlinSourceSetTree.orNull(mainCompilation))
|
||||
assertEquals(KotlinSourceSetTree("fake"), KotlinSourceSetTree.orNull(fakeCompilation))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - sourceSetClassifier - custom name`() = buildProjectWithMPP().runLifecycleAwareTest {
|
||||
fun `test - sourceSetClassifier - custom name`() = project.runLifecycleAwareTest {
|
||||
val target = kotlin.createExternalKotlinTarget<FakeTarget> { defaults() }
|
||||
val compilation = target.createCompilation<FakeCompilation> {
|
||||
defaults(kotlin)
|
||||
@@ -54,7 +60,8 @@ class ExternalKotlinTargetApiTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - sourceSetClassifier - custom property`() = buildProjectWithMPP().runLifecycleAwareTest {
|
||||
fun `test - sourceSetClassifier - custom property`() = project.runLifecycleAwareTest {
|
||||
val kotlin = multiplatformExtension
|
||||
val myProperty = project.objects.property<KotlinSourceSetTree>()
|
||||
val nullProperty = project.objects.property<KotlinSourceSetTree>()
|
||||
|
||||
@@ -132,8 +139,9 @@ class ExternalKotlinTargetApiTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - sourcesElements - default configuration`() = buildProjectWithMPP().runLifecycleAwareTest {
|
||||
fun `test - sourcesElements - default configuration`() = project.runLifecycleAwareTest {
|
||||
val target = kotlin.createExternalKotlinTarget<FakeTarget> { defaults() }
|
||||
target.createCompilation<FakeCompilation> { defaults(kotlin) }
|
||||
|
||||
assertNotEquals(target.sourcesElementsConfiguration, target.sourcesElementsPublishedConfiguration)
|
||||
|
||||
@@ -165,7 +173,7 @@ class ExternalKotlinTargetApiTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - sourcesElements - configure`() = buildProjectWithMPP().runLifecycleAwareTest {
|
||||
fun `test - sourcesElements - configure`() = project.runLifecycleAwareTest {
|
||||
val testAttribute = Attribute.of("for.test", String::class.java)
|
||||
|
||||
val target = kotlin.createExternalKotlinTarget<FakeTarget> {
|
||||
@@ -180,6 +188,7 @@ class ExternalKotlinTargetApiTests {
|
||||
configuration.attributes.attribute(testAttribute, "sourcesElements-published")
|
||||
}
|
||||
}
|
||||
target.createCompilation<FakeCompilation> { defaults(kotlin) }
|
||||
|
||||
/* Check traces left before */
|
||||
assertEquals("sourcesElements", target.sourcesElementsConfiguration.attributes.getAttribute(testAttribute))
|
||||
@@ -187,22 +196,125 @@ class ExternalKotlinTargetApiTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - sourcesElements - publication`() = buildProjectWithMPP().runLifecycleAwareTest {
|
||||
fun `test - sourcesElements - publication`() = project.runLifecycleAwareTest {
|
||||
val target = kotlin.createExternalKotlinTarget<FakeTarget> { defaults() }
|
||||
val component = target.delegate.components.singleOrNull() ?: fail("Expected single 'component' for external target")
|
||||
|
||||
component.usages.find { it.name == target.sourcesElementsPublishedConfiguration.name }
|
||||
// Main/publishable compilation is required to have usages
|
||||
target.createCompilation<FakeCompilation> { defaults(kotlin) }
|
||||
|
||||
KotlinPluginLifecycle.Stage.AfterFinaliseCompilations.await()
|
||||
val component = target.delegate.kotlinComponents.singleOrNull() ?: fail("Expected single 'component' for external target")
|
||||
|
||||
component.internal.usages.find { it.dependencyConfigurationName == target.sourcesElementsPublishedConfiguration.name }
|
||||
?: fail("Missing sourcesElements usage")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - sourcesElements - publication - withSourcesJar set to false`() = buildProjectWithMPP().runLifecycleAwareTest {
|
||||
fun `test - sourcesElements - publication - withSourcesJar set to false`() = project.runLifecycleAwareTest {
|
||||
val target = kotlin.createExternalKotlinTarget<FakeTarget> { defaults() }
|
||||
target.withSourcesJar(false)
|
||||
target.createCompilation<FakeCompilation> { defaults(kotlin) }
|
||||
|
||||
val component = target.delegate.components.singleOrNull() ?: fail("Expected single 'component' for external target")
|
||||
val sourcesUsage = component.usages.find { it.name.contains("sources", true) }
|
||||
if (sourcesUsage != null) {
|
||||
fail("Unexpected usage '${sourcesUsage.name} in target publication")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - gradle usage component contains the same usages as kotlin component`() = project.runLifecycleAwareTest {
|
||||
val target = kotlin.createExternalKotlinTarget<FakeTarget> { defaults() }
|
||||
target.createCompilation<FakeCompilation> { defaults(kotlin) }
|
||||
|
||||
val kotlinComponent = target.delegate.kotlinComponents.singleOrNull()
|
||||
?: fail("Expected single 'kotlinComponent' for external target")
|
||||
|
||||
val gradleComponent = target.delegate.components.singleOrNull()
|
||||
?: fail("Expected single 'component' for external target")
|
||||
|
||||
configurationResult.await()
|
||||
|
||||
val kotlinUsagesNames = kotlinComponent.internal.usages.map { it.dependencyConfigurationName }
|
||||
val gradleUsagesNames = (gradleComponent as SoftwareComponentInternal).usages.map { it.name }
|
||||
|
||||
if (kotlinUsagesNames.toSet() != gradleUsagesNames.toSet())
|
||||
fail("Kotlin Usages ($kotlinUsagesNames) from Kotlin Component didn't match Usages from Gradle Component ($gradleUsagesNames)")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - gradle usage component contains the same usages as kotlin component after project evaluation`() {
|
||||
val target = kotlin.createExternalKotlinTarget<FakeTarget> { defaults() }
|
||||
target.createCompilation<FakeCompilation> { defaults(kotlin) }
|
||||
project.evaluate()
|
||||
|
||||
val kotlinComponent = target.kotlinComponents.singleOrNull() ?: fail("Expected single 'kotlinComponent' for external target")
|
||||
val gradleComponent = target.components.singleOrNull() ?: fail("Expected single 'component' for external target")
|
||||
|
||||
if (kotlinComponent !is SoftwareComponentInternal) error("Expected 'kotlinComponent' to be 'SoftwareComponentInternal'")
|
||||
|
||||
// When kotlin usages is available corresponding gradle component usages should be also available
|
||||
val kotlinUsagesNames = kotlinComponent.usages.map { it.name }
|
||||
val gradleUsagesNames = gradleComponent.usages.map { it.name }
|
||||
|
||||
if (kotlinUsagesNames.toSet() != gradleUsagesNames.toSet())
|
||||
fail("Kotlin Usages ($kotlinUsagesNames) from Kotlin Component didn't match Usages from Gradle Component ($gradleUsagesNames)")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - project structure metadata contains external target variants`() {
|
||||
val target = kotlin.createExternalKotlinTarget<FakeTarget> { defaults() }
|
||||
val mainCompilation = target.createCompilation<FakeCompilation> { defaults(kotlin) }
|
||||
val testCompilation = target.createCompilation<FakeCompilation> {
|
||||
defaults(kotlin)
|
||||
compilationName = "test"
|
||||
defaultSourceSet = kotlin.sourceSets.create("fakeTest")
|
||||
}
|
||||
|
||||
kotlin.linuxX64()
|
||||
kotlin.macosX64()
|
||||
|
||||
fun KotlinHierarchyBuilder.withFakeTarget() = withCompilations { it == mainCompilation || it == testCompilation }
|
||||
kotlin.applyHierarchyTemplate {
|
||||
sourceSetTrees(KotlinSourceSetTree.main, KotlinSourceSetTree.test)
|
||||
common {
|
||||
group("linuxAndFake") {
|
||||
withLinux()
|
||||
withFakeTarget()
|
||||
}
|
||||
|
||||
withMacos()
|
||||
}
|
||||
}
|
||||
|
||||
project.evaluate()
|
||||
|
||||
val projectStructureMetadata = kotlin.kotlinProjectStructureMetadata
|
||||
|
||||
val fakeApiElementsSourceSets = projectStructureMetadata.sourceSetNamesByVariantName["fakeApiElements"]
|
||||
?: fail("Expected 'fakeApiElements' variant")
|
||||
assertEquals(setOf("commonMain", "linuxAndFakeMain"), fakeApiElementsSourceSets)
|
||||
|
||||
val fakeRuntimeElementsSourceSets = projectStructureMetadata.sourceSetNamesByVariantName["fakeRuntimeElements"]
|
||||
?: fail("Expected 'fakeRuntimeElements' variant")
|
||||
assertEquals(setOf("commonMain", "linuxAndFakeMain"), fakeRuntimeElementsSourceSets)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - published component contain user defined attributes`() {
|
||||
val userAttribute = Attribute.of("userAttribute", String::class.java)
|
||||
|
||||
val target = kotlin.createExternalKotlinTarget<FakeTarget> { defaults() }
|
||||
target.createCompilation<FakeCompilation> { defaults(kotlin) }
|
||||
target.attributes.attribute(userAttribute, "foo")
|
||||
|
||||
project.evaluate()
|
||||
|
||||
val component = target.internal.components.singleOrNull() ?: fail("Expected single component")
|
||||
if (component.usages.isEmpty()) fail("Expected at least one usage")
|
||||
component.usages.forEach { usage ->
|
||||
if (!usage.attributes.contains(userAttribute)) fail("Usage '${usage.name}' does not contain user attribute")
|
||||
assertEquals("foo", usage.attributes.getAttribute(userAttribute))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user