mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-23190: Flask CLI run configuration
Flask CLI may me used to run server since 0.12
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.ui.components;
|
||||
|
||||
import com.intellij.ui.TextAccessor;
|
||||
import com.intellij.util.ui.ComponentWithEmptyText;
|
||||
import com.intellij.util.ui.JBInsets;
|
||||
import com.intellij.util.ui.StatusText;
|
||||
@@ -26,7 +27,7 @@ import javax.swing.plaf.TextUI;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
public class JBTextField extends JTextField implements ComponentWithEmptyText {
|
||||
public class JBTextField extends JTextField implements ComponentWithEmptyText, TextAccessor {
|
||||
private TextComponentEmptyText myEmptyText;
|
||||
|
||||
public JBTextField() {
|
||||
|
||||
@@ -53,7 +53,7 @@ import java.util.List;
|
||||
/**
|
||||
* @author max
|
||||
*/
|
||||
public class EditorTextField extends NonOpaquePanel implements DocumentListener, TextComponent, DataProvider,
|
||||
public class EditorTextField extends NonOpaquePanel implements DocumentListener, TextComponent, DataProvider, TextAccessor,
|
||||
DocumentBasedComponent, FocusListener, MouseListener {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.ui.EditorTextField");
|
||||
public static final Key<Boolean> SUPPLEMENTARY_KEY = Key.create("Supplementary");
|
||||
@@ -234,6 +234,7 @@ public class EditorTextField extends NonOpaquePanel implements DocumentListener,
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setText(@Nullable final String text) {
|
||||
ApplicationManager.getApplication().runWriteAction(() -> CommandProcessor.getInstance().executeCommand(getProject(), () -> {
|
||||
myDocument.replaceString(0, myDocument.getTextLength(), StringUtil.notNullize(text));
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
*/
|
||||
package com.jetbrains.extensions
|
||||
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.jetbrains.python.run.AbstractPythonRunConfiguration
|
||||
import com.jetbrains.python.run.targetBasedConfiguration.PyRunTargetVariant
|
||||
import com.jetbrains.python.run.targetBasedConfiguration.TargetWithVariant
|
||||
import com.jetbrains.python.run.targetBasedConfiguration.targetAsPsiElement
|
||||
import com.jetbrains.python.run.targetBasedConfiguration.targetAsVirtualFile
|
||||
|
||||
|
||||
/**
|
||||
* @see targetAsPsiElement
|
||||
*/
|
||||
fun TargetWithVariant.asPsiElement(configuration: AbstractPythonRunConfiguration<*>,
|
||||
workingDirectory: VirtualFile?
|
||||
= LocalFileSystem.getInstance().findFileByPath(configuration.getWorkingDirectorySafe())) =
|
||||
target?.let { targetAsPsiElement(targetVariant, it, configuration, workingDirectory) }
|
||||
|
||||
|
||||
/**
|
||||
* @see targetAsVirtualFile
|
||||
*/
|
||||
fun TargetWithVariant.asVirtualFile() = target?.let { targetAsVirtualFile(targetVariant, it) }
|
||||
|
||||
/**
|
||||
* Sanity check for "target" value. Does not resolve target, only check its syntax
|
||||
* CUSTOM type is not checked.
|
||||
*/
|
||||
fun TargetWithVariant.isWellFormed() = when (targetVariant) {
|
||||
PyRunTargetVariant.PYTHON -> Regex("^[a-zA-Z0-9._]+[a-zA-Z0-9_]$").matches(target ?: "")
|
||||
PyRunTargetVariant.PATH -> !VfsUtil.isBadName(target)
|
||||
else -> true
|
||||
}
|
||||
@@ -133,6 +133,9 @@ public abstract class AbstractPythonRunConfiguration<T extends AbstractPythonRun
|
||||
|
||||
protected abstract SettingsEditor<T> createConfigurationEditor();
|
||||
|
||||
/**
|
||||
* <strong>Always call super</strong> when overwriting this method
|
||||
*/
|
||||
@Override
|
||||
public void checkConfiguration() throws RuntimeConfigurationException {
|
||||
super.checkConfiguration();
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
*/
|
||||
package com.jetbrains.python.run
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.refactoring.listeners.UndoRefactoringElementAdapter
|
||||
|
||||
/**
|
||||
* Tools to support refactoring for configurations
|
||||
* that implements [com.intellij.execution.configurations.RefactoringListenerProvider]
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @see CompositeRefactoringElementListener
|
||||
*/
|
||||
abstract class UndoRefactoringCompletionListener : UndoRefactoringElementAdapter() {
|
||||
public abstract override fun refactored(element: PsiElement, oldQualifiedName: String?)
|
||||
}
|
||||
|
||||
/**
|
||||
* Chains several [com.intellij.refactoring.listeners.RefactoringElementListener]
|
||||
*/
|
||||
class CompositeRefactoringElementListener(private vararg val listeners: UndoRefactoringCompletionListener) : UndoRefactoringElementAdapter() {
|
||||
override fun refactored(element: PsiElement, oldQualifiedName: String?) {
|
||||
listeners.forEach { it.refactored(element, oldQualifiedName) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new listener adding provided one
|
||||
*/
|
||||
operator fun plus(listener: UndoRefactoringCompletionListener) = CompositeRefactoringElementListener(*arrayOf(listener) + listeners)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Renames working directory if folder physically renamed
|
||||
*/
|
||||
class PyWorkingDirectoryRenamer(private val workingDirectoryFile: VirtualFile?,
|
||||
private val conf: AbstractPythonRunConfiguration<*>) : UndoRefactoringCompletionListener() {
|
||||
override fun refactored(element: PsiElement, oldQualifiedName: String?) {
|
||||
workingDirectoryFile?.let {
|
||||
conf.setWorkingDirectory(it.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ public class PythonRunConfiguration extends AbstractPythonRunConfiguration
|
||||
return new PythonScriptCommandLineState(this, env);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkConfiguration() throws RuntimeConfigurationException {
|
||||
super.checkConfiguration();
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
*/
|
||||
package com.jetbrains.python.run.targetBasedConfiguration
|
||||
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.QualifiedName
|
||||
import com.jetbrains.extenstions.ModuleBasedContextAnchor
|
||||
import com.jetbrains.extenstions.QNameResolveContext
|
||||
import com.jetbrains.extenstions.resolveToElement
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext
|
||||
import com.jetbrains.python.run.AbstractPythonRunConfiguration
|
||||
import com.jetbrains.python.run.PythonRunConfigurationForm
|
||||
|
||||
/**
|
||||
* Types of target (symbol, path or custom) many python runners may have
|
||||
*/
|
||||
enum class PyRunTargetVariant(private val customName: String? = null) {
|
||||
PYTHON(PythonRunConfigurationForm.MODULE_NAME), PATH(PythonRunConfigurationForm.SCRIPT_PATH), CUSTOM;
|
||||
|
||||
fun getCustomName() = customName ?: name.toLowerCase().capitalize()
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts target to PSI element if possible resolving it against roots and working directory
|
||||
*/
|
||||
fun targetAsPsiElement(targetType: PyRunTargetVariant,
|
||||
target: String,
|
||||
configuration: AbstractPythonRunConfiguration<*>,
|
||||
workingDirectory: VirtualFile? = LocalFileSystem.getInstance().findFileByPath(
|
||||
configuration.getWorkingDirectorySafe()))
|
||||
: PsiElement? {
|
||||
if (targetType == PyRunTargetVariant.PYTHON) {
|
||||
val module = configuration.getModule() ?: return null
|
||||
val context = TypeEvalContext.userInitiated(configuration.getProject(), null)
|
||||
|
||||
val name = QualifiedName.fromDottedString(target)
|
||||
return name.resolveToElement(QNameResolveContext(ModuleBasedContextAnchor(module), configuration.getSdk(),
|
||||
context, workingDirectory, true))
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts target to file if possible
|
||||
*/
|
||||
fun targetAsVirtualFile(targetType: PyRunTargetVariant, target: String): VirtualFile? {
|
||||
if (targetType == PyRunTargetVariant.PATH) {
|
||||
return LocalFileSystem.getInstance().findFileByPath(target)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implement it to obtain extension methods for [targetAsPsiElement]
|
||||
* and [targetAsVirtualFile]
|
||||
*/
|
||||
interface TargetWithVariant {
|
||||
val target: String?
|
||||
val targetVariant: PyRunTargetVariant
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
*/
|
||||
package com.jetbrains.python.run.targetBasedConfiguration
|
||||
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFileSystemItem
|
||||
import com.intellij.psi.PsiNamedElement
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.jetbrains.extensions.getQName
|
||||
import com.jetbrains.python.psi.PyQualifiedNameOwner
|
||||
import com.jetbrains.python.run.UndoRefactoringCompletionListener
|
||||
|
||||
|
||||
/**
|
||||
* Creates listener that renames target.
|
||||
* @param elementUnderRefactoring "element" argument of [com.intellij.execution.configurations.RefactoringListenerProvider.getRefactoringElementListener]
|
||||
* @param targetPsiElement configuration psi element (see [targetAsPsiElement]] or null if configuration is not qname-based
|
||||
* @param targetVirtualFile configuration virtual file element (see [targetAsVirtualFile]] or null if configuration is not file-based
|
||||
* @param setTarget lambda to set target on rename
|
||||
* @return renamer or null if [elementUnderRefactoring] does not affect current configuration
|
||||
*/
|
||||
fun createRefactoringListenerIfPossible(elementUnderRefactoring: PsiElement,
|
||||
targetPsiElement: PsiElement?,
|
||||
targetVirtualFile: VirtualFile?,
|
||||
setTarget: (String) -> Unit)
|
||||
: UndoRefactoringCompletionListener? {
|
||||
if (targetPsiElement != null && PsiTreeUtil.isAncestor(elementUnderRefactoring, targetPsiElement, false)) {
|
||||
return PyElementTargetRenamer(targetPsiElement, setTarget)
|
||||
}
|
||||
if (targetVirtualFile != null && elementUnderRefactoring is PsiFileSystemItem && VfsUtil.isAncestor(
|
||||
elementUnderRefactoring.virtualFile, targetVirtualFile, false)) {
|
||||
return PyVirtualFileRenamer(targetVirtualFile, setTarget)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Renames python target if python symbol, module or folder renamed
|
||||
*/
|
||||
private class PyElementTargetRenamer(private val originalElement: PsiElement, private val setTarget: (String) -> Unit) :
|
||||
UndoRefactoringCompletionListener() {
|
||||
override fun refactored(element: PsiElement, oldQualifiedName: String?) {
|
||||
if (originalElement is PyQualifiedNameOwner) {
|
||||
originalElement.qualifiedName?.let { setTarget(it) }
|
||||
return
|
||||
}
|
||||
if (originalElement is PsiFileSystemItem) {
|
||||
originalElement.getQName()?.let { setTarget(it.toString()) }
|
||||
return
|
||||
}
|
||||
else if (originalElement is PsiNamedElement) {
|
||||
originalElement.name?.let { setTarget(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames folder target if file or folder really renamed
|
||||
*/
|
||||
private class PyVirtualFileRenamer(private val virtualFile: VirtualFile, private val setTarget: (String) -> Unit) :
|
||||
UndoRefactoringCompletionListener() {
|
||||
override fun refactored(element: PsiElement, oldQualifiedName: String?) {
|
||||
setTarget(virtualFile.path)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
*
|
||||
* For configurations that support target types: File system path and Qualified name path.
|
||||
* Package has enum for this type and utility classes to resolve, refactoring etc
|
||||
*
|
||||
*
|
||||
* There are also tools to support configuration refactoring for configurations based on
|
||||
* {@link com.jetbrains.python.run.targetBasedConfiguration.PyRunTargetVariant}
|
||||
* Configuration may be "qname-based" (PYTHON) or "file-path based" (SCRIPT_PATH)
|
||||
*/
|
||||
package com.jetbrains.python.run.targetBasedConfiguration;
|
||||
@@ -24,6 +24,7 @@ import com.intellij.openapi.options.SettingsEditor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.jetbrains.python.PyNames
|
||||
import com.jetbrains.python.PythonHelper
|
||||
import com.jetbrains.python.run.targetBasedConfiguration.PyRunTargetVariant
|
||||
|
||||
/**
|
||||
* Nose runner
|
||||
@@ -32,7 +33,7 @@ import com.jetbrains.python.PythonHelper
|
||||
class PyNoseTestSettingsEditor(configuration: PyAbstractTestConfiguration) :
|
||||
PyAbstractTestSettingsEditor(
|
||||
PyTestSharedForm.create(configuration, PyTestSharedForm.CustomOption(
|
||||
PyNoseTestConfiguration::regexPattern.name, TestTargetType.PATH)))
|
||||
PyNoseTestConfiguration::regexPattern.name, PyRunTargetVariant.PATH)))
|
||||
|
||||
class PyNoseTestExecutionEnvironment(configuration: PyNoseTestConfiguration, environment: ExecutionEnvironment) :
|
||||
PyTestExecutionEnvironment<PyNoseTestConfiguration>(configuration, environment) {
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.intellij.openapi.options.SettingsEditor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.jetbrains.python.PyNames
|
||||
import com.jetbrains.python.PythonHelper
|
||||
import com.jetbrains.python.run.targetBasedConfiguration.PyRunTargetVariant
|
||||
|
||||
/**
|
||||
* Py.test runner
|
||||
@@ -30,7 +31,7 @@ import com.jetbrains.python.PythonHelper
|
||||
class PyTestSettingsEditor(configuration: PyAbstractTestConfiguration) :
|
||||
PyAbstractTestSettingsEditor(
|
||||
PyTestSharedForm.create(configuration, PyTestSharedForm.CustomOption(
|
||||
PyTestConfiguration::keywords.name, TestTargetType.PATH, TestTargetType.PYTHON)))
|
||||
PyTestConfiguration::keywords.name, PyRunTargetVariant.PATH, PyRunTargetVariant.PYTHON)))
|
||||
|
||||
class PyPyTestExecutionEnvironment(configuration: PyTestConfiguration, environment: ExecutionEnvironment) :
|
||||
PyTestExecutionEnvironment<PyTestConfiguration>(configuration, environment) {
|
||||
|
||||
@@ -36,6 +36,7 @@ import com.jetbrains.python.psi.PyQualifiedNameOwner
|
||||
import com.jetbrains.python.psi.PyUtil
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext
|
||||
import com.jetbrains.python.run.PythonConfigurationFactoryBase
|
||||
import com.jetbrains.python.run.targetBasedConfiguration.PyRunTargetVariant
|
||||
import com.jetbrains.python.testing.AbstractPythonLegacyTestRunConfiguration.TestType
|
||||
import com.jetbrains.python.testing.doctest.PythonDocTestConfigurationProducer
|
||||
import com.jetbrains.python.testing.nosetestLegacy.PythonNoseTestRunConfiguration
|
||||
@@ -262,15 +263,15 @@ private abstract class LegacyConfigurationManager<
|
||||
val virtualFile = getVirtualFileByPath(legacyConfig.scriptName) ?: return
|
||||
val pyFile = virtualFile.asPyFile(legacyConfig.project) ?: return
|
||||
val qualifiedName = getElementFromConfig(pyFile)?.qualifiedName ?: return
|
||||
newConfig.target.targetType = TestTargetType.PYTHON
|
||||
newConfig.target.targetVariant = PyRunTargetVariant.PYTHON
|
||||
newConfig.target.target = qualifiedName
|
||||
}
|
||||
TestType.TEST_FOLDER -> {
|
||||
newConfig.target.targetType = TestTargetType.PATH
|
||||
newConfig.target.targetVariant = PyRunTargetVariant.PATH
|
||||
newConfig.target.target = legacyConfig.folderName
|
||||
}
|
||||
TestType.TEST_SCRIPT -> {
|
||||
newConfig.target.targetType = TestTargetType.PATH
|
||||
newConfig.target.targetVariant = PyRunTargetVariant.PATH
|
||||
newConfig.target.target = legacyConfig.scriptName
|
||||
}
|
||||
else -> {
|
||||
@@ -297,7 +298,7 @@ private class LegacyConfigurationManagerPyTest(newConfig: PyTestConfiguration) :
|
||||
newConfig.additionalArguments = legacyConfig.params
|
||||
|
||||
// Default is PATH
|
||||
newConfig.target.targetType = TestTargetType.PATH
|
||||
newConfig.target.targetVariant = PyRunTargetVariant.PATH
|
||||
|
||||
val oldKeywords = legacyConfig.keywords
|
||||
|
||||
@@ -305,7 +306,7 @@ private class LegacyConfigurationManagerPyTest(newConfig: PyTestConfiguration) :
|
||||
if (virtualFile.isDirectory) {
|
||||
// If target is directory, then it can't point to any symbol
|
||||
newConfig.target.target = virtualFile.path
|
||||
newConfig.target.targetType = TestTargetType.PATH
|
||||
newConfig.target.targetVariant = PyRunTargetVariant.PATH
|
||||
newConfig.keywords = oldKeywords
|
||||
return
|
||||
}
|
||||
@@ -318,7 +319,7 @@ private class LegacyConfigurationManagerPyTest(newConfig: PyTestConfiguration) :
|
||||
//Give up with interpreting
|
||||
newConfig.keywords = oldKeywords
|
||||
newConfig.target.target = script.virtualFile.path
|
||||
newConfig.target.targetType = TestTargetType.PATH
|
||||
newConfig.target.targetVariant = PyRunTargetVariant.PATH
|
||||
return
|
||||
}
|
||||
val classOrFunctionName = keywordsList[0]
|
||||
@@ -329,13 +330,13 @@ private class LegacyConfigurationManagerPyTest(newConfig: PyTestConfiguration) :
|
||||
script.findTopLevelFunction(classOrFunctionName),
|
||||
PyQualifiedNameOwner::class.java) ?: return
|
||||
newConfig.target.target = classOrFunction.qualifiedName ?: return
|
||||
newConfig.target.targetType = TestTargetType.PYTHON
|
||||
newConfig.target.targetVariant = PyRunTargetVariant.PYTHON
|
||||
}
|
||||
if (keywordsList.size == 2) { // Class and method
|
||||
clazz ?: return
|
||||
val method = clazz.findMethodByName(keywordsList[1], true, TypeEvalContext.userInitiated(newConfig.project, script)) ?: return
|
||||
newConfig.target.target = method.qualifiedName ?: return
|
||||
newConfig.target.targetType = TestTargetType.PYTHON
|
||||
newConfig.target.targetVariant = PyRunTargetVariant.PYTHON
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -16,16 +16,12 @@
|
||||
package com.jetbrains.python.testing;
|
||||
|
||||
import com.google.common.collect.ObjectArrays;
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.TextBrowseFolderListener;
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiDirectory;
|
||||
import com.intellij.ui.IdeBorderFactory;
|
||||
import com.intellij.ui.TextAccessor;
|
||||
@@ -35,15 +31,14 @@ import com.intellij.uiDesigner.core.GridConstraints;
|
||||
import com.intellij.util.ThreeState;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import com.jetbrains.PySymbolFieldWithBrowseButton;
|
||||
import com.jetbrains.extensions.python.FileChooserDescriptorExtKt;
|
||||
import com.jetbrains.extenstions.ContextAnchor;
|
||||
import com.jetbrains.extenstions.ModuleBasedContextAnchor;
|
||||
import com.jetbrains.extenstions.ProjectSdkContextAnchor;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import com.jetbrains.python.run.AbstractPyCommonOptionsForm;
|
||||
import com.jetbrains.python.run.PyBrowseActionListener;
|
||||
import com.jetbrains.python.run.PyCommonOptionsFormFactory;
|
||||
import com.jetbrains.python.run.targetBasedConfiguration.PyRunTargetVariant;
|
||||
import com.jetbrains.reflection.ReflectionUtilsKt;
|
||||
import com.jetbrains.reflection.SimplePropertiesProvider;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -156,7 +151,7 @@ public final class PyTestSharedForm implements SimplePropertiesProvider {
|
||||
@NotNull final CustomOption... customOptions) {
|
||||
final PyTestSharedForm form = new PyTestSharedForm(configuration.getModule(), configuration);
|
||||
|
||||
for (final TestTargetType testTargetType : TestTargetType.values()) {
|
||||
for (final PyRunTargetVariant testTargetType : PyRunTargetVariant.values()) {
|
||||
final JBRadioButton button =
|
||||
new JBRadioButton(StringUtil.capitalize(testTargetType.getCustomName().toLowerCase(Locale.getDefault())));
|
||||
button.setActionCommand(testTargetType.name());
|
||||
@@ -174,7 +169,7 @@ public final class PyTestSharedForm implements SimplePropertiesProvider {
|
||||
setBorderToPanel(form.myPanel, configuration.getTestFrameworkName());
|
||||
|
||||
form.addCustomOptions(
|
||||
ObjectArrays.concat(customOptions, new CustomOption(PyTestsSharedKt.getAdditionalArgumentsPropertyName(), TestTargetType.values()))
|
||||
ObjectArrays.concat(customOptions, new CustomOption(PyTestsSharedKt.getAdditionalArgumentsPropertyName(), PyRunTargetVariant.values()))
|
||||
);
|
||||
configuration.copyTo(ReflectionUtilsKt.getProperties(form, null, true));
|
||||
return form;
|
||||
@@ -226,7 +221,7 @@ public final class PyTestSharedForm implements SimplePropertiesProvider {
|
||||
// We should always use system-independent path because only this type of path is processed correctly
|
||||
// when stored (folder changed to macros to prevent hard code)
|
||||
final String targetText = getActiveTextField().getText().trim();
|
||||
return getTargetType() == TestTargetType.PATH ? FileUtil.toSystemIndependentName(targetText) : targetText;
|
||||
return getTargetType() == PyRunTargetVariant.PATH ? FileUtil.toSystemIndependentName(targetText) : targetText;
|
||||
}
|
||||
|
||||
|
||||
@@ -235,7 +230,7 @@ public final class PyTestSharedForm implements SimplePropertiesProvider {
|
||||
}
|
||||
|
||||
private void onTargetTypeChanged() {
|
||||
final TestTargetType targetType = getTargetType();
|
||||
final PyRunTargetVariant targetType = getTargetType();
|
||||
|
||||
for (final OptionHolder optionHolder : myCustomOptions.values()) {
|
||||
optionHolder.setType(targetType);
|
||||
@@ -246,31 +241,31 @@ public final class PyTestSharedForm implements SimplePropertiesProvider {
|
||||
cons.fill = GridBagConstraints.HORIZONTAL;
|
||||
cons.weightx = 1;
|
||||
|
||||
if (targetType == TestTargetType.PATH) {
|
||||
if (targetType == PyRunTargetVariant.PATH) {
|
||||
myPanelForTargetFields.add(myPathTarget, cons);
|
||||
}
|
||||
else if (targetType == TestTargetType.PYTHON) {
|
||||
else if (targetType == PyRunTargetVariant.PYTHON) {
|
||||
myPanelForTargetFields.add(myPythonTarget, cons);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private TextAccessor getActiveTextField() {
|
||||
return (getTargetType() == TestTargetType.PATH ? myPathTarget : myPythonTarget);
|
||||
return (getTargetType() == PyRunTargetVariant.PATH ? myPathTarget : myPythonTarget);
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess") // Accessor for property
|
||||
@NotNull
|
||||
public TestTargetType getTargetType() {
|
||||
return TestTargetType.valueOf(myButtonGroup.getSelection().getActionCommand());
|
||||
public PyRunTargetVariant getTargetType() {
|
||||
return PyRunTargetVariant.valueOf(myButtonGroup.getSelection().getActionCommand());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused") // Mutator for property
|
||||
public void setTargetType(@NotNull final TestTargetType target) {
|
||||
public void setTargetType(@NotNull final PyRunTargetVariant target) {
|
||||
final Enumeration<AbstractButton> elements = myButtonGroup.getElements();
|
||||
while (elements.hasMoreElements()) {
|
||||
final AbstractButton button = elements.nextElement();
|
||||
if (TestTargetType.valueOf(button.getActionCommand()) == target) {
|
||||
if (PyRunTargetVariant.valueOf(button.getActionCommand()) == target) {
|
||||
myButtonGroup.setSelected(button.getModel(), true);
|
||||
break;
|
||||
}
|
||||
@@ -287,10 +282,10 @@ public final class PyTestSharedForm implements SimplePropertiesProvider {
|
||||
/**
|
||||
* Types to display this option for
|
||||
*/
|
||||
private final EnumSet<TestTargetType> mySupportedTypes;
|
||||
private final EnumSet<PyRunTargetVariant> mySupportedTypes;
|
||||
|
||||
CustomOption(@NotNull final String name,
|
||||
@NotNull final TestTargetType... supportedTypes) {
|
||||
@NotNull final PyRunTargetVariant... supportedTypes) {
|
||||
myName = name;
|
||||
mySupportedTypes = EnumSet.copyOf(Arrays.asList(supportedTypes));
|
||||
}
|
||||
@@ -312,7 +307,7 @@ public final class PyTestSharedForm implements SimplePropertiesProvider {
|
||||
myOptionValue = optionValue;
|
||||
}
|
||||
|
||||
private void setType(@NotNull final TestTargetType type) {
|
||||
private void setType(@NotNull final PyRunTargetVariant type) {
|
||||
final boolean visible = myOption.mySupportedTypes.contains(type);
|
||||
myOptionLabel.setVisible(visible);
|
||||
myOptionValue.setVisible(visible);
|
||||
|
||||
@@ -28,7 +28,6 @@ import com.intellij.execution.runners.ExecutionEnvironment
|
||||
import com.intellij.execution.testframework.AbstractTestProxy
|
||||
import com.intellij.execution.testframework.sm.runner.SMTestLocator
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleUtilCore
|
||||
import com.intellij.openapi.module.impl.scopes.ModuleWithDependenciesScope
|
||||
import com.intellij.openapi.options.SettingsEditor
|
||||
import com.intellij.openapi.project.Project
|
||||
@@ -43,14 +42,14 @@ import com.intellij.openapi.vfs.VirtualFileManager
|
||||
import com.intellij.psi.PsiDirectory
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFileSystemItem
|
||||
import com.intellij.psi.PsiNamedElement
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.psi.util.QualifiedName
|
||||
import com.intellij.refactoring.listeners.RefactoringElementListener
|
||||
import com.intellij.refactoring.listeners.UndoRefactoringElementAdapter
|
||||
import com.intellij.util.ThreeState
|
||||
import com.jetbrains.extensions.asPsiElement
|
||||
import com.jetbrains.extensions.asVirtualFile
|
||||
import com.jetbrains.extensions.getQName
|
||||
import com.jetbrains.extensions.isWellFormed
|
||||
import com.jetbrains.extenstions.ModuleBasedContextAnchor
|
||||
import com.jetbrains.extenstions.QNameResolveContext
|
||||
import com.jetbrains.extenstions.getElementAndResolvableName
|
||||
@@ -61,6 +60,9 @@ import com.jetbrains.python.psi.PyFunction
|
||||
import com.jetbrains.python.psi.PyQualifiedNameOwner
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext
|
||||
import com.jetbrains.python.run.*
|
||||
import com.jetbrains.python.run.targetBasedConfiguration.PyRunTargetVariant
|
||||
import com.jetbrains.python.run.targetBasedConfiguration.TargetWithVariant
|
||||
import com.jetbrains.python.run.targetBasedConfiguration.createRefactoringListenerIfPossible
|
||||
import com.jetbrains.reflection.DelegationProperty
|
||||
import com.jetbrains.reflection.Properties
|
||||
import com.jetbrains.reflection.Property
|
||||
@@ -87,7 +89,7 @@ val factories: Array<PythonConfigurationFactoryBase> = arrayOf(
|
||||
fun processTCMessage(text: String): String {
|
||||
val parsedMessage = ServiceMessage.parse(text.trim()) ?: return text // Not a TC message
|
||||
return when (parsedMessage) {
|
||||
is TestStdOut -> parsedMessage.stdOut // TC with stdout
|
||||
is TestStdOut -> parsedMessage.stdOut // TC with stdout
|
||||
is TestStdErr -> parsedMessage.stdErr // TC with stderr
|
||||
else -> "" // TC with out of any output
|
||||
}
|
||||
@@ -152,6 +154,7 @@ private fun findConfigurationFactoryFromSettings(module: Module): ConfigurationF
|
||||
// folder provided by python side. Resolve test names versus it
|
||||
private val PATH_URL = java.util.regex.Pattern.compile("^python<([^<>]+)>$")
|
||||
|
||||
|
||||
/**
|
||||
* Resolves url into element
|
||||
*/
|
||||
@@ -190,7 +193,7 @@ private fun getElementByUrl(protocol: String,
|
||||
// so we cut them out of path not to provide unsupported targets to runners
|
||||
val pathNoParentheses = QualifiedName.fromComponents(
|
||||
qualifiedName.components.filter { !it.contains('(') }).toString()
|
||||
PyTargetBasedPsiLocation(ConfigurationTarget(pathNoParentheses, TestTargetType.PYTHON), element)
|
||||
PyTargetBasedPsiLocation(ConfigurationTarget(pathNoParentheses, PyRunTargetVariant.PYTHON), element)
|
||||
}
|
||||
else {
|
||||
null
|
||||
@@ -222,6 +225,7 @@ object PyTestsLocator : SMTestLocator {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
abstract class PyTestExecutionEnvironment<T : PyAbstractTestConfiguration>(configuration: T,
|
||||
environment: ExecutionEnvironment)
|
||||
: PythonTestCommandLineStateBase<T>(configuration, environment) {
|
||||
@@ -256,12 +260,6 @@ abstract class PyAbstractTestSettingsEditor(private val sharedForm: PyTestShared
|
||||
override fun createEditor(): javax.swing.JComponent = sharedForm.panel
|
||||
}
|
||||
|
||||
enum class TestTargetType(private val customName: String? = null) {
|
||||
PYTHON(PythonRunConfigurationForm.MODULE_NAME), PATH(PythonRunConfigurationForm.SCRIPT_PATH), CUSTOM;
|
||||
|
||||
fun getCustomName() = customName ?: name
|
||||
}
|
||||
|
||||
/**
|
||||
* Default target path (run all tests ion project folder)
|
||||
*/
|
||||
@@ -270,61 +268,39 @@ private val DEFAULT_PATH = ""
|
||||
/**
|
||||
* Target depends on target type. It could be path to file/folder or python target
|
||||
*/
|
||||
data class ConfigurationTarget(@ConfigField var target: String,
|
||||
@ConfigField var targetType: TestTargetType) {
|
||||
data class ConfigurationTarget(@ConfigField override var target: String,
|
||||
@ConfigField override var targetVariant: PyRunTargetVariant) : TargetWithVariant {
|
||||
fun copyTo(dst: ConfigurationTarget) {
|
||||
// TODO: do we have such method it in Kotlin?
|
||||
dst.target = target
|
||||
dst.targetType = targetType
|
||||
dst.targetVariant = targetVariant
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates configuration and throws exception if target is invalid
|
||||
*/
|
||||
fun checkValid() {
|
||||
if (targetType != TestTargetType.CUSTOM && target.isEmpty()) {
|
||||
if (targetVariant != PyRunTargetVariant.CUSTOM && target.isEmpty()) {
|
||||
throw RuntimeConfigurationWarning("Target not provided")
|
||||
}
|
||||
if (targetType == TestTargetType.PYTHON && !Regex("^[a-zA-Z0-9._]+[a-zA-Z0-9_]$").matches(target)) {
|
||||
throw RuntimeConfigurationWarning("Provide a qualified name of function, class or a module")
|
||||
if (targetVariant == PyRunTargetVariant.PYTHON && !isWellFormed()) {
|
||||
throw RuntimeConfigurationError("Provide a qualified name of function, class or a module")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts target to PSI element if possible resolving it against roots and working directory
|
||||
*/
|
||||
fun asPsiElement(configuration: PyAbstractTestConfiguration): PsiElement? {
|
||||
if (targetType == TestTargetType.PYTHON) {
|
||||
val module = configuration.module ?: return null
|
||||
val context = TypeEvalContext.userInitiated(configuration.project, null)
|
||||
val workDir = configuration.getWorkingDirectoryAsVirtual()
|
||||
val name = QualifiedName.fromDottedString(target)
|
||||
return name.resolveToElement(QNameResolveContext(ModuleBasedContextAnchor(module), configuration.sdk, context, workDir, true))
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts target to file if possible
|
||||
*/
|
||||
fun asVirtualFile(): VirtualFile? {
|
||||
if (targetType == TestTargetType.PATH) {
|
||||
return LocalFileSystem.getInstance().findFileByPath(target)
|
||||
}
|
||||
return null
|
||||
}
|
||||
fun asPsiElement(configuration: PyAbstractTestConfiguration) =
|
||||
asPsiElement(configuration, configuration.getWorkingDirectoryAsVirtual())
|
||||
|
||||
fun generateArgumentsLine(configuration: PyAbstractTestConfiguration): List<String> =
|
||||
when (targetType) {
|
||||
TestTargetType.CUSTOM -> emptyList()
|
||||
TestTargetType.PYTHON -> getArgumentsForPythonTarget(configuration)
|
||||
TestTargetType.PATH -> listOf("--path", target.trim())
|
||||
when (targetVariant) {
|
||||
PyRunTargetVariant.CUSTOM -> emptyList()
|
||||
PyRunTargetVariant.PYTHON -> getArgumentsForPythonTarget(configuration)
|
||||
PyRunTargetVariant.PATH -> listOf("--path", target.trim())
|
||||
}
|
||||
|
||||
private fun getArgumentsForPythonTarget(configuration: PyAbstractTestConfiguration): List<String> {
|
||||
val element = asPsiElement(configuration) ?:
|
||||
throw ExecutionException(
|
||||
"Can't resolve $target. Try to remove configuration and generate it again")
|
||||
val element = asPsiElement(configuration) ?: throw ExecutionException(
|
||||
"Can't resolve $target. Try to remove configuration and generate it again")
|
||||
|
||||
if (element is PsiDirectory) {
|
||||
// Directory is special case: we can't run it as package for now, so we run it as path
|
||||
@@ -338,9 +314,9 @@ data class ConfigurationTarget(@ConfigField var target: String,
|
||||
folderToStart = LocalFileSystem.getInstance().findFileByPath(configuration.workingDirectorySafe),
|
||||
allowInaccurateResult = true
|
||||
)
|
||||
val qualifiedNameParts = QualifiedName.fromDottedString(target.trim()).tryResolveAndSplit(qNameResolveContext) ?:
|
||||
throw ExecutionException("Can't find file where $target declared. " +
|
||||
"Make sure it is in project root")
|
||||
val qualifiedNameParts = QualifiedName.fromDottedString(target.trim()).tryResolveAndSplit(qNameResolveContext)
|
||||
?: throw ExecutionException("Can't find file where $target declared. " +
|
||||
"Make sure it is in project root")
|
||||
|
||||
// We can't provide element qname here: it may point to parent class in case of inherited functions,
|
||||
// so we make fix file part, but obey element(symbol) part of qname
|
||||
@@ -358,9 +334,8 @@ data class ConfigurationTarget(@ConfigField var target: String,
|
||||
return listOf("--target", elementAndName.name.toString())
|
||||
}
|
||||
// Use "full" (path from closest root) otherwise
|
||||
val name = (element.containingFile as? PyFile)?.getQName()?.append(qualifiedNameParts.elementName) ?:
|
||||
throw ExecutionException(
|
||||
"Can't get importable name for ${element.containingFile}. Is it a python file in project?")
|
||||
val name = (element.containingFile as? PyFile)?.getQName()?.append(qualifiedNameParts.elementName) ?: throw ExecutionException(
|
||||
"Can't get importable name for ${element.containingFile}. Is it a python file in project?")
|
||||
|
||||
return listOf("--target", name.toString())
|
||||
}
|
||||
@@ -419,7 +394,7 @@ abstract class PyAbstractTestConfiguration(project: Project,
|
||||
: AbstractPythonTestRunConfiguration<PyAbstractTestConfiguration>(project, configurationFactory), PyRerunAwareConfiguration,
|
||||
RefactoringListenerProvider {
|
||||
@DelegationProperty
|
||||
val target = ConfigurationTarget(DEFAULT_PATH, TestTargetType.PATH)
|
||||
val target = ConfigurationTarget(DEFAULT_PATH, PyRunTargetVariant.PATH)
|
||||
@ConfigField
|
||||
var additionalArguments = ""
|
||||
|
||||
@@ -441,16 +416,6 @@ abstract class PyAbstractTestConfiguration(project: Project,
|
||||
@DelegationProperty
|
||||
val legacyConfigurationAdapter = PyTestLegacyConfigurationAdapter(this)
|
||||
|
||||
/**
|
||||
* Renames working directory if folder physically renamed
|
||||
*/
|
||||
private open inner class PyConfigurationRenamer(private val workingDirectoryFile: VirtualFile?) : UndoRefactoringElementAdapter() {
|
||||
override fun refactored(element: PsiElement, oldQualifiedName: String?) {
|
||||
if (workingDirectoryFile != null) {
|
||||
workingDirectory = workingDirectoryFile.path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For real launch use [getWorkingDirectorySafe] instead
|
||||
@@ -471,51 +436,17 @@ abstract class PyAbstractTestConfiguration(project: Project,
|
||||
return target.getElementDirectory(this)?.path ?: super.getWorkingDirectorySafe()
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames python target if python symbol, module or folder renamed
|
||||
*/
|
||||
private inner class PyElementTargetRenamer(private val originalElement: PsiElement,
|
||||
workingDirectoryFile: VirtualFile?) :
|
||||
PyAbstractTestConfiguration.PyConfigurationRenamer(workingDirectoryFile) {
|
||||
override fun refactored(element: PsiElement, oldQualifiedName: String?) {
|
||||
super.refactored(element, oldQualifiedName)
|
||||
if (originalElement is PyQualifiedNameOwner) {
|
||||
target.target = originalElement.qualifiedName ?: return
|
||||
}
|
||||
else if (originalElement is PsiNamedElement) {
|
||||
target.target = originalElement.name ?: return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames folder target if file or folder really renamed
|
||||
*/
|
||||
private inner class PyVirtualFileRenamer(private val virtualFile: VirtualFile,
|
||||
workingDirectoryFile: VirtualFile?) :
|
||||
PyAbstractTestConfiguration.PyConfigurationRenamer(workingDirectoryFile) {
|
||||
override fun refactored(element: PsiElement, oldQualifiedName: String?) {
|
||||
super.refactored(element, oldQualifiedName)
|
||||
target.target = virtualFile.path
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRefactoringElementListener(element: PsiElement?): RefactoringElementListener? {
|
||||
val targetElement = target.asPsiElement(this)
|
||||
val workingDirectoryFile = getWorkingDirectoryAsVirtual()
|
||||
val targetFile = target.asVirtualFile()
|
||||
|
||||
|
||||
if (targetElement != null && PsiTreeUtil.isAncestor(element, targetElement, false)) {
|
||||
return PyElementTargetRenamer(targetElement, workingDirectoryFile)
|
||||
if (element == null) return null
|
||||
var renamer = CompositeRefactoringElementListener(PyWorkingDirectoryRenamer(getWorkingDirectoryAsVirtual(), this))
|
||||
createRefactoringListenerIfPossible(element, target.asPsiElement(this), target.asVirtualFile(), { target.target = it })?.let {
|
||||
renamer = renamer.plus(it)
|
||||
}
|
||||
if (targetFile != null && element is PsiFileSystemItem && VfsUtil.isAncestor(
|
||||
element.virtualFile, targetFile, false)) {
|
||||
return PyVirtualFileRenamer(targetFile, workingDirectoryFile)
|
||||
}
|
||||
return null
|
||||
return renamer
|
||||
}
|
||||
|
||||
|
||||
override fun checkConfiguration() {
|
||||
super.checkConfiguration()
|
||||
if (!isFrameworkInstalled()) {
|
||||
@@ -548,7 +479,7 @@ abstract class PyAbstractTestConfiguration(project: Project,
|
||||
val qualifiedName = (location.psiElement as PyQualifiedNameOwner).qualifiedName ?: return emptyList()
|
||||
|
||||
// Resolve name as python qname as last resort
|
||||
return ConfigurationTarget(qualifiedName, TestTargetType.PYTHON).generateArgumentsLine(this)
|
||||
return ConfigurationTarget(qualifiedName, PyRunTargetVariant.PYTHON).generateArgumentsLine(this)
|
||||
}
|
||||
|
||||
override fun getTestSpec(location: Location<*>,
|
||||
@@ -588,12 +519,12 @@ abstract class PyAbstractTestConfiguration(project: Project,
|
||||
}
|
||||
|
||||
override fun suggestedName() =
|
||||
when (target.targetType) {
|
||||
TestTargetType.PATH -> {
|
||||
when (target.targetVariant) {
|
||||
PyRunTargetVariant.PATH -> {
|
||||
val name = target.asVirtualFile()?.name
|
||||
"$testFrameworkName in " + (name ?: target.target)
|
||||
}
|
||||
TestTargetType.PYTHON -> {
|
||||
PyRunTargetVariant.PYTHON -> {
|
||||
"$testFrameworkName for " + target.target
|
||||
}
|
||||
else -> {
|
||||
@@ -609,7 +540,7 @@ abstract class PyAbstractTestConfiguration(project: Project,
|
||||
|
||||
fun reset() {
|
||||
target.target = DEFAULT_PATH
|
||||
target.targetType = TestTargetType.PATH
|
||||
target.targetVariant = PyRunTargetVariant.PATH
|
||||
additionalArguments = ""
|
||||
}
|
||||
|
||||
@@ -660,8 +591,8 @@ abstract class PyAbstractTestConfiguration(project: Project,
|
||||
* Checks if element could be test target for this config.
|
||||
* Function is used to create tests by context.
|
||||
*
|
||||
* If yes, and element is [PsiElement] then it is [TestTargetType.PYTHON].
|
||||
* If file then [TestTargetType.PATH]
|
||||
* If yes, and element is [PsiElement] then it is [PyRunTargetVariant.PYTHON].
|
||||
* If file then [PyRunTargetVariant.PATH]
|
||||
*/
|
||||
fun couldBeTestTarget(element: PsiElement): Boolean {
|
||||
|
||||
@@ -694,21 +625,6 @@ object PyTestsConfigurationProducer : AbstractPythonTestConfigurationProducer<Py
|
||||
|
||||
override val configurationClass = PyAbstractTestConfiguration::class.java
|
||||
|
||||
override fun createLightConfiguration(context: ConfigurationContext): RunConfiguration? {
|
||||
// Parent implementation does not support several factories
|
||||
// We need to get factory according to settings
|
||||
val psiElement = context.psiLocation ?: return null
|
||||
val module = context.module ?: ModuleUtilCore.findModuleForPsiElement(psiElement) ?: return null
|
||||
val conf = findConfigurationFactoryFromSettings(module).createTemplateConfiguration(context.project)
|
||||
as? PyAbstractTestConfiguration ?: return null
|
||||
|
||||
val ref = Ref(psiElement)
|
||||
if (setupConfigurationFromContext(conf, context, ref)) {
|
||||
return conf
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun cloneTemplateConfiguration(context: ConfigurationContext): RunnerAndConfigurationSettings {
|
||||
return cloneTemplateConfigurationStatic(context, findConfigurationFactoryFromSettings(context.module))
|
||||
}
|
||||
@@ -805,7 +721,7 @@ object PyTestsConfigurationProducer : AbstractPythonTestConfigurationProducer<Py
|
||||
folderToStart = workingDirectory.virtualFile)
|
||||
val parts = element.tryResolveAndSplit(context) ?: return null
|
||||
val qualifiedName = parts.getElementNamePrependingFile(workingDirectory)
|
||||
return Pair(ConfigurationTarget(qualifiedName.toString(), TestTargetType.PYTHON),
|
||||
return Pair(ConfigurationTarget(qualifiedName.toString(), PyRunTargetVariant.PYTHON),
|
||||
workingDirectory.virtualFile.path)
|
||||
}
|
||||
is PsiFileSystemItem -> {
|
||||
@@ -817,7 +733,7 @@ object PyTestsConfigurationProducer : AbstractPythonTestConfigurationProducer<Py
|
||||
is PsiDirectory -> element
|
||||
else -> return null
|
||||
}?.virtualFile?.path ?: return null
|
||||
return Pair(ConfigurationTarget(path.path, TestTargetType.PATH), workingDirectory)
|
||||
return Pair(ConfigurationTarget(path.path, PyRunTargetVariant.PATH), workingDirectory)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.jetbrains.python.PyNames
|
||||
import com.jetbrains.python.PythonHelper
|
||||
import com.jetbrains.python.run.targetBasedConfiguration.PyRunTargetVariant
|
||||
|
||||
/**
|
||||
* unittest
|
||||
@@ -34,7 +35,7 @@ import com.jetbrains.python.PythonHelper
|
||||
class PyUnitTestSettingsEditor(configuration: PyAbstractTestConfiguration) :
|
||||
PyAbstractTestSettingsEditor(
|
||||
PyTestSharedForm.create(configuration,
|
||||
PyTestSharedForm.CustomOption(PyUnitTestConfiguration::pattern.name, TestTargetType.PATH)
|
||||
PyTestSharedForm.CustomOption(PyUnitTestConfiguration::pattern.name, PyRunTargetVariant.PATH)
|
||||
))
|
||||
|
||||
class PyUnitTestExecutionEnvironment(configuration: PyUnitTestConfiguration, environment: ExecutionEnvironment) :
|
||||
@@ -66,7 +67,7 @@ class PyUnitTestConfiguration(project: Project, factory: PyUnitTestFactory) :
|
||||
|
||||
override fun getCustomRawArgumentsString(forRerun: Boolean): String {
|
||||
// Pattern can only be used with folders ("all in folder" in legacy terms)
|
||||
if ((!pattern.isNullOrEmpty()) && target.targetType != TestTargetType.CUSTOM) {
|
||||
if ((!pattern.isNullOrEmpty()) && target.targetVariant != PyRunTargetVariant.CUSTOM) {
|
||||
val path = LocalFileSystem.getInstance().findFileByPath(target.target) ?: return ""
|
||||
// "Pattern" works only for "discovery" mode and for "rerun" we are using "python" targets ("concrete" tests)
|
||||
return if (path.isDirectory && !forRerun) "-p $pattern" else ""
|
||||
@@ -81,7 +82,7 @@ class PyUnitTestConfiguration(project: Project, factory: PyUnitTestFactory) :
|
||||
* @return configuration should use runner for setup.py
|
||||
*/
|
||||
internal fun isSetupPyBased(): Boolean {
|
||||
val setupPy = target.targetType == TestTargetType.PATH && target.target.endsWith(PyNames.SETUP_DOT_PY)
|
||||
val setupPy = target.targetVariant == PyRunTargetVariant.PATH && target.target.endsWith(PyNames.SETUP_DOT_PY)
|
||||
return setupPy
|
||||
}
|
||||
|
||||
@@ -90,7 +91,7 @@ class PyUnitTestConfiguration(project: Project, factory: PyUnitTestFactory) :
|
||||
|
||||
override fun checkConfiguration() {
|
||||
super.checkConfiguration()
|
||||
if (target.targetType == TestTargetType.PATH && target.target.endsWith(".py") && !pattern.isNullOrEmpty()) {
|
||||
if (target.targetVariant == PyRunTargetVariant.PATH && target.target.endsWith(".py") && !pattern.isNullOrEmpty()) {
|
||||
throw RuntimeConfigurationWarning("Pattern can only be used to match files in folder. Can't use pattern for file.")
|
||||
}
|
||||
}
|
||||
|
||||
+8
-7
@@ -18,12 +18,13 @@ package com.jetbrains.env.python.testing;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.jetbrains.extensions.TargetWithVariantExtKt;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyFile;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.testing.ConfigurationTarget;
|
||||
import com.jetbrains.python.testing.PyAbstractTestConfiguration;
|
||||
import com.jetbrains.python.testing.TestTargetType;
|
||||
import com.jetbrains.python.run.targetBasedConfiguration.PyRunTargetVariant;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.Assert;
|
||||
@@ -89,11 +90,11 @@ class CreateConfigurationMultipleCasesTask<T extends PyAbstractTestConfiguration
|
||||
Assert.assertThat("Bad directory", configuration.getWorkingDirectorySafe(), Matchers.endsWith("test-test"));
|
||||
}
|
||||
else if (element instanceof PsiDirectory && elementName.endsWith("package_test")) {
|
||||
Assert.assertEquals("Working directory for folder should be same as folder", target.asVirtualFile(), workingDirectory);
|
||||
Assert.assertEquals("Working directory for folder should be same as folder", TargetWithVariantExtKt.asVirtualFile(target), workingDirectory);
|
||||
Assert.assertThat("Bad target", configuration.getTarget().getTarget(), Matchers.endsWith("package_test"));
|
||||
}
|
||||
else if (element instanceof PyFile && elementName.endsWith("test_in_package.py")) {
|
||||
final VirtualFile targetFile = target.asVirtualFile();
|
||||
final VirtualFile targetFile = TargetWithVariantExtKt.asVirtualFile(target);
|
||||
assert targetFile != null : "Failed to create virtual file for " + target;
|
||||
Assert.assertEquals("Working directory for file should be same as file's parent", targetFile.getParent(), workingDirectory);
|
||||
Assert.assertThat("Bad target", configuration.getTarget().getTarget(), Matchers.endsWith("test_in_package.py"));
|
||||
@@ -104,11 +105,11 @@ class CreateConfigurationMultipleCasesTask<T extends PyAbstractTestConfiguration
|
||||
Assert.assertEquals("Bad configuration for class", "test_in_package.TestLogic", target.getTarget());
|
||||
}
|
||||
else if (element instanceof PsiDirectory && elementName.endsWith("tests_folder")) {
|
||||
Assert.assertEquals("Bad configuration for no package folder", workingDirectory, target.asVirtualFile());
|
||||
Assert.assertEquals("Bad configuration for no package folder", workingDirectory, TargetWithVariantExtKt.asVirtualFile(target));
|
||||
Assert.assertThat("Bad target", configuration.getTarget().getTarget(), Matchers.endsWith("tests_folder"));
|
||||
}
|
||||
else if (element instanceof PyFile && elementName.endsWith("test_lonely.py")) {
|
||||
final VirtualFile targetFile = target.asVirtualFile();
|
||||
final VirtualFile targetFile = TargetWithVariantExtKt.asVirtualFile(target);
|
||||
assert targetFile != null : "Failed to create virtual file for " + target;
|
||||
Assert.assertEquals("Bad configuration for no package file", targetFile.getParent(), workingDirectory);
|
||||
Assert.assertThat("Bad target", configuration.getTarget().getTarget(), Matchers.endsWith("test_lonely.py"));
|
||||
@@ -122,10 +123,10 @@ class CreateConfigurationMultipleCasesTask<T extends PyAbstractTestConfiguration
|
||||
Assert.assertEquals("Bad configuration target", "test_functions.test_test", target.getTarget());
|
||||
}
|
||||
else if (element instanceof PyFunction && elementName.endsWith("foo")) {
|
||||
Assert.assertEquals("non-test function should lead to level-based test", TestTargetType.PATH, target.getTargetType());
|
||||
Assert.assertEquals("non-test function should lead to level-based test", PyRunTargetVariant.PATH, target.getTargetVariant());
|
||||
}
|
||||
else if (element instanceof PyFile && elementName.endsWith("test_foo.py")) {
|
||||
Assert.assertEquals("Wrong path for file-based target", TestTargetType.PATH, target.getTargetType());
|
||||
Assert.assertEquals("Wrong path for file-based target", PyRunTargetVariant.PATH, target.getTargetVariant());
|
||||
}
|
||||
else {
|
||||
throw new AssertionError("Unexpected configuration " + configuration);
|
||||
|
||||
+2
-1
@@ -15,6 +15,7 @@ import com.intellij.util.ObjectUtils;
|
||||
import com.jetbrains.env.PyExecutionFixtureTestTask;
|
||||
import com.jetbrains.python.run.PythonConfigurationFactoryBase;
|
||||
import com.jetbrains.python.run.PythonRunConfiguration;
|
||||
import com.jetbrains.python.run.targetBasedConfiguration.PyRunTargetVariant;
|
||||
import com.jetbrains.python.sdk.InvalidSdkException;
|
||||
import com.jetbrains.python.testing.*;
|
||||
import com.jetbrains.python.tools.sdkTools.SdkCreationType;
|
||||
@@ -185,7 +186,7 @@ public abstract class CreateConfigurationTestTask<T extends AbstractPythonTestRu
|
||||
|
||||
|
||||
protected void validateConfiguration() {
|
||||
getConfiguration().getTarget().setTargetType(TestTargetType.PATH);
|
||||
getConfiguration().getTarget().setTargetVariant(PyRunTargetVariant.PATH);
|
||||
getConfiguration().getTarget().setTarget("");
|
||||
|
||||
getConfiguration().checkConfiguration();
|
||||
|
||||
@@ -12,13 +12,14 @@ import com.intellij.util.PathUtil;
|
||||
import com.jetbrains.env.EnvTestTagsRequired;
|
||||
import com.jetbrains.env.PyEnvTestCase;
|
||||
import com.jetbrains.env.PyProcessWithConsoleTestTask;
|
||||
import com.jetbrains.env.python.testing.CreateConfigurationByFileTask.CreateConfigurationTestAndRenameClassTask;
|
||||
import com.jetbrains.env.python.testing.CreateConfigurationByFileTask.CreateConfigurationTestAndRenameFolderTask;
|
||||
import com.jetbrains.env.python.testing.CreateConfigurationTestTask.PyConfigurationValidationTask;
|
||||
import com.jetbrains.env.ut.PyTestTestProcessRunner;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.testing.*;
|
||||
import com.jetbrains.python.run.targetBasedConfiguration.PyRunTargetVariant;
|
||||
import com.jetbrains.python.testing.PyTestConfiguration;
|
||||
import com.jetbrains.python.testing.PyTestFactory;
|
||||
import com.jetbrains.python.testing.PyTestFrameworkService;
|
||||
import com.jetbrains.python.tools.sdkTools.SdkCreationType;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -433,7 +434,7 @@ public final class PythonPyTestingTest extends PyEnvTestCase {
|
||||
final VirtualFile fullFilePath = myFixture.getTempDirFixture().getFile("dir_test.py");
|
||||
assert fullFilePath != null : String.format("No dir_test.py in %s", myFixture.getTempDirFixture().getTempDirPath());
|
||||
configuration.getTarget().setTarget(fullFilePath.getPath());
|
||||
configuration.getTarget().setTargetType(TestTargetType.PATH);
|
||||
configuration.getTarget().setTargetVariant(PyRunTargetVariant.PATH);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ import com.jetbrains.python.PythonHelper;
|
||||
import com.jetbrains.python.console.PythonConsoleView;
|
||||
import com.jetbrains.python.psi.LanguageLevel;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.run.targetBasedConfiguration.PyRunTargetVariant;
|
||||
import com.jetbrains.python.sdk.InvalidSdkException;
|
||||
import com.jetbrains.python.testing.*;
|
||||
import com.jetbrains.python.tools.sdkTools.SdkCreationType;
|
||||
@@ -60,17 +61,17 @@ public final class PythonUnitTestingTest extends PythonUnitTestingLikeTest<PyUni
|
||||
|
||||
@Test(expected = RuntimeConfigurationWarning.class)
|
||||
public void testEmptyValidation() {
|
||||
new ConfigurationTarget("", TestTargetType.PATH).checkValid();
|
||||
new ConfigurationTarget("", PyRunTargetVariant.PATH).checkValid();
|
||||
}
|
||||
|
||||
@Test(expected = RuntimeConfigurationWarning.class)
|
||||
public void testPythonValidation() {
|
||||
new ConfigurationTarget("c:/bad/", TestTargetType.PYTHON).checkValid();
|
||||
new ConfigurationTarget("c:/bad/", PyRunTargetVariant.PYTHON).checkValid();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidationOk() {
|
||||
new ConfigurationTarget("foo.bar", TestTargetType.PYTHON).checkValid();
|
||||
new ConfigurationTarget("foo.bar", PyRunTargetVariant.PYTHON).checkValid();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,7 +184,7 @@ public final class PythonUnitTestingTest extends PythonUnitTestingLikeTest<PyUni
|
||||
protected void validateConfiguration() {
|
||||
final PyUnitTestConfiguration configuration = getConfiguration();
|
||||
configuration.setPattern("foo");
|
||||
configuration.getTarget().setTargetType(TestTargetType.PATH);
|
||||
configuration.getTarget().setTargetVariant(PyRunTargetVariant.PATH);
|
||||
configuration.getTarget().setTarget("foo.py");
|
||||
configuration.checkConfiguration();
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import com.jetbrains.python.run.AbstractPythonRunConfigurationParams;
|
||||
import com.jetbrains.python.testing.AbstractPythonTestRunConfigurationParams;
|
||||
import com.jetbrains.python.testing.ConfigurationTarget;
|
||||
import com.jetbrains.python.testing.PyAbstractTestConfiguration;
|
||||
import com.jetbrains.python.testing.TestTargetType;
|
||||
import com.jetbrains.python.run.targetBasedConfiguration.PyRunTargetVariant;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -68,10 +68,10 @@ public class PyScriptTestProcessRunner<CONF_T extends AbstractPythonRunConfigura
|
||||
final ConfigurationTarget target = ((PyAbstractTestConfiguration)configuration).getTarget();
|
||||
if (myScriptName.startsWith(TEST_TARGET_PREFIX)) {
|
||||
target.setTarget(myScriptName.substring(TEST_TARGET_PREFIX.length()));
|
||||
target.setTargetType(TestTargetType.PYTHON);
|
||||
target.setTargetVariant(PyRunTargetVariant.PYTHON);
|
||||
} else {
|
||||
target.setTarget(myScriptName);
|
||||
target.setTargetType(TestTargetType.PATH);
|
||||
target.setTargetVariant(PyRunTargetVariant.PATH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import com.jetbrains.python.sdk.flavors.CPythonSdkFlavor;
|
||||
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
|
||||
import com.jetbrains.python.testing.PyUnitTestConfiguration;
|
||||
import com.jetbrains.python.testing.PyUnitTestFactory;
|
||||
import com.jetbrains.python.testing.TestTargetType;
|
||||
import com.jetbrains.python.run.targetBasedConfiguration.PyRunTargetVariant;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -52,7 +52,7 @@ public class PyUnitTestProcessRunner extends PyScriptTestProcessRunner<PyUnitTes
|
||||
}
|
||||
|
||||
if (myScriptName.startsWith(TEST_PATTERN_PREFIX)) {
|
||||
configuration.getTarget().setTargetType(TestTargetType.PATH);
|
||||
configuration.getTarget().setTargetVariant(PyRunTargetVariant.PATH);
|
||||
configuration.getTarget().setTarget(".");
|
||||
configuration.setPattern(myScriptName.substring(TEST_PATTERN_PREFIX.length()));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user