New test runners: convert old configurations to new format

* We can't loose old configurations. This change allows them to be
 imported and saved as new one.
 * Old configurations renamed to "legacy"
 * All legacy-specific code moved to PyUniversalTestLegacyInterop.kt
 to be removed later.
 * One type is now used for old and new configurations
This commit is contained in:
Ilya.Kazakevich
2017-02-01 16:23:01 +03:00
parent a5faad0406
commit d6e45b0963
30 changed files with 544 additions and 265 deletions
+1 -2
View File
@@ -151,8 +151,7 @@
<xdebugger.settings implementation="com.jetbrains.python.debugger.settings.PyDebuggerSettings"/>
<xdebugger.localAttachDebuggerProvider implementation="com.jetbrains.python.debugger.attach.PyLocalAttachDebuggerProvider"/>
<configurationType implementation="com.jetbrains.python.testing.PythonTestOldConfigurationType"/>
<configurationType implementation="com.jetbrains.python.testing.universalTests.PyUniversalTestsConfigurationType"/>
<configurationType implementation="com.jetbrains.python.testing.PythonTestConfigurationType"/>
<configurationType implementation="com.jetbrains.python.testing.tox.PyToxConfigurationType"/>
<runConfigurationProducer implementation="com.jetbrains.python.testing.unittest.PythonUnitTestConfigurationProducer"/>
@@ -52,7 +52,7 @@ import java.io.File;
* For new style see {@link com.jetbrains.python.testing.universalTests}
* User: catherine
*/
public abstract class AbstractPythonOldTestRunConfiguration<T extends AbstractPythonTestRunConfiguration<T>>
public abstract class AbstractPythonLegacyTestRunConfiguration<T extends AbstractPythonTestRunConfiguration<T>>
extends AbstractPythonTestRunConfiguration<T>
implements AbstractPythonRunConfigurationParams,
AbstractPythonTestRunConfigurationParams,
@@ -66,7 +66,7 @@ public abstract class AbstractPythonOldTestRunConfiguration<T extends AbstractPy
private String myPattern = ""; // pattern for modules in folder to match against
private boolean usePattern = false;
protected AbstractPythonOldTestRunConfiguration(Project project, ConfigurationFactory configurationFactory) {
protected AbstractPythonLegacyTestRunConfiguration(Project project, ConfigurationFactory configurationFactory) {
super(project, configurationFactory);
}
@@ -214,7 +214,7 @@ public abstract class AbstractPythonOldTestRunConfiguration<T extends AbstractPy
}
}
public boolean compareSettings(AbstractPythonOldTestRunConfiguration cfg) {
public boolean compareSettings(AbstractPythonLegacyTestRunConfiguration cfg) {
if (cfg == null) return false;
if (getTestType() != cfg.getTestType()) return false;
@@ -46,7 +46,7 @@ public abstract class AbstractPythonTestRunConfiguration<T extends AbstractPytho
}
/**
* Create test spec (string to be passed to runner, probably glued with {@link AbstractPythonOldTestRunConfiguration#TEST_NAME_PARTS_SPLITTER})
* Create test spec (string to be passed to runner, probably glued with {@link AbstractPythonLegacyTestRunConfiguration#TEST_NAME_PARTS_SPLITTER})
*
* @param location test location as reported by runner
* @param failedTest failed test
@@ -35,8 +35,8 @@ public interface AbstractPythonTestRunConfigurationParams {
String getMethodName();
void setMethodName(String methodName);
AbstractPythonOldTestRunConfiguration.TestType getTestType();
void setTestType(AbstractPythonOldTestRunConfiguration.TestType testType);
AbstractPythonLegacyTestRunConfiguration.TestType getTestType();
void setTestType(AbstractPythonLegacyTestRunConfiguration.TestType testType);
boolean usePattern();
void usePattern(boolean isPureUnittest);
@@ -15,25 +15,156 @@
*/
package com.jetbrains.python.testing;
import com.google.common.collect.ObjectArrays;
import com.intellij.execution.configurations.ConfigurationFactory;
import com.intellij.execution.configurations.ConfigurationType;
import com.intellij.execution.configurations.ConfigurationTypeUtil;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.impl.ProjectLifecycleListener;
import com.intellij.openapi.startup.StartupManager;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.run.PythonConfigurationFactoryBase;
import com.jetbrains.python.testing.doctest.PythonDocTestRunConfiguration;
import com.jetbrains.python.testing.nosetest.PythonNoseTestRunConfiguration;
import com.jetbrains.python.testing.pytest.PyTestRunConfiguration;
import com.jetbrains.python.testing.unittest.PythonUnitTestRunConfiguration;
import com.jetbrains.python.testing.universalTests.PyUniversalTestLegacyInteropKt;
import com.jetbrains.python.testing.universalTests.PyUniversalTestsKt;
import icons.PythonIcons;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
/**
* @author Ilya.Kazakevich
* User : catherine
* <p>
* This type is used both with Legacy and New test runners.
* {@link PyUniversalTestLegacyInteropKt} is used to support legacy. To drop legacy support, remove all code that depends on it.
*/
public abstract class PythonTestConfigurationType implements ConfigurationType {
public final class PythonTestConfigurationType implements ConfigurationType {
public static final String ID = "tests";
public final PythonConfigurationFactoryBase PY_DOCTEST_FACTORY = new PythonDocTestConfigurationFactory(this);
public final PythonConfigurationFactoryBase LEGACY_UNITTEST_FACTORY = new PythonLegacyUnitTestConfigurationFactory(this);
public final PythonConfigurationFactoryBase LEGACY_NOSETEST_FACTORY = new PythonLegacyNoseTestConfigurationFactory(this);
public final PythonConfigurationFactoryBase LEGACY_PYTEST_FACTORY = new PythonLegacyPyTestConfigurationFactory(this);
public static PythonTestConfigurationType getInstance() {
return ConfigurationTypeUtil.findConfigurationType(PythonTestConfigurationType.class);
}
public PythonTestConfigurationType() {
/*
According to PyUniversalTestLegacyInteropKt we need to call "projectInitialized" when it is initialized
*/
ApplicationManager.getApplication().getMessageBus().connect().subscribe(ProjectLifecycleListener.TOPIC, new ProjectLifecycleListener() {
@Override
public void projectComponentsInitialized(@NotNull
final Project project) {
if (project.isInitialized()) {
PyUniversalTestLegacyInteropKt.projectInitialized(project);
return;
}
StartupManager.getInstance(project).runWhenProjectIsInitialized(() -> PyUniversalTestLegacyInteropKt.projectInitialized(project));
}
});
}
@Override
public String getDisplayName() {
return PyBundle.message("runcfg.test.display_name");
}
@Override
public String getConfigurationTypeDescription() {
return PyBundle.message("runcfg.test.description");
}
@Override
public Icon getIcon() {
return PythonIcons.Python.PythonTests;
}
private static class PythonLegacyUnitTestConfigurationFactory extends PythonConfigurationFactoryBase {
protected PythonLegacyUnitTestConfigurationFactory(ConfigurationType configurationType) {
super(configurationType);
}
@Override
public RunConfiguration createTemplateConfiguration(Project project) {
return new PythonUnitTestRunConfiguration(project, this);
}
@Override
public String getName() {
return PyBundle.message("runcfg.unittest.display_name");
}
}
private static class PythonDocTestConfigurationFactory extends PythonConfigurationFactoryBase {
protected PythonDocTestConfigurationFactory(ConfigurationType configurationType) {
super(configurationType);
}
@Override
public RunConfiguration createTemplateConfiguration(Project project) {
return new PythonDocTestRunConfiguration(project, this);
}
@Override
public String getName() {
return PyBundle.message("runcfg.doctest.display_name");
}
}
private static class PythonLegacyPyTestConfigurationFactory extends PythonConfigurationFactoryBase {
protected PythonLegacyPyTestConfigurationFactory(ConfigurationType configurationType) {
super(configurationType);
}
@Override
public RunConfiguration createTemplateConfiguration(Project project) {
return new PyTestRunConfiguration(project, this);
}
@Override
public String getName() {
return PyBundle.message("runcfg.pytest.display_name");
}
}
private static class PythonLegacyNoseTestConfigurationFactory extends PythonConfigurationFactoryBase {
protected PythonLegacyNoseTestConfigurationFactory(ConfigurationType configurationType) {
super(configurationType);
}
@Override
public RunConfiguration createTemplateConfiguration(Project project) {
return new PythonNoseTestRunConfiguration(project, this);
}
@Override
public String getName() {
return PyBundle.message("runcfg.nosetests.display_name");
}
}
@NotNull
@Override
public String getId() {
return ID;
}
@Override
public ConfigurationFactory[] getConfigurationFactories() {
// Use new or legacy factories depending to new config
final ConfigurationFactory[] factories = PyUniversalTestLegacyInteropKt.isNewTestsModeEnabled()
? PyUniversalTestsKt.getFactories()
: new ConfigurationFactory[]
{LEGACY_UNITTEST_FACTORY, LEGACY_NOSETEST_FACTORY, LEGACY_PYTEST_FACTORY};
return ObjectArrays.concat(factories, PY_DOCTEST_FACTORY);
}
}
@@ -41,7 +41,7 @@ import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.types.TypeEvalContext;
import com.jetbrains.python.run.PythonRunConfigurationProducer;
import com.jetbrains.python.testing.unittest.PythonUnitTestRunConfiguration;
import com.jetbrains.python.testing.universalTests.PyUniversalTestsKt;
import com.jetbrains.python.testing.universalTests.PyUniversalTestLegacyInteropKt;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -54,14 +54,14 @@ import java.util.List;
/**
* User: ktisha
*/
abstract public class PythonTestOldConfigurationProducer<T extends AbstractPythonOldTestRunConfiguration<T>> extends RunConfigurationProducer<AbstractPythonOldTestRunConfiguration<T>> {
abstract public class PythonTestLegacyConfigurationProducer<T extends AbstractPythonLegacyTestRunConfiguration<T>> extends RunConfigurationProducer<AbstractPythonLegacyTestRunConfiguration<T>> {
public PythonTestOldConfigurationProducer(final ConfigurationFactory configurationFactory) {
public PythonTestLegacyConfigurationProducer(final ConfigurationFactory configurationFactory) {
super(configurationFactory);
}
@Override
public boolean isConfigurationFromContext(AbstractPythonOldTestRunConfiguration configuration, ConfigurationContext context) {
public boolean isConfigurationFromContext(AbstractPythonLegacyTestRunConfiguration configuration, ConfigurationContext context) {
final Location location = context.getLocation();
if (location == null || !isAvailable(location)) return false;
final PsiElement element = location.getPsiElement();
@@ -73,12 +73,12 @@ abstract public class PythonTestOldConfigurationProducer<T extends AbstractPytho
final PyFunction pyFunction = PsiTreeUtil.getParentOfType(element, PyFunction.class, false);
final PyClass pyClass = PsiTreeUtil.getParentOfType(element, PyClass.class);
final AbstractPythonOldTestRunConfiguration.TestType confType = configuration.getTestType();
final AbstractPythonLegacyTestRunConfiguration.TestType confType = configuration.getTestType();
final String workingDirectory = configuration.getWorkingDirectory();
if (element instanceof PsiDirectory) {
final String path = ((PsiDirectory)element).getVirtualFile().getPath();
return confType == AbstractPythonOldTestRunConfiguration.TestType.TEST_FOLDER &&
return confType == AbstractPythonLegacyTestRunConfiguration.TestType.TEST_FOLDER &&
path.equals(configuration.getFolderName()) ||
path.equals(new File(workingDirectory, configuration.getFolderName()).getAbsolutePath());
}
@@ -90,27 +90,27 @@ abstract public class PythonTestOldConfigurationProducer<T extends AbstractPytho
if (pyFunction != null) {
final String methodName = configuration.getMethodName();
if (pyFunction.getContainingClass() == null) {
return confType == AbstractPythonOldTestRunConfiguration.TestType.TEST_FUNCTION &&
return confType == AbstractPythonLegacyTestRunConfiguration.TestType.TEST_FUNCTION &&
methodName.equals(pyFunction.getName()) && isTestFileEquals;
}
else {
final String className = configuration.getClassName();
return confType == AbstractPythonOldTestRunConfiguration.TestType.TEST_METHOD &&
return confType == AbstractPythonLegacyTestRunConfiguration.TestType.TEST_METHOD &&
methodName.equals(pyFunction.getName()) &&
pyClass != null && className.equals(pyClass.getName()) && isTestFileEquals;
}
}
if (pyClass != null) {
final String className = configuration.getClassName();
return confType == AbstractPythonOldTestRunConfiguration.TestType.TEST_CLASS &&
return confType == AbstractPythonLegacyTestRunConfiguration.TestType.TEST_CLASS &&
className.equals(pyClass.getName()) && isTestFileEquals;
}
return confType == AbstractPythonOldTestRunConfiguration.TestType.TEST_SCRIPT && isTestFileEquals;
return confType == AbstractPythonLegacyTestRunConfiguration.TestType.TEST_SCRIPT && isTestFileEquals;
}
@Override
protected boolean setupConfigurationFromContext(AbstractPythonOldTestRunConfiguration<T> configuration,
protected boolean setupConfigurationFromContext(AbstractPythonLegacyTestRunConfiguration<T> configuration,
ConfigurationContext context,
Ref<PsiElement> sourceElement) {
if (context == null) return false;
@@ -147,12 +147,12 @@ abstract public class PythonTestOldConfigurationProducer<T extends AbstractPytho
}
private boolean setupConfigurationFromFolder(@NotNull final PsiDirectory element,
@NotNull final AbstractPythonOldTestRunConfiguration configuration) {
@NotNull final AbstractPythonLegacyTestRunConfiguration configuration) {
final VirtualFile virtualFile = element.getVirtualFile();
if (!isTestFolder(virtualFile, element.getProject())) return false;
final String path = virtualFile.getPath();
configuration.setTestType(AbstractPythonOldTestRunConfiguration.TestType.TEST_FOLDER);
configuration.setTestType(AbstractPythonLegacyTestRunConfiguration.TestType.TEST_FOLDER);
configuration.setFolderName(path);
configuration.setWorkingDirectory(path);
configuration.setGeneratedName();
@@ -160,40 +160,40 @@ abstract public class PythonTestOldConfigurationProducer<T extends AbstractPytho
return true;
}
private static void setModuleSdk(@NotNull final PsiElement element, @NotNull final AbstractPythonOldTestRunConfiguration configuration) {
private static void setModuleSdk(@NotNull final PsiElement element, @NotNull final AbstractPythonLegacyTestRunConfiguration configuration) {
configuration.setUseModuleSdk(true);
configuration.setModule(ModuleUtilCore.findModuleForPsiElement(element));
}
protected boolean setupConfigurationFromFunction(@NotNull final PyFunction pyFunction,
@NotNull final AbstractPythonOldTestRunConfiguration configuration) {
@NotNull final AbstractPythonLegacyTestRunConfiguration configuration) {
final PyClass containingClass = pyFunction.getContainingClass();
configuration.setMethodName(pyFunction.getName());
if (containingClass != null) {
configuration.setClassName(containingClass.getName());
configuration.setTestType(AbstractPythonOldTestRunConfiguration.TestType.TEST_METHOD);
configuration.setTestType(AbstractPythonLegacyTestRunConfiguration.TestType.TEST_METHOD);
}
else {
configuration.setTestType(AbstractPythonOldTestRunConfiguration.TestType.TEST_FUNCTION);
configuration.setTestType(AbstractPythonLegacyTestRunConfiguration.TestType.TEST_FUNCTION);
}
return setupConfigurationScript(configuration, pyFunction);
}
protected boolean setupConfigurationFromClass(@NotNull final PyClass pyClass,
@NotNull final AbstractPythonOldTestRunConfiguration configuration) {
configuration.setTestType(AbstractPythonOldTestRunConfiguration.TestType.TEST_CLASS);
@NotNull final AbstractPythonLegacyTestRunConfiguration configuration) {
configuration.setTestType(AbstractPythonLegacyTestRunConfiguration.TestType.TEST_CLASS);
configuration.setClassName(pyClass.getName());
return setupConfigurationScript(configuration, pyClass);
}
protected boolean setupConfigurationFromFile(@NotNull final PyFile pyFile,
@NotNull final AbstractPythonOldTestRunConfiguration configuration) {
configuration.setTestType(AbstractPythonOldTestRunConfiguration.TestType.TEST_SCRIPT);
@NotNull final AbstractPythonLegacyTestRunConfiguration configuration) {
configuration.setTestType(AbstractPythonLegacyTestRunConfiguration.TestType.TEST_SCRIPT);
return setupConfigurationScript(configuration, pyFile);
}
protected static boolean setupConfigurationScript(@NotNull final AbstractPythonOldTestRunConfiguration cfg,
protected static boolean setupConfigurationScript(@NotNull final AbstractPythonLegacyTestRunConfiguration cfg,
@NotNull final PyElement element) {
final PyFile containingFile = PyUtil.getContainingPyFile(element);
if (containingFile == null) return false;
@@ -227,12 +227,12 @@ abstract public class PythonTestOldConfigurationProducer<T extends AbstractPytho
}
protected boolean isTestClass(@NotNull final PyClass pyClass,
@Nullable final AbstractPythonOldTestRunConfiguration configuration, @Nullable final TypeEvalContext context) {
@Nullable final AbstractPythonLegacyTestRunConfiguration configuration, @Nullable final TypeEvalContext context) {
return PythonUnitTestUtil.isTestCaseClass(pyClass, context);
}
protected boolean isTestFunction(@NotNull final PyFunction pyFunction,
@Nullable final AbstractPythonOldTestRunConfiguration configuration) {
@Nullable final AbstractPythonLegacyTestRunConfiguration configuration) {
return PythonUnitTestUtil.isTestCaseFunction(pyFunction);
}
@@ -264,14 +264,14 @@ abstract public class PythonTestOldConfigurationProducer<T extends AbstractPytho
@Override
public boolean isPreferredConfiguration(ConfigurationFromContext self, ConfigurationFromContext other) {
if (PyUniversalTestsKt.isUniversalModeEnabled()) {
if (PyUniversalTestLegacyInteropKt.isNewTestsModeEnabled()) {
return false;
}
final RunConfiguration configuration = self.getConfiguration();
if (configuration instanceof PythonUnitTestRunConfiguration &&
((AbstractPythonTestRunConfigurationParams)configuration).getTestType() == AbstractPythonOldTestRunConfiguration.TestType.TEST_FOLDER) {
((AbstractPythonTestRunConfigurationParams)configuration).getTestType() == AbstractPythonLegacyTestRunConfiguration.TestType.TEST_FOLDER) {
return true;
}
return other.isProducedBy(PythonTestOldConfigurationProducer.class) || other.isProducedBy(PythonRunConfigurationProducer.class);
return other.isProducedBy(PythonTestLegacyConfigurationProducer.class) || other.isProducedBy(PythonRunConfigurationProducer.class);
}
}
@@ -1,134 +0,0 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.testing;
import com.intellij.execution.configurations.ConfigurationFactory;
import com.intellij.execution.configurations.ConfigurationType;
import com.intellij.execution.configurations.ConfigurationTypeUtil;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.openapi.project.Project;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.run.PythonConfigurationFactoryBase;
import com.jetbrains.python.testing.doctest.PythonDocTestRunConfiguration;
import com.jetbrains.python.testing.nosetest.PythonNoseTestRunConfiguration;
import com.jetbrains.python.testing.pytest.PyTestRunConfiguration;
import com.jetbrains.python.testing.unittest.PythonUnitTestRunConfiguration;
import com.jetbrains.python.testing.universalTests.PyUniversalTestsKt;
import org.jetbrains.annotations.NotNull;
/**
* User : catherine
*/
public class PythonTestOldConfigurationType extends PythonTestConfigurationType {
public static final String ID = "tests";
public final PythonDocTestConfigurationFactory PY_DOCTEST_FACTORY = new PythonDocTestConfigurationFactory(this);
public final PythonUnitTestConfigurationFactory PY_UNITTEST_FACTORY = new PythonUnitTestConfigurationFactory(this);
public final PythonNoseTestConfigurationFactory PY_NOSETEST_FACTORY = new PythonNoseTestConfigurationFactory(this);
public final PythonPyTestConfigurationFactory PY_PYTEST_FACTORY = new PythonPyTestConfigurationFactory(this);
public static PythonTestOldConfigurationType getInstance() {
return ConfigurationTypeUtil.findConfigurationType(PythonTestOldConfigurationType.class);
}
@Override
public String getDisplayName() {
// Only doctests are supported when new mode (isUniversalModeEnabled)
return (PyUniversalTestsKt.isUniversalModeEnabled()
? super.getDisplayName() + ' ' + PY_DOCTEST_FACTORY.getName()
: super.getDisplayName());
}
private static class PythonUnitTestConfigurationFactory extends PythonConfigurationFactoryBase {
protected PythonUnitTestConfigurationFactory(ConfigurationType configurationType) {
super(configurationType);
}
@Override
public RunConfiguration createTemplateConfiguration(Project project) {
return new PythonUnitTestRunConfiguration(project, this);
}
@Override
public String getName() {
return PyBundle.message("runcfg.unittest.display_name");
}
}
private static class PythonDocTestConfigurationFactory extends PythonConfigurationFactoryBase {
protected PythonDocTestConfigurationFactory(ConfigurationType configurationType) {
super(configurationType);
}
@Override
public RunConfiguration createTemplateConfiguration(Project project) {
return new PythonDocTestRunConfiguration(project, this);
}
@Override
public String getName() {
return PyBundle.message("runcfg.doctest.display_name");
}
}
private static class PythonPyTestConfigurationFactory extends PythonConfigurationFactoryBase {
protected PythonPyTestConfigurationFactory(ConfigurationType configurationType) {
super(configurationType);
}
@Override
public RunConfiguration createTemplateConfiguration(Project project) {
return new PyTestRunConfiguration(project, this);
}
@Override
public String getName() {
return PyBundle.message("runcfg.pytest.display_name");
}
}
private static class PythonNoseTestConfigurationFactory extends PythonConfigurationFactoryBase {
protected PythonNoseTestConfigurationFactory(ConfigurationType configurationType) {
super(configurationType);
}
@Override
public RunConfiguration createTemplateConfiguration(Project project) {
return new PythonNoseTestRunConfiguration(project, this);
}
@Override
public String getName() {
return PyBundle.message("runcfg.nosetests.display_name");
}
}
@NotNull
@Override
public String getId() {
return ID;
}
@Override
public ConfigurationFactory[] getConfigurationFactories() {
if (PyUniversalTestsKt.isUniversalModeEnabled()) {
return new ConfigurationFactory[]{PY_DOCTEST_FACTORY};
}
return new ConfigurationFactory[]{PY_UNITTEST_FACTORY, PY_DOCTEST_FACTORY, PY_NOSETEST_FACTORY,
PY_PYTEST_FACTORY};
}
}
@@ -68,7 +68,7 @@ public class PythonTestRunConfigurationForm implements AbstractPythonTestRunConf
private boolean myPatternIsVisible = true;
public PythonTestRunConfigurationForm(final Project project,
final AbstractPythonOldTestRunConfiguration configuration) {
final AbstractPythonLegacyTestRunConfiguration configuration) {
myProject = project;
myCommonOptionsForm = PyCommonOptionsFormFactory.getInstance().createForm(configuration.getCommonOptionsFormData());
myCommonOptionsPlaceholder.add(myCommonOptionsForm.getMainPanel(), BorderLayout.CENTER);
@@ -243,8 +243,8 @@ public class PythonTestRunConfigurationForm implements AbstractPythonTestRunConf
myTestMethodComponent.getLabel().setText(testType == TestType.TEST_METHOD? PyBundle.message("runcfg.unittest.dlg.method_label")
: PyBundle.message("runcfg.unittest.dlg.function_label"));
if (myPatternIsVisible) {
myPatternTextField.setVisible(getTestType() == AbstractPythonOldTestRunConfiguration.TestType.TEST_FOLDER);
myPatternCheckBox.setVisible(getTestType() == AbstractPythonOldTestRunConfiguration.TestType.TEST_FOLDER);
myPatternTextField.setVisible(getTestType() == AbstractPythonLegacyTestRunConfiguration.TestType.TEST_FOLDER);
myPatternCheckBox.setVisible(getTestType() == AbstractPythonLegacyTestRunConfiguration.TestType.TEST_FOLDER);
}
}
@@ -20,7 +20,7 @@ import com.intellij.conversion.CannotConvertException;
import com.intellij.conversion.ConversionProcessor;
import com.intellij.conversion.RunManagerSettings;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.testing.PythonTestOldConfigurationType;
import com.jetbrains.python.testing.PythonTestConfigurationType;
import org.jdom.Element;
/**
@@ -50,7 +50,7 @@ public class PythonTestRunConfigurationsConverter extends ConversionProcessor<Ru
final String confType = element.getAttributeValue("type");
final String factoryName = ourTypeToFactoryNameMap.get(confType);
if (factoryName != null) {
element.setAttribute("type", PythonTestOldConfigurationType.ID);
element.setAttribute("type", PythonTestConfigurationType.ID);
element.setAttribute("factoryName", factoryName);
}
}
@@ -31,28 +31,28 @@ import com.jetbrains.python.psi.PyElement;
import com.jetbrains.python.psi.PyFile;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.types.TypeEvalContext;
import com.jetbrains.python.testing.AbstractPythonOldTestRunConfiguration;
import com.jetbrains.python.testing.PythonTestOldConfigurationProducer;
import com.jetbrains.python.testing.PythonTestOldConfigurationType;
import com.jetbrains.python.testing.AbstractPythonLegacyTestRunConfiguration;
import com.jetbrains.python.testing.PythonTestLegacyConfigurationProducer;
import com.jetbrains.python.testing.PythonTestConfigurationType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class PythonDocTestConfigurationProducer extends PythonTestOldConfigurationProducer {
public class PythonDocTestConfigurationProducer extends PythonTestLegacyConfigurationProducer {
public PythonDocTestConfigurationProducer() {
super(PythonTestOldConfigurationType.getInstance().PY_DOCTEST_FACTORY);
super(PythonTestConfigurationType.getInstance().PY_DOCTEST_FACTORY);
}
@Override
protected boolean isTestFunction(@NotNull final PyFunction pyFunction, @Nullable final AbstractPythonOldTestRunConfiguration configuration) {
protected boolean isTestFunction(@NotNull final PyFunction pyFunction, @Nullable final AbstractPythonLegacyTestRunConfiguration configuration) {
return PythonDocTestUtil.isDocTestFunction(pyFunction);
}
@Override
protected boolean isTestClass(@NotNull PyClass pyClass,
@Nullable final AbstractPythonOldTestRunConfiguration configuration,
@Nullable final AbstractPythonLegacyTestRunConfiguration configuration,
@Nullable final TypeEvalContext context) {
return PythonDocTestUtil.isDocTestClass(pyClass);
}
@@ -23,14 +23,14 @@ import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.jetbrains.python.testing.AbstractPythonOldTestRunConfiguration;
import com.jetbrains.python.testing.AbstractPythonLegacyTestRunConfiguration;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
/**
* User: catherine
*/
public class PythonDocTestRunConfiguration extends AbstractPythonOldTestRunConfiguration<PythonDocTestRunConfiguration>
public class PythonDocTestRunConfiguration extends AbstractPythonLegacyTestRunConfiguration<PythonDocTestRunConfiguration>
implements PythonDocTestRunConfigurationParams {
protected String myPluralTitle = "Doctests";
protected String myTitle = "Doctest";
@@ -27,9 +27,9 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PythonNoseTestConfigurationProducer extends
PythonTestOldConfigurationProducer {
PythonTestLegacyConfigurationProducer {
public PythonNoseTestConfigurationProducer() {
super(PythonTestOldConfigurationType.getInstance().PY_NOSETEST_FACTORY);
super(PythonTestConfigurationType.getInstance().LEGACY_NOSETEST_FACTORY);
}
protected boolean isAvailable(@NotNull final Location location) {
@@ -45,7 +45,7 @@ public class PythonNoseTestConfigurationProducer extends
}
@Override
protected boolean isTestFunction(@NotNull final PyFunction pyFunction, @Nullable final AbstractPythonOldTestRunConfiguration configuration) {
protected boolean isTestFunction(@NotNull final PyFunction pyFunction, @Nullable final AbstractPythonLegacyTestRunConfiguration configuration) {
return PythonUnitTestUtil.isTestCaseFunction(pyFunction, true);
}
}
@@ -26,7 +26,7 @@ import com.intellij.openapi.util.JDOMExternalizerUtil;
import com.intellij.openapi.util.WriteExternalException;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.sdk.PythonSdkType;
import com.jetbrains.python.testing.AbstractPythonOldTestRunConfiguration;
import com.jetbrains.python.testing.AbstractPythonLegacyTestRunConfiguration;
import com.jetbrains.python.testing.VFSTestFrameworkListener;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
@@ -34,7 +34,7 @@ import org.jetbrains.annotations.NotNull;
/**
* User: catherine
*/
public class PythonNoseTestRunConfiguration extends AbstractPythonOldTestRunConfiguration<PythonNoseTestRunConfiguration>
public class PythonNoseTestRunConfiguration extends AbstractPythonLegacyTestRunConfiguration<PythonNoseTestRunConfiguration>
implements PythonNoseTestRunConfigurationParams {
private String myParams = ""; // parameters for nosetests
protected String myTitle = "Nosetest";
@@ -80,7 +80,7 @@ public class PythonNoseTestRunConfiguration extends AbstractPythonOldTestRunConf
}
public static void copyParams(PythonNoseTestRunConfigurationParams source, PythonNoseTestRunConfigurationParams target) {
AbstractPythonOldTestRunConfiguration.copyParams(source.getTestRunConfigurationParams(), target.getTestRunConfigurationParams());
AbstractPythonLegacyTestRunConfiguration.copyParams(source.getTestRunConfigurationParams(), target.getTestRunConfigurationParams());
target.setParams(source.getParams());
target.useParam(source.useParam());
}
@@ -44,14 +44,14 @@ import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.List;
public class PyTestConfigurationProducer extends PythonTestOldConfigurationProducer<PyTestRunConfiguration> {
public class PyTestConfigurationProducer extends PythonTestLegacyConfigurationProducer<PyTestRunConfiguration> {
public PyTestConfigurationProducer() {
super(PythonTestOldConfigurationType.getInstance().PY_PYTEST_FACTORY);
super(PythonTestConfigurationType.getInstance().LEGACY_PYTEST_FACTORY);
}
@Override
protected boolean setupConfigurationFromContext(AbstractPythonOldTestRunConfiguration<PyTestRunConfiguration> configuration,
protected boolean setupConfigurationFromContext(AbstractPythonLegacyTestRunConfiguration<PyTestRunConfiguration> configuration,
ConfigurationContext context,
Ref<PsiElement> sourceElement) {
final PsiElement element = sourceElement.get();
@@ -144,7 +144,7 @@ public class PyTestConfigurationProducer extends PythonTestOldConfigurationProdu
}
@Override
public boolean isConfigurationFromContext(AbstractPythonOldTestRunConfiguration configuration, ConfigurationContext context) {
public boolean isConfigurationFromContext(AbstractPythonLegacyTestRunConfiguration configuration, ConfigurationContext context) {
final Location location = context.getLocation();
if (location == null) return false;
if (!(configuration instanceof PyTestRunConfiguration)) return false;
@@ -33,7 +33,7 @@ import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.sdk.PythonSdkType;
import com.jetbrains.python.testing.AbstractPythonOldTestRunConfiguration;
import com.jetbrains.python.testing.AbstractPythonLegacyTestRunConfiguration;
import com.jetbrains.python.testing.VFSTestFrameworkListener;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
@@ -45,7 +45,7 @@ import java.util.List;
/**
* @author yole
*/
public class PyTestRunConfiguration extends AbstractPythonOldTestRunConfiguration<PyTestRunConfiguration>
public class PyTestRunConfiguration extends AbstractPythonLegacyTestRunConfiguration<PyTestRunConfiguration>
implements PyTestRunConfigurationParams {
private String myTestToRun = "";
private String myKeywords = "";
@@ -23,7 +23,7 @@ import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PythonHelper;
import com.jetbrains.python.testing.AbstractPythonOldTestRunConfiguration;
import com.jetbrains.python.testing.AbstractPythonLegacyTestRunConfiguration;
import com.jetbrains.python.testing.PythonTestCommandLineStateBase;
import com.jetbrains.python.testing.PythonUnitTestTestIdUrlProvider;
import org.jetbrains.annotations.NotNull;
@@ -45,7 +45,7 @@ public class PythonUnitTestCommandLineState extends PythonTestCommandLineStateBa
@Override
protected PythonHelper getRunner() {
if (myConfig.getTestType() == AbstractPythonOldTestRunConfiguration.TestType.TEST_SCRIPT &&
if (myConfig.getTestType() == AbstractPythonLegacyTestRunConfiguration.TestType.TEST_SCRIPT &&
myConfig.getScriptName().endsWith(PyNames.SETUP_DOT_PY))
return PythonHelper.SETUPPY;
return PythonHelper.UT_OLD;
@@ -98,7 +98,7 @@ public class PythonUnitTestCommandLineState extends PythonTestCommandLineStateBa
if (myConfig.useParam() && !StringUtil.isEmptyOrSpaces(myConfig.getParams()))
script_params.addParameter(myConfig.getParams());
if (myConfig.getTestType() != AbstractPythonOldTestRunConfiguration.TestType.TEST_SCRIPT ||
if (myConfig.getTestType() != AbstractPythonLegacyTestRunConfiguration.TestType.TEST_SCRIPT ||
!myConfig.getScriptName().endsWith(PyNames.SETUP_DOT_PY))
script_params.addParameter(String.valueOf(myConfig.isPureUnittest()));
}
@@ -36,9 +36,9 @@ import org.jetbrains.annotations.Nullable;
import java.util.List;
public class PythonUnitTestConfigurationProducer extends PythonTestOldConfigurationProducer {
public class PythonUnitTestConfigurationProducer extends PythonTestLegacyConfigurationProducer {
public PythonUnitTestConfigurationProducer() {
super(PythonTestOldConfigurationType.getInstance().PY_UNITTEST_FACTORY);
super(PythonTestConfigurationType.getInstance().LEGACY_UNITTEST_FACTORY);
}
protected boolean isAvailable(@NotNull final Location location) {
@@ -54,7 +54,7 @@ public class PythonUnitTestConfigurationProducer extends PythonTestOldConfigurat
@Override
protected boolean isTestFunction(@NotNull final PyFunction pyFunction,
@Nullable final AbstractPythonOldTestRunConfiguration configuration) {
@Nullable final AbstractPythonLegacyTestRunConfiguration configuration) {
final boolean isTestFunction = super.isTestFunction(pyFunction, configuration);
return isTestFunction || (configuration instanceof PythonUnitTestRunConfiguration &&
!((PythonUnitTestRunConfiguration)configuration).isPureUnittest());
@@ -62,7 +62,7 @@ public class PythonUnitTestConfigurationProducer extends PythonTestOldConfigurat
@Override
protected boolean isTestClass(@NotNull PyClass pyClass,
@Nullable final AbstractPythonOldTestRunConfiguration configuration,
@Nullable final AbstractPythonLegacyTestRunConfiguration configuration,
TypeEvalContext context) {
final boolean isTestClass = super.isTestClass(pyClass, configuration, context);
return isTestClass || (configuration instanceof PythonUnitTestRunConfiguration &&
@@ -24,7 +24,7 @@ import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.JDOMExternalizerUtil;
import com.intellij.openapi.util.WriteExternalException;
import com.jetbrains.python.testing.AbstractPythonOldTestRunConfiguration;
import com.jetbrains.python.testing.AbstractPythonLegacyTestRunConfiguration;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
@@ -32,7 +32,7 @@ import org.jetbrains.annotations.NotNull;
* @author Leonid Shalupov
*/
public class PythonUnitTestRunConfiguration extends
AbstractPythonOldTestRunConfiguration<PythonUnitTestRunConfiguration>
AbstractPythonLegacyTestRunConfiguration<PythonUnitTestRunConfiguration>
implements PythonUnitTestRunConfigurationParams {
private boolean myIsPureUnittest = true;
protected String myTitle = "Unittest";
@@ -17,7 +17,7 @@ package com.jetbrains.python.testing.unittest;
import com.intellij.openapi.project.Project;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.testing.AbstractPythonOldTestRunConfiguration;
import com.jetbrains.python.testing.AbstractPythonLegacyTestRunConfiguration;
import com.jetbrains.python.testing.AbstractPythonTestRunConfigurationParams;
import com.jetbrains.python.testing.PythonTestRunConfigurationForm;
@@ -45,7 +45,7 @@ public class PythonUnitTestRunConfigurationForm implements PythonUnitTestRunConf
final ActionListener testTypeListener = new ActionListener() {
public void actionPerformed(final ActionEvent e) {
myIsPureUnittest.setVisible(myTestRunConfigurationForm.getTestType() != AbstractPythonOldTestRunConfiguration.TestType.TEST_FUNCTION);
myIsPureUnittest.setVisible(myTestRunConfigurationForm.getTestType() != AbstractPythonLegacyTestRunConfiguration.TestType.TEST_FUNCTION);
}
};
myTestRunConfigurationForm.addTestTypeListener(testTypeListener);
@@ -18,6 +18,7 @@ package com.jetbrains.python.testing.universalTests;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBRadioButton;
@@ -85,13 +86,16 @@ public final class PyUniversalTestForm implements SimplePropertiesProvider {
}
@Override
public void setPropertyValue(@NotNull final String propertyName, @Nullable final String propertyValue) {
public void setPropertyValue(@NotNull
final String propertyName, @Nullable
final String propertyValue) {
myCustomOptions.get(propertyName).setText(propertyValue != null ? propertyValue : "");
}
@Nullable
@Override
public String getPropertyValue(@NotNull final String propertyName) {
public String getPropertyValue(@NotNull
final String propertyName) {
return myCustomOptions.get(propertyName).getText();
}
@@ -103,8 +107,10 @@ public final class PyUniversalTestForm implements SimplePropertiesProvider {
* @param customOptions additional option names this form shall support. Make sure your configuration has appropriate properties.
*/
@NotNull
public static PyUniversalTestForm create(@NotNull final PyUniversalTestConfiguration configuration,
@NotNull final String... customOptions) { // TODO: DOC
public static PyUniversalTestForm create(@NotNull
final PyUniversalTestConfiguration configuration,
@NotNull
final String... customOptions) { // TODO: DOC
final PyUniversalTestForm form = new PyUniversalTestForm();
@@ -134,7 +140,8 @@ public final class PyUniversalTestForm implements SimplePropertiesProvider {
return form;
}
private void addCustomOptions(@NotNull final String... optionNames) {
private void addCustomOptions(@NotNull
final String... optionNames) {
if (optionNames.length == 0) {
return;
}
@@ -175,11 +182,15 @@ public final class PyUniversalTestForm implements SimplePropertiesProvider {
@NotNull
public String getTarget() {
return myTargetText.getText();
// 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 = myTargetText.getText().trim();
return getTargetType() == TestTargetType.PATH ? FileUtil.toSystemIndependentName(targetText) : targetText;
}
public void setTarget(@NotNull final String targetText) {
public void setTarget(@NotNull
final String targetText) {
myTargetText.setText(targetText);
}
@@ -196,7 +207,8 @@ public final class PyUniversalTestForm implements SimplePropertiesProvider {
}
@SuppressWarnings("unused") // Mutator for property
public void setTargetType(@NotNull final TestTargetType target) {
public void setTargetType(@NotNull
final TestTargetType target) {
final Enumeration<AbstractButton> elements = myButtonGroup.getElements();
while (elements.hasMoreElements()) {
final AbstractButton button = elements.nextElement();
@@ -0,0 +1,264 @@
package com.jetbrains.python.testing.universalTests
import com.intellij.execution.RunManager
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.JDOMExternalizable
import com.intellij.openapi.util.registry.Registry
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiDocumentManager
import com.jetbrains.python.psi.PyFile
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.testing.AbstractPythonLegacyTestRunConfiguration
import com.jetbrains.python.testing.AbstractPythonLegacyTestRunConfiguration.TestType
import com.jetbrains.python.testing.PythonTestConfigurationType
import com.jetbrains.python.testing.nosetest.PythonNoseTestRunConfiguration
import com.jetbrains.python.testing.pytest.PyTestRunConfiguration
import com.jetbrains.python.testing.unittest.PythonUnitTestRunConfiguration
import org.jdom.Element
/**
* Module to support legacy configurations.
*
* When legacy configuration is ought to be dropped, just remove this module and all references to it.
* It supports switching back to old runners (see [isNewTestsModeEnabled]) and importing old configs to new one.
* [projectInitialized] shall be called for that.
*
* @author Ilya.Kazakevich
*/
/**
* @return is new mode enabled or not
*/
fun isNewTestsModeEnabled(): Boolean = Registry.`is`("python.tests.enableUniversalTests")
/**
* To be called when project initialized to copy old configs to new one
*/
fun projectInitialized(project: Project) {
assert(project.isInitialized, { "Project is not initialized yet" })
RunManager.getInstance(project).allConfigurationsList.filterIsInstance(PyUniversalTestConfiguration::class.java).forEach {
it.legacyConfigurationAdapter.copyFromLegacyIfNeeded()
}
}
private fun getVirtualFileByPath(path: String): VirtualFile? {
return LocalFileSystem.getInstance().findFileByPath(path) ?: return null
}
private fun VirtualFile.asPyFile(project: Project): PyFile? {
assert(project.isInitialized, { "This function can't be used on uninitialized project" })
if (this.isDirectory) {
return null
}
var file: PyFile? = null
ApplicationManager.getApplication()
.invokeAndWait({
val document = FileDocumentManager.getInstance().getDocument(this)
if (document != null) {
file = PyUtil.`as`(PsiDocumentManager.getInstance(project).getPsiFile(document), PyFile::class.java)
}
})
return file
}
/**
* Manages legacy-to-new configuration binding
* Attach it to new configuration and mark with [com.jetbrains.reflection.DelegationProperty]
*/
class PyUniversalTestLegacyConfigurationAdapter<in T : PyUniversalTestConfiguration>(newConfig: T)
: JDOMExternalizable {
private val configManager: LegacyConfigurationManager<*, *>
/**
* Does configuration contain legacy information or was it created as new config?
* Null is unknown
*/
private var containsLegacyInformation: Boolean? = null
// TODO: DOC null to prevent useless save
/**
* True if configuration [containsLegacyInformation] and this information is already copied to new config, so it should not be
* copied second time.
*
* Null means "false" and used here to prevent saving useless "false" value in .xml for new configurations.
*/
@ConfigField
var legacyInformationCopiedToNew: Boolean? = null
init {
when (newConfig) {
is PyUniversalPyTestConfiguration -> {
configManager = LegacyConfigurationManagerPyTest(newConfig)
}
is PyUniversalNoseTestConfiguration -> {
configManager = LegacyConfigurationManagerNose(newConfig)
}
is PyUniversalUnitTestConfiguration -> {
configManager = LegacyConfigurationManagerUnit(newConfig)
}
else -> {
throw IllegalAccessException("Unknown config: $newConfig")
}
}
}
override fun readExternal(element: Element) {
configManager.legacyConfig.readExternal(element)
containsLegacyInformation = (configManager.legacyConfig.getTestType() != null)
}
override fun writeExternal(element: Element) {
if (containsLegacyInformation ?: return) {
configManager.legacyConfig.writeExternal(element)
}
}
fun copyFromLegacyIfNeeded() {
if (containsLegacyInformation ?: return && !(legacyInformationCopiedToNew ?: false)) {
configManager.copyFromLegacy()
legacyInformationCopiedToNew = true
}
}
}
/**
* Manages legacy-to-new configuration copying process
*/
private abstract class LegacyConfigurationManager<
LEGACY_CONF_T : AbstractPythonLegacyTestRunConfiguration<LEGACY_CONF_T>,
out NEW_CONF_T : PyUniversalTestConfiguration
>(legacyConfFactory: PythonConfigurationFactoryBase, val newConfig: NEW_CONF_T) {
@Suppress("UNCHECKED_CAST") // Factory-to-config mapping should be checked by developer: createTemplateConfiguration is not generic
val legacyConfig = legacyConfFactory.createTemplateConfiguration(newConfig.project) as LEGACY_CONF_T
/**
* Checks test type to interpret target correctly. It could be function, class or method
*/
private fun getElementFromConfig(script: PyFile): PyQualifiedNameOwner? {
if (legacyConfig.testType == TestType.TEST_FUNCTION) {
return script.findTopLevelFunction(legacyConfig.methodName)
}
val clazz = script.findTopLevelClass(legacyConfig.className) ?: return null
if (legacyConfig.testType == TestType.TEST_CLASS) {
return clazz
}
return clazz.findMethodByName(legacyConfig.methodName, true, TypeEvalContext.userInitiated(legacyConfig.project, script))
}
/**
* Copies config from legacy to new configuration.
* Used by all runners but py.test which has very different settings
*/
open fun copyFromLegacy() {
when (legacyConfig.testType) {
TestType.TEST_CLASS, TestType.TEST_FUNCTION, TestType.TEST_METHOD -> {
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.target = qualifiedName
}
TestType.TEST_FOLDER -> {
newConfig.target.targetType = TestTargetType.PATH
newConfig.target.target = legacyConfig.folderName
}
TestType.TEST_SCRIPT -> {
newConfig.target.targetType = TestTargetType.PATH
newConfig.target.target = legacyConfig.scriptName
}
else -> {
Logger.getInstance(LegacyConfigurationManager::class.java).warn("Unknown type {${legacyConfig.testType}")
}
}
}
}
private class LegacyConfigurationManagerPyTest(newConfig: PyUniversalPyTestConfiguration) :
LegacyConfigurationManager<PyTestRunConfiguration, PyUniversalPyTestConfiguration>(
PythonTestConfigurationType.getInstance().LEGACY_PYTEST_FACTORY, newConfig) {
/**
* In Py.test target is provided as keywords, joined with "and".
* "function_foo", "MyClass" or "MyClass and my_method" could be used here.
*/
private val KEYWORDS_SPLIT_PATTERN = java.util.regex.Pattern.compile("\\s+and\\s+", java.util.regex.Pattern.CASE_INSENSITIVE)
override fun copyFromLegacy() {
// Do not call parent since target is always provided as testToRun here
newConfig.additionalArguments = legacyConfig.params
// Default is PATH
newConfig.target.targetType = TestTargetType.PATH
val oldKeywords = legacyConfig.keywords
val virtualFile = getVirtualFileByPath(legacyConfig.testToRun) ?: return
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.keywords = oldKeywords
return
}
// If it is file -- it could be file, class, method or functions (see keywords)
val script = virtualFile.asPyFile(newConfig.project) ?: return
val keywordsList = oldKeywords.split(KEYWORDS_SPLIT_PATTERN)
if (keywordsList.isEmpty() || keywordsList.size > 2 || keywordsList.find { it.contains(" ") } != null) {
//Give up with interpreting
newConfig.keywords = oldKeywords
newConfig.target.target = script.virtualFile.path
newConfig.target.targetType = TestTargetType.PATH
return
}
val classOrFunctionName = keywordsList[0]
val clazz = script.findTopLevelClass(classOrFunctionName)
if (keywordsList.size == 1) { // Class or function
val classOrFunction = PyUtil.`as`(clazz ?:
script.findTopLevelFunction(classOrFunctionName),
PyQualifiedNameOwner::class.java) ?: return
newConfig.target.target = classOrFunction.qualifiedName ?: return
newConfig.target.targetType = TestTargetType.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
}
}
}
//TODO: Support "pattern" field for unittest
private class LegacyConfigurationManagerUnit(newConfig: PyUniversalUnitTestConfiguration) :
LegacyConfigurationManager<PythonUnitTestRunConfiguration, PyUniversalUnitTestConfiguration>(
PythonTestConfigurationType.getInstance().LEGACY_UNITTEST_FACTORY, newConfig) {
override fun copyFromLegacy() {
super.copyFromLegacy()
newConfig.additionalArguments = legacyConfig.params
}
}
private class LegacyConfigurationManagerNose(newConfig: PyUniversalNoseTestConfiguration) :
LegacyConfigurationManager<PythonNoseTestRunConfiguration, PyUniversalNoseTestConfiguration>(
PythonTestConfigurationType.getInstance().LEGACY_NOSETEST_FACTORY, newConfig) {
override fun copyFromLegacy() {
super.copyFromLegacy()
newConfig.additionalArguments = legacyConfig.params
}
}
@@ -38,7 +38,6 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.util.JDOMExternalizerUtil
import com.intellij.openapi.util.Pair
import com.intellij.openapi.util.Ref
import com.intellij.openapi.util.registry.Registry
import com.intellij.psi.PsiDirectory
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
@@ -56,6 +55,7 @@ import com.jetbrains.python.run.PythonConfigurationFactoryBase
import com.jetbrains.python.testing.*
import com.jetbrains.reflection.DelegationProperty
import com.jetbrains.reflection.Properties
import com.jetbrains.reflection.Property
import com.jetbrains.reflection.getProperties
import org.jdom.Element
import java.io.File
@@ -64,13 +64,11 @@ import javax.swing.JComponent
/**
* New (universal) API for test runners.
*
* @author Ilya.Kazakevich
* New configuration factories
*/
fun isUniversalModeEnabled(): Boolean = Registry.`is`("python.tests.enableUniversalTests")
val factories: Array<PythonConfigurationFactoryBase> = arrayOf(PyUniversalUnitTestFactory,
PyUniversalPyTestFactory,
PyUniversalNoseTestFactory)
internal fun getAdditionalArgumentsPropertyName() = PyUniversalTestConfiguration::additionalArguments.name
@@ -79,7 +77,7 @@ internal fun getAdditionalArgumentsPropertyName() = PyUniversalTestConfiguration
*/
private fun findConfigurationFactoryFromSettings(module: Module): ConfigurationFactory {
val name = TestRunnerService.getInstance(module).projectConfiguration
val factories = PyUniversalTestsConfigurationType.configurationFactories
val factories = PythonTestConfigurationType.getInstance().configurationFactories
val configurationFactory = factories.find { it.name == name }
return configurationFactory ?: factories.first()
}
@@ -150,6 +148,14 @@ data class ConfigurationTarget(@ConfigField var target: String, @ConfigField var
}
}
/**
* To prevent legacy configuration options from clashing with new names, we add prefix
* to use for writing/reading xml
*/
private val Property.prefixedName: String
get() = "_new_" + this.getName()
/**
* Parent of all new test configurations.
* All config-specific fields are implemented as properties. They are saved/restored automatically and passed to GUI form.
@@ -167,6 +173,10 @@ abstract class PyUniversalTestConfiguration(project: Project,
val testFrameworkName = configurationFactory.name!!
@Suppress("LeakingThis") // Legacy adapter is used to support legacy configs. Leak is ok here since everything takes place in one thread
@DelegationProperty
val legacyConfigurationAdapter = PyUniversalTestLegacyConfigurationAdapter(this)
private fun getTestSpecForPythonTarget(location: Location<*>): List<String> {
if (location !is PsiLocation) {
@@ -230,12 +240,18 @@ abstract class PyUniversalTestConfiguration(project: Project,
override fun writeExternal(element: Element) {
// Write legacy config to preserve it
legacyConfigurationAdapter.writeExternal(element)
super.writeExternal(element)
val gson = Gson()
getConfigFields().properties.forEach {
JDOMExternalizerUtil.writeField(element, it.getName(), gson.toJson(it.get()))
val value = it.get()
if (value != null) {
// No need to write null since null is default value
JDOMExternalizerUtil.writeField(element, it.prefixedName, gson.toJson(value))
}
}
}
@@ -245,11 +261,12 @@ abstract class PyUniversalTestConfiguration(project: Project,
val gson = Gson()
getConfigFields().properties.forEach {
val fromJson: Any? = gson.fromJson(JDOMExternalizerUtil.readField(element, it.getName()), it.getType())
val fromJson: Any? = gson.fromJson(JDOMExternalizerUtil.readField(element, it.prefixedName), it.getType())
if (fromJson != null) {
it.set(fromJson)
}
}
legacyConfigurationAdapter.readExternal(element)
}
@@ -273,37 +290,23 @@ abstract class PyUniversalTestConfiguration(project: Project,
}
}
object PyUniversalTestsConfigurationType : PythonTestConfigurationType() {
override fun getId() = "py_universal_tests"
override fun getConfigurationFactories(): Array<PyUniversalTestFactory<*>> {
if (isUniversalModeEnabled()) {
return arrayOf(PyUniversalUnitTestFactory,
PyUniversalPyTestFactory,
PyUniversalNoseTestFactory)
}
// Array can't be empty according to contract (type is fetched from first element)
return arrayOf(PyUniversalUnitTestFactory)
}
}
// TODO: DOC
abstract class PyUniversalTestFactory<out CONF_T : PyUniversalTestConfiguration> : PythonConfigurationFactoryBase(
PyUniversalTestsConfigurationType) {
PythonTestConfigurationType.getInstance()) {
override abstract fun createTemplateConfiguration(project: Project): CONF_T
}
/**
* Only one producer is registered with EP, but it uses factory configured by user to prdouce different configs
*/
object PyUniversalTestsConfigurationProducer : RunConfigurationProducer<PyUniversalTestConfiguration>(PyUniversalTestsConfigurationType) {
object PyUniversalTestsConfigurationProducer : RunConfigurationProducer<PyUniversalTestConfiguration>(
PythonTestConfigurationType.getInstance()) {
override fun cloneTemplateConfiguration(context: ConfigurationContext): RunnerAndConfigurationSettings {
return cloneTemplateConfigurationStatic(context, findConfigurationFactoryFromSettings(context.module))
}
override fun findOrCreateConfigurationFromContext(context: ConfigurationContext?): ConfigurationFromContext? {
if (!isUniversalModeEnabled()) {
if (!isNewTestsModeEnabled()) {
return null
}
return super.findOrCreateConfigurationFromContext(context)
@@ -323,7 +326,10 @@ object PyUniversalTestsConfigurationProducer : RunConfigurationProducer<PyUniver
}
// TODO: DOC
/**
* Find concrete element to be used as test target.
* @return configuration name and its target
*/
private fun getNameAndTargetForConfig(configuration: PyUniversalTestConfiguration,
baseElement: PsiElement): Pair<String, ConfigurationTarget>? {
var element = baseElement
@@ -29,6 +29,7 @@ import com.jetbrains.python.testing.PythonTestConfigurationsModel
* unittest
*/
//TODO: Support pattern
class PyUniversalUnitTestSettingsEditor(configuration: PyUniversalTestConfiguration) :
PyUniversalTestSettingsEditor(PyUniversalTestForm.create(configuration))
@@ -23,7 +23,7 @@ import com.intellij.psi.PsiElement;
import com.jetbrains.env.PyExecutionFixtureTestTask;
import com.jetbrains.python.sdk.InvalidSdkException;
import com.jetbrains.python.sdkTools.SdkCreationType;
import com.jetbrains.python.testing.PythonTestOldConfigurationProducer;
import com.jetbrains.python.testing.PythonTestLegacyConfigurationProducer;
import com.jetbrains.python.testing.TestRunnerService;
import org.jetbrains.annotations.NotNull;
import org.junit.Assert;
@@ -43,13 +43,13 @@ class CreateConfigurationTestTask extends PyExecutionFixtureTestTask {
@NotNull
private final String myTestRunnerName;
@NotNull
private final Class<? extends PythonTestOldConfigurationProducer> myProducer;
private final Class<? extends PythonTestLegacyConfigurationProducer> myProducer;
/**
* @param producer class of configuration producer to check
* @param testRunnerName test runner name (to set as default to make sure producer launched)
*/
CreateConfigurationTestTask(@NotNull final Class<? extends PythonTestOldConfigurationProducer> producer,
CreateConfigurationTestTask(@NotNull final Class<? extends PythonTestLegacyConfigurationProducer> producer,
@NotNull final String testRunnerName) {
super("/testRunner/env/createConfigurationTest/");
myProducer = producer;
@@ -74,7 +74,7 @@ class CreateConfigurationTestTask extends PyExecutionFixtureTestTask {
private void checkConfigurationCreatedFrom(@NotNull final PsiElement element) {
final PythonTestOldConfigurationProducer producer = createProducer();
final PythonTestLegacyConfigurationProducer producer = createProducer();
final ConfigurationFromContext context =
producer.createConfigurationFromContext(new ConfigurationContext(element));
Assert.assertNotNull(String.format("Failed to create context for %s", myTestRunnerName), context);
@@ -82,7 +82,7 @@ class CreateConfigurationTestTask extends PyExecutionFixtureTestTask {
}
@NotNull
private PythonTestOldConfigurationProducer createProducer() {
private PythonTestLegacyConfigurationProducer createProducer() {
try {
return myProducer.getConstructor().newInstance();
}
@@ -16,7 +16,7 @@
package com.jetbrains.env.ut;
import com.jetbrains.env.ProcessWithConsoleRunner;
import com.jetbrains.python.testing.PythonTestOldConfigurationType;
import com.jetbrains.python.testing.PythonTestConfigurationType;
import com.jetbrains.python.testing.doctest.PythonDocTestRunConfiguration;
import org.jetbrains.annotations.NotNull;
@@ -27,7 +27,7 @@ import org.jetbrains.annotations.NotNull;
*/
public class PyDocTestProcessRunner extends PyScriptTestProcessRunner<PythonDocTestRunConfiguration> {
public PyDocTestProcessRunner(@NotNull final String scriptName, final int timesToRerunFailedTests) {
super(PythonTestOldConfigurationType.getInstance().PY_DOCTEST_FACTORY,
super(PythonTestConfigurationType.getInstance().PY_DOCTEST_FACTORY,
PythonDocTestRunConfiguration.class, scriptName, timesToRerunFailedTests);
}
}
@@ -16,7 +16,7 @@
package com.jetbrains.env.ut;
import com.jetbrains.env.ProcessWithConsoleRunner;
import com.jetbrains.python.testing.PythonTestOldConfigurationType;
import com.jetbrains.python.testing.PythonTestConfigurationType;
import com.jetbrains.python.testing.nosetest.PythonNoseTestRunConfiguration;
import org.jetbrains.annotations.NotNull;
@@ -27,7 +27,7 @@ import org.jetbrains.annotations.NotNull;
*/
public class PyNoseTestProcessRunner extends PyScriptTestProcessRunner<PythonNoseTestRunConfiguration> {
public PyNoseTestProcessRunner(@NotNull final String scriptName, final int timesToRerunFailedTests) {
super(PythonTestOldConfigurationType.getInstance().PY_NOSETEST_FACTORY,
super(PythonTestConfigurationType.getInstance().LEGACY_NOSETEST_FACTORY,
PythonNoseTestRunConfiguration.class, scriptName, timesToRerunFailedTests);
}
}
@@ -16,7 +16,7 @@
package com.jetbrains.env.ut;
import com.jetbrains.env.ProcessWithConsoleRunner;
import com.jetbrains.python.testing.PythonTestOldConfigurationType;
import com.jetbrains.python.testing.PythonTestConfigurationType;
import com.jetbrains.python.testing.pytest.PyTestRunConfiguration;
import org.jetbrains.annotations.NotNull;
@@ -29,7 +29,7 @@ import java.io.IOException;
*/
public class PyTestTestProcessRunner extends PyScriptTestProcessRunner<PyTestRunConfiguration> {
public PyTestTestProcessRunner(@NotNull final String scriptName, final int timesToRerunFailedTests) {
super(PythonTestOldConfigurationType.getInstance().PY_PYTEST_FACTORY,
super(PythonTestConfigurationType.getInstance().LEGACY_PYTEST_FACTORY,
PyTestRunConfiguration.class, scriptName, timesToRerunFailedTests);
}
@@ -16,7 +16,7 @@
package com.jetbrains.env.ut;
import com.jetbrains.env.ProcessWithConsoleRunner;
import com.jetbrains.python.testing.PythonTestOldConfigurationType;
import com.jetbrains.python.testing.PythonTestConfigurationType;
import com.jetbrains.python.testing.unittest.PythonUnitTestRunConfiguration;
import org.jetbrains.annotations.NotNull;
@@ -27,7 +27,7 @@ import org.jetbrains.annotations.NotNull;
*/
public class PyUnitTestProcessRunner extends PyScriptTestProcessRunner<PythonUnitTestRunConfiguration> {
public PyUnitTestProcessRunner(@NotNull final String scriptName, final int timesToRerunFailedTests) {
super(PythonTestOldConfigurationType.getInstance().PY_UNITTEST_FACTORY,
super(PythonTestConfigurationType.getInstance().LEGACY_UNITTEST_FACTORY,
PythonUnitTestRunConfiguration.class, scriptName, timesToRerunFailedTests);
}
}
+5 -5
View File
@@ -40,8 +40,8 @@ import com.jetbrains.python.PyNames;
import com.jetbrains.python.sdk.PythonEnvUtil;
import com.jetbrains.python.sdk.flavors.JythonSdkFlavor;
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
import com.jetbrains.python.testing.AbstractPythonOldTestRunConfiguration;
import com.jetbrains.python.testing.PythonTestOldConfigurationType;
import com.jetbrains.python.testing.AbstractPythonLegacyTestRunConfiguration;
import com.jetbrains.python.testing.PythonTestConfigurationType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.junit.Assert;
@@ -154,7 +154,7 @@ public abstract class PyUnitTestTask extends PyExecutionFixtureTestTask {
@Override
public void runTestOn(String sdkHome) throws Exception {
final Project project = getProject();
final ConfigurationFactory factory = PythonTestOldConfigurationType.getInstance().PY_UNITTEST_FACTORY;
final ConfigurationFactory factory = PythonTestConfigurationType.getInstance().LEGACY_UNITTEST_FACTORY;
runConfiguration(factory, sdkHome, project);
}
@@ -162,7 +162,7 @@ public abstract class PyUnitTestTask extends PyExecutionFixtureTestTask {
final RunnerAndConfigurationSettings settings =
RunManager.getInstance(project).createRunConfiguration("test", factory);
AbstractPythonOldTestRunConfiguration config = (AbstractPythonOldTestRunConfiguration)settings.getConfiguration();
AbstractPythonLegacyTestRunConfiguration config = (AbstractPythonLegacyTestRunConfiguration)settings.getConfiguration();
config.setSdkHome(sdkHome);
@@ -271,7 +271,7 @@ public abstract class PyUnitTestTask extends PyExecutionFixtureTestTask {
return 60000;
}
protected void configure(AbstractPythonOldTestRunConfiguration config) {
protected void configure(AbstractPythonLegacyTestRunConfiguration config) {
}
/**
@@ -18,7 +18,7 @@ package com.jetbrains.python;
import com.jetbrains.python.fixtures.PyCommandLineTestCase;
import com.jetbrains.python.run.PythonConfigurationType;
import com.jetbrains.python.run.PythonRunConfiguration;
import com.jetbrains.python.testing.PythonTestOldConfigurationType;
import com.jetbrains.python.testing.PythonTestConfigurationType;
import com.jetbrains.python.testing.unittest.PythonUnitTestRunConfiguration;
import junit.framework.Assert;
@@ -31,7 +31,7 @@ public class PythonRunConfigurationTest extends PyCommandLineTestCase {
private static final String PY_SCRIPT = "foo.py";
public void testUnitTestCommandLine() {
PythonUnitTestRunConfiguration configuration = createConfiguration(PythonTestOldConfigurationType.getInstance(),
PythonUnitTestRunConfiguration configuration = createConfiguration(PythonTestConfigurationType.getInstance(),
PythonUnitTestRunConfiguration.class);
configuration.setScriptName(PY_SCRIPT);
final List<String> params = buildRunCommandLine(configuration);