blob: ccf048bdb8ad5e823f701ea1fd856023b7d53ceb [file] [log] [blame]
/*
* Copyright 2000-2013 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.google.common.collect.Sets;
import com.intellij.execution.Location;
import com.intellij.execution.actions.ConfigurationContext;
import com.intellij.execution.actions.ConfigurationFromContext;
import com.intellij.execution.actions.RunConfigurationProducer;
import com.intellij.execution.configurations.ConfigurationFactory;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.facet.Facet;
import com.intellij.facet.FacetManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiFileSystemItem;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.PythonModuleTypeBase;
import com.jetbrains.python.facet.PythonFacetSettings;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.run.PythonRunConfigurationProducer;
import com.jetbrains.python.testing.unittest.PythonUnitTestRunConfiguration;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
/**
* User: ktisha
*/
abstract public class PythonTestConfigurationProducer extends RunConfigurationProducer<AbstractPythonTestRunConfiguration> {
public PythonTestConfigurationProducer(final ConfigurationFactory configurationFactory) {
super(configurationFactory);
}
@Override
public boolean isConfigurationFromContext(AbstractPythonTestRunConfiguration configuration, ConfigurationContext context) {
final Location location = context.getLocation();
if (location == null || !isAvailable(location)) return false;
final PsiElement element = location.getPsiElement();
final PsiFileSystemItem file = element.getContainingFile();
final VirtualFile virtualFile = element instanceof PsiDirectory ? ((PsiDirectory)element).getVirtualFile()
: file.getVirtualFile();
if (virtualFile == null) return false;
final PyFunction pyFunction = PsiTreeUtil.getParentOfType(element, PyFunction.class, false);
final PyClass pyClass = PsiTreeUtil.getParentOfType(element, PyClass.class);
final AbstractPythonTestRunConfiguration.TestType confType = configuration.getTestType();
final String workingDirectory = configuration.getWorkingDirectory();
if (element instanceof PsiDirectory) {
final String path = ((PsiDirectory)element).getVirtualFile().getPath();
return confType == AbstractPythonTestRunConfiguration.TestType.TEST_FOLDER &&
path.equals(configuration.getFolderName()) ||
path.equals(new File(workingDirectory, configuration.getFolderName()).getAbsolutePath());
}
final String scriptName = configuration.getScriptName();
final String path = virtualFile.getPath();
final boolean isTestFileEquals = scriptName.equals(path) || path.equals(new File(workingDirectory, scriptName).getAbsolutePath());
if (pyFunction != null) {
final String methodName = configuration.getMethodName();
if (pyFunction.getContainingClass() == null) {
return confType == AbstractPythonTestRunConfiguration.TestType.TEST_FUNCTION &&
methodName.equals(pyFunction.getName()) && isTestFileEquals;
}
else {
final String className = configuration.getClassName();
return confType == AbstractPythonTestRunConfiguration.TestType.TEST_METHOD &&
methodName.equals(pyFunction.getName()) &&
pyClass != null && className.equals(pyClass.getName()) && isTestFileEquals;
}
}
if (pyClass != null) {
final String className = configuration.getClassName();
return confType == AbstractPythonTestRunConfiguration.TestType.TEST_CLASS &&
className.equals(pyClass.getName()) && isTestFileEquals;
}
return confType == AbstractPythonTestRunConfiguration.TestType.TEST_SCRIPT && isTestFileEquals;
}
@Override
protected boolean setupConfigurationFromContext(AbstractPythonTestRunConfiguration configuration,
ConfigurationContext context,
Ref<PsiElement> sourceElement) {
if (context == null) return false;
final Location location = context.getLocation();
if (location == null || !isAvailable(location)) return false;
final PsiElement element = location.getPsiElement();
if (PythonUnitTestRunnableScriptFilter.isIfNameMain(location)) return false;
final Module module = location.getModule();
if (!isPythonModule(module)) return false;
if (element instanceof PsiDirectory) {
return setupConfigurationFromFolder((PsiDirectory)element, configuration);
}
final PyFunction pyFunction = PsiTreeUtil.getParentOfType(element, PyFunction.class, false);
if (pyFunction != null && isTestFunction(pyFunction, configuration)) {
return setupConfigurationFromFunction(pyFunction, configuration);
}
final PyClass pyClass = PsiTreeUtil.getParentOfType(element, PyClass.class, false);
if (pyClass != null && isTestClass(pyClass, configuration)) {
return setupConfigurationFromClass(pyClass, configuration);
}
final PsiFile file = element.getContainingFile();
if (file instanceof PyFile && isTestFile((PyFile)file)) {
return setupConfigurationFromFile((PyFile)file, configuration);
}
return false;
}
private boolean setupConfigurationFromFolder(@NotNull final PsiDirectory element,
@NotNull final AbstractPythonTestRunConfiguration configuration) {
final VirtualFile virtualFile = element.getVirtualFile();
if (!isTestFolder(virtualFile, element.getProject())) return false;
final String path = virtualFile.getPath();
configuration.setTestType(AbstractPythonTestRunConfiguration.TestType.TEST_FOLDER);
configuration.setFolderName(path);
configuration.setWorkingDirectory(path);
configuration.setGeneratedName();
setModuleSdk(element, configuration);
return true;
}
private static void setModuleSdk(@NotNull final PsiElement element, @NotNull final AbstractPythonTestRunConfiguration configuration) {
configuration.setUseModuleSdk(true);
configuration.setModule(ModuleUtilCore.findModuleForPsiElement(element));
}
protected boolean setupConfigurationFromFunction(@NotNull final PyFunction pyFunction,
@NotNull final AbstractPythonTestRunConfiguration configuration) {
final PyClass containingClass = pyFunction.getContainingClass();
configuration.setMethodName(pyFunction.getName());
if (containingClass != null) {
configuration.setClassName(containingClass.getName());
configuration.setTestType(AbstractPythonTestRunConfiguration.TestType.TEST_METHOD);
}
else {
configuration.setTestType(AbstractPythonTestRunConfiguration.TestType.TEST_FUNCTION);
}
return setupConfigurationScript(configuration, pyFunction);
}
protected boolean setupConfigurationFromClass(@NotNull final PyClass pyClass,
@NotNull final AbstractPythonTestRunConfiguration configuration) {
configuration.setTestType(AbstractPythonTestRunConfiguration.TestType.TEST_CLASS);
configuration.setClassName(pyClass.getName());
return setupConfigurationScript(configuration, pyClass);
}
protected boolean setupConfigurationFromFile(@NotNull final PyFile pyFile,
@NotNull final AbstractPythonTestRunConfiguration configuration) {
configuration.setTestType(AbstractPythonTestRunConfiguration.TestType.TEST_SCRIPT);
return setupConfigurationScript(configuration, pyFile);
}
protected static boolean setupConfigurationScript(@NotNull final AbstractPythonTestRunConfiguration cfg,
@NotNull final PyElement element) {
final PyFile containingFile = PyUtil.getContainingPyFile(element);
if (containingFile == null) return false;
final VirtualFile vFile = containingFile.getVirtualFile();
if (vFile == null) return false;
final VirtualFile parent = vFile.getParent();
if (parent == null) return false;
cfg.setScriptName(vFile.getPath());
if (StringUtil.isEmptyOrSpaces(cfg.getWorkingDirectory()))
cfg.setWorkingDirectory(parent.getPath());
cfg.setGeneratedName();
setModuleSdk(element, cfg);
return true;
}
protected boolean isTestFolder(@NotNull final VirtualFile virtualFile, @NotNull final Project project) {
@NonNls final String name = virtualFile.getName();
final HashSet<VirtualFile> roots = Sets.newHashSet();
final Module[] modules = ModuleManager.getInstance(project).getModules();
for (Module module : modules) {
roots.addAll(PyUtil.getSourceRoots(module));
}
Collections.addAll(roots, ProjectRootManager.getInstance(project).getContentRoots());
return name.toLowerCase().contains("test") || roots.contains(virtualFile);
}
protected boolean isAvailable(@NotNull final Location location) {
return false;
}
protected boolean isTestClass(@NotNull final PyClass pyClass,
@Nullable final AbstractPythonTestRunConfiguration configuration) {
return PythonUnitTestUtil.isTestCaseClass(pyClass);
}
protected boolean isTestFunction(@NotNull final PyFunction pyFunction,
@Nullable final AbstractPythonTestRunConfiguration configuration) {
return PythonUnitTestUtil.isTestCaseFunction(pyFunction);
}
protected boolean isTestFile(@NotNull final PyFile file) {
final List<PyStatement> testCases = getTestCaseClassesFromFile(file);
if (testCases.isEmpty()) return false;
return true;
}
protected static boolean isPythonModule(Module module) {
if (module == null) {
return false;
}
if (ModuleType.get(module) instanceof PythonModuleTypeBase) {
return true;
}
final Facet[] allFacets = FacetManager.getInstance(module).getAllFacets();
for (Facet facet : allFacets) {
if (facet.getConfiguration() instanceof PythonFacetSettings) {
return true;
}
}
return false;
}
protected List<PyStatement> getTestCaseClassesFromFile(@NotNull final PyFile pyFile) {
return PythonUnitTestUtil.getTestCaseClassesFromFile(pyFile);
}
@Override
public boolean isPreferredConfiguration(ConfigurationFromContext self, ConfigurationFromContext other) {
final RunConfiguration configuration = self.getConfiguration();
if (configuration instanceof PythonUnitTestRunConfiguration &&
((PythonUnitTestRunConfiguration)configuration).getTestType() == AbstractPythonTestRunConfiguration.TestType.TEST_FOLDER) {
return true;
}
return other.isProducedBy(PythonTestConfigurationProducer.class) || other.isProducedBy(PythonRunConfigurationProducer.class);
}
}