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/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/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);