Skip to content

Commit 3fe8d63

Browse files
committed
Add a stand-alone CLI to read message files
1 parent 0978e6c commit 3fe8d63

File tree

12 files changed

+315
-90
lines changed

12 files changed

+315
-90
lines changed

java/cli/pom.xml

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
5+
<parent>
6+
<groupId>io.cucumber</groupId>
7+
<artifactId>junit-xml-formatter-parent</artifactId>
8+
<version>0.5.1-SNAPSHOT</version>
9+
</parent>
10+
11+
<artifactId>junit-xml-formatter-cli</artifactId>
12+
<packaging>jar</packaging>
13+
<name>JUnit XML Formatter CLI</name>
14+
<description>CLI to render Cucumber Messages as JUnit XML</description>
15+
<url>https://github.com/cucumber/junit-xml-formatter</url>
16+
17+
<properties>
18+
<project.Automatic-Module-Name>io.cucumber.junitxmlformatter.cli</project.Automatic-Module-Name>
19+
</properties>
20+
21+
<dependencyManagement>
22+
<dependencies>
23+
<dependency>
24+
<groupId>org.junit</groupId>
25+
<artifactId>junit-bom</artifactId>
26+
<version>5.11.3</version>
27+
<type>pom</type>
28+
<scope>import</scope>
29+
</dependency>
30+
31+
<dependency>
32+
<groupId>com.fasterxml.jackson</groupId>
33+
<artifactId>jackson-bom</artifactId>
34+
<version>2.18.0</version>
35+
<type>pom</type>
36+
<scope>import</scope>
37+
</dependency>
38+
</dependencies>
39+
</dependencyManagement>
40+
41+
<dependencies>
42+
<dependency>
43+
<groupId>info.picocli</groupId>
44+
<artifactId>picocli</artifactId>
45+
<version>4.7.6</version>
46+
</dependency>
47+
48+
<dependency>
49+
<groupId>io.cucumber</groupId>
50+
<artifactId>junit-xml-formatter</artifactId>
51+
</dependency>
52+
<dependency>
53+
<groupId>com.fasterxml.jackson.core</groupId>
54+
<artifactId>jackson-databind</artifactId>
55+
</dependency>
56+
57+
<dependency>
58+
<groupId>com.fasterxml.jackson.datatype</groupId>
59+
<artifactId>jackson-datatype-jdk8</artifactId>
60+
</dependency>
61+
<dependency>
62+
<groupId>com.fasterxml.jackson.module</groupId>
63+
<artifactId>jackson-module-parameter-names</artifactId>
64+
</dependency>
65+
66+
<dependency>
67+
<groupId>org.assertj</groupId>
68+
<artifactId>assertj-core</artifactId>
69+
<version>3.26.3</version>
70+
<scope>test</scope>
71+
</dependency>
72+
73+
<dependency>
74+
<groupId>org.junit.jupiter</groupId>
75+
<artifactId>junit-jupiter-engine</artifactId>
76+
<scope>test</scope>
77+
</dependency>
78+
79+
<dependency>
80+
<groupId>org.junit.jupiter</groupId>
81+
<artifactId>junit-jupiter-params</artifactId>
82+
<scope>test</scope>
83+
</dependency>
84+
</dependencies>
85+
86+
<build>
87+
<plugins>
88+
<plugin>
89+
<groupId>org.apache.maven.plugins</groupId>
90+
<artifactId>maven-compiler-plugin</artifactId>
91+
<configuration>
92+
<annotationProcessorPaths>
93+
<path>
94+
<groupId>info.picocli</groupId>
95+
<artifactId>picocli-codegen</artifactId>
96+
<version>4.7.6</version>
97+
</path>
98+
</annotationProcessorPaths>
99+
<compilerArgs>
100+
<arg>-Aproject=${project.groupId}/${project.artifactId}</arg>
101+
</compilerArgs>
102+
</configuration>
103+
</plugin>
104+
</plugins>
105+
</build>
106+
</project>
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package io.cucumber.junitxmlformatter.cli;
2+
3+
import com.fasterxml.jackson.annotation.JsonCreator;
4+
import com.fasterxml.jackson.databind.DeserializationFeature;
5+
import com.fasterxml.jackson.databind.cfg.ConstructorDetector;
6+
import com.fasterxml.jackson.databind.json.JsonMapper;
7+
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
8+
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
9+
import io.cucumber.junitxmlformatter.MessagesToJunitXmlWriter;
10+
import io.cucumber.messages.NdjsonToMessageIterable;
11+
import io.cucumber.messages.NdjsonToMessageIterable.Deserializer;
12+
import io.cucumber.messages.types.Envelope;
13+
import io.cucumber.query.NamingStrategy;
14+
import picocli.CommandLine;
15+
import picocli.CommandLine.Command;
16+
import picocli.CommandLine.Model.CommandSpec;
17+
import picocli.CommandLine.Parameters;
18+
import picocli.CommandLine.Spec;
19+
20+
import java.io.InputStream;
21+
import java.io.PrintWriter;
22+
import java.io.Writer;
23+
import java.nio.file.Files;
24+
import java.nio.file.Path;
25+
import java.util.concurrent.Callable;
26+
27+
import static io.cucumber.query.NamingStrategy.ExampleName.NUMBER_AND_PICKLE_IF_PARAMETERIZED;
28+
29+
@Command(name = "to-junit-xml", mixinStandardHelpOptions = true, version = "to-junit-xml 4.0",
30+
description = "Converts Cucumber messages to JUnit XML")
31+
class ToJUnitXml implements Callable<Integer> {
32+
@Spec
33+
private CommandSpec spec;
34+
35+
@Parameters(index = "0", description = "The source file containing Cucumber messages")
36+
private Path source;
37+
38+
@Override
39+
public Integer call() throws Exception {
40+
JsonMapper jsonMapper = JsonMapper.builder()
41+
.addModule(new Jdk8Module())
42+
.addModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES))
43+
.constructorDetector(ConstructorDetector.USE_PROPERTIES_BASED)
44+
.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
45+
.enable(DeserializationFeature.USE_LONG_FOR_INTS)
46+
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
47+
.build();
48+
Deserializer deserializer = json -> jsonMapper.readValue(json, Envelope.class);
49+
50+
// TODO: Use the CLI options.
51+
NamingStrategy.ExampleName exampleNameStrategy = NUMBER_AND_PICKLE_IF_PARAMETERIZED;
52+
Writer out = spec.commandLine().getOut();
53+
54+
try (InputStream in = Files.newInputStream(source)) {
55+
try (NdjsonToMessageIterable envelopes = new NdjsonToMessageIterable(in, deserializer)) {
56+
try (MessagesToJunitXmlWriter writer = new MessagesToJunitXmlWriter(exampleNameStrategy, out)) {
57+
for (Envelope envelope : envelopes) {
58+
// TODO: What if exception?
59+
writer.write(envelope);
60+
}
61+
}
62+
}
63+
}
64+
return 0;
65+
}
66+
67+
// this example implements Callable, so parsing, error handling and handling user
68+
// requests for usage help or version help can be done with one line of code.
69+
public static void main(String... args) {
70+
int exitCode = new CommandLine(new ToJUnitXml()).execute(args);
71+
System.exit(exitCode);
72+
}
73+
}

