[Gradle][KPM] Implement AbstractIdeFragmentDependenciesTest

- Split IdeFragmentDependencyResolverTest into
  - KpmProjectToKpmProjectIdeFragmentDependenciesTest
  - KpmProjectToMppProjectIdeFragmentDependenciesTest

- Implement secondary constructor for SourceLocalIdeFragmentDependency
This commit is contained in:
sebastian.sellmair
2021-12-07 13:18:16 +01:00
committed by Space
parent 6129d920c7
commit 5b6180ba58
6 changed files with 378 additions and 183 deletions
@@ -0,0 +1,78 @@
/*
* 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.
*/
@file:OptIn(InternalIdeApi::class)
package org.jetbrains.kotlin.gradle.ide
import org.gradle.api.Project
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.testfixtures.ProjectBuilder
import org.jetbrains.kotlin.gradle.MultiplatformExtensionTest
import org.jetbrains.kotlin.gradle.applyMultiplatformPlugin
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.pm20Extension
import org.jetbrains.kotlin.gradle.kpm.applyKpmPlugin
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.ide.*
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragment
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.currentBuildId
@OptIn(InternalIdeApi::class)
abstract class AbstractIdeFragmentDependenciesTest {
val rootProject = ProjectBuilder.builder().build() as ProjectInternal
private val ideFragmentDependencyResolvers: MutableMap<Project, IdeFragmentDependencyResolver> = mutableMapOf()
private fun getIdeFragmentDependencyResolver(project: Project) = ideFragmentDependencyResolvers.getOrPut(project) {
IdeFragmentDependencyResolver.create(project)
}
internal fun resolveIdeDependenciesList(fragment: KotlinGradleFragment): List<IdeFragmentDependency> {
return getIdeFragmentDependencyResolver(fragment.project).resolveDependencies(fragment)
}
internal fun resolveIdeDependenciesSet(fragment: KotlinGradleFragment): Set<IdeFragmentDependency> {
return resolveIdeDependenciesList(fragment).toSet()
}
}
internal inline fun AbstractIdeFragmentDependenciesTest.createKpmProject(
name: String, configure: KotlinPm20ProjectExtension.() -> Unit
): KotlinPm20ProjectExtension {
val project = ProjectBuilder.builder().withName(name).withParent(rootProject).build() as ProjectInternal
project.applyKpmPlugin()
project.pm20Extension.configure()
return project.pm20Extension
}
internal inline fun AbstractIdeFragmentDependenciesTest.createMultiplatformProject(
name: String, configure: KotlinMultiplatformExtension.() -> Unit
): KotlinMultiplatformExtension {
val project = ProjectBuilder.builder().withName(name).withParent(rootProject).build() as ProjectInternal
project.applyMultiplatformPlugin()
project.multiplatformExtension.configure()
return project.multiplatformExtension
}
internal fun ideLocalSourceDependenciesListOf(vararg fragment: KotlinGradleFragment):
List<IdeLocalSourceFragmentDependency> = listOf(*fragment).map { it.toIdeFragmentDependency() }
internal fun ideLocalSourceDependenciesSetOf(vararg fragment: KotlinGradleFragment):
Set<IdeLocalSourceFragmentDependency> = ideLocalSourceDependenciesListOf(*fragment).toSet()
fun KotlinGradleFragment.toIdeFragmentDependency(): IdeLocalSourceFragmentDependency {
return IdeLocalSourceFragmentDependency(
buildId = project.currentBuildId(),
projectPath = project.path,
projectName = project.name,
kotlinModuleName = containingModule.name,
kotlinFragmentName = name
)
}
@@ -1,181 +0,0 @@
/*
* 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.
*/
@file:Suppress("FunctionName")
@file:OptIn(InternalIdeApi::class)
package org.jetbrains.kotlin.gradle.ide
import org.gradle.api.Project
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.testfixtures.ProjectBuilder
import org.jetbrains.kotlin.gradle.applyMultiplatformPlugin
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.pm20Extension
import org.jetbrains.kotlin.gradle.enableHierarchicalStructureByDefault
import org.jetbrains.kotlin.gradle.kpm.applyKpmPlugin
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.*
import org.jetbrains.kotlin.gradle.plugin.ide.IdeLocalSourceFragmentDependency
import org.jetbrains.kotlin.gradle.plugin.ide.IdeFragmentDependencyResolver
import org.jetbrains.kotlin.gradle.plugin.ide.InternalIdeApi
import kotlin.test.Test
import kotlin.test.assertEquals
class IdeFragmentDependencyResolverTest {
@Test
fun `test kpm project to project dependency`() {
/* Setup project p1 & p2 */
val project = ProjectBuilder.builder().build() as ProjectInternal
val p1 = ProjectBuilder.builder().withName("p1").withParent(project).build() as ProjectInternal
val p2 = ProjectBuilder.builder().withName("p2").withParent(project).build() as ProjectInternal
p1.applyKpmPlugin()
p2.applyKpmPlugin()
p2.pm20Extension.apply {
mainAndTest {
setupKotlinVariantsForTest()
}
}
p1.pm20Extension.apply {
mainAndTest {
setupKotlinVariantsForTest()
dependencies {
api(project(":p2"))
}
}
}
/* Test resolver */
val resolver = IdeFragmentDependencyResolver.create(p1)
assertEquals(
setOf(p2.newSourceDependency("main", "common")),
resolver.resolveDependencies("main", "common").toSet()
)
assertEquals(
setOf(
p2.newSourceDependency("main", "common"),
p2.newSourceDependency("main", "native")
),
resolver.resolveDependencies("main", "native").toSet()
)
assertEquals(
setOf(
p2.newSourceDependency("main", "common"),
p2.newSourceDependency("main", "native"),
p2.newSourceDependency("main", "ios")
),
resolver.resolveDependencies("main", "ios").toSet()
)
assertEquals(
setOf(
p2.newSourceDependency("main", "common"),
p2.newSourceDependency("main", "native"),
p2.newSourceDependency("main", "linuxX64")
),
resolver.resolveDependencies("main", "linuxX64").toSet()
)
}
@Test
fun `test kpm project to mpp project dependency`() {
/* Setup project p1 & p2 */
val project = ProjectBuilder.builder().build() as ProjectInternal
val p1 = ProjectBuilder.builder().withName("p1").withParent(project).build() as ProjectInternal
val p2 = ProjectBuilder.builder().withName("p2").withParent(project).build() as ProjectInternal
project.enableHierarchicalStructureByDefault()
p2.enableHierarchicalStructureByDefault()
p2.enableHierarchicalStructureByDefault()
p1.applyKpmPlugin()
p2.applyMultiplatformPlugin()
p2.multiplatformExtension.setupKotlinTargetsForTest()
p1.pm20Extension.apply {
mainAndTest {
setupKotlinVariantsForTest()
dependencies {
api(project(":p2"))
}
}
}
project.evaluate()
p2.evaluate()
p1.evaluate()
/* Test resolver */
val resolver = IdeFragmentDependencyResolver.create(p1)
resolver.resolveDependencies("main", "common").toSet()
resolver.resolveDependencies("main", "native").toSet()
resolver.resolveDependencies("main", "ios").toSet()
resolver.resolveDependencies("main", "linuxX64").toSet()
/* TODO Assert non-empty dependencies
assertEquals(
setOf(p2.newSourceDependency("main", "common")),
resolver.resolveDependencies("main", "common").toSet()
)
*/
}
private fun KotlinGradleModule.setupKotlinVariantsForTest() {
/* Variants */
jvm
val iosX64 = fragments.create("iosX64", KotlinIosX64Variant::class.java)
val iosArm64 = fragments.create("iosArm64", KotlinIosArm64Variant::class.java)
val linuxX64 = fragments.create("linuxX64", KotlinLinuxX64Variant::class.java)
/* Shared fragments */
val ios = fragments.create("ios")
val native = fragments.create("native")
ios.refines(common)
native.refines(common)
iosX64.refines(ios)
iosArm64.refines(ios)
ios.refines(native)
linuxX64.refines(native)
}
private fun KotlinMultiplatformExtension.setupKotlinTargetsForTest() {
jvm()
iosX64()
iosArm64()
linuxX64()
val commonMain = sourceSets.getByName("commonMain")
val iosMain = sourceSets.create("iosMain")
val nativeMain = sourceSets.create("nativeMain")
val iosX64Main = sourceSets.getByName("iosX64Main")
val iosArm64Main = sourceSets.getByName("iosArm64Main")
val linuxX64Main = sourceSets.getByName("linuxX64Main")
iosX64Main.dependsOn(iosMain)
iosArm64Main.dependsOn(iosMain)
iosMain.dependsOn(nativeMain)
nativeMain.dependsOn(commonMain)
linuxX64Main.dependsOn(nativeMain)
nativeMain.dependsOn(commonMain)
}
private fun Project.newSourceDependency(
kotlinModuleName: String, kotlinFragmentName: String
) = IdeLocalSourceFragmentDependency(
buildId = project.currentBuildId(),
projectPath = path,
projectName = name,
kotlinModuleName = kotlinModuleName,
kotlinFragmentName = kotlinFragmentName
)
}
@@ -0,0 +1,196 @@
/*
* 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.
*/
@file:Suppress("FunctionName")
package org.jetbrains.kotlin.gradle.ide
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.*
import kotlin.test.Test
import kotlin.test.assertEquals
class KpmProjectToKpmProjectIdeFragmentDependenciesTest : AbstractIdeFragmentDependenciesTest() {
@Test
fun `sample - with jvm and native targets`() {
fun KotlinPm20ProjectExtension.setupFragmentsForTest() = mainAndTest {
/* Variants */
jvm
val iosX64 = fragments.create("iosX64", KotlinIosX64Variant::class.java)
val iosArm64 = fragments.create("iosArm64", KotlinIosArm64Variant::class.java)
val linuxX64 = fragments.create("linuxX64", KotlinLinuxX64Variant::class.java)
/* Shared fragments */
val ios = fragments.create("ios")
val native = fragments.create("native")
/* Refines edges */
ios.refines(common)
native.refines(common)
iosX64.refines(ios)
iosArm64.refines(ios)
ios.refines(native)
linuxX64.refines(native)
}
val p1 = createKpmProject("p1") {
setupFragmentsForTest()
}
val p2 = createKpmProject("p2") {
setupFragmentsForTest()
mainAndTest {
dependencies {
api(project(":p1"))
}
}
}
/*
Assert :p2 to :p1 dependencies for the main module
*/
assertEquals(
ideLocalSourceDependenciesSetOf(p1.main.common),
resolveIdeDependenciesSet(p2.main.common)
)
assertEquals(
ideLocalSourceDependenciesSetOf(
p1.main.common,
p1.main.fragments.getByName("native"),
p1.main.fragments.getByName("ios")
),
resolveIdeDependenciesSet(p2.main.fragments.getByName("ios"))
)
assertEquals(
ideLocalSourceDependenciesSetOf(
p1.main.common,
p1.main.fragments.getByName("native"),
p1.main.fragments.getByName("ios"),
p1.main.fragments.getByName("iosX64")
),
resolveIdeDependenciesSet(p2.main.fragments.getByName("iosX64"))
)
assertEquals(
ideLocalSourceDependenciesSetOf(
p1.main.common,
p1.main.fragments.getByName("native"),
p1.main.fragments.getByName("ios"),
p1.main.fragments.getByName("iosArm64")
),
resolveIdeDependenciesSet(p2.main.fragments.getByName("iosArm64"))
)
assertEquals(
ideLocalSourceDependenciesSetOf(
p1.main.common,
p1.main.fragments.getByName("native"),
p1.main.fragments.getByName("linuxX64")
),
resolveIdeDependenciesSet(p2.main.fragments.getByName("linuxX64"))
)
assertEquals(
ideLocalSourceDependenciesSetOf(
p1.main.common,
p1.main.fragments.getByName("jvm")
),
resolveIdeDependenciesSet(p2.main.fragments.getByName("jvm"))
)
/*
Assert :p2 to :p1 dependencies for the test module
*/
assertEquals(
ideLocalSourceDependenciesSetOf(
p1.main.common,
p2.main.common
),
resolveIdeDependenciesSet(p2.test.common)
)
assertEquals(
ideLocalSourceDependenciesSetOf(
p1.main.common,
p1.main.fragments.getByName("jvm"),
p2.main.common,
p2.main.fragments.getByName("jvm")
),
resolveIdeDependenciesSet(p2.test.fragments.getByName("jvm"))
)
assertEquals(
ideLocalSourceDependenciesSetOf(
p1.main.common,
p1.main.fragments.getByName("native"),
p2.main.common,
p2.main.fragments.getByName("native")
),
resolveIdeDependenciesSet(p2.test.fragments.getByName("native"))
)
assertEquals(
ideLocalSourceDependenciesSetOf(
p1.main.common,
p1.main.fragments.getByName("native"),
p1.main.fragments.getByName("ios"),
p2.main.common,
p2.main.fragments.getByName("native"),
p2.main.fragments.getByName("ios")
),
resolveIdeDependenciesSet(p2.test.fragments.getByName("ios"))
)
assertEquals(
ideLocalSourceDependenciesSetOf(
p1.main.common,
p1.main.fragments.getByName("native"),
p1.main.fragments.getByName("ios"),
p1.main.fragments.getByName("iosX64"),
p2.main.common,
p2.main.fragments.getByName("native"),
p2.main.fragments.getByName("ios"),
p2.main.fragments.getByName("iosX64")
),
resolveIdeDependenciesSet(p2.test.fragments.getByName("iosX64"))
)
assertEquals(
ideLocalSourceDependenciesSetOf(
p1.main.common,
p1.main.fragments.getByName("native"),
p1.main.fragments.getByName("ios"),
p1.main.fragments.getByName("iosArm64"),
p2.main.common,
p2.main.fragments.getByName("native"),
p2.main.fragments.getByName("ios"),
p2.main.fragments.getByName("iosArm64")
),
resolveIdeDependenciesSet(p2.test.fragments.getByName("iosArm64"))
)
assertEquals(
ideLocalSourceDependenciesSetOf(
p1.main.common,
p1.main.fragments.getByName("native"),
p1.main.fragments.getByName("linuxX64"),
p2.main.common,
p2.main.fragments.getByName("native"),
p2.main.fragments.getByName("linuxX64")
),
resolveIdeDependenciesSet(p2.test.fragments.getByName("linuxX64"))
)
}
}
@@ -0,0 +1,79 @@
/*
* 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.
*/
@file:Suppress("FunctionName", "DuplicatedCode")
@file:OptIn(InternalIdeApi::class)
package org.jetbrains.kotlin.gradle.ide
import org.jetbrains.kotlin.gradle.plugin.ide.IdeLocalSourceFragmentDependency
import org.jetbrains.kotlin.gradle.plugin.ide.InternalIdeApi
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinIosArm64Variant
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinIosX64Variant
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinLinuxX64Variant
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.jvm
import kotlin.test.Test
import kotlin.test.assertEquals
class KpmProjectToMppProjectIdeFragmentDependenciesTest : AbstractIdeFragmentDependenciesTest() {
@Test
fun `sample - with jvm and native targets`() {
val mpp = createMultiplatformProject("mpp") {
jvm()
iosX64()
iosArm64()
linuxX64()
val commonMain = sourceSets.getByName("commonMain")
val iosMain = sourceSets.create("iosMain")
val nativeMain = sourceSets.create("nativeMain")
val iosX64Main = sourceSets.getByName("iosX64Main")
val iosArm64Main = sourceSets.getByName("iosArm64Main")
val linuxX64Main = sourceSets.getByName("linuxX64Main")
iosX64Main.dependsOn(iosMain)
iosArm64Main.dependsOn(iosMain)
iosMain.dependsOn(nativeMain)
nativeMain.dependsOn(commonMain)
linuxX64Main.dependsOn(nativeMain)
nativeMain.dependsOn(commonMain)
}
val kpm = createKpmProject("kpm") {
mainAndTest {
jvm
val iosX64 = fragments.create("iosX64", KotlinIosX64Variant::class.java)
val iosArm64 = fragments.create("iosArm64", KotlinIosArm64Variant::class.java)
val linuxX64 = fragments.create("linuxX64", KotlinLinuxX64Variant::class.java)
val ios = fragments.create("ios")
val native = fragments.create("native")
ios.refines(common)
native.refines(common)
iosX64.refines(ios)
iosArm64.refines(ios)
ios.refines(native)
linuxX64.refines(native)
}
}
resolveIdeDependenciesSet(kpm.main.common)
resolveIdeDependenciesSet(kpm.main.fragments.getByName("ios"))
resolveIdeDependenciesSet(kpm.main.fragments.getByName("iosX64"))
resolveIdeDependenciesSet(kpm.main.fragments.getByName("iosArm64"))
resolveIdeDependenciesSet(kpm.main.fragments.getByName("linuxX64"))
resolveIdeDependenciesSet(kpm.main.fragments.getByName("jvm"))
/* TODO */
runCatching {
assertEquals(
setOf(IdeLocalSourceFragmentDependency(mpp.project, "main", "common")),
resolveIdeDependenciesSet(kpm.main.common)
)
}
}
}
@@ -7,8 +7,10 @@
package org.jetbrains.kotlin.gradle.plugin.ide
import org.gradle.api.Project
import org.gradle.api.artifacts.component.BuildIdentifier
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragment
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.currentBuildId
import java.io.File
/**
@@ -29,7 +31,19 @@ data class IdeLocalSourceFragmentDependency(
val projectName: String,
val kotlinModuleName: String,
val kotlinFragmentName: String
) : IdeFragmentDependency()
) : IdeFragmentDependency() {
constructor(
project: Project,
kotlinModuleName: String,
kotlinFragmentName: String
) : this(
buildId = project.currentBuildId(),
projectPath = project.path,
projectName = project.name,
kotlinModuleName = kotlinModuleName,
kotlinFragmentName = kotlinFragmentName
)
}
/**
* Binary dependency provided by a (maven) repository
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.project.model.LocalModuleIdentifier
*/
@InternalIdeApi
interface IdeFragmentDependencyResolver {
val project: Project
fun resolveDependencies(moduleName: String, fragmentName: String): List<IdeFragmentDependency>
companion object {
@@ -35,8 +36,16 @@ interface IdeFragmentDependencyResolver {
}
}
internal fun IdeFragmentDependencyResolver.resolveDependencies(kotlinGradleFragment: KotlinGradleFragment): List<IdeFragmentDependency> {
require(kotlinGradleFragment.project == project) {
"IdeFragmentDependencyResolver for project ${project.path} can't resolve dependencies for foreign fragment " +
"${kotlinGradleFragment.project.path}:${kotlinGradleFragment.containingModule.name}/${kotlinGradleFragment.name}"
}
return resolveDependencies(kotlinGradleFragment.containingModule.name, kotlinGradleFragment.name)
}
private class IdeFragmentDependencyResolverImpl(
private val project: Project,
override val project: Project,
private val fragmentGranularMetadataResolverFactory: FragmentGranularMetadataResolverFactory
) : IdeFragmentDependencyResolver {
override fun resolveDependencies(moduleName: String, fragmentName: String): List<IdeFragmentDependency> {