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)
}