diff --git a/java/.mvn/jvm.config b/java/.mvn/jvm.config
new file mode 100644
index 0000000..32599ce
--- /dev/null
+++ b/java/.mvn/jvm.config
@@ -0,0 +1,10 @@
+--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
+--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED
+--add-exports jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED
+--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED
+--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED
+--add-exports jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED
+--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
+--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
+--add-opens jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
+--add-opens jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED
diff --git a/java/pom.xml b/java/pom.xml
index 2868b96..05a478d 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -5,11 +5,11 @@
io.cucumber
cucumber-parent
- 4.5.0
+ 5.0.0-SNAPSHOT
junit-xml-formatter
- 0.11.1-SNAPSHOT
+ 0.12.0-SNAPSHOT
jar
JUnit XML Formatter
Renders Cucumber Messages as JUnit XML
@@ -59,12 +59,12 @@
io.cucumber
messages
- [29.0.1,31.0.0)
+ [32.0.0-SNAPSHOT,33.0.0)
io.cucumber
query
- [14.0.1,15.0.0)
+ [15.0.0-SNAPSHOT,16.0.0)
@@ -85,13 +85,6 @@
test
-
- org.hamcrest
- hamcrest
- 3.0
- test
-
-
org.assertj
assertj-core
@@ -111,4 +104,22 @@
test
+
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+
+
+ validate
+ validate
+
+ check
+
+
+
+
+
+
diff --git a/java/src/main/java/io/cucumber/junitxmlformatter/EscapingXmlStreamWriter.java b/java/src/main/java/io/cucumber/junitxmlformatter/EscapingXmlStreamWriter.java
index b375d6b..f647370 100644
--- a/java/src/main/java/io/cucumber/junitxmlformatter/EscapingXmlStreamWriter.java
+++ b/java/src/main/java/io/cucumber/junitxmlformatter/EscapingXmlStreamWriter.java
@@ -56,7 +56,7 @@ void writeAttribute(String localName, String value) throws XMLStreamException {
void writeCData(String data) throws XMLStreamException {
// https://stackoverflow.com/questions/223652/is-there-a-way-to-escape-a-cdata-end-token-in-xml
- for (String part : CDATA_TERMINATOR_SPLIT.split(data)) {
+ for (String part : CDATA_TERMINATOR_SPLIT.split(data, -1)) {
// see https://www.w3.org/TR/xml/#dt-cdsection
writer.writeCData(escapeIllegalChars(part));
}
@@ -88,6 +88,7 @@ private static String escapeIllegalChars(String value) {
return escaped.toString();
}
+ @SuppressWarnings("UnnecessaryParentheses")
private static boolean isLegal(int codePoint) {
// see https://www.w3.org/TR/xml/#charsets
return codePoint == 0x9
diff --git a/java/src/main/java/io/cucumber/junitxmlformatter/MessagesToJunitXmlWriter.java b/java/src/main/java/io/cucumber/junitxmlformatter/MessagesToJunitXmlWriter.java
index 891ee03..3869d8d 100644
--- a/java/src/main/java/io/cucumber/junitxmlformatter/MessagesToJunitXmlWriter.java
+++ b/java/src/main/java/io/cucumber/junitxmlformatter/MessagesToJunitXmlWriter.java
@@ -2,6 +2,7 @@
import io.cucumber.messages.types.Envelope;
import io.cucumber.query.NamingStrategy;
+import org.jspecify.annotations.Nullable;
import javax.xml.stream.XMLStreamException;
import java.io.IOException;
@@ -41,56 +42,11 @@ public static Builder builder() {
return new Builder();
}
- public static class Builder {
-
- private String testSuiteName = DEFAULT_TEST_SUITE_NAME;
- private String testClassName;
- private NamingStrategy testNamingStrategy = NamingStrategy.strategy(LONG)
- .featureName(EXCLUDE)
- .exampleName(NUMBER_AND_PICKLE_IF_PARAMETERIZED)
- .build();
-
- private Builder() {
-
- }
-
- /**
- * Sets the value for the {@code } attribute. Defaults to {@value DEFAULT_TEST_SUITE_NAME}.
- */
- public Builder testSuiteName(String testSuiteName) {
- this.testSuiteName = requireNonNull(testSuiteName);
- return this;
- }
-
- /**
- * Sets the value for the {@code } attribute. Defaults to the name of the
- * feature.
- */
- public Builder testClassName(String testClassName) {
- this.testClassName = testClassName;
- return this;
- }
-
- /**
- * Set the naming strategy used for the {@code attribute}. Defaults to the
- * {@link NamingStrategy.Strategy#LONG} strategy with {@link NamingStrategy.FeatureName#EXCLUDE} and
- * {@link NamingStrategy.ExampleName#NUMBER_AND_PICKLE_IF_PARAMETERIZED}.
- */
- public Builder testNamingStrategy(NamingStrategy namingStrategy) {
- this.testNamingStrategy = requireNonNull(namingStrategy);
- return this;
- }
-
- public MessagesToJunitXmlWriter build(OutputStream out) {
- return new MessagesToJunitXmlWriter(testSuiteName, testClassName, testNamingStrategy, requireNonNull(out));
- }
- }
-
private static NamingStrategy createNamingStrategy(NamingStrategy.ExampleName exampleName) {
return NamingStrategy.strategy(NamingStrategy.Strategy.LONG).featureName(NamingStrategy.FeatureName.EXCLUDE).exampleName(exampleName).build();
}
- private MessagesToJunitXmlWriter(String testSuiteName, String testClassName, NamingStrategy testNamingStrategy, OutputStream out) {
+ private MessagesToJunitXmlWriter(String testSuiteName, @Nullable String testClassName, NamingStrategy testNamingStrategy, OutputStream out) {
this.data = new XmlReportData(testSuiteName, testClassName, testNamingStrategy);
this.out = new OutputStreamWriter(
requireNonNull(out),
@@ -136,4 +92,49 @@ public void close() throws IOException {
}
}
}
+
+ public final static class Builder {
+
+ private String testSuiteName = DEFAULT_TEST_SUITE_NAME;
+ private @Nullable String testClassName;
+ private NamingStrategy testNamingStrategy = NamingStrategy.strategy(LONG)
+ .featureName(EXCLUDE)
+ .exampleName(NUMBER_AND_PICKLE_IF_PARAMETERIZED)
+ .build();
+
+ private Builder() {
+
+ }
+
+ /**
+ * Sets the value for the {@code } attribute. Defaults to {@value DEFAULT_TEST_SUITE_NAME}.
+ */
+ public Builder testSuiteName(String testSuiteName) {
+ this.testSuiteName = requireNonNull(testSuiteName);
+ return this;
+ }
+
+ /**
+ * Sets the value for the {@code } attribute. Defaults to the name of the
+ * feature.
+ */
+ public Builder testClassName(@Nullable String testClassName) {
+ this.testClassName = testClassName;
+ return this;
+ }
+
+ /**
+ * Set the naming strategy used for the {@code attribute}. Defaults to the
+ * {@link NamingStrategy.Strategy#LONG} strategy with {@link NamingStrategy.FeatureName#EXCLUDE} and
+ * {@link NamingStrategy.ExampleName#NUMBER_AND_PICKLE_IF_PARAMETERIZED}.
+ */
+ public Builder testNamingStrategy(NamingStrategy namingStrategy) {
+ this.testNamingStrategy = requireNonNull(namingStrategy);
+ return this;
+ }
+
+ public MessagesToJunitXmlWriter build(OutputStream out) {
+ return new MessagesToJunitXmlWriter(testSuiteName, testClassName, testNamingStrategy, requireNonNull(out));
+ }
+ }
}
diff --git a/java/src/main/java/io/cucumber/junitxmlformatter/XmlReportData.java b/java/src/main/java/io/cucumber/junitxmlformatter/XmlReportData.java
index ef4ddd6..1671cd4 100644
--- a/java/src/main/java/io/cucumber/junitxmlformatter/XmlReportData.java
+++ b/java/src/main/java/io/cucumber/junitxmlformatter/XmlReportData.java
@@ -16,6 +16,7 @@
import io.cucumber.query.NamingStrategy;
import io.cucumber.query.Query;
import io.cucumber.query.Repository;
+import org.jspecify.annotations.Nullable;
import java.time.Duration;
import java.util.AbstractMap.SimpleEntry;
@@ -40,10 +41,10 @@ class XmlReportData {
.build();
private final Query query = new Query(repository);
private final String testSuiteName;
- private final String testClassName;
+ private final @Nullable String testClassName;
private final NamingStrategy testNamingStrategy;
- XmlReportData(String testSuiteName, String testClassName, NamingStrategy testNamingStrategy) {
+ XmlReportData(String testSuiteName, @Nullable String testClassName, NamingStrategy testNamingStrategy) {
this.testSuiteName = requireNonNull(testSuiteName);
this.testClassName = testClassName;
this.testNamingStrategy = requireNonNull(testNamingStrategy);
@@ -140,7 +141,7 @@ List getAllTestCaseStarted() {
}
private static final io.cucumber.messages.types.Duration ZERO_DURATION =
- new io.cucumber.messages.types.Duration(0L, 0L);
+ new io.cucumber.messages.types.Duration(0L, 0);
// By definition, but see https://github.com/cucumber/gherkin/issues/11
private static final TestStepResult SCENARIO_WITH_NO_STEPS = new TestStepResult(ZERO_DURATION, null, PASSED, null);
diff --git a/java/src/main/java/io/cucumber/junitxmlformatter/XmlReportWriter.java b/java/src/main/java/io/cucumber/junitxmlformatter/XmlReportWriter.java
index dd3f407..2fd9f4f 100644
--- a/java/src/main/java/io/cucumber/junitxmlformatter/XmlReportWriter.java
+++ b/java/src/main/java/io/cucumber/junitxmlformatter/XmlReportWriter.java
@@ -53,7 +53,7 @@ private void writeSuiteAttributes(EscapingXmlStreamWriter writer) throws XMLStre
Map counts = data.getTestCaseStatusCounts();
writer.writeAttribute("tests", String.valueOf(data.getTestCaseCount()));
- writer.writeAttribute("skipped", counts.get(SKIPPED).toString());
+ writer.writeAttribute("skipped", String.valueOf(counts.get(SKIPPED)));
writer.writeAttribute("failures", String.valueOf(countFailures(counts)));
writer.writeAttribute("errors", "0");
diff --git a/java/src/main/java/io/cucumber/junitxmlformatter/package-info.java b/java/src/main/java/io/cucumber/junitxmlformatter/package-info.java
new file mode 100644
index 0000000..402db55
--- /dev/null
+++ b/java/src/main/java/io/cucumber/junitxmlformatter/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package io.cucumber.junitxmlformatter;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/java/src/test/java/io/cucumber/junitxmlformatter/Jackson.java b/java/src/test/java/io/cucumber/junitxmlformatter/Jackson.java
index 3187e57..447cd25 100644
--- a/java/src/test/java/io/cucumber/junitxmlformatter/Jackson.java
+++ b/java/src/test/java/io/cucumber/junitxmlformatter/Jackson.java
@@ -1,7 +1,6 @@
package io.cucumber.junitxmlformatter;
import com.fasterxml.jackson.annotation.JsonCreator.Mode;
-import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -11,11 +10,14 @@
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
+import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_ABSENT;
+import static com.fasterxml.jackson.annotation.JsonInclude.Value.construct;
+
final class Jackson {
public static final ObjectMapper OBJECT_MAPPER = JsonMapper.builder()
.addModule(new Jdk8Module())
.addModule(new ParameterNamesModule(Mode.PROPERTIES))
- .serializationInclusion(Include.NON_ABSENT)
+ .defaultPropertyInclusion(construct(NON_ABSENT, NON_ABSENT))
.constructorDetector(ConstructorDetector.USE_PROPERTIES_BASED)
.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)
.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
diff --git a/java/src/test/java/io/cucumber/junitxmlformatter/MessagesToJunitXmlWriterAcceptanceTest.java b/java/src/test/java/io/cucumber/junitxmlformatter/MessagesToJunitXmlWriterAcceptanceTest.java
index e649036..8202120 100644
--- a/java/src/test/java/io/cucumber/junitxmlformatter/MessagesToJunitXmlWriterAcceptanceTest.java
+++ b/java/src/test/java/io/cucumber/junitxmlformatter/MessagesToJunitXmlWriterAcceptanceTest.java
@@ -2,8 +2,6 @@
import io.cucumber.messages.NdjsonToMessageIterable;
import io.cucumber.messages.types.Envelope;
-import io.cucumber.query.NamingStrategy;
-import io.cucumber.query.NamingStrategy.Strategy;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.params.ParameterizedTest;
@@ -31,14 +29,14 @@
import static io.cucumber.junitxmlformatter.Jackson.OBJECT_MAPPER;
import static io.cucumber.query.NamingStrategy.Strategy.LONG;
import static io.cucumber.query.NamingStrategy.strategy;
+import static java.util.Objects.requireNonNull;
import static org.xmlunit.assertj.XmlAssert.assertThat;
class MessagesToJunitXmlWriterAcceptanceTest {
- private static final NdjsonToMessageIterable.Deserializer deserializer = (json) -> OBJECT_MAPPER.readValue(json, Envelope.class);
+ private static final NdjsonToMessageIterable.Deserializer deserializer = json -> OBJECT_MAPPER.readValue(json, Envelope.class);
static List acceptance() throws IOException {
List testCases = new ArrayList<>();
-
try (Stream paths = Files.list(Paths.get("../testdata/src"))) {
paths
.filter(path -> path.getFileName().toString().endsWith(".ndjson"))
@@ -130,7 +128,6 @@ private static T writeJunitXmlReport(TestCase testCase,
static class TestCase {
private final Path source;
private final Path expected;
-
private final String name;
private final MessagesToJunitXmlWriter.Builder builder;
private final String strategyName;
@@ -139,7 +136,7 @@ static class TestCase {
this.source = source;
String fileName = source.getFileName().toString();
this.name = fileName.substring(0, fileName.lastIndexOf(".ndjson"));
- this.expected = source.getParent().resolve(name + "." + namingStrategyName + ".xml");
+ this.expected = requireNonNull(source.getParent()).resolve(name + "." + namingStrategyName + ".xml");
this.builder = builder;
this.strategyName = namingStrategyName;
}
@@ -155,15 +152,13 @@ public String toString() {
@Override
public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- TestCase testCase = (TestCase) o;
- return source.equals(testCase.source);
+ if (!(o instanceof TestCase testCase)) return false;
+ return Objects.equals(source, testCase.source) && Objects.equals(strategyName, testCase.strategyName);
}
@Override
public int hashCode() {
- return Objects.hash(source);
+ return Objects.hash(source, strategyName);
}
}
diff --git a/java/src/test/java/io/cucumber/junitxmlformatter/MessagesToJunitXmlWriterTest.java b/java/src/test/java/io/cucumber/junitxmlformatter/MessagesToJunitXmlWriterTest.java
index cb7f1dc..b201b81 100644
--- a/java/src/test/java/io/cucumber/junitxmlformatter/MessagesToJunitXmlWriterTest.java
+++ b/java/src/test/java/io/cucumber/junitxmlformatter/MessagesToJunitXmlWriterTest.java
@@ -3,6 +3,7 @@
import io.cucumber.messages.types.Envelope;
import io.cucumber.messages.types.TestRunFinished;
import io.cucumber.messages.types.TestRunStarted;
+import io.cucumber.messages.types.Timestamp;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayOutputStream;
@@ -48,7 +49,9 @@ void it_throws_when_writing_after_close() throws IOException {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
MessagesToJunitXmlWriter messagesToHtmlWriter = new MessagesToJunitXmlWriter(bytes);
messagesToHtmlWriter.close();
- assertThrows(IOException.class, () -> messagesToHtmlWriter.write(null));
+ assertThrows(IOException.class, () -> messagesToHtmlWriter.write(
+ Envelope.of(new TestRunStarted(new Timestamp(0L, 0), ""))
+ ));
}
@Test