Importing test tasks and targets is implemented

This commit is contained in:
Andrey Uskov
2019-09-09 20:05:34 +03:00
parent e2f9eaa483
commit 715fad849d
19 changed files with 302 additions and 40 deletions
@@ -15,6 +15,7 @@ import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.util.Key
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.config.ExternalSystemTestTask
import org.jetbrains.kotlin.gradle.*
import org.jetbrains.kotlin.idea.util.CopyableDataNodeUserDataProperty
import org.jetbrains.plugins.gradle.util.GradleConstants
@@ -48,6 +49,7 @@ class KotlinSourceSetInfo @PropertyMapping("kotlinModule") constructor(val kotli
var isTestModule: Boolean = false
var sourceSetIdsByName: MutableMap<String, String> = LinkedHashMap()
var dependsOn: List<String> = emptyList()
var externalSystemTestTasks: Collection<ExternalSystemTestTask> = emptyList()
}
class KotlinAndroidSourceSetData @PropertyMapping("sourceSetInfos") constructor(val sourceSetInfos: List<KotlinSourceSetInfo>
@@ -15,6 +15,7 @@ import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.util.Key
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.config.ExternalSystemTestTask
import org.jetbrains.kotlin.gradle.*
import org.jetbrains.kotlin.idea.util.CopyableDataNodeUserDataProperty
import org.jetbrains.plugins.gradle.util.GradleConstants
@@ -44,6 +45,7 @@ class KotlinSourceSetInfo(val kotlinModule: KotlinModule) : Serializable {
var isTestModule: Boolean = false
var sourceSetIdsByName: MutableMap<String, String> = LinkedHashMap()
var dependsOn: List<String> = emptyList()
var externalSystemTestTasks: Collection<ExternalSystemTestTask> = emptyList()
}
class KotlinAndroidSourceSetData(
@@ -30,10 +30,12 @@ import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.ManualLanguageFeatureSetting
import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments
import org.jetbrains.kotlin.config.ExternalSystemTestTask
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.gradle.*
import org.jetbrains.kotlin.idea.configuration.GradlePropertiesFileFacade.Companion.KOTLIN_NOT_IMPORTED_COMMON_SOURCE_SETS_SETTING
import org.jetbrains.kotlin.idea.platform.IdePlatformKindTooling
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
import org.jetbrains.plugins.gradle.model.*
import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
@@ -259,6 +261,8 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
val sourceSetMap = projectDataNode.getUserData(GradleProjectResolver.RESOLVED_SOURCE_SETS)!!
val sourceSetToTestTasks = calculateTestTasks(mppModel, gradleModule, resolverCtx)
val sourceSetToCompilationData = LinkedHashMap<KotlinSourceSet, MutableSet<GradleSourceSetData>>()
for (target in mppModel.targets) {
if (target.platform == KotlinPlatform.ANDROID) continue
@@ -305,7 +309,13 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
it.sdkName = jdkName
}
val kotlinSourceSet = createSourceSetInfo(compilation, gradleModule, resolverCtx) ?: continue
val kotlinSourceSet = createSourceSetInfo(
compilation,
gradleModule,
resolverCtx
) ?: continue
kotlinSourceSet.externalSystemTestTasks =
compilation.sourceSets.firstNotNullResult { sourceSetToTestTasks[it] } ?: emptyList()
if (compilation.platform == KotlinPlatform.JVM || compilation.platform == KotlinPlatform.ANDROID) {
compilationData.targetCompatibility = (kotlinSourceSet.compilerArguments as? K2JVMCompilerArguments)?.jvmTarget
@@ -364,6 +374,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
}
val kotlinSourceSet = createSourceSetInfo(sourceSet, gradleModule, resolverCtx) ?: continue
kotlinSourceSet.externalSystemTestTasks = sourceSetToTestTasks[sourceSet] ?: emptyList()
val sourceSetDataNode =
(existingSourceSetDataNode ?: mainModuleNode.createChild(GradleSourceSetData.KEY, sourceSetData)).also {
@@ -383,6 +394,34 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
mainModuleNode.coroutines = mppModel.extraFeatures.coroutinesState
mainModuleNode.isHmpp = mppModel.extraFeatures.isHMPPEnabled
//TODO improve passing version of used multiplatform
}
private fun calculateTestTasks(
mppModel: KotlinMPPGradleModel,
gradleModule: IdeaModule,
resolverCtx: ProjectResolverContext
): Map<KotlinSourceSet, Collection<ExternalSystemTestTask>> {
val sourceSetToTestTasks: MutableMap<KotlinSourceSet, MutableCollection<ExternalSystemTestTask>> = HashMap()
val dependsOnReverseGraph: MutableMap<String, MutableSet<KotlinSourceSet>> = HashMap()
mppModel.targets.forEach { target ->
target.compilations.forEach { compilation ->
val testTasks = target.testTasks.filter { testTask -> testTask.compilationName == compilation.name }
.map { ExternalSystemTestTask(it.taskName, getKotlinModuleId(gradleModule, compilation, resolverCtx), target.name) }
compilation.sourceSets.forEach { sourceSet ->
sourceSetToTestTasks.getOrPut(sourceSet) { LinkedHashSet() } += testTasks
sourceSet.dependsOnSourceSets.forEach { dependentModule ->
dependsOnReverseGraph.getOrPut(dependentModule) { LinkedHashSet() } += sourceSet
}
}
}
}
mppModel.sourceSets.forEach { (sourceSetName, sourceSet) ->
dependsOnReverseGraph[sourceSetName]?.forEach { dependingSourceSet ->
sourceSetToTestTasks.getOrPut(sourceSet) { LinkedHashSet() } += sourceSetToTestTasks[dependingSourceSet] ?: emptyList()
}
}
return sourceSetToTestTasks
}
fun populateContentRoots(
@@ -567,7 +606,12 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
return ideModule.findChildModuleById(usedModuleId)
}
private fun createContentRootData(sourceDirs: Set<File>, sourceType: ExternalSystemSourceType, packagePrefix: String?, parentNode: DataNode<*>) {
private fun createContentRootData(
sourceDirs: Set<File>,
sourceType: ExternalSystemSourceType,
packagePrefix: String?,
parentNode: DataNode<*>
) {
for (sourceDir in sourceDirs) {
val contentRootData = ContentRootData(GradleConstants.SYSTEM_ID, sourceDir.absolutePath)
contentRootData.storePath(sourceType, sourceDir.absolutePath, packagePrefix)
@@ -652,7 +696,11 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
return PathUtilRt.suggestFileName(moduleName.toString(), true, false)
}
private fun createExternalSourceSet(compilation: KotlinCompilation, compilationData: GradleSourceSetData, mppModel: KotlinMPPGradleModel): ExternalSourceSet {
private fun createExternalSourceSet(
compilation: KotlinCompilation,
compilationData: GradleSourceSetData,
mppModel: KotlinMPPGradleModel
): ExternalSourceSet {
return DefaultExternalSourceSet().also { sourceSet ->
val effectiveClassesDir = compilation.output.effectiveClassesDir
val resourcesDir = compilation.output.resourcesDir
@@ -688,7 +736,11 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
}
private fun createExternalSourceSet(ktSourceSet: KotlinSourceSet, ktSourceSetData: GradleSourceSetData, mppModel: KotlinMPPGradleModel): ExternalSourceSet {
private fun createExternalSourceSet(
ktSourceSet: KotlinSourceSet,
ktSourceSetData: GradleSourceSetData,
mppModel: KotlinMPPGradleModel
): ExternalSourceSet {
return DefaultExternalSourceSet().also { sourceSet ->
sourceSet.name = ktSourceSet.name
sourceSet.targetCompatibility = ktSourceSetData.targetCompatibility
@@ -745,6 +797,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
}
// TODO: Unite with other createSourceSetInfo
// This method is used in Android side of import and it's signature could not be changed
fun createSourceSetInfo(
compilation: KotlinCompilation,
gradleModule: IdeaModule,
@@ -150,6 +150,7 @@ class KotlinSourceSetDataService : AbstractProjectDataService<GradleSourceSetDat
kind = kotlinSourceSet.kotlinModule.kind
isTestModule = kotlinSourceSet.isTestModule
externalSystemTestTasks = ArrayList(kotlinSourceSet.externalSystemTestTasks)
externalProjectId = kotlinSourceSet.gradleModuleId
@@ -348,6 +348,7 @@ class HierarchicalMultiplatformProjectImportingTest : MultiplePluginVersionGradl
exhaustiveModuleList,
exhaustiveSourceSourceRootList,
exhaustiveDependencyList,
false,
body
)
}
@@ -38,7 +38,7 @@ class KaptImportingTest : MultiplePluginVersionGradleImportingTestCase() {
configureByFiles()
importProject(true)
checkProjectStructure(myProject, projectPath, true, true, true) {
checkProjectStructure(myProject, projectPath, true, true, true, false) {
module("project")
module("project_main") {
sourceFolder("build/generated/source/kapt/main", JavaSourceRootType.SOURCE)
@@ -65,7 +65,7 @@ class KaptImportingTest : MultiplePluginVersionGradleImportingTestCase() {
configureByFiles()
importProject(false)
checkProjectStructure(myProject, projectPath, true, true, true) {
checkProjectStructure(myProject, projectPath, true, true, true, false) {
module("project") {
sourceFolder("build/generated/source/kapt/main", JavaSourceRootType.SOURCE)
sourceFolder("build/generated/source/kaptKotlin/main", JavaSourceRootType.SOURCE)
@@ -33,7 +33,8 @@ class NewMultiplatformKaptProjectImportingTest : MultiplePluginVersionGradleImpo
projectPath,
exhaustiveModuleList = true,
exhaustiveSourceSourceRootList = false,
exhaustiveDependencyList = false
exhaustiveDependencyList = false,
exhaustiveTestsList = false
) {
module("project")
@@ -395,6 +395,33 @@ class NewMultiplatformProjectImportingTest : MultiplePluginVersionGradleImportin
}
}
//TODObub(auskov): enable this test after publishing new api in gradle plugin
//@Test
fun testImportTestsAndTargets() {
configureByFiles()
importProject()
checkProjectStructure(exhaustiveSourceSourceRootList = false, exhaustiveDependencyList = false, exhaustiveTestsList = true) {
module("project")
module("project_commonMain")
module("project_commonTest") {
externalSystemTestTask("jsBrowserTest", "project:jsTest", "js")
externalSystemTestTask("jsNodeTest", "project:jsTest", "js")
externalSystemTestTask("test", "project:jvmTest", "jvm")
}
module("project_jsMain")
module("project_jsTest") {
externalSystemTestTask("jsBrowserTest", "project:jsTest", "js")
externalSystemTestTask("jsNodeTest", "project:jsTest", "js")
}
module("project_jvmMain")
module("project_jvmTest") {
externalSystemTestTask("test", "project:jvmTest", "jvm")
}
}
}
@Test
fun testDependencyOnRoot() {
configureByFiles()
@@ -773,6 +800,7 @@ class NewMultiplatformProjectImportingTest : MultiplePluginVersionGradleImportin
exhaustiveModuleList: Boolean = true,
exhaustiveSourceSourceRootList: Boolean = true,
exhaustiveDependencyList: Boolean = true,
exhaustiveTestsList: Boolean = false,
body: ProjectInfo.() -> Unit = {}
) {
checkProjectStructure(
@@ -781,6 +809,7 @@ class NewMultiplatformProjectImportingTest : MultiplePluginVersionGradleImportin
exhaustiveModuleList,
exhaustiveSourceSourceRootList,
exhaustiveDependencyList,
exhaustiveTestsList,
body)
}
@@ -27,7 +27,8 @@ class PackagePrefixImportingTest : MultiplePluginVersionGradleImportingTestCase(
projectPath,
exhaustiveModuleList = true,
exhaustiveSourceSourceRootList = true,
exhaustiveDependencyList = false
exhaustiveDependencyList = false,
exhaustiveTestsList = false
) {
module("project") {
}
@@ -14,11 +14,12 @@ import com.intellij.openapi.roots.impl.ModuleOrderEntryImpl
import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.jps.model.module.JpsModuleSourceRootType
import org.jetbrains.jps.util.JpsPathUtil
import org.jetbrains.kotlin.config.ExternalSystemTestTask
import org.jetbrains.kotlin.idea.facet.KotlinFacet
import org.jetbrains.kotlin.idea.facet.externalSystemTestTasks
import org.jetbrains.kotlin.idea.project.isHMPPEnabled
import org.jetbrains.kotlin.idea.project.languageVersionSettings
import org.jetbrains.kotlin.idea.project.platform
import org.jetbrains.kotlin.platform.SimplePlatform
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.presentableDescription
@@ -42,7 +43,8 @@ class ProjectInfo(
internal val projectPath: String,
internal val exhaustiveModuleList: Boolean,
internal val exhaustiveSourceSourceRootList: Boolean,
internal val exhaustiveDependencyList: Boolean
internal val exhaustiveDependencyList: Boolean,
internal val exhaustiveTestsList: Boolean
) {
internal val messageCollector = MessageCollector()
private val moduleManager = ModuleManager.getInstance(project)
@@ -88,6 +90,8 @@ class ModuleInfo(
private val rootModel = module.rootManager
private val expectedDependencyNames = HashSet<String>()
private val expectedSourceRoots = HashSet<String>()
private val expectedExternalSystemTestTasks = ArrayList<ExternalSystemTestTask>()
private val sourceFolderByPath by lazy {
rootModel.contentEntries.asSequence()
.flatMap { it.sourceFolders.asSequence() }
@@ -156,6 +160,10 @@ class ModuleInfo(
}
}
fun externalSystemTestTask(taskName: String, projectId: String, targetName: String) {
expectedExternalSystemTestTasks.add(ExternalSystemTestTask(taskName, projectId, targetName))
}
fun libraryDependency(libraryName: String, scope: DependencyScope) {
val libraryEntries = rootModel.orderEntries.filterIsInstance<LibraryOrderEntry>().filter { it.libraryName == libraryName }
if (libraryEntries.size > 1) {
@@ -271,6 +279,12 @@ class ModuleInfo(
}
}
if ((!module.externalSystemTestTasks().containsAll(expectedExternalSystemTestTasks)) || (projectInfo.exhaustiveTestsList && (module.externalSystemTestTasks() != expectedExternalSystemTestTasks))) {
projectInfo.messageCollector.report(
"Module '${module.name}': Expected tests list $expectedExternalSystemTestTasks doesn't match the actual one: ${module.externalSystemTestTasks()}"
)
}
if (projectInfo.exhaustiveSourceSourceRootList) {
val actualSourceRoots = sourceFolderByPath.keys.sorted()
val expectedSourceRoots = expectedSourceRoots.sorted()
@@ -319,6 +333,7 @@ fun checkProjectStructure(
exhaustiveModuleList: Boolean,
exhaustiveSourceSourceRootList: Boolean,
exhaustiveDependencyList: Boolean,
exhaustiveTestsList: Boolean,
body: ProjectInfo.() -> Unit = {}
) {
ProjectInfo(
@@ -326,6 +341,7 @@ fun checkProjectStructure(
projectPath,
exhaustiveModuleList,
exhaustiveSourceSourceRootList,
exhaustiveDependencyList
exhaustiveDependencyList,
exhaustiveTestsList
).run(body)
}
@@ -153,6 +153,18 @@ enum class KotlinMultiplatformVersion(val version: Int) {
get() = version >= 3
}
data class ExternalSystemTestTask(val testName: String, val externalSystemProjectId: String, val targetName: String?) {
fun toStringRepresentation() = "$testName|$externalSystemProjectId|$targetName"
companion object {
fun fromStringRepresentation(line: String) =
line.split("|").let { if (it.size == 3) ExternalSystemTestTask(it[0], it[1], it[2]) else null }
}
override fun toString() = "$testName@$externalSystemProjectId [$targetName]"
}
class KotlinFacetSettings {
companion object {
// Increment this when making serialization-incompatible changes to configuration data
@@ -219,6 +231,7 @@ class KotlinFacetSettings {
return field
}
var externalSystemTestTasks: List<ExternalSystemTestTask> = emptyList()
@Suppress("DEPRECATION_ERROR")
@Deprecated(
@@ -136,8 +136,11 @@ private fun readV2AndLaterConfig(element: Element): KotlinFacetSettings {
element.getAttributeValue("useProjectSettings")?.let { useProjectSettings = it.toBoolean() }
val targetPlatform = element.getFacetPlatformByConfigurationElement()
this.targetPlatform = targetPlatform
readModulesList(element, "implements", "implement")?.let { implementedModuleNames = it }
readModulesList(element, "dependsOnModuleNames", "dependsOn")?.let { dependsOnModuleNames = it }
readElementsList(element, "implements", "implement")?.let { implementedModuleNames = it }
readElementsList(element, "dependsOnModuleNames", "dependsOn")?.let { dependsOnModuleNames = it }
readElementsList(element, "externalSystemTestTasks", "externalSystemTestTask")?.let {
externalSystemTestTasks = it.mapNotNull { ExternalSystemTestTask.fromStringRepresentation(it) }
}
element.getChild("sourceSets")?.let {
val items = it.getChildren("sourceSet")
@@ -178,7 +181,7 @@ private fun readV2AndLaterConfig(element: Element): KotlinFacetSettings {
}
}
private fun readModulesList(element: Element, rootElementName: String, elementName: String): List<String>? {
private fun readElementsList(element: Element, rootElementName: String, elementName: String): List<String>? {
element.getChild(rootElementName)?.let {
val items = it.getChildren(elementName)
return if (items.isNotEmpty()) {
@@ -310,8 +313,8 @@ private fun KotlinFacetSettings.writeLatestConfig(element: Element) {
if (!useProjectSettings) {
element.setAttribute("useProjectSettings", useProjectSettings.toString())
}
saveModulesList(element, implementedModuleNames, "implements", "implement")
saveModulesList(element, dependsOnModuleNames, "dependsOnModuleNames", "dependsOn")
saveElementsList(element, implementedModuleNames, "implements", "implement")
saveElementsList(element, dependsOnModuleNames, "dependsOnModuleNames", "dependsOn")
if (sourceSetNames.isNotEmpty()) {
element.addContent(
@@ -330,6 +333,14 @@ private fun KotlinFacetSettings.writeLatestConfig(element: Element) {
if (isHmppEnabled) {
element.setAttribute("isHmppProject", isHmppEnabled.toString())
}
if (externalSystemTestTasks.isNotEmpty()) {
saveElementsList(
element,
externalSystemTestTasks.map { it.toStringRepresentation() },
"externalSystemTestTasks",
"externalSystemTestTask"
)
}
if (pureKotlinSourceFolders.isNotEmpty()) {
element.setAttribute("pureKotlinSourceFolders", pureKotlinSourceFolders.joinToString(";"))
}
@@ -354,15 +365,15 @@ private fun KotlinFacetSettings.writeLatestConfig(element: Element) {
}
}
private fun saveModulesList(element: Element, moduleList: List<String>, rootElementName: String, elementName: String) {
if (moduleList.isNotEmpty()) {
private fun saveElementsList(element: Element, elementsList: List<String>, rootElementName: String, elementName: String) {
if (elementsList.isNotEmpty()) {
element.addContent(
Element(rootElementName).apply {
val singleModule = moduleList.singleOrNull()
val singleModule = elementsList.singleOrNull()
if (singleModule != null) {
addContent(singleModule)
} else {
moduleList.map { addContent(Element(elementName).apply { addContent(it) }) }
elementsList.map { addContent(Element(elementName).apply { addContent(it) }) }
}
}
)
@@ -151,6 +151,7 @@ interface KotlinTarget : Serializable {
interface KotlinTestTask : Serializable {
val taskName: String
val compilationName: String
}
interface ExtraFeatures : Serializable {
@@ -15,6 +15,7 @@ import org.gradle.api.logging.Logging
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.Exec
import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.KotlinMPPGradleModel.Companion.NO_KOTLIN_NATIVE_HOME
import org.jetbrains.plugins.gradle.model.*
import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder
@@ -26,6 +27,9 @@ import java.io.File
import java.lang.reflect.Method
class KotlinMPPGradleModelBuilder : ModelBuilderService {
// This flag enables import of source sets which do not belong to any compilation
private val DEFAULT_IMPORT_ORPHAN_SOURCE_SETS = true
override fun getErrorMessageBuilder(project: Project, e: Exception): ErrorMessageBuilder {
return ErrorMessageBuilder
.create(project, e, "Gradle import errors")
@@ -53,7 +57,7 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
reportUnresolvedDependencies(targets)
val kotlinNativeHome = KotlinNativeHomeEvaluator.getKotlinNativeHome(project) ?: NO_KOTLIN_NATIVE_HOME
return KotlinMPPGradleModelImpl(
sourceSetMap,
filterOrphanSourceSets(sourceSetMap, targets, project),
targets,
ExtraFeaturesImpl(coroutinesState, isHMPPEnabled(project)),
kotlinNativeHome,
@@ -61,8 +65,21 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
)
}
private fun filterOrphanSourceSets(
sourceSets: Map<String, KotlinSourceSetImpl>,
targets: Collection<KotlinTarget>,
project: Project
): Map<String, KotlinSourceSetImpl> {
if (project.properties["import_orphan_source_sets"]?.toString()?.toBoolean() ?: DEFAULT_IMPORT_ORPHAN_SOURCE_SETS) return sourceSets
val compiledSourceSets: Collection<String> = targets.flatMap { it.compilations }.flatMap { it.sourceSets }.flatMap { it.dependsOnSourceSets.union(listOf(it.name)) }.distinct()
sourceSets.filter { !compiledSourceSets.contains(it.key) }.forEach {
logger.warn("[sync warning] Source set \"${it.key}\" is not compiled with any compilation. This source set is not imported in the IDE.")
}
return sourceSets.filter { compiledSourceSets.contains(it.key) }
}
private fun isHMPPEnabled(project: Project): Boolean {
//TODO(auskov): replace with Project.isKotlinGranularMetadataEnabled after merging with gradle pranch
//TODO(auskov): replace with Project.isKotlinGranularMetadataEnabled after merging with gradle branch
return (project.findProperty("kotlin.mpp.enableGranularSourceSetsMetadata") as? String)?.toBoolean() ?: false
}
@@ -314,7 +331,24 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
}
private fun buildTestTasks(project: Project, gradleTarget: Named): Collection<KotlinTestTask> {
// TODO: use the test runs API to extract the test tasks once it is available
val getTestRunsMethod = gradleTarget.javaClass.getMethodOrNull("getTestRuns")
if (getTestRunsMethod != null) {
val testRuns = getTestRunsMethod?.invoke(gradleTarget) as? Iterable<Any>
if (testRuns != null) {
val testReports = testRuns.mapNotNull { (it.javaClass.getMethodOrNull("getExecutionTask")?.invoke(it) as? TaskProvider<Task>)?.get() }
val testTasks = testReports.flatMap {
((it.javaClass.getMethodOrNull("getTestTasks")?.invoke(it) as? Collection<Provider<Task>>)?.map { it.get() })
?: listOf(it)
}
return testTasks.mapNotNull {
val name = it.name
val compilation = it.javaClass.getMethodOrNull("getCompilation")?.invoke(it)
val compilationName = compilation?.javaClass?.getMethodOrNull("getCompilationName")?.invoke(compilation)?.toString() ?: KotlinCompilation.TEST_COMPILATION_NAME
KotlinTestTaskImpl(name, compilationName)
}.toList()
}
return emptyList()
}
// Otherwise, find the Kotlin test task with names matching the target name. This is a workaround that makes assumptions about
// the tasks naming logic and is therefore an unstable and temporary solution until test runs API is implemented:
@@ -335,14 +369,23 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
// The 'targetName' of a test task matches the target disambiguation classifier, potentially with suffix, e.g. jsBrowser
val getTargetName = kotlinTestTaskClass.getDeclaredMethodOrNull("getTargetName") ?: return emptyList()
return project.tasks.filter { kotlinTestTaskClass.isInstance(it) }.mapNotNull { task ->
val testTaskDisambiguationClassifier = getTargetName(task) as String?
val jvmTestTaskClass = try {
gradleTarget.javaClass.classLoader.loadClass("org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmTest") as Class<out Task>
} catch (_: ClassNotFoundException) {
return emptyList()
}
val getJvmTargetName = jvmTestTaskClass.getDeclaredMethodOrNull("getTargetName") ?: return emptyList()
return project.tasks.filter { kotlinTestTaskClass.isInstance(it) || jvmTestTaskClass.isInstance(it)}.mapNotNull { task ->
val testTaskDisambiguationClassifier =
(if (kotlinTestTaskClass.isInstance(task)) getTargetName(task) else getJvmTargetName(task)) as String?
task.name.takeIf {
targetDisambiguationClassifier.isNullOrEmpty() ||
testTaskDisambiguationClassifier != null &&
testTaskDisambiguationClassifier.startsWith(targetDisambiguationClassifier.orEmpty())
}
}.map(::KotlinTestTaskImpl)
}.map { KotlinTestTaskImpl(it, KotlinCompilation.TEST_COMPILATION_NAME) }
}
private fun buildTargetJar(gradleTarget: Named, project: Project): KotlinTargetJar? {
@@ -544,21 +587,14 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
for (compilation in target.compilations) {
for (sourceSet in compilation.sourceSets) {
sourceSetToCompilations.getOrPut(sourceSet) { LinkedHashSet() } += compilation
sourceSet.dependsOnSourceSets.mapNotNull { sourceSets[it] }.forEach {
sourceSetToCompilations.getOrPut(it) { LinkedHashSet() } += compilation
}
}
}
}
for (sourceSet in sourceSets.values) {
val name = sourceSet.name
if (name == KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME) {
sourceSet.isTestModule = false
continue
}
if (name == KotlinSourceSet.COMMON_TEST_SOURCE_SET_NAME) {
sourceSet.isTestModule = true
continue
}
val compilations = sourceSetToCompilations[sourceSet]
if (compilations != null) {
val platforms = compilations.map { it.platform }
@@ -572,8 +608,16 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
sourceSet.isTestModule = compilations.all { it.isTestModule }
} else {
// TODO: change me after design about it
sourceSet.isTestModule = "Test" in sourceSet.name
//TODO(auskov): remove this branch as far as import of orphan source sets is dropped
val name = sourceSet.name
if (name == KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME) {
sourceSet.isTestModule = false
continue
}
if (name == KotlinSourceSet.COMMON_TEST_SOURCE_SET_NAME) {
sourceSet.isTestModule = true
continue
}
}
}
}
@@ -150,7 +150,7 @@ data class KotlinTargetImpl(
}
}.toList(),
target.testTasks.map { initialTestTask ->
(cloningCache[initialTestTask] as? KotlinTestTask) ?: KotlinTestTaskImpl(initialTestTask.taskName).also {
(cloningCache[initialTestTask] as? KotlinTestTask) ?: KotlinTestTaskImpl(initialTestTask.taskName, initialTestTask.compilationName).also {
cloningCache[initialTestTask] = it
}
},
@@ -160,7 +160,8 @@ data class KotlinTargetImpl(
}
data class KotlinTestTaskImpl(
override val taskName: String
override val taskName: String,
override val compilationName: String
) : KotlinTestTask
data class ExtraFeaturesImpl(
@@ -188,6 +188,8 @@ fun KotlinFacet.configureFacet(
module.externalCompilerVersion = compilerVersion
}
fun Module.externalSystemTestTasks() = KotlinFacetSettingsProvider.getInstance(project).getInitializedSettings(this).externalSystemTestTasks
@Suppress("DEPRECATION_ERROR", "DeprecatedCallableAddReplaceWith")
@Deprecated(
message = "IdePlatform is deprecated and will be removed soon, please, migrate to org.jetbrains.kotlin.platform.TargetPlatform",
@@ -0,0 +1,65 @@
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '1.3.50'
}
repositories {
mavenLocal()
maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
mavenCentral()
}
group 'com.example'
version '0.0.1'
apply plugin: 'maven-publish'
kotlin {
jvm {
}
js {
browser {
}
nodejs {
}
}
sourceSets {
commonMain {
dependencies {
implementation kotlin('stdlib-common')
}
}
commonTest {
dependencies {
implementation kotlin('test-common')
implementation kotlin('test-annotations-common')
}
}
jvmMain {
dependencies {
implementation kotlin('stdlib-jdk8')
}
}
jvmTest {
dependencies {
implementation kotlin('test')
implementation kotlin('test-junit')
}
}
jsMain {
dependencies {
implementation kotlin('stdlib-js')
}
}
jsTest {
dependencies {
implementation kotlin('test-js')
}
}
}
}
@@ -0,0 +1 @@
kotlin.code.style=official
@@ -0,0 +1,18 @@
pluginManagement {
repositories {
mavenLocal()
maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
mavenCentral()
maven { url 'https://plugins.gradle.org/m2/' }
}
}
rootProject.name = 'project'
enableFeaturePreview('GRADLE_METADATA')