package hudson.plugins.emailext.plugins.content; import hudson.Extension; import hudson.FilePath; import hudson.model.AbstractBuild; import hudson.model.Run; import hudson.model.TaskListener; import hudson.tasks.junit.CaseResult; import hudson.tasks.test.AbstractTestResultAction; import hudson.tasks.test.TestResult; import org.jenkinsci.plugins.tokenmacro.DataBoundTokenMacro; import org.jenkinsci.plugins.tokenmacro.MacroEvaluationException; import java.io.IOException; /** * An EmailContent for failing tests. Only shows tests that have failed. * * @author markltbaker */ @Extension public class FailedTestsContent extends DataBoundTokenMacro { @Parameter public boolean showStack = true; @Parameter public boolean showMessage = true; @Parameter public int maxTests = Integer.MAX_VALUE; @Parameter public boolean onlyRegressions = false; @Parameter public int maxLength = Integer.MAX_VALUE; public static final String MACRO_NAME = "FAILED_TESTS"; @Override public boolean acceptsMacroName(String macroName) { return macroName.equals(MACRO_NAME); } @Override public String evaluate(AbstractBuild<?, ?> build, TaskListener listener, String macroName) throws MacroEvaluationException, IOException, InterruptedException { return evaluate(build, build.getWorkspace(), listener, macroName); } @Override public String evaluate(Run<?, ?> run, FilePath workspace, TaskListener listener, String macroName) throws MacroEvaluationException, IOException, InterruptedException { StringBuilder buffer = new StringBuilder(); AbstractTestResultAction<?> testResult = run.getAction(AbstractTestResultAction.class); if (null == testResult) { return "No tests ran."; } int failCount = testResult.getFailCount(); if (failCount == 0) { buffer.append("All tests passed"); } else { buffer.append(failCount).append(" tests failed.\n"); boolean showOldFailures = !onlyRegressions; if(maxLength < Integer.MAX_VALUE) { maxLength *= 1024; } if (maxTests > 0) { int printedTests = 0; int printedLength = 0; for (TestResult failedTest : testResult.getFailedTests()) { if (showOldFailures || getTestAge(failedTest) == 1) { if (printedTests < maxTests && printedLength <= maxLength) { printedLength += outputTest(buffer, failedTest, showStack, showMessage, maxLength-printedLength); printedTests++; } } } if (failCount > printedTests) { buffer.append("... and ").append(failCount - printedTests).append(" other failed tests.\n\n"); } if (printedLength >= maxLength) { buffer.append("\n\n... output truncated.\n\n"); } } } return buffer.toString(); } private int getTestAge(TestResult result) { if(result.isPassed()) return 0; else if (result.getRun() != null) { return result.getRun().getNumber()-result.getFailedSince()+1; } else { return 0; } } private int outputTest(StringBuilder buffer, TestResult failedTest, boolean showStack, boolean showMessage, int lengthLeft) { StringBuilder local = new StringBuilder(); local.append(failedTest.isPassed() ? "PASSED" : "FAILED").append(": "); if(failedTest instanceof CaseResult) { local.append(((CaseResult)failedTest).getClassName()); } else { local.append(failedTest.getFullName()); } local.append('.').append(failedTest.getDisplayName()).append('\n'); if (showMessage) { local.append("\nError Message:\n").append(failedTest.getErrorDetails()).append('\n'); } if (showStack) { local.append("\nStack Trace:\n").append(failedTest.getErrorStackTrace()).append('\n'); } if (showMessage || showStack) { local.append('\n'); } if(local.length() > lengthLeft) { local.setLength(lengthLeft); } buffer.append(local.toString()); return local.length(); } }