diff --git a/META-INF/plugin.xml b/META-INF/plugin.xml
index 4fff986..edc39f1 100644
--- a/META-INF/plugin.xml
+++ b/META-INF/plugin.xml
@@ -72,6 +72,8 @@
+
+
@@ -79,4 +81,4 @@
com.jetbrains.php
com.intellij.modules.platform
-
\ No newline at end of file
+
diff --git a/src/org/atoum/intellij/plugin/atoum/Utils.java b/src/org/atoum/intellij/plugin/atoum/Utils.java
index 1333e8d..8132d3a 100644
--- a/src/org/atoum/intellij/plugin/atoum/Utils.java
+++ b/src/org/atoum/intellij/plugin/atoum/Utils.java
@@ -1,6 +1,8 @@
package org.atoum.intellij.plugin.atoum;
import com.google.common.collect.Lists;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.php.PhpIndex;
@@ -98,4 +100,21 @@ public static PhpClass getFirstClassFromFile(PhpFile phpFile) {
Collection phpClasses = PsiTreeUtil.collectElementsOfType(phpFile, PhpClass.class);
return phpClasses.size() == 0 ? null : phpClasses.iterator().next();
}
+
+ public static void saveFiles(PhpClass currentTestClass, Project project) {
+ FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
+
+ Document documentTestClass = fileDocumentManager.getDocument(currentTestClass.getContainingFile().getVirtualFile());
+ if (documentTestClass != null) {
+ fileDocumentManager.saveDocument(documentTestClass);
+ }
+
+ PhpClass currentTestedClass = Utils.locateTestedClass(project, currentTestClass);
+ if (currentTestedClass != null) {
+ Document documentTestedClass = fileDocumentManager.getDocument(currentTestedClass.getContainingFile().getVirtualFile());
+ if (documentTestedClass != null) {
+ fileDocumentManager.saveDocument(documentTestedClass);
+ }
+ }
+ }
}
diff --git a/src/org/atoum/intellij/plugin/atoum/actions/RerunFailedTestsAction.java b/src/org/atoum/intellij/plugin/atoum/actions/RerunFailedTestsAction.java
new file mode 100644
index 0000000..cad34fa
--- /dev/null
+++ b/src/org/atoum/intellij/plugin/atoum/actions/RerunFailedTestsAction.java
@@ -0,0 +1,81 @@
+package org.atoum.intellij.plugin.atoum.actions;
+
+import com.intellij.execution.Location;
+import com.intellij.execution.testframework.sm.runner.SMTestProxy;
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.jetbrains.php.lang.psi.PhpFile;
+import com.jetbrains.php.lang.psi.elements.Method;
+import com.jetbrains.php.lang.psi.elements.PhpClass;
+import org.atoum.intellij.plugin.atoum.Utils;
+import org.atoum.intellij.plugin.atoum.model.RunnerConfiguration;
+import org.atoum.intellij.plugin.atoum.run.Runner;
+
+public class RerunFailedTestsAction extends AnAction
+{
+ private SMTestProxy.SMRootTestProxy tests;
+
+ // Action class must have a no argument constructor
+ public RerunFailedTestsAction() {
+ this.getTemplatePresentation().setText("Rerun Failed Tests");
+ this.getTemplatePresentation().setIcon(AllIcons.RunConfigurations.RerunFailedTests);
+ }
+
+ public RerunFailedTestsAction(SMTestProxy.SMRootTestProxy tests)
+ {
+ this();
+ this.tests = tests;
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ Presentation presentation = e.getPresentation();
+ presentation.setEnabled(false);
+ presentation.setVisible(true);
+
+ if (!this.tests.isInProgress() && !this.tests.isPassed()) {
+ presentation.setEnabled(true);
+ }
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent anActionEvent)
+ {
+ RunnerConfiguration runConfiguration = new RunnerConfiguration();
+ Project project = getEventProject(anActionEvent);
+
+ for (SMTestProxy methodProxy: tests.getAllTests()) {
+ if (!methodProxy.isPassed()) {
+ Location loc = methodProxy.getLocation(project, GlobalSearchScope.EMPTY_SCOPE);
+
+ if (loc == null) {
+ continue;
+ }
+
+ PsiElement elem = loc.getPsiElement();
+
+ if (elem instanceof PhpClass) {
+ Utils.saveFiles((PhpClass) elem, project);
+ runConfiguration.setFile((PhpFile)elem.getContainingFile());
+ continue;
+ }
+
+ while (elem != null && !(elem instanceof Method)) {
+ elem = elem.getParent();
+ }
+
+ if (elem != null) {
+ runConfiguration.addMethod((Method) elem);
+ }
+ }
+ }
+
+ Runner runner = new Runner(project);
+ runner.run(runConfiguration);
+ }
+}
diff --git a/src/org/atoum/intellij/plugin/atoum/actions/Run.java b/src/org/atoum/intellij/plugin/atoum/actions/Run.java
index 2104e29..a5aa4db 100644
--- a/src/org/atoum/intellij/plugin/atoum/actions/Run.java
+++ b/src/org/atoum/intellij/plugin/atoum/actions/Run.java
@@ -4,8 +4,6 @@
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.jetbrains.php.lang.psi.PhpFile;
@@ -42,23 +40,6 @@ public void update(AnActionEvent event) {
}
}
- protected void saveFiles(PhpClass currentTestClass, Project project) {
- FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
-
- Document documentTestClass = fileDocumentManager.getDocument(currentTestClass.getContainingFile().getVirtualFile());
- if (documentTestClass != null) {
- fileDocumentManager.saveDocument(documentTestClass);
- }
-
- PhpClass currentTestedClass = Utils.locateTestedClass(project, currentTestClass);
- if (currentTestedClass != null) {
- Document documentTestedClass = fileDocumentManager.getDocument(currentTestedClass.getContainingFile().getVirtualFile());
- if (documentTestedClass != null) {
- fileDocumentManager.saveDocument(documentTestedClass);
- }
- }
- }
-
public void actionPerformed(final AnActionEvent e) {
PhpClass currentTestClass = getCurrentTestClass(e);
VirtualFile selectedDir = null;
@@ -73,12 +54,12 @@ public void actionPerformed(final AnActionEvent e) {
RunnerConfiguration runConfiguration = new RunnerConfiguration();
if (null != currentTestClass) {
- saveFiles(currentTestClass, project);
+ Utils.saveFiles(currentTestClass, project);
runConfiguration.setFile((PhpFile)currentTestClass.getContainingFile());
Method currentTestMethod = getCurrentTestMethod(e);
if (currentTestMethod != null) {
- runConfiguration.setMethod(currentTestMethod);
+ runConfiguration.addMethod(currentTestMethod);
}
}
diff --git a/src/org/atoum/intellij/plugin/atoum/model/MethodResult.java b/src/org/atoum/intellij/plugin/atoum/model/MethodResult.java
index 09c49b9..14209af 100644
--- a/src/org/atoum/intellij/plugin/atoum/model/MethodResult.java
+++ b/src/org/atoum/intellij/plugin/atoum/model/MethodResult.java
@@ -10,10 +10,12 @@ public class MethodResult {
protected String name;
protected String content;
+ protected Integer failLineNumber;
- public MethodResult(String name, String content) {
+ public MethodResult(String name, String content, Integer failLineNumber) {
this.name = name;
this.content = content;
+ this.failLineNumber = failLineNumber;
}
public void definedStatePassed() {
@@ -42,4 +44,9 @@ public String getState()
{
return this.state;
}
+
+ public Integer getFailLineNumber()
+ {
+ return this.failLineNumber;
+ }
}
diff --git a/src/org/atoum/intellij/plugin/atoum/model/RunnerConfiguration.java b/src/org/atoum/intellij/plugin/atoum/model/RunnerConfiguration.java
index adbc367..ef130bb 100644
--- a/src/org/atoum/intellij/plugin/atoum/model/RunnerConfiguration.java
+++ b/src/org/atoum/intellij/plugin/atoum/model/RunnerConfiguration.java
@@ -4,13 +4,16 @@
import com.jetbrains.php.lang.psi.PhpFile;
import com.jetbrains.php.lang.psi.elements.Method;
+import java.util.ArrayList;
+import java.util.List;
+
public class RunnerConfiguration {
protected PhpFile file;
protected VirtualFile directory;
- protected Method method;
+ protected List methods = new ArrayList<>();
public PhpFile getFile () {
return file;
@@ -28,11 +31,11 @@ public void setDirectory(VirtualFile directory) {
this.directory = directory;
}
- public void setMethod(Method method) {
- this.method = method;
+ public void addMethod(Method method) {
+ this.methods.add(method);
}
- public Method getMethod () {
- return method;
+ public List getMethods () {
+ return methods;
}
}
diff --git a/src/org/atoum/intellij/plugin/atoum/model/TestsResultFactory.java b/src/org/atoum/intellij/plugin/atoum/model/TestsResultFactory.java
index 438cae4..17ead1e 100644
--- a/src/org/atoum/intellij/plugin/atoum/model/TestsResultFactory.java
+++ b/src/org/atoum/intellij/plugin/atoum/model/TestsResultFactory.java
@@ -13,6 +13,7 @@ public static TestsResult createFromTapOutput(String tapOutput)
Pattern statusLinePattern = Pattern.compile("((?:not )?ok) (\\d+)(?: (?:# SKIP|# TODO|-) (.+)::(.+)\\(\\))?$");
Pattern nameLinePattern = Pattern.compile("^# ([\\w\\\\]+)::(.+)\\(\\)$");
Pattern planLinePattern = Pattern.compile("^\\d+\\.\\.\\d+$");
+ Pattern failLocationPattern = Pattern.compile("^# .+:([0-9]+)$");
String[] tapOutputLines = tapOutput.split("\n");
@@ -21,6 +22,7 @@ public static TestsResult createFromTapOutput(String tapOutput)
String currentContent = "";
String currentClassname = "";
String currentStatus = "";
+ Integer currentLineNumber = null;
for (Integer i = 0; i < tapOutputLines.length; i++) {
String currentLine = tapOutputLines[i];
@@ -33,13 +35,14 @@ public static TestsResult createFromTapOutput(String tapOutput)
if (statusLineMatcher.matches()) {
if (infosFound) {
- flushLine(testsResult, currentClassname, currentMethodName, currentContent, currentStatus);
+ flushLine(testsResult, currentClassname, currentMethodName, currentContent, currentStatus, currentLineNumber);
}
currentMethodName = statusLineMatcher.group(4);
currentContent = "";
currentClassname = statusLineMatcher.group(3);
currentStatus = statusLineMatcher.group(1);
+ currentLineNumber = null;
infosFound = true;
} else {
@@ -47,21 +50,26 @@ public static TestsResult createFromTapOutput(String tapOutput)
if (nameLineMatcher.matches()) {
currentClassname = nameLineMatcher.group(1);
currentMethodName = nameLineMatcher.group(2);
- } else if (currentLine.length() > 0) {
- currentContent += currentLine.substring(1) + "\n";
+ } else {
+ Matcher failLocationMatcher = failLocationPattern.matcher(currentLine);
+ if (failLocationMatcher.matches()) {
+ currentLineNumber = Integer.parseInt(failLocationMatcher.group(1));
+ } else if (currentLine.length() > 0) {
+ currentContent += currentLine.substring(1) + "\n";
+ }
}
}
}
if (infosFound) {
- flushLine(testsResult, currentClassname, currentMethodName, currentContent, currentStatus);
+ flushLine(testsResult, currentClassname, currentMethodName, currentContent, currentStatus, currentLineNumber);
}
return testsResult;
}
- protected static void flushLine(TestsResult testsResult, String currentClassname, String currentMethodName, String currentContent, String currentStatus) {
- MethodResult methodResult = new MethodResult(currentMethodName, currentContent);
+ protected static void flushLine(TestsResult testsResult, String currentClassname, String currentMethodName, String currentContent, String currentStatus, Integer lineNumber) {
+ MethodResult methodResult = new MethodResult(currentMethodName, currentContent, lineNumber);
if (!testsResult.hasClassResult(currentClassname)) {
ClassResult classResult = new ClassResult();
classResult.setName(currentClassname);
diff --git a/src/org/atoum/intellij/plugin/atoum/run/CommandLineArgumentsBuilder.java b/src/org/atoum/intellij/plugin/atoum/run/CommandLineArgumentsBuilder.java
index 4979cf8..57f4f92 100644
--- a/src/org/atoum/intellij/plugin/atoum/run/CommandLineArgumentsBuilder.java
+++ b/src/org/atoum/intellij/plugin/atoum/run/CommandLineArgumentsBuilder.java
@@ -1,6 +1,7 @@
package org.atoum.intellij.plugin.atoum.run;
import com.jetbrains.php.config.interpreters.PhpConfigurationOptionData;
+import com.jetbrains.php.lang.psi.elements.Method;
import org.atoum.intellij.plugin.atoum.model.RunnerConfiguration;
import java.io.File;
@@ -53,9 +54,9 @@ public CommandLineArgumentsBuilder useConfiguration(RunnerConfiguration runnerCo
this.commandLineArgs.add("-f");
this.commandLineArgs.add(this.relativizePath(runnerConfiguration.getFile().getVirtualFile().getPath()));
}
- if (null != runnerConfiguration.getMethod()) {
+ for (Method method : runnerConfiguration.getMethods()) {
this.commandLineArgs.add("-m");
- this.commandLineArgs.add("*::" + runnerConfiguration.getMethod().getName());
+ this.commandLineArgs.add("*::" + method.getName());
}
return this;
diff --git a/src/org/atoum/intellij/plugin/atoum/run/Runner.java b/src/org/atoum/intellij/plugin/atoum/run/Runner.java
index d9c520a..d74b5b1 100644
--- a/src/org/atoum/intellij/plugin/atoum/run/Runner.java
+++ b/src/org/atoum/intellij/plugin/atoum/run/Runner.java
@@ -18,14 +18,17 @@
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.ActionToolbar;
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.SimpleToolWindowPanel;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowAnchor;
import com.intellij.openapi.wm.ToolWindowManager;
-import com.intellij.psi.PsiDirectory;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentManager;
import com.jetbrains.php.config.PhpProjectConfigurationFacade;
@@ -34,6 +37,7 @@
import com.jetbrains.php.run.PhpRunConfiguration;
import com.jetbrains.php.run.PhpRunConfigurationFactoryBase;
import org.atoum.intellij.plugin.atoum.AtoumUtils;
+import org.atoum.intellij.plugin.atoum.actions.RerunFailedTestsAction;
import org.atoum.intellij.plugin.atoum.model.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -137,14 +141,23 @@ public RunConfiguration createTemplateConfiguration(Project project) {
commandLineBuilder.useConfigFile(phpstormConfigFile);
}
+ ActionManager am = ActionManager.getInstance();
+ DefaultActionGroup buttonGroup = new DefaultActionGroup();
+ ActionToolbar viewToolbar = am.createActionToolbar("atoum.ConsoleToolbar", buttonGroup, false);
+
+ SimpleToolWindowPanel toolWindowPanel = new SimpleToolWindowPanel(false, true);
+ toolWindowPanel.setContent(testsOutputConsoleView.getComponent());
+ toolWindowPanel.setToolbar(viewToolbar.getComponent());
+
ContentManager contentManager = toolWindow.getContentManager();
- Content myContent;
- myContent = toolWindow.getContentManager().getFactory().createContent(testsOutputConsoleView.getComponent(), "tests results", false);
- toolWindow.getContentManager().removeAllContents(true);
- toolWindow.getContentManager().addContent(myContent);
+ Content myContent = contentManager.getFactory().createContent(toolWindowPanel.getComponent(), "tests results", false);
+ contentManager.removeAllContents(true);
+ contentManager.addContent(myContent);
final SMTRunnerConsoleView console = (SMTRunnerConsoleView)testsOutputConsoleView;
+ buttonGroup.add(new RerunFailedTestsAction(console.getResultsViewer().getTestsRootNode()));
+
String[] commandLineArgs = commandLineBuilder.build();
HashMap environnmentVariables = new HashMap();
diff --git a/src/org/atoum/intellij/plugin/atoum/run/SMTRootTestProxyFactory.java b/src/org/atoum/intellij/plugin/atoum/run/SMTRootTestProxyFactory.java
index 2566b7e..77c284b 100644
--- a/src/org/atoum/intellij/plugin/atoum/run/SMTRootTestProxyFactory.java
+++ b/src/org/atoum/intellij/plugin/atoum/run/SMTRootTestProxyFactory.java
@@ -1,23 +1,99 @@
package org.atoum.intellij.plugin.atoum.run;
+import com.intellij.execution.Location;
+import com.intellij.execution.PsiLocation;
+import com.intellij.execution.testframework.sm.runner.SMTestLocator;
import com.intellij.execution.testframework.sm.runner.SMTestProxy;
+import com.intellij.openapi.editor.LazyRangeMarkerFactory;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.jetbrains.php.PhpIndex;
+import com.jetbrains.php.lang.psi.elements.Method;
+import com.jetbrains.php.lang.psi.elements.PhpClass;
import org.atoum.intellij.plugin.atoum.model.ClassResult;
import org.atoum.intellij.plugin.atoum.model.MethodResult;
import org.atoum.intellij.plugin.atoum.model.TestsResult;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
public class SMTRootTestProxyFactory {
public static SMTestProxy.SMRootTestProxy createFromClassResult(ClassResult classResult) {
+ SMTestLocator methodLocator = new SMTestLocator() {
+ @NotNull
+ @Override
+ public List getLocation(@NotNull String protocol, @NotNull String path, @NotNull Project project, @NotNull GlobalSearchScope globalSearchScope) {
+ String[] parts = path.split(":");
+
+ Collection classes = PhpIndex.getInstance(project).getClassesByFQN(parts[0]);
+ if (classes.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ Method method = classes.iterator().next().findMethodByName(parts[1]);
+
+ if (method == null) {
+ return new ArrayList<>();
+ }
+
+ ArrayList locations = new ArrayList<>(1);
+ PsiElement elem = null;
+
+ if (parts.length == 3) {
+ PsiFile file = method.getContainingFile();
+ int line = Integer.parseInt(parts[2]);
+ int offset = LazyRangeMarkerFactory.getInstance(project).createRangeMarker(file.getVirtualFile(), line, 0, false).getStartOffset();
+ elem = file.findElementAt(offset);
+ }
+
+ if (elem == null) {
+ elem = method;
+ }
+
+ locations.add(new PsiLocation<>(elem));
+
+ return locations;
+ }
+ };
+
SMTestProxy.SMRootTestProxy classNode = new SMTestProxy.SMRootTestProxy();
classNode.setPresentation(classResult.getName());
classNode.setFinished();
+ classNode.setRootLocationUrl("atoum://" + classResult.getName());
+
+ classNode.setLocator(new SMTestLocator() {
+ @NotNull
+ @Override
+ public List getLocation(@NotNull String protocol, @NotNull String path, @NotNull Project project, @NotNull GlobalSearchScope globalSearchScope) {
+ Collection classes = PhpIndex.getInstance(project).getClassesByFQN(path);
+ if (classes.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ ArrayList locations = new ArrayList<>(1);
+ locations.add(new PsiLocation<>(classes.iterator().next()));
+
+ return locations;
+ }
+ });
if (classResult.getState().equals(ClassResult.STATE_FAILED)) {
classNode.setTestFailed("", "", true);
}
for (MethodResult methodsResult: classResult.getMethods()) {
- SMTestProxy methodNode = new SMTestProxy(methodsResult.getName(), false, "");
+ String url = "atoum://" + classResult.getName() + ":" + methodsResult.getName();
+ if (methodsResult.getFailLineNumber() != null) {
+ url += ":" + methodsResult.getFailLineNumber();
+ }
+
+ SMTestProxy methodNode = new SMTestProxy(methodsResult.getName(), false, url);
+ methodNode.setLocator(methodLocator);
if (methodsResult.getState().equals(MethodResult.STATE_FAILED)) {
methodNode.setTestFailed(methodsResult.getName() + " Failed", methodsResult.getContent(), true);