Skip to content

Commit a94fcfa

Browse files
committed
Write test cases in canonical order
1 parent e6691ef commit a94fcfa

File tree

2 files changed

+130
-7
lines changed

2 files changed

+130
-7
lines changed
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package io.cucumber.junitxmlformatter;
2+
3+
import io.cucumber.messages.types.Envelope;
4+
5+
import java.util.ArrayList;
6+
import java.util.Collections;
7+
import java.util.Iterator;
8+
import java.util.List;
9+
import java.util.Random;
10+
import java.util.function.Consumer;
11+
12+
class MessageOrderer {
13+
14+
/**
15+
* Make test cases deterministically reproducible.
16+
*/
17+
private static final Random random = new Random(202509171740L);
18+
19+
static Consumer<List<Envelope>> originalOrder() {
20+
return envelopes -> {
21+
};
22+
}
23+
24+
/**
25+
* Simulates parallel execution of testcases by interleaving the
26+
* execution of different test cases.
27+
*/
28+
static Consumer<List<Envelope>> simulateParallelExecution() {
29+
return messages -> {
30+
List<Envelope> testCaseMessagesInSerial = testCasesSubList(messages);
31+
List<List<Envelope>> testCaseMessagesGrouped = groupTestCasesIntoBuckets(testCaseMessagesInSerial);
32+
List<Envelope> testCaseMessagesInterleaved = interleafMessagesFromTestCases(testCaseMessagesGrouped);
33+
replaceAll(testCaseMessagesInSerial, testCaseMessagesInterleaved);
34+
};
35+
}
36+
37+
private static void replaceAll(List<Envelope> testCaseMessagesInSerial, List<Envelope> envelopes) {
38+
for (int i = 0; i < testCaseMessagesInSerial.size(); i++) {
39+
testCaseMessagesInSerial.set(i, envelopes.get(i));
40+
}
41+
}
42+
43+
private static List<Envelope> interleafMessagesFromTestCases(List<List<Envelope>> messagesGroupedByTestCase) {
44+
List<Envelope> serial = new ArrayList<>();
45+
while (!messagesGroupedByTestCase.isEmpty()) {
46+
Collections.shuffle(messagesGroupedByTestCase, random);
47+
Iterator<List<Envelope>> bucketIterator = messagesGroupedByTestCase.iterator();
48+
while (bucketIterator.hasNext()) {
49+
List<Envelope> bucket = bucketIterator.next();
50+
if (bucket.isEmpty()) {
51+
bucketIterator.remove();
52+
} else {
53+
serial.add(bucket.remove(0));
54+
}
55+
}
56+
}
57+
return serial;
58+
}
59+
60+
private static List<List<Envelope>> groupTestCasesIntoBuckets(List<Envelope> testCaseMessages) {
61+
List<List<Envelope>> buckets = new ArrayList<>();
62+
List<Envelope> currentBucket = new ArrayList<>();
63+
for (Envelope middleMessage : testCaseMessages) {
64+
if (middleMessage.getTestCaseStarted().isPresent()) {
65+
buckets.add(currentBucket);
66+
currentBucket = new ArrayList<>();
67+
}
68+
currentBucket.add(middleMessage);
69+
}
70+
buckets.add(currentBucket);
71+
return buckets;
72+
}
73+
74+
private static List<Envelope> testCasesSubList(List<Envelope> messages) {
75+
int testRunStartedIndex = findTestRunStartedIndex(messages);
76+
int testRunFinishedIndex = findTestRunFinishedIndex(messages);
77+
return messages.subList(testRunStartedIndex + 1, testRunFinishedIndex - 1);
78+
}
79+
80+
private static int findTestRunFinishedIndex(List<Envelope> messages) {
81+
int testRunFinishedIndex = messages.size() - 1;
82+
for (; testRunFinishedIndex >= 0; testRunFinishedIndex--) {
83+
if (messages.get(testRunFinishedIndex).getTestRunFinished().isPresent()) {
84+
break;
85+
}
86+
}
87+
return testRunFinishedIndex;
88+
}
89+
90+
private static int findTestRunStartedIndex(List<Envelope> messages) {
91+
int testRunStartedIndex = 0;
92+
for (; testRunStartedIndex < messages.size(); testRunStartedIndex++) {
93+
if (messages.get(testRunStartedIndex).getTestRunStarted().isPresent()) {
94+
break;
95+
}
96+
}
97+
return testRunStartedIndex;
98+
}
99+
}

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

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,13 @@
2424
import java.util.Comparator;
2525
import java.util.List;
2626
import java.util.Objects;
27+
import java.util.function.Consumer;
2728
import java.util.stream.Collectors;
2829
import java.util.stream.Stream;
2930