java/lib/pom.xml

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
5+
<parent>
6+
<groupId>io.cucumber</groupId>
7+
<artifactId>junit-xml-formatter-parent</artifactId>
8+
<version>0.5.1-SNAPSHOT</version>
9+
</parent>
10+
11+
<artifactId>junit-xml-formatter</artifactId>
12+
<packaging>jar</packaging>
13+
<name>JUnit XML Formatter</name>
14+
<description>Renders Cucumber Messages as JUnit XML</description>
15+
<url>https://github.com/cucumber/junit-xml-formatter</url>
16+
17+
<properties>
18+
<project.Automatic-Module-Name>io.cucumber.junitxmlformatter</project.Automatic-Module-Name>
19+
</properties>
20+
21+
<dependencyManagement>
22+
<dependencies>
23+
<dependency>
24+
<groupId>org.junit</groupId>
25+
<artifactId>junit-bom</artifactId>
26+
<version>5.11.3</version>
27+
<type>pom</type>
28+
<scope>import</scope>
29+
</dependency>
30+
31+
<dependency>
32+
<groupId>com.fasterxml.jackson</groupId>
33+
<artifactId>jackson-bom</artifactId>
34+
<version>2.18.0</version>
35+
<type>pom</type>
36+
<scope>import</scope>
37+
</dependency>
38+
</dependencies>
39+
</dependencyManagement>
40+
41+
<dependencies>
42+
<dependency>
43+
<groupId>io.cucumber</groupId>
44+
<artifactId>messages</artifactId>
45+
<version>[24.0.0,27.0.0)</version>
46+
</dependency>
47+
<dependency>
48+
<groupId>io.cucumber</groupId>
49+
<artifactId>query</artifactId>
50+
<version>[12.2.0,13.0.0)</version>
51+
</dependency>
52+
53+
<dependency>
54+
<groupId>com.fasterxml.jackson.core</groupId>
55+
<artifactId>jackson-databind</artifactId>
56+
<scope>test</scope>
57+
</dependency>
58+
59+
<dependency>
60+
<groupId>com.fasterxml.jackson.datatype</groupId>
61+
<artifactId>jackson-datatype-jdk8</artifactId>
62+
<scope>test</scope>
63+
</dependency>
64+
65+
<dependency>
66+
<groupId>com.fasterxml.jackson.module</groupId>
67+
<artifactId>jackson-module-parameter-names</artifactId>
68+
<scope>test</scope>
69+
</dependency>
70+
71+
<dependency>
72+
<groupId>org.hamcrest</groupId>
73+
<artifactId>hamcrest</artifactId>
74+
<version>3.0</version>
75+
<scope>test</scope>
76+
</dependency>
77+
78+
<dependency>
79+
<groupId>org.assertj</groupId>
80+
<artifactId>assertj-core</artifactId>
81+
<version>3.26.3</version>
82+
<scope>test</scope>
83+
</dependency>
84+
85+
<dependency>
86+
<groupId>org.xmlunit</groupId>
87+
<artifactId>xmlunit-assertj</artifactId>
88+
<version>2.10.0</version>
89+
<scope>test</scope>
90+
</dependency>
91+
92+
<dependency>
93+
<groupId>org.junit.jupiter</groupId>
94+
<artifactId>junit-jupiter-engine</artifactId>
95+
<scope>test</scope>
96+
</dependency>
97+
98+
<dependency>
99+
<groupId>org.junit.jupiter</groupId>
100+
<artifactId>junit-jupiter-params</artifactId>
101+
<scope>test</scope>
102+
</dependency>
103+
</dependencies>
104+
</project>

