Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 45 additions & 21 deletions src/main/java/hudson/tasks/junit/CaseResult.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -128,42 +137,59 @@ private static float parseTime(Element testCase) {
skippedMessage = getSkippedMessage(testCase);
@SuppressWarnings("LeakingThisInConstructor")
Collection<CaseResult> _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<CaseResult> results, boolean keepLongStdio, String stdio) { // HUDSON-6516
static String possiblyTrimStdio(Collection<CaseResult> 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<CaseResult> results, boolean keepLongStdio, File stdio) throws IOException {
static String possiblyTrimStdio(Collection<CaseResult> 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);
Expand All @@ -180,15 +206,13 @@ static String possiblyTrimStdio(Collection<CaseResult> 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<CaseResult> results) {
private static boolean hasFailed(Collection<CaseResult> results) {
for (CaseResult result : results) {
if (result.errorStackTrace != null) {
return HALF_MAX_FAILING_SIZE;
return true;
}
}
return HALF_MAX_SIZE;
return false;
}


Expand Down
47 changes: 30 additions & 17 deletions src/main/java/hudson/tasks/junit/JUnitParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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? */
Expand All @@ -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;
}

Expand Down Expand Up @@ -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<TestResult> {
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;
}

Expand All @@ -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) {
Expand Down
43 changes: 38 additions & 5 deletions src/main/java/hudson/tasks/junit/JUnitResultArchiver.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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<Publisher> {
public String getDisplayName() {
Expand Down
Loading