diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinMPPGradleProjectResolver.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinMPPGradleProjectResolver.kt index 056c5a6c758..353332918fe 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinMPPGradleProjectResolver.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinMPPGradleProjectResolver.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2020 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. */ @@ -473,8 +473,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp val dependsOnReverseGraph: MutableMap> = HashMap() mppModel.targets.forEach { target -> target.compilations.forEach { compilation -> - val testRunTasks = target.testRunTasks - .filter { task -> task.compilationName == compilation.name } + val testRunTasks = target.testTasksFor(compilation) .map { ExternalSystemTestRunTask( it.taskName, @@ -1102,6 +1101,13 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp } } +private fun KotlinTarget.testTasksFor(compilation: KotlinCompilation) = testRunTasks.filter { task -> + when (name) { + "android" -> task.taskName.endsWith(compilation.name, true) + else -> task.compilationName == compilation.name + } +} + fun ProjectResolverContext.getMppModel(gradleModule: IdeaModule): KotlinMPPGradleModel? { val mppModel = this.getExtraProject(gradleModule, KotlinMPPGradleModel::class.java) return if (mppModel is Proxy) { diff --git a/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt b/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt index edded4c3878..3e38b88f4b1 100644 --- a/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt +++ b/idea/kotlin-gradle-tooling/src/KotlinMPPGradleModelBuilder.kt @@ -473,6 +473,15 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService { } } + private fun Named.testTaskClass(className: String) = try { + // 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: + @Suppress("UNCHECKED_CAST") + this.javaClass.classLoader.loadClass(className) as Class + } catch (_: ClassNotFoundException) { + null + } + private fun buildTestRunTasks(project: Project, gradleTarget: Named): Collection { val getTestRunsMethod = gradleTarget.javaClass.getMethodOrNull("getTestRuns") if (getTestRunsMethod != null) { @@ -501,14 +510,9 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService { 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: - @Suppress("UNCHECKED_CAST") - val kotlinTestTaskClass = try { - gradleTarget.javaClass.classLoader.loadClass("org.jetbrains.kotlin.gradle.tasks.KotlinTest") as Class - } catch (_: ClassNotFoundException) { - return emptyList() - } + // Otherwise, find the Kotlin/JVM test task with names matching the target name or + // aggregate Android JVM tasks (like testDebugUnitTest). + val kotlinTestTaskClass = gradleTarget.testTaskClass("org.jetbrains.kotlin.gradle.tasks.KotlinTest") ?: return emptyList() val targetDisambiguationClassifier = run { val getDisambiguationClassifier = gradleTarget.javaClass.getMethodOrNull("getDisambiguationClassifier") @@ -520,13 +524,16 @@ 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() - val jvmTestTaskClass = try { - gradleTarget.javaClass.classLoader.loadClass("org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmTest") as Class - } catch (_: ClassNotFoundException) { - return emptyList() - } + val jvmTestTaskClass = gradleTarget.testTaskClass("org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmTest") ?: return emptyList() val getJvmTargetName = jvmTestTaskClass.getDeclaredMethodOrNull("getTargetName") ?: return emptyList() + if (targetDisambiguationClassifier == "android") { + val androidUnitTestClass = gradleTarget.testTaskClass("com.android.build.gradle.tasks.factory.AndroidUnitTest") + ?: return emptyList() + + return project.tasks.filter { androidUnitTestClass.isInstance(it) }.mapNotNull { task -> task.name } + .map { KotlinTestRunTaskImpl(it, KotlinCompilation.TEST_COMPILATION_NAME) } + } return project.tasks.filter { kotlinTestTaskClass.isInstance(it) || jvmTestTaskClass.isInstance(it) }.mapNotNull { task -> val testTaskDisambiguationClassifier =