Skip to content

Commit

Permalink
Add a "keepProperties" option (#546)
Browse files Browse the repository at this point in the history
  • Loading branch information
jjhiblot authored Jul 4, 2023
1 parent 4340188 commit 4297208
Show file tree
Hide file tree
Showing 11 changed files with 153 additions and 54 deletions.
22 changes: 12 additions & 10 deletions src/main/java/hudson/tasks/junit/CaseResult.java
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ public CaseResult(
this.properties = Collections.emptyMap();
}

CaseResult(SuiteResult parent, Element testCase, String testClassName, boolean keepLongStdio) {
CaseResult(SuiteResult parent, Element testCase, String testClassName, boolean keepLongStdio, boolean keepProperties) {
// 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 @@ -199,15 +199,17 @@ public CaseResult(

// parse properties
Map<String, String> properties = new HashMap<String, String>();
Element properties_element = testCase.element("properties");
if (properties_element != null) {
List<Element> property_elements = properties_element.elements("property");
for (Element prop : property_elements){
if (prop.attributeValue("name") != null) {
if (prop.attributeValue("value") != null)
properties.put(prop.attributeValue("name"), prop.attributeValue("value"));
else
properties.put(prop.attributeValue("name"), prop.getText());
if (keepProperties) {
Element properties_element = testCase.element("properties");
if (properties_element != null) {
List<Element> property_elements = properties_element.elements("property");
for (Element prop : property_elements){
if (prop.attributeValue("name") != null) {
if (prop.attributeValue("value") != null)
properties.put(prop.attributeValue("name"), prop.attributeValue("value"));
else
properties.put(prop.attributeValue("name"), prop.getText());
}
}
}
}
Expand Down
26 changes: 15 additions & 11 deletions src/main/java/hudson/tasks/junit/JUnitParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public class JUnitParser extends TestResultParser {
private static final Logger LOGGER = Logger.getLogger(JUnitParser.class.getName());

private final boolean keepLongStdio;
private final boolean keepProperties;
private final boolean allowEmptyResults;

private final boolean skipOldReports;
Expand All @@ -69,7 +70,7 @@ public JUnitParser() {
*/
@Deprecated
public JUnitParser(boolean keepLongStdio) {
this(keepLongStdio , false, false);
this(keepLongStdio , false, false, false);
}

/**
Expand All @@ -79,11 +80,12 @@ public JUnitParser(boolean keepLongStdio) {
*/
@Deprecated
public JUnitParser(boolean keepLongStdio, boolean allowEmptyResults) {
this(keepLongStdio , allowEmptyResults, false);
this(keepLongStdio, false, allowEmptyResults, false);
}

public JUnitParser(boolean keepLongStdio, boolean allowEmptyResults, boolean skipOldReports) {
public JUnitParser(boolean keepLongStdio, boolean keepProperties, boolean allowEmptyResults, boolean skipOldReports) {
this.keepLongStdio = keepLongStdio;
this.keepProperties = keepProperties;
this.allowEmptyResults = allowEmptyResults;
this.skipOldReports = skipOldReports;
}
Expand Down Expand Up @@ -115,14 +117,14 @@ public TestResult parseResult(String testResultLocations, Run<?,?> build, FilePa
public TestResult parseResult(String testResultLocations, Run<?,?> build, PipelineTestDetails pipelineTestDetails,
FilePath workspace, Launcher launcher, TaskListener listener)
throws InterruptedException, IOException {
return workspace.act(new DirectParseResultCallable(testResultLocations, build, keepLongStdio, allowEmptyResults,
return workspace.act(new DirectParseResultCallable(testResultLocations, build, keepLongStdio, keepProperties, allowEmptyResults,
pipelineTestDetails, listener, skipOldReports));
}

public TestResultSummary summarizeResult(String testResultLocations, Run<?,?> build, PipelineTestDetails pipelineTestDetails,
FilePath workspace, Launcher launcher, TaskListener listener, JunitTestResultStorage storage)
throws InterruptedException, IOException {
return workspace.act(new StorageParseResultCallable(testResultLocations, build, keepLongStdio, allowEmptyResults,
return workspace.act(new StorageParseResultCallable(testResultLocations, build, keepLongStdio, keepProperties, allowEmptyResults,
pipelineTestDetails, listener, storage.createRemotePublisher(build), skipOldReports));
}

Expand All @@ -136,21 +138,23 @@ private static abstract class ParseResultCallable<T> extends MasterToSlaveFileCa
private final String testResults;
private final long nowMaster;
private final boolean keepLongStdio;
private final boolean keepProperties;
private final boolean allowEmptyResults;
private final PipelineTestDetails pipelineTestDetails;
private final TaskListener listener;

private boolean skipOldReports;

private ParseResultCallable(String testResults, Run<?,?> build,
boolean keepLongStdio, boolean allowEmptyResults,
boolean keepLongStdio, boolean keepProperties, boolean allowEmptyResults,
PipelineTestDetails pipelineTestDetails, TaskListener listener,
boolean skipOldReports) {
this.buildStartTimeInMillis = build.getStartTimeInMillis();
this.buildTimeInMillis = build.getTimeInMillis();
this.testResults = testResults;
this.nowMaster = System.currentTimeMillis();
this.keepLongStdio = keepLongStdio;
this.keepProperties = keepProperties;
this.allowEmptyResults = allowEmptyResults;
this.pipelineTestDetails = pipelineTestDetails;
this.listener = listener;
Expand All @@ -173,7 +177,7 @@ public T invoke(File ws, VirtualChannel channel) throws IOException {
+ ",buildTimeInMillis:" + buildTimeInMillis + ",filesTimestamp:" + filesTimestamp + ",nowSlave:"
+ nowSlave + ",nowMaster:" + nowMaster);
}
result = new TestResult(filesTimestamp, ds, keepLongStdio, pipelineTestDetails, skipOldReports);
result = new TestResult(filesTimestamp, ds, keepLongStdio, keepProperties, pipelineTestDetails, skipOldReports);
result.tally();
} else {
if (this.allowEmptyResults) {
Expand All @@ -193,9 +197,9 @@ public T invoke(File ws, VirtualChannel channel) throws IOException {

private static final class DirectParseResultCallable extends ParseResultCallable<TestResult> {

DirectParseResultCallable(String testResults, Run<?,?> build, boolean keepLongStdio, boolean allowEmptyResults,
DirectParseResultCallable(String testResults, Run<?,?> build, boolean keepLongStdio, boolean keepProperties, boolean allowEmptyResults,
PipelineTestDetails pipelineTestDetails, TaskListener listener, boolean skipOldReports) {
super(testResults, build, keepLongStdio, allowEmptyResults, pipelineTestDetails, listener, skipOldReports);
super(testResults, build, keepLongStdio, keepProperties, allowEmptyResults, pipelineTestDetails, listener, skipOldReports);
}

@Override
Expand All @@ -209,9 +213,9 @@ private static final class StorageParseResultCallable extends ParseResultCallabl

private final RemotePublisher publisher;

StorageParseResultCallable(String testResults, Run<?,?> build, boolean keepLongStdio, boolean allowEmptyResults,
StorageParseResultCallable(String testResults, Run<?,?> build, boolean keepLongStdio, boolean keepProperties, boolean allowEmptyResults,
PipelineTestDetails pipelineTestDetails, TaskListener listener, RemotePublisher publisher, boolean skipOldReports) {
super(testResults, build, keepLongStdio, allowEmptyResults, pipelineTestDetails, listener, skipOldReports);
super(testResults, build, keepLongStdio, keepProperties, allowEmptyResults, pipelineTestDetails, listener, skipOldReports);
this.publisher = publisher;
}

Expand Down
25 changes: 20 additions & 5 deletions src/main/java/hudson/tasks/junit/JUnitResultArchiver.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ public class JUnitResultArchiver extends Recorder implements SimpleBuildStep, JU
*/
private boolean keepLongStdio;

private boolean keepProperties;
/**
* {@link TestDataPublisher}s configured for this archiver, to process the recorded data.
* For compatibility reasons, can be null.
Expand Down Expand Up @@ -125,17 +126,19 @@ public JUnitResultArchiver(
String testResults,
boolean keepLongStdio,
DescribableList<TestDataPublisher, Descriptor<TestDataPublisher>> testDataPublishers) {
this(testResults, keepLongStdio, testDataPublishers, 1.0);
this(testResults, keepLongStdio, false, testDataPublishers, 1.0);
}

@Deprecated
public JUnitResultArchiver(
String testResults,
boolean keepLongStdio,
boolean keepProperties,
DescribableList<TestDataPublisher, Descriptor<TestDataPublisher>> testDataPublishers,
double healthScaleFactor) {
this.testResults = testResults;
setKeepLongStdio(keepLongStdio);
setKeepProperties(keepProperties);
setTestDataPublishers(testDataPublishers == null ? Collections.emptyList() : testDataPublishers);
setHealthScaleFactor(healthScaleFactor);
setAllowEmptyResults(false);
Expand All @@ -153,7 +156,7 @@ private static TestResult parse(@NonNull JUnitTask task, PipelineTestDetails pip
String expandedTestResults, Run<?,?> run, @NonNull FilePath workspace,
Launcher launcher, TaskListener listener)
throws IOException, InterruptedException {
return new JUnitParser(task.isKeepLongStdio(), task.isAllowEmptyResults(), task.isSkipOldReports())
return new JUnitParser(task.isKeepLongStdio(), task.isKeepProperties(), task.isAllowEmptyResults(), task.isSkipOldReports())
.parseResult(expandedTestResults, run, pipelineTestDetails, workspace, launcher, listener);
}

Expand Down Expand Up @@ -252,7 +255,7 @@ public static TestResultSummary parseAndSummarize(@NonNull JUnitTask task, Pipel
summary = null; // see below
} else {
result = new TestResult(storage.load(build.getParent().getFullName(), build.getNumber())); // irrelevant
summary = new JUnitParser(task.isKeepLongStdio(), task.isAllowEmptyResults(), task.isSkipOldReports())
summary = new JUnitParser(task.isKeepLongStdio(), task.isKeepProperties(), task.isAllowEmptyResults(), task.isSkipOldReports())
.summarizeResult(testResults, build, pipelineTestDetails, workspace, launcher, listener, storage);
}

Expand Down Expand Up @@ -395,6 +398,18 @@ public boolean isKeepLongStdio() {
this.keepLongStdio = keepLongStdio;
}

/**
* @return the keepProperties.
*/
@Override
public boolean isKeepProperties() {
return keepProperties;
}

@DataBoundSetter public final void setKeepProperties(boolean keepProperties) {
this.keepProperties = keepProperties;
}

/**
*
* @return the allowEmptyResults
Expand All @@ -406,8 +421,8 @@ public boolean isAllowEmptyResults() {

/**
* Should we skip publishing checks to the checks API plugin.
*
* @return if publishing checks should be skipped, {@code false} otherwise
*
* @return if publishing checks should be skipped, {@code false} otherwise
*/
@Override
public boolean isSkipPublishingChecks() {
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/hudson/tasks/junit/JUnitTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public interface JUnitTask {

boolean isKeepLongStdio();

boolean isKeepProperties();

boolean isAllowEmptyResults();

boolean isSkipPublishingChecks();
Expand Down
49 changes: 30 additions & 19 deletions src/main/java/hudson/tasks/junit/SuiteResult.java
Original file line number Diff line number Diff line change
Expand Up @@ -155,18 +155,24 @@ public static class SuiteResultParserConfigurationContext {
}
}

@Deprecated
static List<SuiteResult> parse(File xmlReport, boolean keepLongStdio, PipelineTestDetails pipelineTestDetails)
throws DocumentException, IOException, InterruptedException {
return parse(xmlReport, keepLongStdio, false, pipelineTestDetails);
}

/**
* Parses the JUnit XML file into {@link SuiteResult}s.
* This method returns a collection, as a single XML may have multiple &lt;testsuite>
* elements wrapped into the top-level &lt;testsuites>.
*/
static List<SuiteResult> parse(File xmlReport, boolean keepLongStdio, PipelineTestDetails pipelineTestDetails)
static List<SuiteResult> parse(File xmlReport, boolean keepLongStdio, boolean keepProperties, PipelineTestDetails pipelineTestDetails)
throws DocumentException, IOException, InterruptedException {
List<SuiteResult> r = new ArrayList<>();

// parse into DOM
SAXReader saxReader = new SAXReader();

//source: https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet => SAXReader
// setFeatureQuietly(saxReader, "http://apache.org/xml/features/disallow-doctype-decl", true);
// setFeatureQuietly(saxReader, "http://xml.org/sax/features/external-parameter-entities", false);
Expand All @@ -180,7 +186,7 @@ static List<SuiteResult> parse(File xmlReport, boolean keepLongStdio, PipelineTe
Document result = saxReader.read(xmlReportStream);
Element root = result.getRootElement();

parseSuite(xmlReport, keepLongStdio, r, root, pipelineTestDetails);
parseSuite(xmlReport, keepLongStdio, keepProperties, r, root, pipelineTestDetails);
}

return r;
Expand All @@ -195,24 +201,28 @@ private static void setFeatureQuietly(SAXReader reader, String feature, boolean
}
}

private static void parseSuite(File xmlReport, boolean keepLongStdio, List<SuiteResult> r, Element root,
private static void parseSuite(File xmlReport, boolean keepLongStdio, boolean keepProperties, List<SuiteResult> r, Element root,
PipelineTestDetails pipelineTestDetails) throws DocumentException, IOException {
// nested test suites
List<Element> testSuites = root.elements("testsuite");
for (Element suite : testSuites)
parseSuite(xmlReport, keepLongStdio, r, suite, pipelineTestDetails);
parseSuite(xmlReport, keepLongStdio, keepProperties, r, suite, pipelineTestDetails);

// 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, pipelineTestDetails));
r.add(new SuiteResult(xmlReport, root, keepLongStdio, keepProperties, pipelineTestDetails));
}

private SuiteResult(File xmlReport, Element suite, boolean keepLongStdio, @CheckForNull PipelineTestDetails pipelineTestDetails)
throws DocumentException, IOException {
this(xmlReport, suite, keepLongStdio, false, pipelineTestDetails);
}
/**
* @param xmlReport A JUnit XML report file whose top level element is 'testsuite'.
* @param suite The parsed result of {@code xmlReport}
*/
private SuiteResult(File xmlReport, Element suite, boolean keepLongStdio, @CheckForNull PipelineTestDetails pipelineTestDetails)
private SuiteResult(File xmlReport, Element suite, boolean keepLongStdio, boolean keepProperties, @CheckForNull PipelineTestDetails pipelineTestDetails)
throws DocumentException, IOException {
this.file = xmlReport.getAbsolutePath();
String name = suite.attributeValue("name");
Expand Down Expand Up @@ -241,7 +251,7 @@ private SuiteResult(File xmlReport, Element suite, boolean keepLongStdio, @Check
Element ex = suite.element("error");
if (ex != null) {
// according to junit-noframes.xsl l.229, this happens when the test class failed to load
addCase(new CaseResult(this, suite, "<init>", keepLongStdio));
addCase(new CaseResult(this, suite, "<init>", keepLongStdio, keepProperties));
}

List<Element> testCases = suite.elements("testcase");
Expand All @@ -265,7 +275,7 @@ private SuiteResult(File xmlReport, Element suite, boolean keepLongStdio, @Check
// one wants to use @name from <testsuite>,
// the other wants to use @classname from <testcase>.

addCase(new CaseResult(this, e, classname, keepLongStdio));
addCase(new CaseResult(this, e, classname, keepLongStdio, keepProperties));
}

String stdout = CaseResult.possiblyTrimStdio(cases, keepLongStdio, suite.elementText("system-out"));
Expand All @@ -291,19 +301,20 @@ private SuiteResult(File xmlReport, Element suite, boolean keepLongStdio, @Check

// parse properties
Map<String, String> properties = new HashMap<String, String>();
Element properties_element = suite.element("properties");
if (properties_element != null) {
List<Element> property_elements = properties_element.elements("property");
for (Element prop : property_elements){
if (prop.attributeValue("name") != null) {
if (prop.attributeValue("value") != null)
properties.put(prop.attributeValue("name"), prop.attributeValue("value"));
else
properties.put(prop.attributeValue("name"), prop.getText());
if (keepProperties) {
Element properties_element = suite.element("properties");
if (properties_element != null) {
List<Element> property_elements = properties_element.elements("property");
for (Element prop : property_elements){
if (prop.attributeValue("name") != null) {
if (prop.attributeValue("value") != null)
properties.put(prop.attributeValue("name"), prop.attributeValue("value"));
else
properties.put(prop.attributeValue("name"), prop.getText());
}
}
}
}

this.properties = properties;
}

Expand Down
Loading

0 comments on commit 4297208

Please sign in to comment.