PY-23190: Flask CLI run configuration

Flask CLI may me used to run server since 0.12
This commit is contained in:
Ilya.Kazakevich
2018-01-25 04:01:36 +03:00
parent 663771d680
commit 6e89c1f9c6
21 changed files with 343 additions and 186 deletions
@@ -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
}
@@ -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.")
}
}
@@ -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);
@@ -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()));
}