// Copyright 2016 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).
package com.twitter.intellij.pants.execution;
import com.intellij.execution.actions.ConfigurationContext;
import com.intellij.openapi.externalSystem.model.execution.ExternalSystemTaskExecutionSettings;
import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.codeInsight.testIntegration.PyTestFinder;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyFile;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.testing.pytest.PyTestUtil;
import com.twitter.intellij.pants.util.PantsConstants;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
public class PantsPythonTestRunConfigurationProducer extends PantsTestRunConfigurationProducer {
@Override
protected boolean setupConfigurationFromContext(
@NotNull ExternalSystemRunConfiguration configuration,
@NotNull ConfigurationContext context,
@NotNull Ref<PsiElement> sourceElement
) {
Optional<PantsConfigurationContext> contextOptional = PantsConfigurationContext.validatesAndCreate(context);
if (!contextOptional.isPresent()) {
return false;
}
PantsConfigurationContext configurationContext = contextOptional.get();
final List<String> targets = configurationContext.getTargets();
final PsiElement psiLocation = configurationContext.getLocation();
final ExternalSystemTaskExecutionSettings taskSettings = configuration.getSettings();
taskSettings.setTaskNames(Collections.singletonList("test"));
boolean isPython = isOrContainsPyTests(psiLocation);
if (!isPython) {
return false;
}
if (psiLocation instanceof PyFile) {
PyFile file = (PyFile) psiLocation;
return buildFromPyTest(file, file.getName(), file.getVirtualFile().getPath(), targets, taskSettings, sourceElement, configuration);
}
else if (psiLocation instanceof PsiDirectory) {
PsiDirectory dir = (PsiDirectory) psiLocation;
return buildFromPyTest(dir, dir.getName(), dir.getVirtualFile().getPath(), targets, taskSettings, sourceElement, configuration);
}
else {
PyFile file = (PyFile) psiLocation.getContainingFile();
PyFunction pyFunction = PsiTreeUtil.getParentOfType(psiLocation, PyFunction.class, false);
PyClass pyClass = PsiTreeUtil.getParentOfType(psiLocation, PyClass.class, false);
if (pyFunction != null) {
return buildFromPyTest(
psiLocation,
"'" + pyClass.getName() + " and " + pyFunction.getName() + "'",
file.getVirtualFile().getPath(),
targets,
taskSettings,
sourceElement,
configuration
);
}
if (pyClass != null) {
return buildFromPyTest(
psiLocation, pyClass.getName(), file.getVirtualFile().getPath(), targets, taskSettings, sourceElement, configuration);
}
return buildFromPyTest(
psiLocation,
file.getName(),
file.getVirtualFile().getPath(),
targets,
taskSettings,
sourceElement,
configuration
);
}
}
private boolean buildFromPyTest(
PsiElement testElem,
String elemStr,
String path,
List<String> targets,
ExternalSystemTaskExecutionSettings taskSettings,
Ref<PsiElement> sourceElement,
ExternalSystemRunConfiguration configuration
) {
sourceElement.set(testElem);
configuration.setName("Pants tests in " + elemStr);
taskSettings.setExternalProjectPath(path);
String scriptParams = StringUtil.join(targets, " ");
scriptParams += " " + PantsConstants.PANTS_CLI_OPTION_PYTEST + "=\"-k " + elemStr + "\"";
taskSettings.setExecutionName(elemStr);
taskSettings.setScriptParameters(scriptParams);
return true;
}
private boolean isOrContainsPyTests(PsiElement element) {
if (new PyTestFinder().isTest(element)) {
return true;
}
if (element instanceof PyFile) {
PyFile pyFile = (PyFile) element;
for (PyClass pyClass : pyFile.getTopLevelClasses()) {
if (PyTestUtil.isPyTestClass(pyClass, null)) {
return true;
}
}
}
else if (element instanceof PsiDirectory) {
PsiDirectory directory = (PsiDirectory) element;
for (PsiFile file : directory.getFiles()) {
if (isOrContainsPyTests(file)) {
return true;
}
}
}
return false;
}
}