java/src/main/java/io/cucumber/junitxmlformatter/MessagesToJunitXmlWriter.java renamed to java/lib/src/main/java/io/cucumber/junitxmlformatter/MessagesToJunitXmlWriter.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.io.IOException;
88
import java.io.OutputStream;
99
import java.io.OutputStreamWriter;
10+
import java.io.Writer;
1011
import java.nio.charset.StandardCharsets;
1112

1213
import static io.cucumber.query.NamingStrategy.FeatureName.EXCLUDE;
@@ -22,7 +23,7 @@
2223
*/
2324
public class MessagesToJunitXmlWriter implements AutoCloseable {
2425

25-
private final OutputStreamWriter out;
26+
private final Writer out;
2627
private final XmlReportData data;
2728
private boolean streamClosed = false;
2829

@@ -34,18 +35,25 @@ public MessagesToJunitXmlWriter(NamingStrategy.ExampleName exampleNameStrategy,
3435
this(createNamingStrategy(requireNonNull(exampleNameStrategy)), out);
3536
}
3637

38+
public MessagesToJunitXmlWriter(NamingStrategy.ExampleName exampleNameStrategy, Writer out) {
39+
this(createNamingStrategy(exampleNameStrategy), out);
40+
}
41+
42+
3743
private static NamingStrategy createNamingStrategy(NamingStrategy.ExampleName exampleName) {
3844
return NamingStrategy.strategy(LONG).featureName(EXCLUDE).exampleName(exampleName).build();
3945
}
4046

4147
private MessagesToJunitXmlWriter(NamingStrategy namingStrategy, OutputStream out) {
42-
this.data = new XmlReportData(namingStrategy);
43-
this.out = new OutputStreamWriter(
44-
requireNonNull(out),
45-
StandardCharsets.UTF_8
48+
this(namingStrategy, new OutputStreamWriter(requireNonNull(out), StandardCharsets.UTF_8)
4649
);
4750
}
4851

52+
private MessagesToJunitXmlWriter(NamingStrategy namingStrategy, Writer out) {
53+
this.data = new XmlReportData(namingStrategy);
54+
this.out = out;
55+
}
56+
4957
/**
5058
* Writes a cucumber message to the xml output.
5159
*

java/src/test/java/io/cucumber/junitxmlformatter/MessagesToJunitXmlWriterAcceptanceTest.java renamed to java/lib/src/test/java/io/cucumber/junitxmlformatter/MessagesToJunitXmlWriterAcceptanceTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class MessagesToJunitXmlWriterAcceptanceTest {
3434
private static final NdjsonToMessageIterable.Deserializer deserializer = (json) -> OBJECT_MAPPER.readValue(json, Envelope.class);
3535

3636
static List<TestCase> acceptance() throws IOException {
37-
try (Stream<Path> paths = Files.list(Paths.get("../testdata"))) {
37+
try (Stream<Path> paths = Files.list(Paths.get("../../testdata"))) {
3838
return paths
3939
.filter(path -> path.getFileName().toString().endsWith(".ndjson"))
4040
.map(TestCase::new)
@@ -57,7 +57,7 @@ void test(TestCase testCase) throws IOException {
5757
void validateAgainstJenkins(TestCase testCase) throws IOException {
5858
ByteArrayOutputStream bytes = writeJunitXmlReport(testCase, new ByteArrayOutputStream());
5959
Source actual = Input.fromByteArray(bytes.toByteArray()).build();
60-
Source jenkinsSchema = Input.fromPath(Paths.get("../jenkins-junit.xsd")).build();
60+
Source jenkinsSchema = Input.fromPath(Paths.get("../../jenkins-junit.xsd")).build();
6161
assertThat(actual).isValidAgainst(jenkinsSchema);
6262
}
6363

@@ -75,7 +75,7 @@ void validateAgainstJenkins(TestCase testCase) throws IOException {
7575
void validateAgainstSurefire(TestCase testCase) throws IOException {
7676
ByteArrayOutputStream bytes = writeJunitXmlReport(testCase, new ByteArrayOutputStream());
7777
Source actual = Input.fromByteArray(bytes.toByteArray()).build();
78-
Source surefireSchema = Input.fromPath(Paths.get("../surefire-test-report-3.0.xsd")).build();
78+
Source surefireSchema = Input.fromPath(Paths.get("../../surefire-test-report-3.0.xsd")).build();
7979
if (!testCasesWithMissingException.contains(testCase.name)) {
8080
assertThat(actual).isValidAgainst(surefireSchema);
8181
return;

0 commit comments

Comments
 (0)