/******************************************************************************* * * Copyright (c) 2009, Yahoo!, Inc. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * * * * *******************************************************************************/ package hudson.tasks.test; import hudson.tasks.junit.TestAction; import hudson.model.AbstractBuild; import hudson.model.Run; import hudson.model.Result; import java.util.Collection; import static java.util.Collections.emptyList; /** * A class that represents a general concept of a test result, without any * language or implementation specifics. Subclasses must add * * @Exported annotation to the fields they want to export. * * @sine 1.343 */ public abstract class TestResult extends TestObject { /** * If the concept of a parent action is important to a subclass, then it * should provide a non-noop implementation of this method. * * @param action */ public void setParentAction(AbstractTestResultAction action) { } /** * Returns the action that points to the top level test result includes this * test result. * * @return */ public AbstractTestResultAction getParentAction() { return getOwner().getTestResultAction(); } /** * Request that the result update its counts of its children. Does not * require a parent action or owner or siblings. Subclasses should implement * this, unless they are *always* in a tallied state. */ public void tally() { } /** * Sets the parent test result * * @param parent */ public void setParent(TestObject parent) { } /** * Gets the human readable title of this result object. */ public /* abstract */ String getTitle() { return ""; } /** * Mark a build as unstable if there are failures. Otherwise, leave the * build result unchanged. * * @return {@link Result#UNSTABLE} if there are test failures, null * otherwise. * */ public Result getBuildResult() { if (getFailCount() > 0) { return Result.UNSTABLE; } else { return null; } } /** * Time it took to run this test. In seconds. */ public /* abstract */ float getDuration() { return 0.0f; } /** * Gets the total number of passed tests. */ public /* abstract */ int getPassCount() { return 0; } /** * Gets the total number of failed tests. */ public /* abstract */ int getFailCount() { return 0; } /** * Gets the total number of skipped tests. */ public /* abstract */ int getSkipCount() { return 0; } /** * Gets the counter part of this {@link TestResult} in the previous run. * * @return null if no such counter part exists. */ public TestResult getPreviousResult() { AbstractBuild<?, ?> b = getOwner(); if (b == null) { return null; } while (true) { b = b.getPreviousBuild(); if (b == null) { return null; } AbstractTestResultAction r = b.getAction(getParentAction().getClass()); if (r != null) { TestResult result = r.findCorrespondingResult(this.getId()); if (result != null) { return result; } } } } /** * Gets the counter part of this {@link TestResult} in the specified run. * * @return null if no such counter part exists. */ public TestResult getResultInBuild(AbstractBuild<?, ?> build) { AbstractTestResultAction tra = build.getAction(getParentAction().getClass()); if (tra == null) { tra = build.getAction(AbstractTestResultAction.class); } return (tra == null) ? null : tra.findCorrespondingResult(this.getId()); } /** * Gets the "children" of this test result that failed * * @return the children of this test result, if any, or an empty collection */ public Collection<? extends TestResult> getFailedTests() { return emptyList(); } /** * Gets the "children" of this test result that passed * * @return the children of this test result, if any, or an empty collection */ public Collection<? extends TestResult> getPassedTests() { return emptyList(); } /** * Gets the "children" of this test result that were skipped * * @return the children of this test result, if any, or an empty list */ public Collection<? extends TestResult> getSkippedTests() { return emptyList(); } /** * If this test failed, then return the build number when this test started * failing. */ public int getFailedSince() { return 0; } /** * If this test failed, then return the run when this test started failing. */ public Run<?, ?> getFailedSinceRun() { return null; } /** * The stdout of this test. */ public String getStdout() { return ""; } /** * The stderr of this test. */ public String getStderr() { return ""; } /** * If there was an error or a failure, this is the stack trace, or otherwise * null. */ public String getErrorStackTrace() { return ""; } /** * If there was an error or a failure, this is the text from the message. */ public String getErrorDetails() { return ""; } /** * @return true if the test was not skipped and did not fail, false * otherwise. */ public boolean isPassed() { return ((getSkipCount() == 0) && (getFailCount() == 0)); } public String toPrettyString() { StringBuilder sb = new StringBuilder(); sb.append("{"); sb.append("Name: ").append(this.getName()).append(", "); sb.append("Result: ").append(this.getBuildResult()).append(",\n"); sb.append("Total Count: ").append(this.getTotalCount()).append(", "); sb.append("Fail: ").append(this.getFailCount()).append(", "); sb.append("Skipt: ").append(this.getSkipCount()).append(", "); sb.append("Pass: ").append(this.getSkipCount()).append(",\n"); sb.append("Test Result Class: ").append(this.getClass().getName()).append(" }\n"); return sb.toString(); } /** * Annotate some text -- what does this do? * * @param text * @return */ public String annotate(String text) { if (text == null) { return null; } text = text.replace("&", "&").replace("<", "<").replaceAll( "\\b(https?://[^\\s)>]+)", "<a href=\"$1\">$1</a>"); for (TestAction action : getTestActions()) { text = action.annotate(text); } return text; } }