From 853e9a2aa041ec55c7548afce7c36ca4d7e913ff Mon Sep 17 00:00:00 2001 From: "Pavel V. Talanov" Date: Wed, 27 Feb 2019 17:02:38 +0100 Subject: [PATCH] Prevent calls to `onFirstRun` default implementation for gradle rc This is temporary until we get compatible API from IDEA to support gradle run configurations for MPP projects in IDEA 191+ --- ...nGradleTestRunConfigurationProducer.kt.191 | 241 ++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100644 idea/idea-gradle/src/org/jetbrains/kotlin/idea/run/KotlinGradleTestRunConfigurationProducer.kt.191 diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/run/KotlinGradleTestRunConfigurationProducer.kt.191 b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/run/KotlinGradleTestRunConfigurationProducer.kt.191 new file mode 100644 index 00000000000..6064383fe65 --- /dev/null +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/run/KotlinGradleTestRunConfigurationProducer.kt.191 @@ -0,0 +1,241 @@ +/* + * 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.ConfigurationFromContext +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.kotlin.idea.caches.project.isMPPModule +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 + ): 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 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 + + 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 + ): 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 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 + + 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\" " + } + } +}