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()
}
@@ -23,6 +23,7 @@ import com.intellij.psi.PsiMember
import com.intellij.psi.PsiMethod
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.idea.caches.project.isNewMPPModule
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtFile
@@ -35,6 +36,11 @@ class KotlinJUnitRunConfigurationProducer : RunConfigurationProducer<JUnitConfig
return other.isProducedBy(JUnitConfigurationProducer::class.java) || other.isProducedBy(AbstractPatternBasedConfigurationProducer::class.java)
}
private fun isAvailableInMpp(context: ConfigurationContext): Boolean {
val module = context.module
return module == null || !module.isNewMPPModule || !forceGradleRunnerInMPP()
}
override fun isConfigurationFromContext(
configuration: JUnitConfiguration,
context: ConfigurationContext
@@ -43,6 +49,10 @@ class KotlinJUnitRunConfigurationProducer : RunConfigurationProducer<JUnitConfig
return false
}
if (!isAvailableInMpp(context)) {
return false
}
val leaf = context.location?.psiElement ?: return false
val method = getTestMethod(leaf)
val testClass = getTestClass(leaf)
@@ -75,6 +85,10 @@ class KotlinJUnitRunConfigurationProducer : RunConfigurationProducer<JUnitConfig
): Boolean {
if (DumbService.getInstance(context.project).isDumb) return false
if (!isAvailableInMpp(context)) {
return false
}
val location = context.location ?: return false
val leaf = location.psiElement
val module = context.module?.asJvmModule() ?: return false
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.idea.run
import com.intellij.openapi.module.Module
import com.intellij.openapi.util.registry.Registry
import org.jetbrains.kotlin.idea.project.platform
import org.jetbrains.kotlin.platform.jvm.isJvm
@@ -15,3 +16,4 @@ fun Module.asJvmModule(): Module? {
return null
}
fun forceGradleRunnerInMPP() = Registry.`is`("kotlin.mpp.tests.force.gradle", true)
+4 -2
View File
@@ -25,8 +25,10 @@
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinNativeLibraryDataService"/>
<externalSystemTaskNotificationListener implementation="org.jetbrains.kotlin.idea.core.script.ReloadGradleTemplatesOnSync"/>
<runConfigurationProducer implementation="org.jetbrains.kotlin.idea.run.KotlinTestClassGradleConfigurationProducer"/>
<runConfigurationProducer implementation="org.jetbrains.kotlin.idea.run.KotlinTestMethodGradleConfigurationProducer"/>
<runConfigurationProducer implementation="org.jetbrains.kotlin.idea.run.KotlinJvmTestClassGradleConfigurationProducer"/>
<runConfigurationProducer implementation="org.jetbrains.kotlin.idea.run.KotlinMultiplatformJvmTestClassGradleConfigurationProducer"/>
<runConfigurationProducer implementation="org.jetbrains.kotlin.idea.run.KotlinJvmTestMethodGradleConfigurationProducer"/>
<runConfigurationProducer implementation="org.jetbrains.kotlin.idea.run.KotlinMultiplatformJvmTestMethodGradleConfigurationProducer"/>
</extensions>
+2 -2
View File
@@ -24,8 +24,8 @@
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.KotlinJavaMPPSourceSetDataService"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinNativeLibraryDataService"/>
<externalSystemTaskNotificationListener implementation="org.jetbrains.kotlin.idea.core.script.ReloadGradleTemplatesOnSync"/>
<runConfigurationProducer implementation="org.jetbrains.kotlin.idea.run.KotlinTestClassGradleConfigurationProducer"/>
<runConfigurationProducer implementation="org.jetbrains.kotlin.idea.run.KotlinTestMethodGradleConfigurationProducer"/>
<runConfigurationProducer implementation="org.jetbrains.kotlin.idea.run.KotlinJvmTestClassGradleConfigurationProducer"/>
<runConfigurationProducer implementation="org.jetbrains.kotlin.idea.run.KotlinJvmTestMethodGradleConfigurationProducer"/>
<orderEnumerationHandlerFactory implementation="org.jetbrains.kotlin.android.KotlinAndroidGradleOrderEnumerationHandler$FactoryImpl" order="first"/>
</extensions>
+2 -2
View File
@@ -25,8 +25,8 @@
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinNativeLibraryDataService"/>
<externalSystemTaskNotificationListener implementation="org.jetbrains.kotlin.idea.core.script.ReloadGradleTemplatesOnSync"/>
<runConfigurationProducer implementation="org.jetbrains.kotlin.idea.run.KotlinTestClassGradleConfigurationProducer"/>
<runConfigurationProducer implementation="org.jetbrains.kotlin.idea.run.KotlinTestMethodGradleConfigurationProducer"/>
<runConfigurationProducer implementation="org.jetbrains.kotlin.idea.run.KotlinJvmTestClassGradleConfigurationProducer"/>
<runConfigurationProducer implementation="org.jetbrains.kotlin.idea.run.KotlinJvmTestMethodGradleConfigurationProducer"/>
</extensions>
+5
View File
@@ -22,6 +22,11 @@
<projectTaskRunner implementation="org.jetbrains.kotlin.idea.gradle.execution.KotlinMPPGradleProjectTaskRunner" order="first, before gradle"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinSourceSetDataService"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleProjectSettingsDataService"/>
<registryKey key="kotlin.mpp.tests.force.gradle"
description="Run multi-platform tests with Gradle runner even if the platform runner is used by default.
This setting currently affects only HMPP projects. You may need to delete existing test configurations for the change to take place."
defaultValue="true"
restartRequired="false"/>
</extensions>
<actions>
+5
View File
@@ -21,6 +21,11 @@
<projectTaskRunner implementation="org.jetbrains.kotlin.idea.gradle.execution.KotlinMPPGradleProjectTaskRunner" order="first, before gradle"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinSourceSetDataService"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.idea.configuration.KotlinGradleProjectSettingsDataService"/>
<registryKey key="kotlin.mpp.tests.force.gradle"
description="Run multi-platform tests with Gradle runner even if the platform runner is used by default.
This setting currently affects only HMPP projects. You may need to delete existing test configurations for the change to take place."
defaultValue="true"
restartRequired="false"/>
</extensions>
<actions>
@@ -15,7 +15,7 @@ interface KotlinJSRunConfigurationData {
val jsOutputFilePath: String
}
interface KotlinJSRunConfigurationDataProvider<out T : KotlinJSRunConfigurationData> {
interface KotlinJSRunConfigurationDataProvider<out T : Any> {
val isForTests: Boolean
fun getConfigurationData(context: ConfigurationContext): T?
}