From 4cc6b98e1bdcb3fc5ea1fa2538be4ce656b6caa2 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Fri, 11 Nov 2016 11:49:08 +0100 Subject: [PATCH] Run configuration producer for Gradle tests (KT-10700) --- idea/src/META-INF/gradle.xml | 3 + ...otlinGradleTestRunConfigurationProducer.kt | 180 ++++++++++++++++++ .../KotlinJUnitRunConfigurationProducer.kt | 50 ++--- 3 files changed, 209 insertions(+), 24 deletions(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/run/KotlinGradleTestRunConfigurationProducer.kt diff --git a/idea/src/META-INF/gradle.xml b/idea/src/META-INF/gradle.xml index 036de587a2a..e3d118e272f 100644 --- a/idea/src/META-INF/gradle.xml +++ b/idea/src/META-INF/gradle.xml @@ -30,5 +30,8 @@ language="Groovy" hasStaticDescription="true" level="WARNING"/> + + + diff --git a/idea/src/org/jetbrains/kotlin/idea/run/KotlinGradleTestRunConfigurationProducer.kt b/idea/src/org/jetbrains/kotlin/idea/run/KotlinGradleTestRunConfigurationProducer.kt new file mode 100644 index 00000000000..e694f28bd76 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/run/KotlinGradleTestRunConfigurationProducer.kt @@ -0,0 +1,180 @@ +/* + * Copyright 2010-2016 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.JavaRunConfigurationExtensionManager +import com.intellij.execution.actions.ConfigurationContext +import com.intellij.execution.actions.RunConfigurationProducer +import com.intellij.execution.junit.PatternConfigurationProducer +import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration +import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil +import com.intellij.openapi.util.Ref +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 + +class KotlinTestClassGradleConfigurationProducer : TestClassGradleConfigurationProducer() { + + override fun doSetupConfigurationFromContext(configuration: ExternalSystemRunConfiguration, + context: ConfigurationContext, + sourceElement: Ref): Boolean { + val contextLocation = context.location ?: return false + val module = context.module ?: return false + + if (RunConfigurationProducer.getInstance(PatternConfigurationProducer::class.java).isMultipleElementsSelected(context)) { + return false + } + val leaf = context.location?.psiElement ?: return false + val testClass = KotlinJUnitRunConfigurationProducer.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 + + JavaRunConfigurationExtensionManager.getInstance().extendCreatedConfiguration(configuration, contextLocation) + return true + } + + override fun doIsConfigurationFromContext(configuration: ExternalSystemRunConfiguration, context: ConfigurationContext): Boolean { + val leaf = context.location?.psiElement ?: return false + if (context.module == null) return false + + if (RunConfigurationProducer.getInstance(PatternConfigurationProducer::class.java).isMultipleElementsSelected(context)) { + return false + } + + val methodLocation = KotlinJUnitRunConfigurationProducer.getTestMethodLocation(leaf) + if (methodLocation != null) return false + + val testClass = KotlinJUnitRunConfigurationProducer.getTestClass(leaf) + if (testClass == null || testClass.qualifiedName == null) return false + + + val projectPath = resolveProjectPath(context.module) ?: return false + if (projectPath != configuration.settings.externalProjectPath) { + return false + } + if (!configuration.settings.taskNames.containsAll(getTasksToRun(context.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 { + val contextLocation = context.location ?: return false + if (context.module == null) return false + + if (RunConfigurationProducer.getInstance(PatternConfigurationProducer::class.java).isMultipleElementsSelected(context)) { + return false + } + + val methodLocation = KotlinJUnitRunConfigurationProducer.getTestMethodLocation(contextLocation.psiElement) ?: return false + val psiMethod = methodLocation.psiElement + sourceElement.set(psiMethod) + + val containingClass = psiMethod.containingClass ?: return false + + + if (!applyTestMethodConfiguration(configuration, context, psiMethod, containingClass)) return false + + JavaRunConfigurationExtensionManager.getInstance().extendCreatedConfiguration(configuration, contextLocation) + return true + } + + override fun doIsConfigurationFromContext(configuration: ExternalSystemRunConfiguration, context: ConfigurationContext): Boolean { + if (RunConfigurationProducer.getInstance(PatternConfigurationProducer::class.java).isMultipleElementsSelected(context)) { + return false + } + + val contextLocation = context.location ?: return false + val module = context.module ?: return false + + val methodLocation = KotlinJUnitRunConfigurationProducer.getTestMethodLocation(contextLocation.psiElement) ?: return false + val psiMethod = methodLocation.psiElement + + 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\" " + } + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/run/KotlinJUnitRunConfigurationProducer.kt b/idea/src/org/jetbrains/kotlin/idea/run/KotlinJUnitRunConfigurationProducer.kt index 290bac0929b..5c9edb16d5c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/run/KotlinJUnitRunConfigurationProducer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/run/KotlinJUnitRunConfigurationProducer.kt @@ -137,33 +137,35 @@ class KotlinJUnitRunConfigurationProducer : RunConfigurationProducer? { - val function = leaf.getParentOfType(false) ?: return null - val owner = PsiTreeUtil.getParentOfType(function, KtFunction::class.java, KtClass::class.java) - - if (owner is KtClass) { - val delegate = owner.toLightClass() ?: return null - val method = delegate.methods.firstOrNull() { it.navigationElement == function } ?: return null - val methodLocation = PsiLocation.fromPsiElement(method) - if (JUnitUtil.isTestMethod(methodLocation, false)) { - return methodLocation + companion object { + fun getTestClass(leaf: PsiElement): PsiClass? { + val containingFile = leaf.containingFile as? KtFile ?: return null + var ktClass = leaf.getParentOfType(false) + if (!ktClass.isJUnitTestClass()) { + ktClass = getTestClassInFile(containingFile) } + return ktClass?.toLightClass() } - return null - } - private fun getTestClass(leaf: PsiElement): PsiClass? { - val containingFile = leaf.containingFile as? KtFile ?: return null - var jetClass = leaf.getParentOfType(false) - if (!jetClass.isJUnitTestClass()) { - jetClass = getTestClassInFile(containingFile) + fun getTestMethodLocation(leaf: PsiElement): Location? { + val function = leaf.getParentOfType(false) ?: return null + val owner = PsiTreeUtil.getParentOfType(function, KtFunction::class.java, KtClass::class.java) + + if (owner is KtClass) { + val delegate = owner.toLightClass() ?: return null + val method = delegate.methods.firstOrNull() { it.navigationElement == function } ?: return null + val methodLocation = PsiLocation.fromPsiElement(method) + if (JUnitUtil.isTestMethod(methodLocation, false)) { + return methodLocation + } + } + return null } - return jetClass?.toLightClass() + + private fun KtClass?.isJUnitTestClass() = + this?.toLightClass()?.let { JUnitUtil.isTestClass(it, false, true) } ?: false + + private fun getTestClassInFile(ktFile: KtFile) = + ktFile.declarations.filterIsInstance().singleOrNull { it.isJUnitTestClass() } } - - private fun KtClass?.isJUnitTestClass() = - this?.toLightClass()?.let { JUnitUtil.isTestClass(it, false, true) } ?: false - - private fun getTestClassInFile(jetFile: KtFile) = - jetFile.declarations.filterIsInstance().singleOrNull { it.isJUnitTestClass() } }