From 2bd7af9c2f2d83e59870050fa14f87c8569fe785 Mon Sep 17 00:00:00 2001 From: Wei Zhang Date: Tue, 25 Dec 2018 22:56:30 -0800 Subject: [PATCH 1/2] Fix SparkJobRun/Debug actions issues for non Spark context --- ...BatchJobLocalRunConfigurationProducer.java | 162 ++---------------- .../hdinsight/spark/run/SparkContextUtils.kt | 122 +++++++++++++ .../run/action/RunConfigurationActionUtils.kt | 4 + .../run/action/SparkRunConfigurationAction.kt | 96 +++++++++-- 4 files changed, 226 insertions(+), 158 deletions(-) create mode 100644 PluginsAndFeatures/azure-toolkit-for-intellij/src/com/microsoft/azure/hdinsight/spark/run/SparkContextUtils.kt diff --git a/PluginsAndFeatures/azure-toolkit-for-intellij/src/com/microsoft/azure/hdinsight/spark/run/SparkBatchJobLocalRunConfigurationProducer.java b/PluginsAndFeatures/azure-toolkit-for-intellij/src/com/microsoft/azure/hdinsight/spark/run/SparkBatchJobLocalRunConfigurationProducer.java index 9f94abf12f..b3188ecb35 100644 --- a/PluginsAndFeatures/azure-toolkit-for-intellij/src/com/microsoft/azure/hdinsight/spark/run/SparkBatchJobLocalRunConfigurationProducer.java +++ b/PluginsAndFeatures/azure-toolkit-for-intellij/src/com/microsoft/azure/hdinsight/spark/run/SparkBatchJobLocalRunConfigurationProducer.java @@ -22,43 +22,24 @@ package com.microsoft.azure.hdinsight.spark.run; -import com.intellij.analysis.AnalysisScope; -import com.intellij.codeInsight.TestFrameworks; -import com.intellij.execution.JavaExecutionUtil; -import com.intellij.execution.Location; import com.intellij.execution.actions.ConfigurationContext; import com.intellij.execution.actions.ConfigurationFromContext; -import com.intellij.execution.application.ApplicationConfigurationType; import com.intellij.execution.configurations.ConfigurationType; -import com.intellij.execution.configurations.ConfigurationUtil; import com.intellij.execution.junit.JavaRunConfigurationProducerBase; import com.intellij.openapi.module.Module; -import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Ref; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.packageDependencies.DependenciesBuilder; -import com.intellij.packageDependencies.ForwardDependenciesBuilder; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiMethod; -import com.intellij.psi.util.PsiMethodUtil; -import com.intellij.psi.util.PsiTreeUtil; import com.microsoft.azure.hdinsight.spark.common.SparkBatchJobConfigurableModel; import com.microsoft.azure.hdinsight.spark.run.action.SelectSparkApplicationTypeAction; import com.microsoft.azure.hdinsight.spark.run.action.SparkApplicationType; import com.microsoft.azure.hdinsight.spark.run.configuration.LivySparkBatchJobRunConfiguration; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.plugins.scala.lang.psi.api.statements.ScFunctionDefinition; -import scala.Option; -import scala.Tuple2; +import com.microsoft.azure.hdinsight.spark.run.configuration.LivySparkBatchJobRunConfigurationType; +import com.microsoft.azuretools.azurecommons.helpers.NotNull; +import org.apache.commons.lang3.StringUtils; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.AbstractMap.SimpleImmutableEntry; import java.util.Optional; -import java.util.Set; public class SparkBatchJobLocalRunConfigurationProducer extends JavaRunConfigurationProducerBase { private SparkApplicationType applicationType; @@ -76,10 +57,10 @@ public boolean setupConfigurationFromContext(LivySparkBatchJobRunConfiguration c } else { return Optional.ofNullable(context.getModule()) .map(Module::getProject) - .flatMap(project -> getMainClassFromContext(context) - .filter(mcPair -> isSparkContext(project, mcPair.getKey().getContainingFile()))) - .map(mcPair -> { - setupConfiguration(configuration, mcPair.getValue(), context); + .flatMap(project -> Optional.ofNullable(SparkContextUtilsKt.getSparkMainClassWithElement(context)) + .filter(mainClass -> SparkContextUtilsKt.isSparkContext(mainClass.getContainingFile()))) + .map(mainClass -> { + setupConfiguration(configuration, mainClass, context); return true; }) @@ -87,25 +68,6 @@ public boolean setupConfigurationFromContext(LivySparkBatchJobRunConfiguration c } } - private boolean isSparkContext(Project project, PsiFile sourceFile) { - // To determine if the current context has Spark Context dependence - DependenciesBuilder db = new ForwardDependenciesBuilder( - project, new AnalysisScope(sourceFile)); - - db.analyze(); - - return Optional.ofNullable(db.getDependencies().get(sourceFile)) - .map((Set t) -> t.stream() - .map(PsiFile::getVirtualFile) - .map(VirtualFile::getNameWithoutExtension) - .anyMatch(className -> className.equals("SparkContext") || - className.equals("JavaSparkContext") || - className.equals("SparkConf") || - className.equals("StreamingContext") || - className.equals("SparkSession"))) - .orElse(false); - } - private void setupConfiguration(LivySparkBatchJobRunConfiguration configuration, final PsiClass clazz, final ConfigurationContext context) { SparkBatchJobConfigurableModel jobModel = configuration.getModel(); @@ -121,98 +83,7 @@ private void setupConfiguration(LivySparkBatchJobRunConfiguration configuration, } private static Optional getNormalizedClassName(@NotNull PsiClass clazz) { - return Optional.ofNullable(JavaExecutionUtil.getRuntimeQualifiedName(clazz)) - .map(mainClass -> mainClass.substring( - 0, - Optional.of(mainClass.lastIndexOf('$')) - .filter(o -> o >= 0) - .orElse(mainClass.length()))); - } - - private static Optional> findMainMethod(PsiElement element) { - PsiMethod method; - - while ((method = PsiTreeUtil.getParentOfType(element, PsiMethod.class)) != null) { - if (PsiMethodUtil.isMainMethod(method)) { - return Optional.of(new SimpleImmutableEntry(method, method.getContainingClass())) - .filter(pair -> ConfigurationUtil.MAIN_CLASS.value(pair.getValue())); - } - - element = method.getParent(); - } - - return Optional.empty(); - } - - private static Optional> findJavaMainClass(PsiElement element) { - return Optional.ofNullable(ApplicationConfigurationType.getMainClass(element)) - .map(clazz -> new SimpleImmutableEntry(clazz, clazz)); - } - - private static Optional> findScalaMainClass(PsiElement element) { - // TODO: Replace with the following code after IDEA 2018.1 - // Option> ceOption = ScalaMainMethodUtil.findMainClassAndSourceElem(element); - try { - // Added from IDEA 2017.2 - Method findMainClassAndSourceElemMethod = Class - .forName("org.jetbrains.plugins.scala.runner.ScalaMainMethodUtil") - .getDeclaredMethod("findMainClassAndSourceElem", PsiElement.class); - - Object option = findMainClassAndSourceElemMethod.invoke(null, element); - if (option instanceof scala.None$ || !(option instanceof Option)) { - return Optional.empty(); - } - - Option> ceOption = (Option>) option; - - return ceOption.isDefined() ? - Optional.of(new SimpleImmutableEntry<>(ceOption.get()._1(), ceOption.get()._1())) : - Optional.empty(); - } catch (NoSuchMethodException ignored) { - // Use old one for IDEA 2017.1 - try { - Method findContainingMainMethod = Class - .forName("org.jetbrains.plugins.scala.runner.ScalaMainMethodUtil") - .getDeclaredMethod("findContainingMainMethod", PsiElement.class); - - Object option = findContainingMainMethod.invoke(null, element); - if (option instanceof scala.None$ || !(option instanceof Option)) { - return Optional.empty(); - } - - Option funDefOption = (Option) option; - - return funDefOption.isDefined() ? - Optional.of(new SimpleImmutableEntry( - funDefOption.get().containingClass(), - funDefOption.get().containingClass())) : - Optional.empty(); - } catch (Exception ignore) { - return Optional.empty(); - } - } catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException ignored) { - return Optional.empty(); - } - } - - private static Optional> getMainClassFromContext(ConfigurationContext context) { - final Optional location = Optional.ofNullable(context.getLocation()); - - return location - .map(JavaExecutionUtil::stepIntoSingleClass) - .map(Location::getPsiElement) - .filter(PsiElement::isPhysical) - .flatMap(element -> { - Optional> mcPair = findMainMethod(element); - - if (mcPair.isPresent()) { - return mcPair; - } else { - Optional> ccPair = findJavaMainClass(element); - - return ccPair.isPresent() ? ccPair : findScalaMainClass(element); - } - }); + return Optional.ofNullable(SparkContextUtilsKt.getNormalizedClassNameForSpark(clazz)); } /** @@ -223,16 +94,13 @@ private static Optional> getMainClass */ @Override public boolean isConfigurationFromContext(LivySparkBatchJobRunConfiguration jobConfiguration, ConfigurationContext context) { - return getMainClassFromContext(context) - .filter(mcPair -> getNormalizedClassName(mcPair.getValue()) - .map(name -> name.equals(jobConfiguration.getModel().getLocalRunConfigurableModel().getRunClass())) - .orElse(false)) - .filter(mcPair -> Optional.of(mcPair.getKey()) - .filter(e -> e instanceof PsiMethod) - .map(PsiMethod.class::cast) - .map(method -> !TestFrameworks.getInstance().isTestMethod(method)) - .orElse(true)) - .map(mcPair -> { + return Optional.ofNullable(SparkContextUtilsKt.getSparkMainClassWithElement(context)) + .map(mainClass -> { + if (!StringUtils.equals(jobConfiguration.getModel().getLocalRunConfigurableModel().getRunClass(), + SparkContextUtilsKt.getNormalizedClassNameForSpark(mainClass))) { + return false; + } + final Module configurationModule = jobConfiguration.getConfigurationModule().getModule(); if (!Comparing.equal(context.getModule(), configurationModule)) { diff --git a/PluginsAndFeatures/azure-toolkit-for-intellij/src/com/microsoft/azure/hdinsight/spark/run/SparkContextUtils.kt b/PluginsAndFeatures/azure-toolkit-for-intellij/src/com/microsoft/azure/hdinsight/spark/run/SparkContextUtils.kt new file mode 100644 index 0000000000..7b94d5b82a --- /dev/null +++ b/PluginsAndFeatures/azure-toolkit-for-intellij/src/com/microsoft/azure/hdinsight/spark/run/SparkContextUtils.kt @@ -0,0 +1,122 @@ +/* + * Copyright (c) Microsoft Corporation + * + * All rights reserved. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.microsoft.azure.hdinsight.spark.run + +import com.intellij.analysis.AnalysisScope +import com.intellij.codeInsight.TestFrameworks +import com.intellij.execution.JavaExecutionUtil +import com.intellij.execution.actions.ConfigurationContext +import com.intellij.execution.actions.ConfigurationContext.SHARED_CONTEXT +import com.intellij.execution.application.ApplicationConfigurationType +import com.intellij.ide.DataManager +import com.intellij.openapi.actionSystem.DataContext +import com.intellij.packageDependencies.ForwardDependenciesBuilder +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.util.PsiMethodUtil +import com.microsoft.azure.hdinsight.spark.console.SparkScalaPluginDelegate +import scala.Option +import scala.Tuple2 +import java.lang.reflect.InvocationTargetException + +val sparkContextKeywords = arrayOf( + "SparkContext", + "JavaSparkContext", + "SparkConf", + "StreamingContext", + "SparkSession" +) + +fun PsiFile.isSparkContext(): Boolean { + // To determine if the current context has Spark Context dependence + val db = ForwardDependenciesBuilder(project, AnalysisScope(this)) + + db.analyze() + + return db.dependencies[this] + ?.map { psFile -> psFile.virtualFile?.nameWithoutExtension } + ?.any { className -> className in sparkContextKeywords } + ?: false +} + +fun PsiClass.getNormalizedClassNameForSpark(): String? { + return JavaExecutionUtil.getRuntimeQualifiedName(this)?.substringBeforeLast('$') +} + +fun DataContext.getSparkConfigurationContext(): ConfigurationContext? { + val dataManager = DataManager.getInstance() + return dataManager.loadFromDataContext(this, SHARED_CONTEXT)?.takeIf { + it.getSparkMainClassWithElement()?.containingFile?.isSparkContext() == true + } +} + +fun DataContext.isSparkContext(): Boolean { + return getSparkConfigurationContext() != null +} + +fun ConfigurationContext.getSparkMainClassWithElement(): PsiClass? { + return location?.let { + val elem = JavaExecutionUtil.stepIntoSingleClass(it).psiElement.takeIf { psiElem -> psiElem.isPhysical } + + (elem as? PsiClass)?.findMainMethod() + ?: elem?.findJavaMainClass() + ?: elem?.findScalaObjectMainClass() + } +} + +fun PsiClass.findMainMethod(): PsiClass? { + return PsiMethodUtil.findMainMethod(this)?.takeUnless { TestFrameworks.getInstance().isTestMethod(it) } + ?.containingClass +} + +fun PsiElement.findJavaMainClass(): PsiClass? { + return ApplicationConfigurationType.getMainClass(this)?.takeUnless { TestFrameworks.getInstance().isTestClass(it) } +} + +fun PsiElement.findScalaObjectMainClass(): PsiClass? { + val scalaMainMethodUtilDelegate = SparkScalaPluginDelegate("org.jetbrains.plugins.scala.runner.ScalaMainMethodUtil") + + if (!scalaMainMethodUtilDelegate.isEnabled) { + return null + } + + val findMainClassAndSourceElemMethod = scalaMainMethodUtilDelegate + .getMethod("findMainClassAndSourceElem", PsiElement::class.java) ?: return null + + try { + val option = findMainClassAndSourceElemMethod.invoke(null, this) + if (option is scala.`None$` || option !is Option<*>) { + return null + } + + return ((option as Option>).takeIf { it.isDefined })?.get()?._1() + ?.takeUnless { TestFrameworks.getInstance().isTestClass(it) } + } catch (ex: Exception) { + when (ex) { + is IllegalAccessException, is InvocationTargetException -> return null + else -> throw ex + } + } +} + diff --git a/PluginsAndFeatures/azure-toolkit-for-intellij/src/com/microsoft/azure/hdinsight/spark/run/action/RunConfigurationActionUtils.kt b/PluginsAndFeatures/azure-toolkit-for-intellij/src/com/microsoft/azure/hdinsight/spark/run/action/RunConfigurationActionUtils.kt index c824f87d31..48cb0f831f 100644 --- a/PluginsAndFeatures/azure-toolkit-for-intellij/src/com/microsoft/azure/hdinsight/spark/run/action/RunConfigurationActionUtils.kt +++ b/PluginsAndFeatures/azure-toolkit-for-intellij/src/com/microsoft/azure/hdinsight/spark/run/action/RunConfigurationActionUtils.kt @@ -56,6 +56,10 @@ object RunConfigurationActionUtils: ILogger { } try { + if (setting.isEditBeforeRun && !RunDialog.editConfiguration(environment, "Edit configuration")) { + return + } + environment.assignNewExecutionId() runner.execute(environment) } catch (e: ExecutionException) { diff --git a/PluginsAndFeatures/azure-toolkit-for-intellij/src/com/microsoft/azure/hdinsight/spark/run/action/SparkRunConfigurationAction.kt b/PluginsAndFeatures/azure-toolkit-for-intellij/src/com/microsoft/azure/hdinsight/spark/run/action/SparkRunConfigurationAction.kt index 504390da1b..0b44b71b3d 100644 --- a/PluginsAndFeatures/azure-toolkit-for-intellij/src/com/microsoft/azure/hdinsight/spark/run/action/SparkRunConfigurationAction.kt +++ b/PluginsAndFeatures/azure-toolkit-for-intellij/src/com/microsoft/azure/hdinsight/spark/run/action/SparkRunConfigurationAction.kt @@ -22,11 +22,18 @@ package com.microsoft.azure.hdinsight.spark.run.action -import com.intellij.execution.* +import com.intellij.execution.Executor +import com.intellij.execution.RunManagerEx +import com.intellij.execution.RunnerAndConfigurationSettings +import com.intellij.execution.RunnerRegistry import com.intellij.execution.runners.ExecutionEnvironmentBuilder import com.intellij.openapi.actionSystem.AnActionEvent import com.microsoft.azure.hdinsight.common.logger.ILogger import com.microsoft.azure.hdinsight.spark.run.configuration.LivySparkBatchJobRunConfiguration +import com.microsoft.azure.hdinsight.spark.run.getNormalizedClassNameForSpark +import com.microsoft.azure.hdinsight.spark.run.getSparkConfigurationContext +import com.microsoft.azure.hdinsight.spark.run.getSparkMainClassWithElement +import com.microsoft.azure.hdinsight.spark.run.isSparkContext import com.microsoft.azuretools.ijidea.utility.AzureAnAction import com.microsoft.intellij.util.runInReadAction import javax.swing.Icon @@ -41,29 +48,96 @@ abstract class SparkRunConfigurationAction : AzureAnAction, ILogger { open fun canRun(setting: RunnerAndConfigurationSettings): Boolean = setting.configuration is LivySparkBatchJobRunConfiguration && - RunnerRegistry.getInstance().getRunner(runExecutor.id, setting.configuration) != null + RunnerRegistry.getInstance().getRunner(runExecutor.id, setting.configuration) + ?.canRun(runExecutor.id, setting.configuration) == true override fun update(actionEvent: AnActionEvent) { - val presentation = actionEvent.presentation.apply { isEnabled = false } + val presentation = actionEvent.presentation.apply { isEnabledAndVisible = false } val project = actionEvent.project ?: return val runManagerEx = RunManagerEx.getInstanceEx(project) - val selectedConfigSettings = runManagerEx.selectedConfiguration ?: return + val selectedConfigSettings = runManagerEx.selectedConfiguration - presentation.isEnabled = canRun(selectedConfigSettings) + presentation.apply { + when { + actionEvent.isFromActionToolbar -> { + isEnabledAndVisible = canRun(selectedConfigSettings?: return) + } + else -> { + // From context menu or Line marker action menu + isEnabledAndVisible = actionEvent.dataContext.isSparkContext() + + if (!isEnabledAndVisible) { + return@apply + } + + // In Spark Context + if (selectedConfigSettings?.let { canRun(it) } == true) { + text = "${runExecutor.id} ${selectedConfigSettings.name}" + description = "Submit a Spark job with the current configuration for this main class" + } else { + /** + * FIXME with [LivySparkBatchJobRunConfiguration.suggestedName] + * to create a new run configuration to submit a Spark job for this main class + */ +// val className = actionEvent.dataContext.getSparkConfigurationContext() +// ?.getSparkMainClassWithElement() +// ?.getNormalizedClassNameForSpark() +// ?: "" +// +// text = "${runExecutor.id} [Spark Job] $className" + isEnabledAndVisible = false + } + } + } + } } override fun onActionPerformed(actionEvent: AnActionEvent?) { val project = actionEvent?.project ?: return val runManagerEx = RunManagerEx.getInstanceEx(project) - val selectedConfigSettings = runManagerEx.selectedConfiguration ?: return + val selectedConfigSettings = runManagerEx.selectedConfiguration - // Try current selected Configuration - if (!canRun(selectedConfigSettings)) { - return - } + when { + actionEvent.isFromActionToolbar -> { + // Try current selected Configuration + selectedConfigSettings?.also { + if (canRun(it)) { + runExisting(it) + } + } + } + else -> { + // From context menu or Line marker action menu + if (!actionEvent.dataContext.isSparkContext()) { + // No action for out of Spark Context + return + } + + // In Spark Context + val className = actionEvent.dataContext.getSparkConfigurationContext() + ?.getSparkMainClassWithElement() + ?.getNormalizedClassNameForSpark() + ?: "" - runExisting(selectedConfigSettings) + if (selectedConfigSettings?.let { canRun(it) } == true) { + val savedIsEditBeforeRun = selectedConfigSettings.isEditBeforeRun + + selectedConfigSettings.isEditBeforeRun = true + (selectedConfigSettings.configuration as LivySparkBatchJobRunConfiguration).submitModel.mainClassName = + className + + runExisting(selectedConfigSettings) + + selectedConfigSettings.isEditBeforeRun = savedIsEditBeforeRun + } else { + /** + * FIXME with [LivySparkBatchJobRunConfiguration.suggestedName] + * to create a new run configuration to submit a Spark job for this main class + */ + } + } + } } private fun runExisting(setting: RunnerAndConfigurationSettings) { From e793e1130cb04d8f67775c3a5accf14a5d265422 Mon Sep 17 00:00:00 2001 From: Wei Zhang Date: Wed, 26 Dec 2018 00:07:20 -0800 Subject: [PATCH 2/2] Remove SparkJobRun/Debug actions from test sources context --- .../SparkBatchJobLocalRunConfigurationProducer.java | 12 ++++++++++-- .../spark/run/action/SparkRunConfigurationAction.kt | 11 ++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/PluginsAndFeatures/azure-toolkit-for-intellij/src/com/microsoft/azure/hdinsight/spark/run/SparkBatchJobLocalRunConfigurationProducer.java b/PluginsAndFeatures/azure-toolkit-for-intellij/src/com/microsoft/azure/hdinsight/spark/run/SparkBatchJobLocalRunConfigurationProducer.java index b3188ecb35..4bcd3a6bd6 100644 --- a/PluginsAndFeatures/azure-toolkit-for-intellij/src/com/microsoft/azure/hdinsight/spark/run/SparkBatchJobLocalRunConfigurationProducer.java +++ b/PluginsAndFeatures/azure-toolkit-for-intellij/src/com/microsoft/azure/hdinsight/spark/run/SparkBatchJobLocalRunConfigurationProducer.java @@ -41,6 +41,8 @@ import java.util.Optional; +import static com.intellij.openapi.roots.TestSourcesFilter.isTestSources; + public class SparkBatchJobLocalRunConfigurationProducer extends JavaRunConfigurationProducerBase { private SparkApplicationType applicationType; @@ -57,8 +59,10 @@ public boolean setupConfigurationFromContext(LivySparkBatchJobRunConfiguration c } else { return Optional.ofNullable(context.getModule()) .map(Module::getProject) - .flatMap(project -> Optional.ofNullable(SparkContextUtilsKt.getSparkMainClassWithElement(context)) - .filter(mainClass -> SparkContextUtilsKt.isSparkContext(mainClass.getContainingFile()))) + .flatMap(project -> Optional + .ofNullable(SparkContextUtilsKt.getSparkMainClassWithElement(context)) + .filter(mainClass -> SparkContextUtilsKt.isSparkContext(mainClass.getContainingFile()) && + !isTestSources(mainClass.getContainingFile().getVirtualFile(), project))) .map(mainClass -> { setupConfiguration(configuration, mainClass, context); @@ -101,6 +105,10 @@ public boolean isConfigurationFromContext(LivySparkBatchJobRunConfiguration jobC return false; } + if (isTestSources(mainClass.getContainingFile().getVirtualFile(), jobConfiguration.getProject())) { + return false; + } + final Module configurationModule = jobConfiguration.getConfigurationModule().getModule(); if (!Comparing.equal(context.getModule(), configurationModule)) { diff --git a/PluginsAndFeatures/azure-toolkit-for-intellij/src/com/microsoft/azure/hdinsight/spark/run/action/SparkRunConfigurationAction.kt b/PluginsAndFeatures/azure-toolkit-for-intellij/src/com/microsoft/azure/hdinsight/spark/run/action/SparkRunConfigurationAction.kt index 0b44b71b3d..e08334bd43 100644 --- a/PluginsAndFeatures/azure-toolkit-for-intellij/src/com/microsoft/azure/hdinsight/spark/run/action/SparkRunConfigurationAction.kt +++ b/PluginsAndFeatures/azure-toolkit-for-intellij/src/com/microsoft/azure/hdinsight/spark/run/action/SparkRunConfigurationAction.kt @@ -28,6 +28,7 @@ import com.intellij.execution.RunnerAndConfigurationSettings import com.intellij.execution.RunnerRegistry import com.intellij.execution.runners.ExecutionEnvironmentBuilder import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.roots.TestSourcesFilter.isTestSources import com.microsoft.azure.hdinsight.common.logger.ILogger import com.microsoft.azure.hdinsight.spark.run.configuration.LivySparkBatchJobRunConfiguration import com.microsoft.azure.hdinsight.spark.run.getNormalizedClassNameForSpark @@ -58,11 +59,15 @@ abstract class SparkRunConfigurationAction : AzureAnAction, ILogger { val runManagerEx = RunManagerEx.getInstanceEx(project) val selectedConfigSettings = runManagerEx.selectedConfiguration + val mainClass = actionEvent.dataContext.getSparkConfigurationContext()?.getSparkMainClassWithElement() + if (mainClass?.containingFile?.let { isTestSources(it.virtualFile, project) } == true) { + return + } + presentation.apply { when { - actionEvent.isFromActionToolbar -> { - isEnabledAndVisible = canRun(selectedConfigSettings?: return) - } + + actionEvent.isFromActionToolbar -> isEnabledAndVisible = canRun(selectedConfigSettings?: return) else -> { // From context menu or Line marker action menu isEnabledAndVisible = actionEvent.dataContext.isSparkContext()