diff --git a/src/main/java/hudson/tasks/junit/CaseResult.java b/src/main/java/hudson/tasks/junit/CaseResult.java index 00d314e93..52af6ccf0 100644 --- a/src/main/java/hudson/tasks/junit/CaseResult.java +++ b/src/main/java/hudson/tasks/junit/CaseResult.java @@ -24,14 +24,12 @@ package hudson.tasks.junit; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import hudson.util.TextFile; -import org.apache.commons.io.FileUtils; -import org.jvnet.localizer.Localizable; - import hudson.model.Run; import hudson.tasks.test.TestResult; - +import hudson.util.TextFile; +import org.apache.commons.io.FileUtils; import org.dom4j.Element; +import org.jvnet.localizer.Localizable; import org.kohsuke.stapler.export.Exported; import java.io.File; @@ -95,7 +93,18 @@ private static float parseTime(Element testCase) { return new TimeToFloat(time).parse(); } + /** + * @deprecated in favor of {@link #CaseResult(SuiteResult, Element, String, KeepStdioConfig)}. + */ + @Deprecated CaseResult(SuiteResult parent, Element testCase, String testClassName, boolean keepLongStdio) { + this(parent, testCase, testClassName, KeepStdioConfig.defaults(keepLongStdio)); + } + + /** + * @since 1.23 + */ + CaseResult(SuiteResult parent, Element testCase, String testClassName, KeepStdioConfig config) { // schema for JUnit report XML format is not available in Ant, // so I don't know for sure what means what. // reports in http://www.nabble.com/difference-in-junit-publisher-and-ant-junitreport-tf4308604.html#a12265700 @@ -128,42 +137,59 @@ private static float parseTime(Element testCase) { skippedMessage = getSkippedMessage(testCase); @SuppressWarnings("LeakingThisInConstructor") Collection _this = Collections.singleton(this); - stdout = possiblyTrimStdio(_this, keepLongStdio, testCase.elementText("system-out")); - stderr = possiblyTrimStdio(_this, keepLongStdio, testCase.elementText("system-err")); + stdout = possiblyTrimStdio(_this, config, testCase.elementText("system-out")); + stderr = possiblyTrimStdio(_this, config, testCase.elementText("system-err")); } - static String possiblyTrimStdio(Collection results, boolean keepLongStdio, String stdio) { // HUDSON-6516 + static String possiblyTrimStdio(Collection results, KeepStdioConfig config, String stdio) { // HUDSON-6516 if (stdio == null) { return null; } - if (keepLongStdio) { + if (config.isKeepLongStdio()) { return stdio; } int len = stdio.length(); - int halfMaxSize = halfMaxSize(results); - int middle = len - halfMaxSize * 2; + + int maxSize = config.getMaxSize(hasFailed(results)); + if (maxSize < 0) { + return stdio; + } + if (maxSize == 0) { + return null; + } + + int middle = len - maxSize; if (middle <= 0) { return stdio; } + + int halfMaxSize = maxSize / 2; return stdio.subSequence(0, halfMaxSize) + "\n...[truncated " + middle + " chars]...\n" + stdio.subSequence(len - halfMaxSize, len); } /** - * Flavor of {@link #possiblyTrimStdio(Collection, boolean, String)} that doesn't try to read the whole thing into memory. + * Flavor of {@link #possiblyTrimStdio(Collection, KeepStdioConfig, String)} that doesn't try to read the whole thing into memory. */ @SuppressFBWarnings(value = "DM_DEFAULT_ENCODING", justification = "Expected behavior") - static String possiblyTrimStdio(Collection results, boolean keepLongStdio, File stdio) throws IOException { + static String possiblyTrimStdio(Collection results, KeepStdioConfig config, File stdio) throws IOException { long len = stdio.length(); - if (keepLongStdio && len < 1024 * 1024) { + if (config.isKeepLongStdio() && len < 1024 * 1024) { + return FileUtils.readFileToString(stdio); + } + int maxSize = config.getMaxSize(hasFailed(results)); + if (maxSize < 0) { return FileUtils.readFileToString(stdio); } - int halfMaxSize = halfMaxSize(results); + if (maxSize == 0) { + return null; + } - long middle = len - halfMaxSize * 2; + long middle = len - maxSize; if (middle <= 0) { return FileUtils.readFileToString(stdio); } + int halfMaxSize = maxSize / 2; TextFile tx = new TextFile(stdio); String head = tx.head(halfMaxSize); String tail = tx.fastTail(halfMaxSize); @@ -180,15 +206,13 @@ static String possiblyTrimStdio(Collection results, boolean keepLong return head + "\n...[truncated " + middle + " bytes]...\n" + tail; } - private static final int HALF_MAX_SIZE = 500; - private static final int HALF_MAX_FAILING_SIZE = 50000; - private static int halfMaxSize(Collection results) { + private static boolean hasFailed(Collection results) { for (CaseResult result : results) { if (result.errorStackTrace != null) { - return HALF_MAX_FAILING_SIZE; + return true; } } - return HALF_MAX_SIZE; + return false; } diff --git a/src/main/java/hudson/tasks/junit/JUnitParser.java b/src/main/java/hudson/tasks/junit/JUnitParser.java index 866c5abe7..9c037de42 100644 --- a/src/main/java/hudson/tasks/junit/JUnitParser.java +++ b/src/main/java/hudson/tasks/junit/JUnitParser.java @@ -23,19 +23,22 @@ */ package hudson.tasks.junit; -import hudson.model.TaskListener; -import hudson.tasks.test.TestResultParser; -import hudson.*; +import hudson.AbortException; +import hudson.Extension; +import hudson.FilePath; +import hudson.Launcher; +import hudson.Util; import hudson.model.AbstractBuild; import hudson.model.Run; +import hudson.model.TaskListener; import hudson.remoting.VirtualChannel; - -import java.io.IOException; -import java.io.File; +import hudson.tasks.test.TestResultParser; import jenkins.MasterToSlaveFileCallable; - -import org.apache.tools.ant.types.FileSet; import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.types.FileSet; + +import java.io.File; +import java.io.IOException; /** * Parse some JUnit xml files and generate a TestResult containing all the @@ -44,7 +47,7 @@ @Extension public class JUnitParser extends TestResultParser { - private final boolean keepLongStdio; + private final KeepStdioConfig config; private final boolean allowEmptyResults; /** TODO TestResultParser.all does not seem to ever be called so why must this be an Extension? */ @@ -59,17 +62,27 @@ public JUnitParser() { */ @Deprecated public JUnitParser(boolean keepLongStdio) { - this.keepLongStdio = keepLongStdio; + this.config = KeepStdioConfig.defaults(keepLongStdio); this.allowEmptyResults = false; } + /** + * @param config stdio configuration. + * @param allowEmptyResults if true, empty results are allowed + * @since 1.23 + */ + public JUnitParser(KeepStdioConfig config, boolean allowEmptyResults) { + this.config = config; + this.allowEmptyResults = allowEmptyResults; + } + /** * @param keepLongStdio if true, retain a suite's complete stdout/stderr even if this is huge and the suite passed * @param allowEmptyResults if true, empty results are allowed * @since 1.10 */ public JUnitParser(boolean keepLongStdio, boolean allowEmptyResults) { - this.keepLongStdio = keepLongStdio; + this.config = KeepStdioConfig.defaults(keepLongStdio); this.allowEmptyResults = allowEmptyResults; } @@ -101,22 +114,22 @@ public TestResult parseResult(String testResultLocations, // also get code that deals with testDataPublishers from JUnitResultArchiver.perform return workspace.act(new ParseResultCallable(testResultLocations, buildTime, - timeOnMaster, keepLongStdio, allowEmptyResults)); + timeOnMaster, config, allowEmptyResults)); } private static final class ParseResultCallable extends MasterToSlaveFileCallable { private final long buildTime; private final String testResults; private final long nowMaster; - private final boolean keepLongStdio; + private final KeepStdioConfig config; private final boolean allowEmptyResults; - private ParseResultCallable(String testResults, long buildTime, long nowMaster, - boolean keepLongStdio, boolean allowEmptyResults) { + private ParseResultCallable(String testResults, long buildTime, long nowMaster, KeepStdioConfig config, + boolean allowEmptyResults) { this.buildTime = buildTime; this.testResults = testResults; this.nowMaster = nowMaster; - this.keepLongStdio = keepLongStdio; + this.config = config; this.allowEmptyResults = allowEmptyResults; } @@ -129,7 +142,7 @@ public TestResult invoke(File ws, VirtualChannel channel) throws IOException { String[] files = ds.getIncludedFiles(); if (files.length > 0) { - result = new TestResult(buildTime + (nowSlave - nowMaster), ds, keepLongStdio); + result = new TestResult(buildTime + (nowSlave - nowMaster), ds, config); result.tally(); } else { if (this.allowEmptyResults) { diff --git a/src/main/java/hudson/tasks/junit/JUnitResultArchiver.java b/src/main/java/hudson/tasks/junit/JUnitResultArchiver.java index 86e86071a..882789e0a 100644 --- a/src/main/java/hudson/tasks/junit/JUnitResultArchiver.java +++ b/src/main/java/hudson/tasks/junit/JUnitResultArchiver.java @@ -43,19 +43,19 @@ import hudson.tasks.junit.TestResultAction.Data; import hudson.util.DescribableList; import hudson.util.FormValidation; +import jenkins.tasks.SimpleBuildStep; import org.apache.tools.ant.DirectoryScanner; import org.apache.tools.ant.types.FileSet; import org.jenkinsci.Symbol; import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.DataBoundSetter; import org.kohsuke.stapler.QueryParameter; +import javax.annotation.Nonnull; import java.io.IOException; import java.util.Collections; import java.util.List; -import javax.annotation.Nonnull; -import jenkins.tasks.SimpleBuildStep; -import org.kohsuke.stapler.DataBoundSetter; /** * Generates HTML report from JUnit test result XML files. @@ -75,6 +75,18 @@ public class JUnitResultArchiver extends Recorder implements SimpleBuildStep { */ private boolean keepLongStdio; + /** + * Size of stdout/stderr to keep for a succeeded test. + * @since 1.23 + */ + private int maxSucceededSize; + + /** + * Size of stdout/stderr to keep for a failed test. + * @since 1.23 + */ + private int maxFailedSize; + /** * {@link TestDataPublisher}s configured for this archiver, to process the recorded data. * For compatibility reasons, can be null. @@ -124,8 +136,11 @@ public JUnitResultArchiver( private TestResult parse(String expandedTestResults, Run run, @Nonnull FilePath workspace, Launcher launcher, TaskListener listener) throws IOException, InterruptedException { - return new JUnitParser(this.isKeepLongStdio(), - this.isAllowEmptyResults()).parseResult(expandedTestResults, run, workspace, launcher, listener); + return new JUnitParser(new KeepStdioConfig(this.isKeepLongStdio(), + this.getMaxSucceededSize(), + this.getMaxFailedSize()), + this.isAllowEmptyResults()) + .parseResult(expandedTestResults, run, workspace, launcher, listener); } @Deprecated @@ -262,6 +277,16 @@ public boolean isKeepLongStdio() { this.keepLongStdio = keepLongStdio; } + /** @since 1.23 */ + @DataBoundSetter public final void setMaxSucceededSize(int size) { + this.maxSucceededSize = size; + } + + /** @since 1.23 */ + @DataBoundSetter public final void setMaxFailedSize(int size) { + this.maxFailedSize = size; + } + /** * * @return the allowEmptyResults @@ -277,6 +302,14 @@ public boolean isAllowEmptyResults() { private static final long serialVersionUID = 1L; + public int getMaxSucceededSize() { + return maxSucceededSize; + } + + public int getMaxFailedSize() { + return maxFailedSize; + } + @Extension @Symbol("junit") public static class DescriptorImpl extends BuildStepDescriptor { public String getDisplayName() { diff --git a/src/main/java/hudson/tasks/junit/KeepStdioConfig.java b/src/main/java/hudson/tasks/junit/KeepStdioConfig.java new file mode 100644 index 000000000..d48c25582 --- /dev/null +++ b/src/main/java/hudson/tasks/junit/KeepStdioConfig.java @@ -0,0 +1,86 @@ +package hudson.tasks.junit; + +import java.io.Serializable; + +/** + * Object storing all stdio-keeping related configuration. + */ +public final class KeepStdioConfig implements Serializable { + + private final static int DEFAULT_MAX_SUCCEEDED_SIZE = 1000; + + private final static int DEFAULT_MAX_FAILED_SIZE = 100000; + + private final boolean keepLongStdio; + + private final int maxSucceededSize; + + private final int maxFailedSize; + + public KeepStdioConfig(boolean keepLongStdio, int maxSucceededSize, int maxFailedSize) { + this.keepLongStdio = keepLongStdio; + this.maxFailedSize = maxFailedSize; + this.maxSucceededSize = maxSucceededSize; + } + + /** + * If true, retain a suite's complete stdout/stderr even if this is huge and the + * suite passed. + * + * @return true if all stdio should be kept. + */ + public boolean isKeepLongStdio() { + return keepLongStdio; + } + + /** + * @return maximum number of bytes to keep for a succeeded test, or -1 if infinite. + */ + public int getMaxSucceededSize() { + return maxSucceededSize; + } + + /** + * @return maximum number of bytes to keep for a failed test, or -1 if infinite. + */ + public int getMaxFailedSize() { + return maxFailedSize; + } + + /** + * Get a new {@link KeepStdioConfig} initialized with defaults values. + */ + public static KeepStdioConfig defaults() { + return new KeepStdioConfig(false, DEFAULT_MAX_SUCCEEDED_SIZE, DEFAULT_MAX_FAILED_SIZE); + } + + /** + * Get a new {@link KeepStdioConfig} with given keepLongStdio, and + * defaults values for other configuration parameters. + */ + public static KeepStdioConfig defaults(boolean keepLongStdio) { + return new KeepStdioConfig(keepLongStdio, DEFAULT_MAX_SUCCEEDED_SIZE, DEFAULT_MAX_FAILED_SIZE); + } + + /** + * Get the maximum size of data to keep for a test, according to configuration. + * + * @param failed if test has failed. + * @return maximum number of bytes to keep. + */ + public int getMaxSize(boolean failed) { + if (keepLongStdio) { + return -1; + } + + if (failed) { + if (maxFailedSize < 0) + return -1; + return maxFailedSize; + } else { + if (maxSucceededSize < 0) + return -1; + return maxSucceededSize; + } + } +} diff --git a/src/main/java/hudson/tasks/junit/SuiteResult.java b/src/main/java/hudson/tasks/junit/SuiteResult.java index 3e60467f1..a888ccc06 100644 --- a/src/main/java/hudson/tasks/junit/SuiteResult.java +++ b/src/main/java/hudson/tasks/junit/SuiteResult.java @@ -120,8 +120,19 @@ public static class SuiteResultParserConfigurationContext { * Parses the JUnit XML file into {@link SuiteResult}s. * This method returns a collection, as a single XML may have multiple <testsuite> * elements wrapped into the top-level <testsuites>. + * @deprecated in favor of {@link #parse(File, KeepStdioConfig)} */ static List parse(File xmlReport, boolean keepLongStdio) throws DocumentException, IOException, InterruptedException { + return parse(xmlReport, KeepStdioConfig.defaults(keepLongStdio)); + } + + /** + * Parses the JUnit XML file into {@link SuiteResult}s. + * This method returns a collection, as a single XML may have multiple <testsuite> + * elements wrapped into the top-level <testsuites>. + * @since 1.23 + */ + static List parse(File xmlReport, KeepStdioConfig config) throws DocumentException, IOException, InterruptedException { List r = new ArrayList(); // parse into DOM @@ -134,7 +145,7 @@ static List parse(File xmlReport, boolean keepLongStdio) throws Doc Document result = saxReader.read(xmlReportStream); Element root = result.getRootElement(); - parseSuite(xmlReport,keepLongStdio,r,root); + parseSuite(xmlReport,config,r,root); } finally { xmlReportStream.close(); } @@ -142,17 +153,17 @@ static List parse(File xmlReport, boolean keepLongStdio) throws Doc return r; } - private static void parseSuite(File xmlReport, boolean keepLongStdio, List r, Element root) throws DocumentException, IOException { + private static void parseSuite(File xmlReport, KeepStdioConfig config, List r, Element root) throws DocumentException, IOException { // nested test suites @SuppressWarnings("unchecked") List testSuites = (List)root.elements("testsuite"); for (Element suite : testSuites) - parseSuite(xmlReport, keepLongStdio, r, suite); + parseSuite(xmlReport, config, r, suite); // child test cases // FIXME: do this also if no testcases! if (root.element("testcase")!=null || root.element("error")!=null) - r.add(new SuiteResult(xmlReport, root, keepLongStdio)); + r.add(new SuiteResult(xmlReport, root, config)); } /** @@ -161,7 +172,7 @@ private static void parseSuite(File xmlReport, boolean keepLongStdio, List", keepLongStdio)); + addCase(new CaseResult(this, suite, "", config)); } - + @SuppressWarnings("unchecked") List testCases = (List)suite.elements("testcase"); for (Element e : testCases) { @@ -208,11 +219,11 @@ private SuiteResult(File xmlReport, Element suite, boolean keepLongStdio) throws // one wants to use @name from , // the other wants to use @classname from . - addCase(new CaseResult(this, e, classname, keepLongStdio)); + addCase(new CaseResult(this, e, classname, config)); } - String stdout = CaseResult.possiblyTrimStdio(cases, keepLongStdio, suite.elementText("system-out")); - String stderr = CaseResult.possiblyTrimStdio(cases, keepLongStdio, suite.elementText("system-err")); + String stdout = CaseResult.possiblyTrimStdio(cases, config, suite.elementText("system-out")); + String stderr = CaseResult.possiblyTrimStdio(cases, config, suite.elementText("system-err")); if (stdout==null && stderr==null) { // Surefire never puts stdout/stderr in the XML. Instead, it goes to a separate file (when ${maven.test.redirectTestOutputToFile}). Matcher m = SUREFIRE_FILENAME.matcher(xmlReport.getName()); @@ -221,7 +232,7 @@ private SuiteResult(File xmlReport, Element suite, boolean keepLongStdio) throws File mavenOutputFile = new File(xmlReport.getParentFile(),m.group(1)+"-output.txt"); if (mavenOutputFile.exists()) { try { - stdout = CaseResult.possiblyTrimStdio(cases, keepLongStdio, mavenOutputFile); + stdout = CaseResult.possiblyTrimStdio(cases, config, mavenOutputFile); } catch (IOException e) { throw new IOException("Failed to read "+mavenOutputFile,e); } @@ -236,7 +247,7 @@ private SuiteResult(File xmlReport, Element suite, boolean keepLongStdio) throws /*package*/ void addCase(CaseResult cr) { cases.add(cr); casesByName().put(cr.getName(), cr); - + //if suite time was not specified use sum of the cases' times if( !hasTimeAttr() ){ duration += cr.getDuration(); @@ -369,7 +380,7 @@ void setParent(hudson.tasks.junit.TestResult parent) { /** * Merges another SuiteResult into this one. - * + * * @param sr the SuiteResult to merge into this one */ public void merge(SuiteResult sr) { diff --git a/src/main/java/hudson/tasks/junit/TestResult.java b/src/main/java/hudson/tasks/junit/TestResult.java index f531d6464..b525a07ff 100644 --- a/src/main/java/hudson/tasks/junit/TestResult.java +++ b/src/main/java/hudson/tasks/junit/TestResult.java @@ -29,6 +29,11 @@ import hudson.tasks.test.AbstractTestResultAction; import hudson.tasks.test.MetaTabulatedResult; import hudson.tasks.test.TestObject; +import org.apache.tools.ant.DirectoryScanner; +import org.dom4j.DocumentException; +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerResponse; +import org.kohsuke.stapler.export.Exported; import java.io.File; import java.io.IOException; @@ -42,12 +47,6 @@ import java.util.Map; import java.util.TreeMap; -import org.apache.tools.ant.DirectoryScanner; -import org.dom4j.DocumentException; -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; -import org.kohsuke.stapler.export.Exported; - /** * Root of all the test results for one build. * @@ -94,20 +93,28 @@ public final class TestResult extends MetaTabulatedResult { */ private transient List failedTests; - private final boolean keepLongStdio; + private final KeepStdioConfig config; /** * Creates an empty result. */ public TestResult() { - this(false); + this(KeepStdioConfig.defaults()); } /** * @since 1.522 */ + @Deprecated public TestResult(boolean keepLongStdio) { - this.keepLongStdio = keepLongStdio; + this(KeepStdioConfig.defaults(keepLongStdio)); + } + + /** + * @since 1.23 + */ + public TestResult(KeepStdioConfig config) { + this.config = config; } @Deprecated @@ -120,9 +127,21 @@ public TestResult(long buildTime, DirectoryScanner results) throws IOException { * filtering out all files that were created before the given time. * @param keepLongStdio if true, retain a suite's complete stdout/stderr even if this is huge and the suite passed * @since 1.358 + * @deprecated in favor of {@link #TestResult(long, DirectoryScanner, KeepStdioConfig)}. */ + @Deprecated public TestResult(long buildTime, DirectoryScanner results, boolean keepLongStdio) throws IOException { - this.keepLongStdio = keepLongStdio; + this(buildTime, results, KeepStdioConfig.defaults(keepLongStdio)); + } + + /** + * Collect reports from the given {@link DirectoryScanner}, while + * filtering out all files that were created before the given time. + * @param config stdio configuration. + * @since 1.23 + */ + public TestResult(long buildTime, DirectoryScanner results, KeepStdioConfig config) throws IOException { + this.config = config; parse(buildTime, results); } @@ -296,7 +315,7 @@ private boolean nullSafeEq(Object lhs, Object rhs) { */ public void parse(File reportFile) throws IOException { try { - for (SuiteResult suiteResult : SuiteResult.parse(reportFile, keepLongStdio)) + for (SuiteResult suiteResult : SuiteResult.parse(reportFile, config)) add(suiteResult); } catch (InterruptedException e) { throw new IOException("Failed to read "+reportFile,e); diff --git a/src/main/resources/hudson/tasks/junit/JUnitResultArchiver/config.jelly b/src/main/resources/hudson/tasks/junit/JUnitResultArchiver/config.jelly index b7cc707f9..b2f786ccc 100644 --- a/src/main/resources/hudson/tasks/junit/JUnitResultArchiver/config.jelly +++ b/src/main/resources/hudson/tasks/junit/JUnitResultArchiver/config.jelly @@ -33,6 +33,14 @@ THE SOFTWARE. + + + + + + + + diff --git a/src/main/resources/hudson/tasks/junit/JUnitResultArchiver/help-maxFailedSize.html b/src/main/resources/hudson/tasks/junit/JUnitResultArchiver/help-maxFailedSize.html new file mode 100644 index 000000000..aea01cc2f --- /dev/null +++ b/src/main/resources/hudson/tasks/junit/JUnitResultArchiver/help-maxFailedSize.html @@ -0,0 +1,4 @@ +
+ Maximum size of standard output or error to keep in the case the test is failing, + if keepLongStdio is unchecked. A negative value will force all output to be kept. +
diff --git a/src/main/resources/hudson/tasks/junit/JUnitResultArchiver/help-maxSucceededSize.html b/src/main/resources/hudson/tasks/junit/JUnitResultArchiver/help-maxSucceededSize.html new file mode 100644 index 000000000..0ed73d75d --- /dev/null +++ b/src/main/resources/hudson/tasks/junit/JUnitResultArchiver/help-maxSucceededSize.html @@ -0,0 +1,4 @@ +
+ Maximum size of standard output or error to keep in the case the test is succeeding, + if keepLongStdio is unchecked. A negative value will force all output to be kept. +
diff --git a/src/test/java/hudson/tasks/junit/TrimStdioTest.java b/src/test/java/hudson/tasks/junit/TrimStdioTest.java new file mode 100644 index 000000000..5757aeb7c --- /dev/null +++ b/src/test/java/hudson/tasks/junit/TrimStdioTest.java @@ -0,0 +1,198 @@ +package hudson.tasks.junit; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.jvnet.hudson.test.JenkinsRule; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.*; + +/** + * Tests various configuration of junit plugin wrt. size of data to keep. + */ +@RunWith(Parameterized.class) +public class TrimStdioTest { + + private final static String STDIO = stdio(); + + private final static int STDIO_LEN = STDIO.length(); + + @Rule + public TemporaryFolder root = new TemporaryFolder(); + + @Rule + public JenkinsRule j = new JenkinsRule(); + + @Parameterized.Parameter(value = 0) + public String name; + + @Parameterized.Parameter(value = 1) + public KeepStdioConfig config; + + @Parameterized.Parameters(name = "{0}") + public static List parameters() { + ArrayList list = new ArrayList(5); + list.add(new Object[] { "defaults", KeepStdioConfig.defaults() }); + list.add(new Object[] { "keep all", KeepStdioConfig.defaults(true) }); + list.add(new Object[] { "maxPassed=10, maxFailed=100", new KeepStdioConfig(false, 10, 100)}); + list.add(new Object[] { "maxPassed=0, maxFailed=all", new KeepStdioConfig(false, 0, -1)}); + return list; + } + + private static String stdio() { + String nl = String.format("%n"); + StringBuilder builder = new StringBuilder(); + builder.append("First line is intact.").append(nl); + for (int i = 0; i < 10; i++) { + builder.append("Line #").append(i).append(" might be elided because we reach the limit.").append(nl); + } + builder.append("Last line is intact.").append(nl); + return builder.toString(); + } + + private File createResults() throws IOException { + File f = root.newFile("test.xml"); + PrintWriter pw = new PrintWriter(f, "UTF-8"); + try { + pw.println(""); + pw.println(""); + pw.println("oops"); + pw.print(""); + pw.print(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.print(""); + pw.print(""); + pw.println(""); + pw.println(""); + pw.flush(); + } finally { + pw.close(); + } + return f; + } + + private void checkStdout(String stdout, int maxSize) { + if (!config.isKeepLongStdio() && maxSize == 0) { + assertNull(stdout); + return; + } + + assertNotNull(stdout); + + int outl = stdout.length(); + + if (config.isKeepLongStdio() || maxSize < 0 || maxSize > STDIO_LEN) { + assertEquals(stdout, STDIO_LEN, outl); + return; + } + + assertEquals(stdout, maxSize + 29, outl); + } + + /** + * Check size of both stdout and stderr wrt. maxsize. + */ + private void checkBoth(String stdout, String stderr, int maxSize) { + if (!config.isKeepLongStdio() && maxSize == 0) { + assertNull(stderr); + assertNull(stdout); + return; + } + + assertNotNull(stderr); + assertNotNull(stdout); + + int outl = stdout.length(); + int errl = stderr.length(); + + if (config.isKeepLongStdio() || maxSize < 0 || maxSize > STDIO_LEN) { + assertEquals(stdout, STDIO_LEN, outl); + assertEquals(stderr, STDIO_LEN, errl); + return; + } + + assertEquals(stdout, maxSize + 29, outl); + assertEquals(stderr, maxSize + 29, errl); + } + + @Test + public void trimStdio() throws Exception { + List results = SuiteResult.parse(createResults(), config); + assertEquals(2, results.size()); + + // failure + SuiteResult failure = results.get(0); + assertNotNull(failure); + assertTrue(failure.getCase("x").isFailed()); + checkBoth(failure.getStdout(), failure.getStderr(), config.getMaxFailedSize()); + + // success + SuiteResult success = results.get(1); + assertNotNull(success); + assertTrue(success.getCase("s").isPassed()); + checkBoth(success.getStdout(), success.getStderr(), config.getMaxSucceededSize()); + } + + @Test + public void trimStdioSurefire() throws Exception { + File f = root.newFile("TEST-abcd.xml"); + PrintWriter pw = new PrintWriter(f, "UTF-8"); + try { + pw.println(""); + pw.println(""); + pw.println("oops"); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + pw.flush(); + } finally { + pw.close(); + } + + File data = new File(f.getParentFile(), "abcd-output.txt"); + pw = new PrintWriter(data, "UTF-8"); + try { + pw.print(STDIO); + pw.flush(); + } finally { + pw.close(); + } + + List results = SuiteResult.parse(f, config); + assertEquals(2, results.size()); + + // failure + SuiteResult failure = results.get(0); + assertNotNull(failure); + assertTrue(failure.getCase("x").isFailed()); + assertNull(failure.getStderr()); + checkStdout(failure.getStdout(), config.getMaxFailedSize()); + + // success + SuiteResult success = results.get(1); + assertNotNull(success); + assertTrue(success.getCase("s").isPassed()); + assertNull(success.getStderr()); + checkStdout(success.getStdout(), config.getMaxSucceededSize()); + } +}