Skip to content

Commit ba13e24

Browse files
authored
[CCK] Add integration tests against Cucumber Compatibility Kit (#1920)
Implements a number of tests from the cucumber compatibility kit. This will help ensure consistency between Cucumber implementations.
1 parent 28881b3 commit ba13e24

38 files changed

+1133
-44
lines changed
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/bin/bash
2+
3+
rm -rf src/test/resources/*
4+
cp -r ../../cucumber/compatibility-kit/javascript/features src/test/resources/

compatibility/pom.xml

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<artifactId>cucumber-jvm</artifactId>
7+
<groupId>io.cucumber</groupId>
8+
<version>6.0.0-SNAPSHOT</version>
9+
</parent>
10+
<modelVersion>4.0.0</modelVersion>
11+
12+
<artifactId>cucumber-compatibility</artifactId>
13+
<name>Cucumber JVM: Compatibility Kit</name>
14+
15+
<dependencies>
16+
17+
<dependency>
18+
<groupId>io.cucumber</groupId>
19+
<artifactId>cucumber-java</artifactId>
20+
<scope>test</scope>
21+
</dependency>
22+
23+
<dependency>
24+
<groupId>org.hamcrest</groupId>
25+
<artifactId>hamcrest-core</artifactId>
26+
<scope>test</scope>
27+
</dependency>
28+
29+
<dependency>
30+
<groupId>org.junit.jupiter</groupId>
31+
<artifactId>junit-jupiter</artifactId>
32+
<scope>test</scope>
33+
</dependency>
34+
35+
</dependencies>
36+
37+
<build>
38+
<pluginManagement>
39+
<plugins>
40+
<plugin>
41+
<artifactId>maven-jar-plugin</artifactId>
42+
<configuration>
43+
<skipIfEmpty>true</skipIfEmpty>
44+
</configuration>
45+
</plugin>
46+
<plugin>
47+
<artifactId>maven-install-plugin</artifactId>
48+
<configuration>
49+
<skip>true</skip>
50+
</configuration>
51+
</plugin>
52+
<plugin>
53+
<artifactId>maven-javadoc-plugin</artifactId>
54+
<configuration>
55+
<skip>true</skip>
56+
</configuration>
57+
</plugin>
58+
<plugin>
59+
<artifactId>maven-deploy-plugin</artifactId>
60+
<configuration>
61+
<skip>true</skip>
62+
</configuration>
63+
</plugin>
64+
<plugin>
65+
<groupId>org.sonatype.plugins</groupId>
66+
<artifactId>nexus-staging-maven-plugin</artifactId>
67+
<configuration>
68+
<skipNexusStagingDeployMojo>true</skipNexusStagingDeployMojo>
69+
</configuration>
70+
</plugin>
71+
<plugin>
72+
<groupId>org.revapi</groupId>
73+
<artifactId>revapi-maven-plugin</artifactId>
74+
<configuration>
75+
<skip>true</skip>
76+
</configuration>
77+
</plugin>
78+
</plugins>
79+
</pluginManagement>
80+
</build>
81+
82+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package io.cucumber.compatibility;
2+
3+
import io.cucumber.compatibility.matchers.AComparableMessage;
4+
import io.cucumber.core.options.RuntimeOptionsBuilder;
5+
import io.cucumber.core.plugin.MessageFormatter;
6+
import io.cucumber.core.runtime.Runtime;
7+
import io.cucumber.core.runtime.TimeServiceEventBus;
8+
import io.cucumber.messages.Messages;
9+
import io.cucumber.messages.NdjsonToMessageIterable;
10+
import io.cucumber.messages.internal.com.google.protobuf.GeneratedMessageV3;
11+
import org.hamcrest.Matcher;
12+
import org.junit.jupiter.params.ParameterizedTest;
13+
import org.junit.jupiter.params.provider.MethodSource;
14+
15+
import java.io.File;
16+
import java.io.FileOutputStream;
17+
import java.io.IOException;
18+
import java.io.InputStream;
19+
import java.nio.file.Files;
20+
import java.nio.file.Path;
21+
import java.time.Clock;
22+
import java.util.ArrayList;
23+
import java.util.LinkedHashMap;
24+
import java.util.List;
25+
import java.util.Map;
26+
import java.util.UUID;
27+
import java.util.stream.Collectors;
28+
29+
import static org.hamcrest.CoreMatchers.is;
30+
import static org.hamcrest.MatcherAssert.assertThat;
31+
import static org.hamcrest.collection.IsIterableContainingInRelativeOrder.containsInRelativeOrder;
32+
import static org.hamcrest.collection.IsMapContaining.hasEntry;
33+
34+
public class CompatibilityTest {
35+
36+
@ParameterizedTest
37+
@MethodSource("io.cucumber.compatibility.TestCase#testCases")
38+
void produces_expected_output_for(TestCase testCase) throws IOException {
39+
File parentDir = new File("target/messages/" + testCase.getId());
40+
parentDir.mkdirs();
41+
File output = new File(parentDir, "out.ndjson");
42+
43+
try {
44+
Runtime.builder()
45+
.withRuntimeOptions(new RuntimeOptionsBuilder()
46+
.addGlue(testCase.getGlue())
47+
.addFeature(testCase.getFeature())
48+
.build())
49+
.withAdditionalPlugins(new MessageFormatter(new FileOutputStream(output)))
50+
.withEventBus(new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID))
51+
.build()
52+
.run();
53+
} catch (Exception ignored) {
54+
55+
}
56+
57+
List<Messages.Envelope> expected = readAllMessages(testCase.getExpectedFile());
58+
List<Messages.Envelope> actual = readAllMessages(output.toPath());
59+
60+
Map<String, List<GeneratedMessageV3>> expectedEnvelopes = openEnvelopes(expected);
61+
Map<String, List<GeneratedMessageV3>> actualEnvelopes = openEnvelopes(actual);
62+
63+
expectedEnvelopes.forEach((messageType, expectedMessages) ->
64+
assertThat(
65+
actualEnvelopes,
66+
hasEntry(is(messageType), containsInRelativeOrder(aComparableMessage(expectedMessages)))
67+
)
68+
);
69+
}
70+
71+
private static List<Messages.Envelope> readAllMessages(Path output) throws IOException {
72+
List<Messages.Envelope> expectedEnvelopes = new ArrayList<>();
73+
InputStream input = Files.newInputStream(output);
74+
new NdjsonToMessageIterable(input)
75+
.forEach(expectedEnvelopes::add);
76+
return expectedEnvelopes;
77+
}
78+
79+
@SuppressWarnings("unchecked")
80+
private static <T> Map<String, List<T>> openEnvelopes(List<? extends GeneratedMessageV3> actual) {
81+
Map<String, List<T>> map = new LinkedHashMap<>();
82+
actual.forEach(envelope -> envelope.getAllFields()
83+
.forEach((fieldDescriptor, value) -> {
84+
String jsonName = fieldDescriptor.getJsonName();
85+
map.putIfAbsent(jsonName, new ArrayList<>());
86+
map.get(jsonName).add((T) value);
87+
}));
88+
return map;
89+
}
90+
91+
92+
private static List<Matcher<? super GeneratedMessageV3>> aComparableMessage(List<GeneratedMessageV3> expectedMessages) {
93+
return expectedMessages.stream()
94+
.map(AComparableMessage::new)
95+
.collect(Collectors.toList());
96+
}
97+
}
98+
99+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package io.cucumber.compatibility;
2+
3+
import io.cucumber.core.feature.FeatureWithLines;
4+
import io.cucumber.core.feature.GluePath;
5+
6+
import java.io.IOException;
7+
import java.net.URI;
8+
import java.nio.file.DirectoryStream;
9+
import java.nio.file.Files;
10+
import java.nio.file.Path;
11+
import java.nio.file.Paths;
12+
import java.util.ArrayList;
13+
import java.util.Comparator;
14+
import java.util.List;
15+
16+
import static java.util.Comparator.comparing;
17+
18+
final class TestCase {
19+
private static final String FEATURES_DIRECTORY = "src/test/resources/features";
20+
private static final String FEATURES_PACKAGE = "io.cucumber.compatibility";
21+
22+
private final String packageName;
23+
private final String id;
24+
25+
private TestCase(String packageName, String id) {
26+
this.packageName = packageName;
27+
this.id = id;
28+
}
29+
30+
String getId() {
31+
return id;
32+
}
33+
34+
URI getGlue() {
35+
return GluePath.parse(FEATURES_PACKAGE +"."+ packageName);
36+
}
37+
38+
FeatureWithLines getFeature() {
39+
return FeatureWithLines.parse("file:" + FEATURES_DIRECTORY + "/" + id + "/" + id + ".feature");
40+
}
41+
42+
Path getExpectedFile() {
43+
return Paths.get(FEATURES_DIRECTORY + "/" + id + "/" + id + ".ndjson");
44+
}
45+
46+
@Override
47+
public String toString() {
48+
return id;
49+
}
50+
51+
static List<TestCase> testCases() throws IOException {
52+
List<TestCase> testCases = new ArrayList<>();
53+
Path dir = Paths.get(FEATURES_DIRECTORY);
54+
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
55+
for (Path path : stream) {
56+
if (path.toFile().isDirectory()) {
57+
String id = path.getFileName().toString();
58+
testCases.add(new TestCase(id.replace("-", ""), id));
59+
}
60+
}
61+
}
62+
testCases.sort(comparing(TestCase::getId));
63+
return testCases;
64+
}
65+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package io.cucumber.compatibility.attachments;
2+
3+
import io.cucumber.java.Before;
4+
import io.cucumber.java.Scenario;
5+
import io.cucumber.java.en.When;
6+
7+
import java.io.IOException;
8+
import java.nio.file.Files;
9+
import java.nio.file.Path;
10+
import java.nio.file.Paths;
11+
12+
import static java.nio.charset.StandardCharsets.UTF_8;
13+
14+
public class Attachments {
15+
16+
Scenario scenario;
17+
18+
@Before
19+
public void before(Scenario scenario) {
20+
this.scenario = scenario;
21+
}
22+
23+
@When("the string {string} is attached as {string}")
24+
public void theStringIsAttachedAs(String text, String contentType) {
25+
scenario.embed(text.getBytes(UTF_8), contentType, null);
26+
}
27+
28+
@When("the string {string} is logged")
29+
public void theStringIsLogged(String text) {
30+
scenario.write(text);
31+
}
32+
33+
@When("an array with {int} bytes are attached as {string}")
34+
public void anArrayWithBytesAreAttachedAs(int n, String mediaType) {
35+
byte[] bytes = new byte[n];
36+
for (byte i = 0; i < n; i++) {
37+
bytes[i] = i;
38+
}
39+
scenario.embed(bytes, mediaType, null);
40+
}
41+
42+
@When("a stream with {int} bytes are attached as {string}")
43+
public void aStreamWithBytesAreAttachedAs(int n, String mediaType) {
44+
byte[] bytes = new byte[n];
45+
for (byte i = 0; i < n; i++) {
46+
bytes[i] = i;
47+
}
48+
scenario.embed(bytes, mediaType, null);
49+
}
50+
51+
52+
@When("a JPEG image is attached")
53+
public void aJPEGImageIsAttached() throws IOException {
54+
Path path = Paths.get("src/test/resources/features/attachments/cucumber-growing-on-vine.jpg");
55+
byte[] bytes = Files.readAllBytes(path);
56+
String fileName = path.getFileName().toString();
57+
scenario.embed(bytes, "image/jpg", fileName);
58+
}
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package io.cucumber.compatibility.datatables;
2+
3+
import io.cucumber.datatable.DataTable;
4+
import io.cucumber.java.en.Then;
5+
import io.cucumber.java.en.When;
6+
7+
import static org.junit.jupiter.api.Assertions.assertEquals;
8+
9+
public class DataTables {
10+
private DataTable toTranspose;
11+
12+
@When("the following table is transposed:")
13+
public void theFollowingTableIsTransposed(DataTable toTranspose) {
14+
this.toTranspose = toTranspose;
15+
}
16+
17+
@Then("it should be:")
18+
public void itShouldBe(DataTable expected) {
19+
assertEquals(expected, toTranspose.transpose());
20+
}
21+
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package io.cucumber.compatibility.examplestables;
2+
3+
import io.cucumber.java.en.Given;
4+
import io.cucumber.java.en.Then;
5+
import io.cucumber.java.en.When;
6+
import org.junit.jupiter.api.Assertions;
7+
8+
import static org.junit.jupiter.api.Assertions.assertEquals;
9+
10+
public class ExamplesTable {
11+
12+
private int count;
13+
14+
@Given("there are {int} cucumbers")
15+
public void thereAreStartCucumbers(int cucumbers) {
16+
this.count = cucumbers;
17+
}
18+
19+
@When("I eat {int} cucumbers")
20+
public void iEatEatCucumbers(int eatCount) {
21+
this.count -= eatCount;
22+
}
23+
24+
@Then("I should have {int} cucumbers")
25+
public void iShouldHaveLeftCucumbers(int expectedCount) {
26+
assertEquals(expectedCount, this.count);
27+
}
28+
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package io.cucumber.compatibility.hooks;
2+
3+
import io.cucumber.java.After;
4+
import io.cucumber.java.Before;
5+
import io.cucumber.java.en.When;
6+
7+
public class Hooks {
8+
9+
@Before
10+
public void before() {}
11+
12+
@When("a step passes")
13+
public void aStepPasses() {
14+
}
15+
16+
@When("a step throws an exception")
17+
public void test() throws Exception {
18+
throw new Exception("Boom");
19+
}
20+
21+
@After
22+
public void afterWithException() throws Exception {
23+
throw new Exception("Exception in hook");
24+
}
25+
26+
@After("@some-tag or @some-other-tag")
27+
public void taggedAfterWithException() throws Exception {
28+
throw new Exception("Exception in conditional hook");
29+
}
30+
31+
}

0 commit comments

Comments
 (0)