Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 3bf6ad6

Browse files
committedNov 15, 2024··
Add timestamp attribute
While the timestamp attribute is not part of the JUnit or Surefire XSD in practice it seems to be a common enough property[1] that we can add it without expecting any of the popular tools to break. Closes: #44 1. https://github.com/testmoapp/junitxml
1 parent 4b5ddd5 commit 3bf6ad6

20 files changed

+51
-28
lines changed
 

‎CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111
- Update dependency io.cucumber:messages up to v26 ((#38)[https://github.com/cucumber/query/pull/38])
12+
- Add `timestamp` attribute ((#45)[https://github.com/cucumber/junit-xml-formatter/pull/45])
1213

1314
## [0.5.0] - 2024-06-22
1415
### Added

‎java/src/main/java/io/cucumber/junitxmlformatter/XmlReportData.java

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package io.cucumber.junitxmlformatter;
22

3+
import io.cucumber.messages.Convertor;
34
import io.cucumber.messages.types.Envelope;
45
import io.cucumber.messages.types.Feature;
56
import io.cucumber.messages.types.Pickle;
67
import io.cucumber.messages.types.PickleStep;
78
import io.cucumber.messages.types.Step;
89
import io.cucumber.messages.types.TestCaseStarted;
10+
import io.cucumber.messages.types.TestRunStarted;
911
import io.cucumber.messages.types.TestStep;
1012
import io.cucumber.messages.types.TestStepFinished;
1113
import io.cucumber.messages.types.TestStepResult;
@@ -22,6 +24,7 @@
2224
import java.util.Optional;
2325

2426
import static io.cucumber.messages.types.TestStepResultStatus.PASSED;
27+
import static java.time.format.DateTimeFormatter.ISO_INSTANT;
2528
import static java.util.concurrent.TimeUnit.SECONDS;
2629
import static java.util.stream.Collectors.toList;
2730

@@ -123,4 +126,11 @@ TestStepResult getTestCaseStatus(TestCaseStarted testCaseStarted) {
123126
.orElse(SCENARIO_WITH_NO_STEPS);
124127
}
125128

126-
}
129+
public String getTestRunStartedTimeStamp() {
130+
return query.findTestRunStarted()
131+
.map(TestRunStarted::getTimestamp)
132+
.map(Convertor::toInstant)
133+
.map(ISO_INSTANT::format)
134+
.orElse(null);
135+
}
136+
}

‎java/src/main/java/io/cucumber/junitxmlformatter/XmlReportWriter.java

+5
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ private void writeSuiteAttributes(EscapingXmlStreamWriter writer) throws XMLStre
5959
writer.writeAttribute("skipped", counts.getOrDefault(SKIPPED, 0L).toString());
6060
writer.writeAttribute("failures", String.valueOf(countFailures(counts)));
6161
writer.writeAttribute("errors", "0");
62+
63+
String testRunStartedTimeStamp = data.getTestRunStartedTimeStamp();
64+
if (testRunStartedTimeStamp != null) {
65+
writer.writeAttribute("timestamp", testRunStartedTimeStamp);
66+
}
6267
}
6368

6469
private static long countFailures(Map<TestStepResultStatus, Long> counts) {

‎java/src/test/java/io/cucumber/junitxmlformatter/MessagesToJunitXmlWriterAcceptanceTest.java

+18-11
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import org.junit.jupiter.api.Disabled;
77
import org.junit.jupiter.params.ParameterizedTest;
88
import org.junit.jupiter.params.provider.MethodSource;
9+
import org.xmlunit.assertj.XmlAssert;
910
import org.xmlunit.builder.Input;
1011
import org.xmlunit.validation.JAXPValidator;
1112
import org.xmlunit.validation.Languages;
@@ -20,6 +21,7 @@
2021
import java.nio.file.Files;
2122
import java.nio.file.Path;
2223
import java.nio.file.Paths;
24+
import java.util.ArrayList;
2325
import java.util.Arrays;
2426
import java.util.Comparator;
2527
import java.util.List;
@@ -28,6 +30,7 @@
2830
import java.util.stream.Stream;
2931

3032
import static io.cucumber.junitxmlformatter.Jackson.OBJECT_MAPPER;
33+
import static org.assertj.core.api.Assertions.assertThat;
3134
import static org.xmlunit.assertj.XmlAssert.assertThat;
3235

3336
class MessagesToJunitXmlWriterAcceptanceTest {
@@ -49,7 +52,7 @@ void test(TestCase testCase) throws IOException {
4952
ByteArrayOutputStream bytes = writeJunitXmlReport(testCase, new ByteArrayOutputStream());
5053
Source expected = Input.fromPath(testCase.expected).build();
5154
Source actual = Input.fromByteArray(bytes.toByteArray()).build();
52-
assertThat(actual).and(expected).ignoreWhitespace().areIdentical();
55+
XmlAssert.assertThat(actual).and(expected).ignoreWhitespace().areIdentical();
5356
}
5457

5558
@ParameterizedTest
@@ -58,7 +61,7 @@ void validateAgainstJenkins(TestCase testCase) throws IOException {
5861
ByteArrayOutputStream bytes = writeJunitXmlReport(testCase, new ByteArrayOutputStream());
5962
Source actual = Input.fromByteArray(bytes.toByteArray()).build();
6063
Source jenkinsSchema = Input.fromPath(Paths.get("../jenkins-junit.xsd")).build();
61-
assertThat(actual).isValidAgainst(jenkinsSchema);
64+
XmlAssert.assertThat(actual).isValidAgainst(jenkinsSchema);
6265
}
6366

6467
static final List<String> testCasesWithMissingException = Arrays.asList(
@@ -76,11 +79,16 @@ void validateAgainstSurefire(TestCase testCase) throws IOException {
7679
ByteArrayOutputStream bytes = writeJunitXmlReport(testCase, new ByteArrayOutputStream());
7780
Source actual = Input.fromByteArray(bytes.toByteArray()).build();
7881
Source surefireSchema = Input.fromPath(Paths.get("../surefire-test-report-3.0.xsd")).build();
79-
if (!testCasesWithMissingException.contains(testCase.name)) {
80-
assertThat(actual).isValidAgainst(surefireSchema);
81-
return;
82-
}
8382

83+
JAXPValidator validator = new JAXPValidator(Languages.W3C_XML_SCHEMA_NS_URI);
84+
validator.setSchemaSource(surefireSchema);
85+
ValidationResult validationResult = validator.validateInstance(actual);
86+
87+
List<String> expectedProblems = new ArrayList<>();
88+
/*
89+
* We add the timestamp attribute to all reports.
90+
*/
91+
expectedProblems.add("cvc-complex-type.3.2.2: Attribute 'timestamp' is not allowed to appear in element 'testsuite'.");
8492
/*
8593
This report tries to be compatible with the Jenkins XSD. The Surefire
8694
XSD is a bit stricter and generally assumes tests fail with an
@@ -94,12 +102,11 @@ void validateAgainstSurefire(TestCase testCase) throws IOException {
94102
Since the Surefire XSD is also relatively popular we do check it and
95103
exclude the cases that don't pass selectively.
96104
*/
97-
JAXPValidator validator = new JAXPValidator(Languages.W3C_XML_SCHEMA_NS_URI);
98-
validator.setSchemaSource(surefireSchema);
99-
ValidationResult validationResult = validator.validateInstance(actual);
105+
if (testCasesWithMissingException.contains(testCase.name)) {
106+
expectedProblems.add("cvc-complex-type.4: Attribute 'type' must appear on element 'failure'.");
107+
}
100108
Iterable<ValidationProblem> problems = validationResult.getProblems();
101-
Assertions.assertThat(problems).extracting(ValidationProblem::getMessage)
102-
.containsOnly("cvc-complex-type.4: Attribute 'type' must appear on element 'failure'.");
109+
assertThat(problems).extracting(ValidationProblem::getMessage).containsAll(expectedProblems);
103110
}
104111

105112
@ParameterizedTest

‎java/src/test/java/io/cucumber/junitxmlformatter/MessagesToJunitXmlWriterTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ void it_writes_two_messages_to_xml() throws IOException {
2828

2929
assertThat(html).isEqualTo("" +
3030
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
31-
"<testsuite name=\"Cucumber\" time=\"20\" tests=\"0\" skipped=\"0\" failures=\"0\" errors=\"0\">\n" +
31+
"<testsuite name=\"Cucumber\" time=\"20\" tests=\"0\" skipped=\"0\" failures=\"0\" errors=\"0\" timestamp=\"1970-01-01T00:00:10Z\">\n" +
3232
"</testsuite>\n"
3333
);
3434
}

‎testdata/attachments.feature.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<testsuite name="Cucumber" time="0.045" tests="11" skipped="0" failures="0" errors="0">
2+
<testsuite name="Cucumber" time="0.045" tests="11" skipped="0" failures="0" errors="0" timestamp="1970-01-01T00:00:00Z">
33
<testcase classname="Attachments" name="Strings can be attached with a media type" time="0.003">
44
<system-out><![CDATA[
55
When the string "hello" is attached as "application/octet-stream"...........passed

‎testdata/cdata.feature.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<testsuite name="Cucumber" time="0.005" tests="1" skipped="0" failures="0" errors="0">
2+
<testsuite name="Cucumber" time="0.005" tests="1" skipped="0" failures="0" errors="0" timestamp="1970-01-01T00:00:00Z">
33
<testcase classname="cdata" name="cdata" time="0.003">
44
<system-out><![CDATA[
55
Given I have 42 <![CDATA[cukes]]]]><![CDATA[> in my belly...............................passed

‎testdata/data-tables.feature.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<testsuite name="Cucumber" time="0.007" tests="1" skipped="0" failures="0" errors="0">
2+
<testsuite name="Cucumber" time="0.007" tests="1" skipped="0" failures="0" errors="0" timestamp="1970-01-01T00:00:00Z">
33
<testcase classname="Data Tables" name="transposed table" time="0.005">
44
<system-out><![CDATA[
55
When the following table is transposed:.....................................passed

‎testdata/examples-tables.feature.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<testsuite name="Cucumber" time="0.073" tests="9" skipped="0" failures="4" errors="0">
2+
<testsuite name="Cucumber" time="0.073" tests="9" skipped="0" failures="4" errors="0" timestamp="1970-01-01T00:00:00Z">
33
<testcase classname="Examples Tables" name="Eating cucumbers - These are passing - #1.1" time="0.007">
44
<system-out><![CDATA[
55
Given there are 12 cucumbers................................................passed

‎testdata/hooks.feature.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<testsuite name="Cucumber" time="0.055" tests="5" skipped="0" failures="3" errors="0">
2+
<testsuite name="Cucumber" time="0.055" tests="5" skipped="0" failures="3" errors="0" timestamp="1970-01-01T00:00:00Z">
33
<testcase classname="Hooks" name="No tags and a passed step" time="0.009">
44
<system-out><![CDATA[
55
When a step passes..........................................................passed

‎testdata/markdown.feature.md.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<testsuite name="Cucumber" time="0.021" tests="2" skipped="0" failures="1" errors="0">
2+
<testsuite name="Cucumber" time="0.021" tests="2" skipped="0" failures="1" errors="0" timestamp="1970-01-01T00:00:00Z">
33
<testcase classname="Cheese" name="Nom nom nom - Ylajali! - because we need more tables - #1.1" time="0.009">
44
<failure type="Error" message="You asked me to fail">
55
<![CDATA[You asked me to fail

‎testdata/minimal.feature.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<testsuite name="Cucumber" time="0.005" tests="1" skipped="0" failures="0" errors="0">
2+
<testsuite name="Cucumber" time="0.005" tests="1" skipped="0" failures="0" errors="0" timestamp="1970-01-01T00:00:00Z">
33
<testcase classname="minimal" name="cukes" time="0.003">
44
<system-out><![CDATA[
55
Given I have 42 cukes in my belly...........................................passed

‎testdata/parameter-types.feature.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<testsuite name="Cucumber" time="0.005" tests="1" skipped="0" failures="0" errors="0">
2+
<testsuite name="Cucumber" time="0.005" tests="1" skipped="0" failures="0" errors="0" timestamp="1970-01-01T00:00:00Z">
33
<testcase classname="Parameter Types" name="Flight transformer" time="0.003">
44
<system-out><![CDATA[
55
Given LHR-CDG has been delayed..............................................passed

‎testdata/pending.feature.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<testsuite name="Cucumber" time="0.017" tests="3" skipped="0" failures="3" errors="0">
2+
<testsuite name="Cucumber" time="0.017" tests="3" skipped="0" failures="3" errors="0" timestamp="1970-01-01T00:00:00Z">
33
<testcase classname="Pending steps" name="Unimplemented step signals pending status" time="0.003">
44
<failure>
55
<![CDATA[TODO]]>

‎testdata/retry.feature.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<testsuite name="Cucumber" time="0.041" tests="10" skipped="0" failures="7" errors="0">
2+
<testsuite name="Cucumber" time="0.041" tests="10" skipped="0" failures="7" errors="0" timestamp="1970-01-01T00:00:00Z">
33
<testcase classname="Retry" name="Test cases that pass aren't retried" time="0.003">
44
<system-out><![CDATA[
55
Given a step that always passes.............................................passed

‎testdata/rules.feature.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<testsuite name="Cucumber" time="0.031" tests="3" skipped="0" failures="0" errors="0">
2+
<testsuite name="Cucumber" time="0.031" tests="3" skipped="0" failures="0" errors="0" timestamp="1970-01-01T00:00:00Z">
33
<testcase classname="Usage of a `Rule`" name="A sale cannot happen if the customer does not have enough money - Not enough money" time="0.009">
44
<system-out><![CDATA[
55
Given the customer has 100 cents............................................passed

‎testdata/skipped.feature.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<testsuite name="Cucumber" time="0.019" tests="3" skipped="3" failures="0" errors="0">
2+
<testsuite name="Cucumber" time="0.019" tests="3" skipped="3" failures="0" errors="0" timestamp="1970-01-01T00:00:00Z">
33
<testcase classname="Skipping scenarios" name="Skipping from a Before hook" time="0.005">
44
<skipped/>
55
<system-out><![CDATA[

‎testdata/stack-traces.feature.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<testsuite name="Cucumber" time="0.005" tests="1" skipped="0" failures="1" errors="0">
2+
<testsuite name="Cucumber" time="0.005" tests="1" skipped="0" failures="1" errors="0" timestamp="1970-01-01T00:00:00Z">
33
<testcase classname="Stack traces" name="A failing step" time="0.003">
44
<failure type="Error" message="BOOM">
55
<![CDATA[BOOM

‎testdata/undefined.feature.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<testsuite name="Cucumber" time="0.017" tests="3" skipped="0" failures="3" errors="0">
2+
<testsuite name="Cucumber" time="0.017" tests="3" skipped="0" failures="3" errors="0" timestamp="1970-01-01T00:00:00Z">
33
<testcase classname="Undefined steps" name="An undefined step causes a failure" time="0.003">
44
<failure/>
55
<system-out><![CDATA[

‎testdata/unknown-parameter-type.feature.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<testsuite name="Cucumber" time="0.005" tests="1" skipped="0" failures="1" errors="0">
2+
<testsuite name="Cucumber" time="0.005" tests="1" skipped="0" failures="1" errors="0" timestamp="1970-01-01T00:00:00Z">
33
<testcase classname="Parameter Types" name="undefined parameter type" time="0.003">
44
<failure/>
55
<system-out><![CDATA[

0 commit comments

Comments
 (0)
Please sign in to comment.