Implement class/method run configurations for MPP projects

This commit is contained in:
Yan Zhulanow
2019-09-02 18:48:49 +09:00
parent 0a8f15c745
commit f408c249f1
21 changed files with 584 additions and 364 deletions
@@ -0,0 +1,136 @@
/*
* Copyright 2010-2019 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 org.jetbrains.kotlin.idea.run
import com.intellij.execution.Location
import com.intellij.execution.actions.ConfigurationContext
import com.intellij.execution.actions.ConfigurationFromContext
import com.intellij.execution.junit.InheritorChooser
import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration
import com.intellij.openapi.module.Module
import com.intellij.openapi.util.Ref
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import org.jetbrains.kotlin.idea.caches.project.isNewMPPModule
import org.jetbrains.kotlin.idea.project.platform
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.plugins.gradle.execution.test.runner.*
import org.jetbrains.plugins.gradle.service.execution.GradleRunConfiguration
import org.jetbrains.plugins.gradle.util.GradleConstants
import org.jetbrains.plugins.gradle.util.GradleExecutionSettingsUtil.createTestFilterFrom
abstract class AbstractKotlinMultiplatformTestClassGradleConfigurationProducer : AbstractKotlinTestClassGradleConfigurationProducer() {
override val forceGradleRunner: Boolean get() = true
override val hasTestFramework: Boolean get() = true
private val mppTestTasksChooser = MultiplatformTestTasksChooser()
abstract fun isApplicable(module: Module, platform: TargetPlatform): Boolean
final override fun isApplicable(module: Module): Boolean {
if (!module.isNewMPPModule) {
return false
}
val platform = module.platform ?: return false
return isApplicable(module, platform)
}
override fun isPreferredConfiguration(self: ConfigurationFromContext?, other: ConfigurationFromContext): Boolean {
return other.isJpsJunitConfiguration()
}
override fun shouldReplace(self: ConfigurationFromContext, other: ConfigurationFromContext): Boolean {
return other.isJpsJunitConfiguration()
}
override fun onFirstRun(fromContext: ConfigurationFromContext, context: ConfigurationContext, performRunnable: Runnable) {
val inheritorChooser: InheritorChooser = object : InheritorChooser() {
override fun runForClasses(classes: List<PsiClass>, method: PsiMethod?, context: ConfigurationContext, runnable: Runnable) {
chooseTestClassConfiguration(fromContext, context, runnable, classes)
}
override fun runForClass(aClass: PsiClass, psiMethod: PsiMethod?, context: ConfigurationContext, runnable: Runnable) {
chooseTestClassConfiguration(fromContext, context, runnable, listOf(aClass))
}
}
val sourceElement = fromContext.sourceElement as PsiClass
if (inheritorChooser.runMethodInAbstractClass(context, performRunnable, null, sourceElement)) {
return
}
chooseTestClassConfiguration(fromContext, context, performRunnable, listOf(sourceElement))
}
private fun chooseTestClassConfiguration(
fromContext: ConfigurationFromContext,
context: ConfigurationContext,
performRunnable: Runnable,
classes: List<PsiClass>
) {
val locationName = classes.singleOrNull()?.name
val dataContext = TestTasksChooser.contextWithLocationName(context.dataContext, locationName)
mppTestTasksChooser.multiplatformChooseTasks(context.project, dataContext, classes) { tasks ->
val configuration = fromContext.configuration as ExternalSystemRunConfiguration
val settings = configuration.settings
val createFilter = { clazz: PsiClass -> createTestFilterFrom(clazz, hasSuffix = true) }
if (!settings.applyTestConfiguration(context.module, tasks, classes, createFilter)) {
LOG.warn("Cannot apply class test configuration, uses raw run configuration")
performRunnable.run()
}
configuration.name = classes.joinToString("|") { it.name ?: "<error>" }
performRunnable.run()
}
}
}
abstract class AbstractKotlinTestClassGradleConfigurationProducer
: TestClassGradleConfigurationProducer(), KotlinGradleConfigurationProducer
{
override fun isConfigurationFromContext(configuration: ExternalSystemRunConfiguration, context: ConfigurationContext): Boolean {
if (!context.check()) {
return false
}
if (!forceGradleRunner) {
return super.isConfigurationFromContext(configuration, context)
}
if (GradleConstants.SYSTEM_ID != configuration.settings.externalSystemId) return false
return doIsConfigurationFromContext(configuration, context)
}
override fun setupConfigurationFromContext(
configuration: ExternalSystemRunConfiguration,
context: ConfigurationContext,
sourceElement: Ref<PsiElement>
): Boolean {
if (!context.check()) {
return false
}
if (!forceGradleRunner) {
return super.setupConfigurationFromContext(configuration, context, sourceElement)
}
if (GradleConstants.SYSTEM_ID != configuration.settings.externalSystemId) return false
if (sourceElement.isNull) return false
(configuration as? GradleRunConfiguration)?.isScriptDebugEnabled = false
return doSetupConfigurationFromContext(configuration, context, sourceElement)
}
private fun ConfigurationContext.check(): Boolean {
return hasTestFramework && module != null && isApplicable(module)
}
override fun getPsiClassForLocation(contextLocation: Location<*>) = getTestClassForKotlinTest(contextLocation)
override fun getPsiMethodForLocation(contextLocation: Location<*>) = getTestMethodForKotlinTest(contextLocation)
}
@@ -0,0 +1,139 @@
/*
* Copyright 2010-2019 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 org.jetbrains.kotlin.idea.run
import com.intellij.execution.Location
import com.intellij.execution.actions.ConfigurationContext
import com.intellij.execution.actions.ConfigurationFromContext
import com.intellij.execution.junit.InheritorChooser
import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration
import com.intellij.openapi.module.Module
import com.intellij.openapi.util.Ref
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import org.jetbrains.kotlin.idea.caches.project.isNewMPPModule
import org.jetbrains.kotlin.idea.project.platform
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer
import org.jetbrains.plugins.gradle.execution.test.runner.TestTasksChooser
import org.jetbrains.plugins.gradle.execution.test.runner.applyTestConfiguration
import org.jetbrains.plugins.gradle.service.execution.GradleRunConfiguration
import org.jetbrains.plugins.gradle.util.GradleConstants
import org.jetbrains.plugins.gradle.util.GradleExecutionSettingsUtil.createTestFilterFrom
abstract class AbstractKotlinMultiplatformTestMethodGradleConfigurationProducer : AbstractKotlinTestMethodGradleConfigurationProducer() {
override val forceGradleRunner: Boolean get() = true
override val hasTestFramework: Boolean get() = true
private val mppTestTasksChooser = MultiplatformTestTasksChooser()
abstract fun isApplicable(module: Module, platform: TargetPlatform): Boolean
final override fun isApplicable(module: Module): Boolean {
if (!module.isNewMPPModule) {
return false
}
val platform = module.platform ?: return false
return isApplicable(module, platform)
}
override fun isPreferredConfiguration(self: ConfigurationFromContext?, other: ConfigurationFromContext): Boolean {
return other.isJpsJunitConfiguration()
}
override fun shouldReplace(self: ConfigurationFromContext, other: ConfigurationFromContext): Boolean {
return other.isJpsJunitConfiguration()
}
override fun onFirstRun(fromContext: ConfigurationFromContext, context: ConfigurationContext, performRunnable: Runnable) {
val psiMethod = fromContext.sourceElement as PsiMethod
val psiClass = psiMethod.containingClass ?: return
val inheritorChooser = object : InheritorChooser() {
override fun runForClasses(classes: List<PsiClass>, method: PsiMethod?, context: ConfigurationContext, runnable: Runnable) {
chooseTestClassConfiguration(fromContext, context, runnable, psiMethod, *classes.toTypedArray())
}
override fun runForClass(aClass: PsiClass, psiMethod: PsiMethod, context: ConfigurationContext, runnable: Runnable) {
chooseTestClassConfiguration(fromContext, context, runnable, psiMethod, aClass)
}
}
if (inheritorChooser.runMethodInAbstractClass(context, performRunnable, psiMethod, psiClass)) return
chooseTestClassConfiguration(fromContext, context, performRunnable, psiMethod, psiClass)
}
private fun chooseTestClassConfiguration(
fromContext: ConfigurationFromContext,
context: ConfigurationContext,
performRunnable: Runnable,
psiMethod: PsiMethod,
vararg classes: PsiClass
) {
val dataContext = TestTasksChooser.contextWithLocationName(context.dataContext, psiMethod.name)
mppTestTasksChooser.multiplatformChooseTasks(context.project, dataContext, classes.asList()) { tasks ->
val configuration = fromContext.configuration as ExternalSystemRunConfiguration
val settings = configuration.settings
val result = settings.applyTestConfiguration(context.module, tasks, *classes) {
createTestFilterFrom(context.location, it, psiMethod, true)
}
if (result) {
configuration.name = (if (classes.size == 1) classes[0].name!! + "." else "") + psiMethod.name
performRunnable.run()
} else {
LOG.warn("Cannot apply method test configuration, uses raw run configuration")
performRunnable.run()
}
}
}
}
abstract class AbstractKotlinTestMethodGradleConfigurationProducer
: TestMethodGradleConfigurationProducer(), KotlinGradleConfigurationProducer
{
override fun isConfigurationFromContext(configuration: ExternalSystemRunConfiguration, context: ConfigurationContext): Boolean {
if (!context.check()) {
return false
}
if (!forceGradleRunner) {
return super.isConfigurationFromContext(configuration, context)
}
if (GradleConstants.SYSTEM_ID != configuration.settings.externalSystemId) return false
return doIsConfigurationFromContext(configuration, context)
}
override fun setupConfigurationFromContext(
configuration: ExternalSystemRunConfiguration,
context: ConfigurationContext,
sourceElement: Ref<PsiElement>
): Boolean {
if (!context.check()) {
return false
}
if (!forceGradleRunner) {
return super.setupConfigurationFromContext(configuration, context, sourceElement)
}
if (GradleConstants.SYSTEM_ID != configuration.settings.externalSystemId) return false
if (sourceElement.isNull) return false
(configuration as? GradleRunConfiguration)?.isScriptDebugEnabled = false
return doSetupConfigurationFromContext(configuration, context, sourceElement)
}
private fun ConfigurationContext.check(): Boolean {
return hasTestFramework && module != null && isApplicable(module)
}
override fun getPsiMethodForLocation(contextLocation: Location<*>) = getTestMethodForKotlinTest(contextLocation)
}
@@ -0,0 +1,14 @@
/*
* Copyright 2010-2019 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 org.jetbrains.kotlin.idea.run
import com.intellij.openapi.module.Module
interface KotlinGradleConfigurationProducer {
val forceGradleRunner: Boolean
val hasTestFramework: Boolean
fun isApplicable(module: Module): Boolean
}
@@ -1,135 +0,0 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.run
import com.intellij.execution.Location
import com.intellij.execution.actions.ConfigurationContext
import com.intellij.execution.actions.ConfigurationFromContext
import com.intellij.ide.plugins.PluginManager
import com.intellij.openapi.extensions.PluginId.getId
import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration
import com.intellij.openapi.util.Ref
import com.intellij.openapi.util.component1
import com.intellij.openapi.util.component2
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import org.jetbrains.kotlin.idea.caches.project.isMPPModule
import org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer
import org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer
private val IS_JUNIT_ENABLED by lazy { isPluginEnabled("JUnit") }
private val IS_TESTNG_ENABLED by lazy { isPluginEnabled("TestNG-J") }
private val IS_TEST_FRAMEWORK_PLUGIN_ENABLED by lazy { IS_JUNIT_ENABLED || IS_TESTNG_ENABLED }
private fun isPluginEnabled(id: String): Boolean {
return PluginManager.isPluginInstalled(getId(id)) && id !in PluginManager.getDisabledPlugins()
}
private fun getTestClass(leaf: PsiElement): PsiClass? {
if (IS_JUNIT_ENABLED) {
KotlinJUnitRunConfigurationProducer.getTestClass(leaf)?.let { return it }
}
if (IS_TESTNG_ENABLED) {
KotlinTestNgConfigurationProducer.getTestClassAndMethod(leaf)?.let { (testClass, testMethod) ->
return if (testMethod == null) testClass else null
}
}
return null
}
private fun getTestMethod(leaf: PsiElement): PsiMethod? {
if (IS_JUNIT_ENABLED) {
KotlinJUnitRunConfigurationProducer.getTestMethod(leaf)?.let { return it }
}
if (IS_TESTNG_ENABLED) {
KotlinTestNgConfigurationProducer.getTestClassAndMethod(leaf)?.second?.let { return it }
}
return null
}
class KotlinTestClassGradleConfigurationProducer : TestClassGradleConfigurationProducer() {
override fun doSetupConfigurationFromContext(
configuration: ExternalSystemRunConfiguration,
context: ConfigurationContext,
sourceElement: Ref<PsiElement>
): Boolean {
if (!IS_TEST_FRAMEWORK_PLUGIN_ENABLED) return false
context.module?.asJvmModule() ?: return false
return super.doSetupConfigurationFromContext(configuration, context, sourceElement)
}
override fun getPsiClassForLocation(contextLocation: Location<*>): PsiClass? {
val leaf = contextLocation.psiElement ?: return null
return getTestClass(leaf)
}
override fun getPsiMethodForLocation(contextLocation: Location<*>): PsiMethod? {
val leaf = contextLocation.psiElement ?: return null
return getTestMethod(leaf)
}
override fun onFirstRun(fromContext: ConfigurationFromContext, context: ConfigurationContext, performRunnable: Runnable) {
if (context.location?.module?.isMPPModule == true) {
// TODO: remove hack when IDEA has new API
performRunnable.run()
} else {
super.onFirstRun(fromContext, context, performRunnable)
}
}
override fun doIsConfigurationFromContext(configuration: ExternalSystemRunConfiguration, context: ConfigurationContext): Boolean {
if (!IS_TEST_FRAMEWORK_PLUGIN_ENABLED) return false
context.module?.asJvmModule() ?: return false
return super.doIsConfigurationFromContext(configuration, context)
}
}
class KotlinTestMethodGradleConfigurationProducer : TestMethodGradleConfigurationProducer() {
override fun doSetupConfigurationFromContext(
configuration: ExternalSystemRunConfiguration,
context: ConfigurationContext,
sourceElement: Ref<PsiElement>
): Boolean {
if (!IS_TEST_FRAMEWORK_PLUGIN_ENABLED) return false
context.module?.asJvmModule() ?: return false
return super.doSetupConfigurationFromContext(configuration, context, sourceElement)
}
override fun getPsiMethodForLocation(contextLocation: Location<*>): PsiMethod? {
val leaf = contextLocation.psiElement ?: return null
return getTestMethod(leaf)
}
override fun onFirstRun(fromContext: ConfigurationFromContext, context: ConfigurationContext, performRunnable: Runnable) {
if (context.location?.module?.isMPPModule == true) {
// TODO: remove hack when IDEA has new API
performRunnable.run()
} else {
super.onFirstRun(fromContext, context, performRunnable)
}
}
override fun doIsConfigurationFromContext(configuration: ExternalSystemRunConfiguration, context: ConfigurationContext): Boolean {
if (!IS_TEST_FRAMEWORK_PLUGIN_ENABLED) return false
context.module?.asJvmModule() ?: return false
return super.doIsConfigurationFromContext(configuration, context)
}
}
@@ -1,221 +0,0 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.run
import com.intellij.execution.actions.ConfigurationContext
import com.intellij.execution.actions.RunConfigurationProducer
import com.intellij.execution.junit.PatternConfigurationProducer
import com.intellij.ide.plugins.PluginManager
import com.intellij.openapi.extensions.PluginId.getId
import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.util.Ref
import com.intellij.openapi.util.component1
import com.intellij.openapi.util.component2
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer
import org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer
import org.jetbrains.plugins.gradle.util.GradleConstants
private val IS_JUNIT_ENABLED by lazy { isPluginEnabled("JUnit") }
private val IS_TESTNG_ENABLED by lazy { isPluginEnabled("TestNG-J") }
private val IS_TEST_FRAMEWORK_PLUGIN_ENABLED by lazy { IS_JUNIT_ENABLED || IS_TESTNG_ENABLED }
private fun isPluginEnabled(id: String): Boolean {
return PluginManager.isPluginInstalled(getId(id)) && id !in PluginManager.getDisabledPlugins()
}
private fun getTestClass(leaf: PsiElement): PsiClass? {
if (IS_JUNIT_ENABLED) {
KotlinJUnitRunConfigurationProducer.getTestClass(leaf)?.let { return it }
}
if (IS_TESTNG_ENABLED) {
KotlinTestNgConfigurationProducer.getTestClassAndMethod(leaf)?.let { (testClass, testMethod) ->
return if (testMethod == null) testClass else null
}
}
return null
}
private fun getTestMethod(leaf: PsiElement): PsiMethod? {
if (IS_JUNIT_ENABLED) {
KotlinJUnitRunConfigurationProducer.getTestMethod(leaf)?.let { return it }
}
if (IS_TESTNG_ENABLED) {
KotlinTestNgConfigurationProducer.getTestClassAndMethod(leaf)?.second?.let { return it }
}
return null
}
class KotlinTestClassGradleConfigurationProducer : TestClassGradleConfigurationProducer() {
override fun doSetupConfigurationFromContext(
configuration: ExternalSystemRunConfiguration,
context: ConfigurationContext,
sourceElement: Ref<PsiElement>
): Boolean {
if (!IS_TEST_FRAMEWORK_PLUGIN_ENABLED) return false
val contextLocation = context.location ?: return false
val module = context.module?.asJvmModule() ?: return false
if (RunConfigurationProducer.getInstance(PatternConfigurationProducer::class.java).isMultipleElementsSelected(context)) {
return false
}
val leaf = context.location?.psiElement ?: return false
val testClass = getTestClass(leaf) ?: return false
sourceElement.set(testClass)
if (!ExternalSystemApiUtil.isExternalSystemAwareModule(GradleConstants.SYSTEM_ID, module)) return false
val projectPath = resolveProjectPath(module) ?: return false
val tasksToRun = getTasksToRun(module)
if (tasksToRun.isEmpty()) return false
configuration.settings.externalProjectPath = projectPath
configuration.settings.taskNames = tasksToRun
configuration.settings.scriptParameters = String.format("--tests \"%s\"", testClass.qualifiedName)
configuration.name = testClass.name ?: "unknown"
JavaRunConfigurationExtensionManagerUtil.getInstance().extendCreatedConfiguration(configuration, contextLocation)
return true
}
override fun doIsConfigurationFromContext(configuration: ExternalSystemRunConfiguration, context: ConfigurationContext): Boolean {
if (!IS_TEST_FRAMEWORK_PLUGIN_ENABLED) return false
val leaf = context.location?.psiElement ?: return false
val module = context.module?.asJvmModule() ?: return false
if (RunConfigurationProducer.getInstance(PatternConfigurationProducer::class.java).isMultipleElementsSelected(context)) {
return false
}
if (getTestMethod(leaf) != null) return false
val testClass = getTestClass(leaf)
if (testClass == null || testClass.qualifiedName == null) return false
val projectPath = resolveProjectPath(module) ?: return false
if (projectPath != configuration.settings.externalProjectPath) {
return false
}
if (!configuration.settings.taskNames.containsAll(getTasksToRun(module))) return false
val scriptParameters = configuration.settings.scriptParameters + ' '
val i = scriptParameters.indexOf("--tests ")
if (i == -1) return false
val str = scriptParameters.substringAfter("--tests ").trim() + ' '
return str.startsWith("\"" + testClass.qualifiedName + "\"" + ' ') && !str.contains("--tests")
}
}
class KotlinTestMethodGradleConfigurationProducer : TestMethodGradleConfigurationProducer() {
override fun doSetupConfigurationFromContext(
configuration: ExternalSystemRunConfiguration,
context: ConfigurationContext,
sourceElement: Ref<PsiElement>
): Boolean {
if (!IS_TEST_FRAMEWORK_PLUGIN_ENABLED) return false
val contextLocation = context.location ?: return false
context.module?.asJvmModule() ?: return false
if (RunConfigurationProducer.getInstance(PatternConfigurationProducer::class.java).isMultipleElementsSelected(context)) {
return false
}
val psiMethod = getTestMethod(contextLocation.psiElement) ?: return false
sourceElement.set(psiMethod)
val containingClass = psiMethod.containingClass ?: return false
if (!applyTestMethodConfiguration(configuration, context, psiMethod, containingClass)) return false
JavaRunConfigurationExtensionManagerUtil.getInstance().extendCreatedConfiguration(configuration, contextLocation)
return true
}
override fun doIsConfigurationFromContext(configuration: ExternalSystemRunConfiguration, context: ConfigurationContext): Boolean {
if (!IS_TEST_FRAMEWORK_PLUGIN_ENABLED) return false
if (RunConfigurationProducer.getInstance(PatternConfigurationProducer::class.java).isMultipleElementsSelected(context)) {
return false
}
val contextLocation = context.location ?: return false
val module = context.module?.asJvmModule() ?: return false
val psiMethod = getTestMethod(contextLocation.psiElement) ?: return false
val containingClass = psiMethod.containingClass ?: return false
val projectPath = resolveProjectPath(module) ?: return false
if (projectPath != configuration.settings.externalProjectPath) {
return false
}
if (!configuration.settings.taskNames.containsAll(getTasksToRun(module))) return false
val scriptParameters = configuration.settings.scriptParameters + ' '
val testFilter = createTestFilter(containingClass, psiMethod)
return scriptParameters.contains(testFilter!!)
}
private fun applyTestMethodConfiguration(
configuration: ExternalSystemRunConfiguration,
context: ConfigurationContext,
psiMethod: PsiMethod,
vararg containingClasses: PsiClass
): Boolean {
val module = context.module ?: return false
if (!ExternalSystemApiUtil.isExternalSystemAwareModule(GradleConstants.SYSTEM_ID, module)) return false
val projectPath = resolveProjectPath(module) ?: return false
val tasksToRun = getTasksToRun(module)
if (tasksToRun.isEmpty()) return false
configuration.settings.externalProjectPath = projectPath
configuration.settings.taskNames = tasksToRun
val params = containingClasses.joinToString("") { aClass -> createTestFilter(aClass, psiMethod) ?: "" }
configuration.settings.scriptParameters = params.trim()
configuration.name = (if (containingClasses.size == 1) containingClasses[0].name + "." else "") + psiMethod.name
return true
}
companion object {
private fun createTestFilter(aClass: PsiClass, psiMethod: PsiMethod): String? {
return createTestFilter(aClass.qualifiedName, psiMethod.name)
}
fun createTestFilter(aClass: String?, method: String?): String? {
if (aClass == null) return null
val testFilterPattern = aClass + if (method == null) "" else '.' + method
return "--tests \"$testFilterPattern\" "
}
}
}
@@ -0,0 +1,21 @@
/*
* Copyright 2010-2019 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 org.jetbrains.kotlin.idea.run
import com.intellij.execution.Location
import com.intellij.openapi.module.Module
import org.jetbrains.kotlin.idea.caches.project.isNewMPPModule
import org.jetbrains.kotlin.idea.project.platform
import org.jetbrains.kotlin.platform.jvm.isJvm
class KotlinJvmTestClassGradleConfigurationProducer : AbstractKotlinTestClassGradleConfigurationProducer() {
override val forceGradleRunner get() = false
override val hasTestFramework get() = canRunJvmTests()
override fun isApplicable(module: Module) = module.platform.isJvm() && !module.isNewMPPModule
override fun getPsiClassForLocation(contextLocation: Location<*>) = getTestClassForJvm(contextLocation)
override fun getPsiMethodForLocation(contextLocation: Location<*>) = getTestMethodForJvm(contextLocation)
}
@@ -0,0 +1,20 @@
/*
* Copyright 2010-2019 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 org.jetbrains.kotlin.idea.run
import com.intellij.execution.Location
import com.intellij.openapi.module.Module
import org.jetbrains.kotlin.idea.caches.project.isNewMPPModule
import org.jetbrains.kotlin.idea.project.platform
import org.jetbrains.kotlin.platform.jvm.isJvm
class KotlinJvmTestMethodGradleConfigurationProducer : AbstractKotlinTestMethodGradleConfigurationProducer() {
override val forceGradleRunner get() = false
override val hasTestFramework get() = canRunJvmTests()
override fun isApplicable(module: Module) = module.platform.isJvm() && !module.isNewMPPModule
override fun getPsiMethodForLocation(contextLocation: Location<*>) = getTestMethodForJvm(contextLocation)
}
@@ -50,7 +50,8 @@ class KotlinMPPGradleTestTasksProvider : GradleTestTasksProvider {
}
private fun isMultiplatformTestModule(module: Module): Boolean {
val settings = KotlinFacetSettingsProvider.getInstance(module.project).getInitializedSettings(module)
val settingsProvider = KotlinFacetSettingsProvider.getInstance(module.project) ?: return false
val settings = settingsProvider.getInitializedSettings(module)
return settings.isMPPModule && settings.isTestModule
}
@@ -0,0 +1,19 @@
/*
* Copyright 2010-2019 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 org.jetbrains.kotlin.idea.run
import com.intellij.execution.Location
import com.intellij.openapi.module.Module
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.jvm.isJvm
class KotlinMultiplatformJvmTestClassGradleConfigurationProducer : AbstractKotlinMultiplatformTestClassGradleConfigurationProducer() {
override val forceGradleRunner get() = forceGradleRunnerInMPP()
override fun isApplicable(module: Module, platform: TargetPlatform) = platform.isJvm()
override fun getPsiClassForLocation(contextLocation: Location<*>) = getTestClassForJvm(contextLocation)
override fun getPsiMethodForLocation(contextLocation: Location<*>) = getTestMethodForJvm(contextLocation)
}
@@ -0,0 +1,18 @@
/*
* Copyright 2010-2019 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 org.jetbrains.kotlin.idea.run
import com.intellij.execution.Location
import com.intellij.openapi.module.Module
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.jvm.isJvm
class KotlinMultiplatformJvmTestMethodGradleConfigurationProducer : AbstractKotlinMultiplatformTestMethodGradleConfigurationProducer() {
override val forceGradleRunner get() = forceGradleRunnerInMPP()
override fun isApplicable(module: Module, platform: TargetPlatform) = platform.isJvm()
override fun getPsiMethodForLocation(contextLocation: Location<*>) = getTestMethodForJvm(contextLocation)
}
@@ -0,0 +1,94 @@
/*
* Copyright 2010-2019 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 org.jetbrains.kotlin.idea.run
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.ExternalSystemTestTask
import org.jetbrains.kotlin.idea.facet.externalSystemTestTasks
import org.jetbrains.kotlin.idea.util.module
import org.jetbrains.plugins.gradle.execution.test.runner.*
import org.jetbrains.plugins.gradle.util.TasksToRun
import java.util.*
import java.util.function.Consumer
class MultiplatformTestTasksChooser : TestTasksChooser() {
fun multiplatformChooseTasks(
project: Project,
dataContext: DataContext,
elements: Iterable<PsiElement>,
handler: (List<Map<SourcePath, TestTasks>>) -> Unit
) {
val consumer = Consumer<List<Map<SourcePath, TestTasks>>> { handler(it) }
val testTasks = resolveTestTasks(elements)
when {
testTasks.isEmpty() -> super.chooseTestTasks(project, dataContext, elements, consumer)
testTasks.size == 1 -> consumer.accept(testTasks.values.toList())
else -> chooseTestTasks(project, dataContext, testTasks, consumer)
}
}
private fun resolveTestTasks(elements: Iterable<PsiElement>): Map<TestName, Map<SourcePath, TasksToRun>> {
val tasks = mutableMapOf<TestName, MutableMap<SourcePath, TasksToRun>>()
for (element in elements) {
val module = element.module ?: continue
val sourceFile = getSourceFile(element) ?: continue
val groupedTasks = module.externalSystemTestTasks().groupBy { it.targetName }
for ((group, tasksInGroup) in groupedTasks) {
if (tasksInGroup.isEmpty()) {
continue
} else if (tasksInGroup.size == 1) {
val task = tasksInGroup[0]
val presentableName = task.presentableName
val tasksMap = tasks.getOrPut(presentableName) { LinkedHashMap() }
tasksMap[sourceFile.path] = TasksToRun.Impl(presentableName, getTaskNames(task))
} else {
for (task in tasksInGroup) {
val rawTaskName = ':' + task.testName
val presentableName = if (group != null) "$group ($rawTaskName)" else rawTaskName
val tasksMap = tasks.getOrPut(presentableName) { LinkedHashMap() }
tasksMap[sourceFile.path] = TasksToRun.Impl(presentableName, getTaskNames(task))
}
}
}
}
return tasks
}
override fun chooseTestTasks(
project: Project,
context: DataContext,
testTasks: Map<TestName, Map<SourcePath, TasksToRun>>,
consumer: Consumer<List<Map<SourcePath, TestTasks>>>
) {
if (ApplicationManager.getApplication().isUnitTestMode) {
val result = mutableListOf<Map<SourcePath, TestTasks>>()
for (tasks in testTasks.values) {
result += tasks.mapValues { it.value }
}
consumer.accept(result)
return
}
super.chooseTestTasks(project, context, testTasks, consumer)
}
private fun getTaskNames(task: ExternalSystemTestTask): List<String> {
return listOf("clean" + task.testName.capitalize(), task.testName)
}
}
private val ExternalSystemTestTask.presentableName: String
get() = targetName ?: (":$testName")
@@ -0,0 +1,57 @@
/*
* Copyright 2010-2019 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 org.jetbrains.kotlin.idea.run
import com.intellij.execution.Location
import com.intellij.execution.actions.ConfigurationFromContext
import com.intellij.execution.junit.JUnitConfigurationProducer
import com.intellij.execution.testframework.AbstractPatternBasedConfigurationProducer
import com.intellij.ide.plugins.PluginManager
import com.intellij.openapi.extensions.PluginId.getId
import com.intellij.openapi.util.component1
import com.intellij.openapi.util.component2
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiMethod
private val isJUnitEnabled by lazy { isPluginEnabled("JUnit") }
private val isTestNgEnabled by lazy { isPluginEnabled("TestNG-J") }
private fun isPluginEnabled(id: String): Boolean {
return PluginManager.isPluginInstalled(getId(id)) && id !in PluginManager.getDisabledPlugins()
}
internal fun ConfigurationFromContext.isJpsJunitConfiguration(): Boolean {
return isProducedBy(JUnitConfigurationProducer::class.java)
|| isProducedBy(AbstractPatternBasedConfigurationProducer::class.java)
}
internal fun canRunJvmTests() = isJUnitEnabled || isTestNgEnabled
internal fun getTestClassForJvm(location: Location<*>): PsiClass? {
val leaf = location.psiElement ?: return null
if (isJUnitEnabled) {
KotlinJUnitRunConfigurationProducer.getTestClass(leaf)?.let { return it }
}
if (isTestNgEnabled) {
KotlinTestNgConfigurationProducer.getTestClassAndMethod(leaf)?.let { (testClass, testMethod) ->
return if (testMethod == null) testClass else null
}
}
return null
}
internal fun getTestMethodForJvm(location: Location<*>): PsiMethod? {
val leaf = location.psiElement ?: return null
if (isJUnitEnabled) {
KotlinJUnitRunConfigurationProducer.getTestMethod(leaf)?.let { return it }
}
if (isTestNgEnabled) {
KotlinTestNgConfigurationProducer.getTestClassAndMethod(leaf)?.second?.let { return it }
}
return null
}
@@ -0,0 +1,29 @@
/*
* Copyright 2010-2019 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 org.jetbrains.kotlin.idea.run
import com.intellij.execution.Location
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiMethod
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
internal fun getTestMethodForKotlinTest(location: Location<*>): PsiMethod? {
val leaf = location.psiElement
val function = leaf?.getParentOfType<KtNamedFunction>(false) ?: return null
val owner = function.getParentOfType<KtDeclaration>(true) as? KtClass ?: return null
val delegate = owner.toLightClass() ?: return null
return delegate.methods.firstOrNull { it.navigationElement == function } ?: return null
}
internal fun getTestClassForKotlinTest(location: Location<*>): PsiClass? {
val leaf = location.psiElement
val owner = leaf?.getParentOfType<KtDeclaration>(true) as? KtClass ?: return null
return owner.toLightClass()
}