| /* |
| * Copyright 2000-2009 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.theoryinpractice.testng.model; |
| |
| import com.intellij.execution.Location; |
| import com.intellij.execution.PsiLocation; |
| import com.intellij.execution.testframework.*; |
| import com.intellij.execution.testframework.stacktrace.DiffHyperlink; |
| import com.intellij.execution.ui.ConsoleViewContentType; |
| import com.intellij.ide.util.EditSourceUtil; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Comparing; |
| import com.intellij.openapi.util.registry.Registry; |
| import com.intellij.pom.Navigatable; |
| import com.intellij.psi.*; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.Nullable; |
| import org.testng.remote.strprotocol.MessageHelper; |
| import org.testng.remote.strprotocol.TestResultMessage; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| /** |
| * @author Hani Suleiman Date: Jul 28, 2005 Time: 10:52:51 PM |
| */ |
| public class TestProxy extends AbstractTestProxy { |
| @NonNls public static final Pattern COMPARISION_PATTERN = |
| Pattern.compile("(.*)expected same with:\\<(.*)\\> but was:\\<(.*)\\>.*", Pattern.DOTALL); |
| @NonNls public static final Pattern EXPECTED_BUT_WAS_PATTERN = |
| Pattern.compile("(.*)expected:\\<(.*)\\> but was:\\<(.*)\\>.*", Pattern.DOTALL); |
| @NonNls public static final Pattern EXPECTED_BUT_WAS_SET_PATTERN = |
| Pattern.compile("(.*)expected \\[(.*)\\] but got \\[(.*)\\].*", Pattern.DOTALL); |
| @NonNls public static final Pattern EXPECTED_NOT_SAME_BUT_WAS_PATTERN = |
| Pattern.compile("(.*)expected not same with:\\<(.*)\\> but was same:\\<(.*)\\>.*", Pattern.DOTALL); |
| @NonNls public static final Pattern EXPECTED_BUT_FOUND_PATTERN = |
| Pattern.compile("(.*)expected \\[(.*)\\] but found \\[(.*)\\].*", Pattern.DOTALL); |
| private final List<TestProxy> results = new ArrayList<TestProxy>(); |
| private TestResultMessage resultMessage; |
| private String name; |
| private TestProxy parent; |
| private SmartPsiElementPointer psiElement; |
| private boolean inProgress; |
| private boolean myTearDownFailure; |
| private DiffHyperlink myHyperlink; |
| |
| public TestProxy() {} |
| |
| public TestProxy(String name) { |
| this.name = name; |
| } |
| |
| public String getName() { |
| return name; |
| } |
| |
| @Nullable |
| public PsiElement getPsiElement() { |
| return psiElement != null ? psiElement.getElement() : null; |
| } |
| |
| public void setPsiElement(PsiElement psiElement) { |
| if (psiElement != null) { |
| final Project project = psiElement.getProject(); |
| PsiDocumentManager.getInstance(project).commitAllDocuments(); |
| this.psiElement = SmartPointerManager.getInstance(project).createSmartPsiElementPointer(psiElement); |
| } else { |
| this.psiElement = null; |
| } |
| } |
| |
| public boolean isResult() { |
| return resultMessage != null; |
| } |
| |
| public List<AbstractTestProxy> getResults(Filter filter) { |
| return filter.select(results); |
| } |
| |
| public List<TestProxy> getChildren() { |
| return results; |
| } |
| |
| public TestResultMessage getResultMessage() { |
| return resultMessage; |
| } |
| |
| public void setResultMessage(final TestResultMessage resultMessage) { |
| //if we have a result, then our parent is a class, so we can look up our method |
| //this is a bit fragile as it assumes parent is set first and correctly |
| ApplicationManager.getApplication().runReadAction(new Runnable() { |
| public void run() { |
| PsiClass psiClass = (PsiClass)getParent().getPsiElement(); |
| if (psiClass != null) { |
| PsiMethod[] methods = psiClass.getAllMethods(); |
| for (PsiMethod method : methods) { |
| if (method.getName().equals(resultMessage.getMethod())) { |
| setPsiElement(method); |
| break; |
| } |
| } |
| } |
| } |
| }); |
| |
| TestProxy current = this; |
| while (current != null) { |
| current.inProgress = resultMessage.getResult() == MessageHelper.TEST_STARTED; |
| current = current.getParent(); |
| } |
| if (this.resultMessage == null || this.resultMessage.getResult() == MessageHelper.TEST_STARTED) { |
| this.resultMessage = resultMessage; |
| final PsiElement psiElement = getPsiElement(); |
| this.name = toDisplayText(resultMessage, psiElement != null ? psiElement.getProject() : null); |
| } |
| } |
| |
| public boolean isInProgress() { |
| final TestProxy parentProxy = getParent(); |
| return (parentProxy == null || parentProxy.isInProgress()) && inProgress; |
| } |
| |
| public boolean isDefect() { |
| return isNotPassed(); |
| } |
| |
| public boolean shouldRun() { |
| return true; |
| } |
| |
| public int getMagnitude() { |
| return -1; |
| } |
| |
| public boolean isLeaf() { |
| return isResult(); |
| } |
| |
| public boolean isPassed() { |
| return !isNotPassed(); |
| } |
| |
| public Location getLocation(final Project project, GlobalSearchScope searchScope) { |
| if (psiElement == null) return null; |
| final PsiElement element = psiElement.getElement(); |
| if (element == null) return null; |
| return new PsiLocation<PsiElement>(project, element); |
| } |
| |
| @Nullable |
| public Navigatable getDescriptor(final Location location, final TestConsoleProperties testConsoleProperties) { |
| if (location == null) return null; |
| return EditSourceUtil.getDescriptor(location.getPsiElement()); |
| } |
| |
| @Override |
| public String toString() { |
| return name + ' ' + results; |
| } |
| |
| public void addChild(TestProxy proxy) { |
| results.add(proxy); |
| proxy.setParent(this); |
| proxy.setPrinter(myPrinter); |
| addLast(proxy); |
| } |
| |
| public void setParent(TestProxy parent) { |
| this.parent = parent; |
| } |
| |
| public TestProxy getParent() { |
| return parent; |
| } |
| |
| public boolean isNotPassed() { |
| if (resultNotPassed()) return true; |
| //we just added the node, so we don't know if it has passes or fails |
| if (resultMessage == null && results.size() == 0) return true; |
| for (TestProxy child : results) { |
| if (child.isNotPassed()) return true; |
| } |
| return false; |
| } |
| |
| private boolean resultNotPassed() { |
| return resultMessage != null && resultMessage.getResult() != MessageHelper.PASSED_TEST; |
| } |
| |
| public List<TestProxy> getAllTests() { |
| List<TestProxy> total = new ArrayList<TestProxy>(); |
| total.add(this); |
| for (TestProxy child : results) { |
| total.addAll(child.getAllTests()); |
| } |
| return total; |
| } |
| |
| public int getChildCount() { |
| return results.size(); |
| } |
| |
| public TestProxy getChildAt(int i) { |
| return results.get(i); |
| } |
| |
| public TestProxy getFirstDefect() { |
| for (TestProxy child : results) { |
| if (child.isNotPassed() && child.isResult()) return child; |
| TestProxy firstDefect = child.getFirstDefect(); |
| if (firstDefect != null) return firstDefect; |
| } |
| return null; |
| } |
| |
| |
| public boolean isInterrupted() { |
| return !isInProgress() && inProgress; |
| } |
| |
| @Override |
| public boolean isIgnored() { |
| return resultMessage != null && MessageHelper.SKIPPED_TEST == resultMessage.getResult(); |
| } |
| |
| public boolean isTearDownFailure() { |
| for (TestProxy result : results) { |
| if (result.isTearDownFailure()) return true; |
| } |
| return myTearDownFailure; |
| } |
| |
| public void setTearDownFailure(boolean tearDownFailure) { |
| myTearDownFailure = tearDownFailure; |
| } |
| |
| public void appendStacktrace(TestResultMessage result) { |
| if (result.getResult() == MessageHelper.PASSED_TEST && Registry.is("testng.skip.expected.exceptions")) return; |
| final String stackTrace = result.getStackTrace(); |
| if (stackTrace != null) { |
| final List<Printable> printables = getPrintables(result); |
| for (Printable printable : printables) { |
| if (myHyperlink == null && printable instanceof DiffHyperlink) { |
| myHyperlink = (DiffHyperlink)printable; |
| } |
| addLast(printable); |
| } |
| } |
| } |
| |
| @Override |
| public Long getDuration() { |
| TestResultMessage message = getResultMessage(); |
| if (message != null) { |
| return (message.getEndMillis() - message.getStartMillis()); |
| } |
| else { |
| // TODO cache? |
| long duration = 0; |
| for (TestProxy testProxy : getChildren()) { |
| final Long d = testProxy.getDuration(); |
| duration += (d == null ? 0 : d.longValue()); |
| } |
| return duration; |
| } |
| } |
| |
| @Override |
| public boolean shouldSkipRootNodeForExport() { |
| return true; |
| } |
| |
| @Override |
| public AssertEqualsDiffViewerProvider getDiffViewerProvider() { |
| if (myHyperlink == null) { |
| return null; |
| } |
| return new AssertEqualsMultiDiffViewProvider() { |
| @Override |
| public void openDiff(Project project) { |
| myHyperlink.openDiff(project); |
| } |
| |
| @Override |
| public String getExpected() { |
| return myHyperlink.getLeft(); |
| } |
| |
| @Override |
| public String getActual() { |
| return myHyperlink.getRight(); |
| } |
| |
| @Override |
| public void openMultiDiff(Project project, AssertEqualsDiffChain chain) { |
| myHyperlink.openMultiDiff(project, chain); |
| } |
| |
| @Override |
| public String getFilePath() { |
| return myHyperlink.getFilePath(); |
| } |
| }; |
| } |
| |
| private static String trimStackTrace(String stackTrace) { |
| String[] lines = stackTrace.split("\n"); |
| StringBuilder builder = new StringBuilder(); |
| |
| if (lines.length > 0) { |
| int i = lines.length - 1; |
| while (i >= 0) { |
| //first 4 chars are '\t at ' |
| int startIndex = lines[i].indexOf('a') + 3; |
| if (lines[i].length() > 4 && |
| (lines[i].startsWith("org.testng.", startIndex) || |
| lines[i].startsWith("org.junit.", startIndex) || |
| lines[i].startsWith("sun.reflect.DelegatingMethodAccessorImpl", startIndex) || |
| lines[i].startsWith("sun.reflect.NativeMethodAccessorImpl", startIndex) || |
| lines[i].startsWith("java.lang.reflect.Method", startIndex) || |
| lines[i].startsWith("com.intellij.rt.execution.application.AppMain", startIndex))) { |
| |
| } |
| else { |
| // we're done with internals, so we know the rest are ok |
| break; |
| } |
| i--; |
| } |
| for (int j = 0; j <= i; j++) { |
| builder.append(lines[j]); |
| builder.append('\n'); |
| } |
| } |
| return builder.toString(); |
| } |
| |
| private static List<Printable> getPrintables(final TestResultMessage result) { |
| String s = trimStackTrace(result.getStackTrace()); |
| List<Printable> printables = new ArrayList<Printable>(); |
| //figure out if we have a diff we need to hyperlink |
| if (appendDiffChuncks(result, s, printables, COMPARISION_PATTERN)) { |
| return printables; |
| } |
| if (appendDiffChuncks(result, s, printables, EXPECTED_BUT_WAS_PATTERN)) { |
| return printables; |
| } |
| if (appendDiffChuncks(result, s, printables, EXPECTED_BUT_WAS_SET_PATTERN)) { |
| return printables; |
| } |
| if (appendDiffChuncks(result, s, printables, EXPECTED_NOT_SAME_BUT_WAS_PATTERN)) { |
| return printables; |
| } |
| if (appendDiffChuncks(result, s, printables, EXPECTED_BUT_FOUND_PATTERN)) { |
| return printables; |
| } |
| printables.add(new Chunk(s, ConsoleViewContentType.ERROR_OUTPUT)); |
| return printables; |
| } |
| |
| private static boolean appendDiffChuncks(final TestResultMessage result, String s, List<Printable> printables, final Pattern pattern) { |
| final Matcher matcher = pattern.matcher(s); |
| if (matcher.matches()) { |
| printables.add(new Chunk(matcher.group(1), ConsoleViewContentType.ERROR_OUTPUT)); |
| //we have an assert with expected/actual, so we parse it out and create a diff hyperlink |
| DiffHyperlink link = new DiffHyperlink(matcher.group(2), matcher.group(3), null) { |
| protected String getTitle() { |
| //TODO should do some more farting about to find the equality assertion that failed and show that as title |
| return result.getTestClass() + '#' + result.getMethod() + "() failed"; |
| } |
| }; |
| //same as junit diff view |
| printables.add(link); |
| printables.add(new Chunk(trimStackTrace(s.substring(matcher.end(3) + 1)), ConsoleViewContentType.ERROR_OUTPUT)); |
| return true; |
| } |
| return false; |
| } |
| |
| public static String toDisplayText(TestResultMessage message, Project project) { |
| String name = message.getName(); |
| if (project != null && Comparing.strEqual(name, project.getName())) { |
| name = message.getMethod(); |
| } |
| final String mainNamePart = name; |
| final String[] parameters = message.getParameters(); |
| if (parameters != null && parameters.length > 0) { |
| final String[] parameterTypes = message.getParameterTypes(); |
| name += " ("; |
| for(int i= 0; i < parameters.length; i++) { |
| if(i > 0) { |
| name += ", "; |
| } |
| if(CommonClassNames.JAVA_LANG_STRING.equals(parameterTypes[i]) && !("null".equals(parameters[i]) || "\"\"".equals(parameters[i]))) { |
| name += "\"" + parameters[i] + "\""; |
| } |
| else { |
| name += parameters[i]; |
| } |
| |
| } |
| name += ")"; |
| } |
| final String testDescription = message.getTestDescription(); |
| if (testDescription != null && !Comparing.strEqual(testDescription, mainNamePart)) { |
| name += " [" + testDescription + "]"; |
| } |
| return name; |
| } |
| |
| public static class Chunk implements Printable { |
| public String text; |
| public ConsoleViewContentType contentType; |
| |
| public void printOn(Printer printer) { |
| printer.print(text, contentType); |
| } |
| |
| public Chunk(String text, ConsoleViewContentType contentType) { |
| this.text = text; |
| this.contentType = contentType; |
| } |
| |
| public String toString() { |
| return text; |
| } |
| } |
| } |