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