3031
import static io.cucumber.junitxmlformatter.Jackson.OBJECT_MAPPER;
32+
import static io.cucumber.junitxmlformatter.MessageOrderer.simulateParallelExecution;
33+
import static java.nio.file.Files.readAllBytes;
3134
import static org.xmlunit.assertj.XmlAssert.assertThat;
3235

3336
class MessagesToJunitXmlWriterAcceptanceTest {
@@ -51,6 +54,16 @@ void test(TestCase testCase) throws IOException {
5154
Source actual = Input.fromByteArray(bytes.toByteArray()).build();
5255
assertThat(actual).and(expected).ignoreWhitespace().areIdentical();
5356
}
57+
58+
@ParameterizedTest
59+
@MethodSource("acceptance")
60+
void testWithSimulatedParallelExecution(TestCase testCase) throws IOException {
61+
ByteArrayOutputStream bytes = writeJunitXmlReport(testCase, new ByteArrayOutputStream(), simulateParallelExecution());
62+
Source expected = Input.fromPath(testCase.expected).build();
63+
Source actual = Input.fromByteArray(bytes.toByteArray()).build();
64+
assertThat(actual).and(expected).ignoreWhitespace().areIdentical();
65+
}
66+
5467

5568
@ParameterizedTest
5669
@MethodSource("acceptance")
@@ -81,7 +94,7 @@ void validateAgainstSurefire(TestCase testCase) throws IOException {
8194
Iterable<ValidationProblem> problems = validationResult.getProblems();
8295
Assertions.assertThat(problems).extracting(ValidationProblem::getMessage).containsAll(expectedProblems);
8396
}
84-
97+
8598
@ParameterizedTest
8699
@MethodSource("acceptance")
87100
@Disabled
@@ -90,19 +103,30 @@ void updateExpectedXmlReportFiles(TestCase testCase) throws IOException {
90103
writeJunitXmlReport(testCase, out);
91104
}
92105
}
93-
94-
private static <T extends OutputStream> T writeJunitXmlReport(TestCase testCase, T out) throws IOException {
106+
107+
private static <T extends OutputStream> T writeJunitXmlReport(TestCase testCase, T out, Consumer<List<Envelope>> orderer) throws IOException {
108+
List<Envelope> messages = new ArrayList<>();
95109
try (InputStream in = Files.newInputStream(testCase.source)) {
96110
try (NdjsonToMessageIterable envelopes = new NdjsonToMessageIterable(in, deserializer)) {
97-
try (MessagesToJunitXmlWriter writer = new MessagesToJunitXmlWriter(out)) {
98-
for (Envelope envelope : envelopes) {
99-
writer.write(envelope);
100-
}
111+
for (Envelope envelope : envelopes) {
112+
messages.add(envelope);
101113
}
102114
}
103115
}
116+
orderer.accept(messages);
117+
118+
try (MessagesToJunitXmlWriter writer = new MessagesToJunitXmlWriter(out)) {
119+
for (Envelope envelope : messages) {
120+
writer.write(envelope);
121+
}
122+
}
123+
104124
return out;
105125
}
126+
127+
private static <T extends OutputStream> T writeJunitXmlReport(TestCase testCase, T out) throws IOException {
128+
return writeJunitXmlReport(testCase, out, MessageOrderer.originalOrder());
129+
}
106130

107131
static class TestCase {
108132
private final Path source;

0 commit comments

Comments
 (0)