Skip to content

Commit 1730fe7

Browse files
Use @testfactory's execution mode for dynamic tests/containers
Co-authored-by: Leonard Brünings <[email protected]>
1 parent d82a791 commit 1730fe7

File tree

4 files changed

+142
-14
lines changed

4 files changed

+142
-14
lines changed

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicNodeTestDescriptor.java

+8
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@
1010

1111
package org.junit.jupiter.engine.descriptor;
1212

13+
import static org.junit.platform.commons.annotation.ExecutionMode.Concurrent;
14+
1315
import org.junit.jupiter.api.DynamicNode;
1416
import org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;
17+
import org.junit.platform.commons.annotation.ExecutionMode;
1518
import org.junit.platform.engine.TestDescriptor;
1619
import org.junit.platform.engine.TestSource;
1720
import org.junit.platform.engine.UniqueId;
@@ -30,6 +33,11 @@ abstract class DynamicNodeTestDescriptor extends JupiterTestDescriptor {
3033
this.index = index;
3134
}
3235

36+
@Override
37+
public ExecutionMode getExecutionMode() {
38+
return getParent().map(parent -> ((JupiterTestDescriptor) parent).getExecutionMode()).orElse(Concurrent);
39+
}
40+
3341
@Override
3442
public String getLegacyReportingName() {
3543
// @formatter:off

junit-platform-engine/src/test/java/org/junit/platform/engine/test/event/ExecutionEvent.java

+8
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import static org.junit.platform.engine.test.event.ExecutionEvent.Type.SKIPPED;
1919
import static org.junit.platform.engine.test.event.ExecutionEvent.Type.STARTED;
2020

21+
import java.time.Instant;
2122
import java.util.Optional;
2223
import java.util.function.Predicate;
2324

@@ -70,16 +71,22 @@ public static <T> Predicate<ExecutionEvent> byPayload(Class<T> payloadClass, Pre
7071
return event -> event.getPayload(payloadClass).filter(predicate).isPresent();
7172
}
7273

74+
private final Instant timestamp;
7375
private final ExecutionEvent.Type type;
7476
private final TestDescriptor testDescriptor;
7577
private final Object payload;
7678

7779
private ExecutionEvent(ExecutionEvent.Type type, TestDescriptor testDescriptor, Object payload) {
80+
this.timestamp = Instant.now();
7881
this.type = type;
7982
this.testDescriptor = testDescriptor;
8083
this.payload = payload;
8184
}
8285

86+
public Instant getTimestamp() {
87+
return timestamp;
88+
}
89+
8390
public ExecutionEvent.Type getType() {
8491
return type;
8592
}
@@ -97,6 +104,7 @@ public String toString() {
97104
// @formatter:off
98105
return new ToStringBuilder(this)
99106
.append("type", type)
107+
.append("timestamp", timestamp)
100108
.append("testDescriptor", testDescriptor)
101109
.append("payload", payload)
102110
.toString();
+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import org.junit.platform.engine.UniqueId;
2525
import org.junit.platform.launcher.LauncherDiscoveryRequest;
2626

27-
class TreeWalkerIntegrationTests {
27+
class NodeWalkerIntegrationTests {
2828

2929
@Test
3030
void pullUpExclusiveChildResourcesToTestClass() {

platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ParallelExecutionTests.java

+125-13
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,37 @@
1111
package org.junit.platform.engine.support.hierarchical;
1212

1313
import static java.util.concurrent.TimeUnit.SECONDS;
14+
import static java.util.stream.Collectors.toList;
1415
import static org.assertj.core.api.Assertions.assertThat;
1516
import static org.junit.jupiter.api.Assertions.assertEquals;
17+
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
1618
import static org.junit.jupiter.engine.Constants.PARALLEL_EXECUTION_ENABLED;
1719
import static org.junit.jupiter.engine.Constants.PARALLEL_EXECUTION_LEVEL;
1820
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
1921
import static org.junit.platform.engine.test.event.ExecutionEvent.Type.REPORTING_ENTRY_PUBLISHED;
2022
import static org.junit.platform.engine.test.event.ExecutionEventConditions.event;
2123
import static org.junit.platform.engine.test.event.ExecutionEventConditions.finishedSuccessfully;
2224
import static org.junit.platform.engine.test.event.ExecutionEventConditions.finishedWithFailure;
25+
import static org.junit.platform.engine.test.event.ExecutionEventConditions.started;
2326
import static org.junit.platform.engine.test.event.ExecutionEventConditions.test;
2427
import static org.junit.platform.engine.test.event.ExecutionEventConditions.type;
2528
import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;
2629

30+
import java.time.Instant;
2731
import java.util.List;
2832
import java.util.concurrent.CountDownLatch;
2933
import java.util.concurrent.atomic.AtomicInteger;
34+
import java.util.stream.IntStream;
3035
import java.util.stream.Stream;
3136

37+
import org.assertj.core.api.Condition;
38+
import org.junit.jupiter.api.BeforeAll;
39+
import org.junit.jupiter.api.DynamicTest;
3240
import org.junit.jupiter.api.Test;
41+
import org.junit.jupiter.api.TestFactory;
3342
import org.junit.jupiter.api.TestReporter;
3443
import org.junit.jupiter.engine.JupiterTestEngine;
44+
import org.junit.platform.commons.annotation.SameThreadExecution;
3545
import org.junit.platform.commons.annotation.UseResource;
3646
import org.junit.platform.engine.reporting.ReportEntry;
3747
import org.junit.platform.engine.test.event.ExecutionEvent;
@@ -40,6 +50,22 @@
4050

4151
class ParallelExecutionTests {
4252

53+
@Test
54+
void successfulParallelTest(TestReporter reporter) {
55+
List<ExecutionEvent> executionEvents = execute(SuccessfulParallelTestCase.class);
56+
57+
List<Instant> startedTimestamps = getTimestampsFor(executionEvents, event(test(), started()));
58+
List<Instant> finishedTimestamps = getTimestampsFor(executionEvents, event(test(), finishedSuccessfully()));
59+
reporter.publishEntry("startedTimestamps", startedTimestamps.toString());
60+
reporter.publishEntry("finishedTimestamps", finishedTimestamps.toString());
61+
62+
assertThat(startedTimestamps).hasSize(3);
63+
assertThat(finishedTimestamps).hasSize(3);
64+
assertThat(startedTimestamps).allMatch(startTimestamp -> finishedTimestamps.stream().allMatch(
65+
finishedTimestamp -> finishedTimestamp.isAfter(startTimestamp)));
66+
assertThat(getThreadNames(executionEvents)).hasSize(3);
67+
}
68+
4369
@Test
4470
void failingTestWithoutLock() {
4571
List<ExecutionEvent> executionEvents = execute(FailingTestWithoutLock.class);
@@ -49,29 +75,85 @@ void failingTestWithoutLock() {
4975
@Test
5076
void successfulTestWithMethodLock() {
5177
List<ExecutionEvent> executionEvents = execute(SuccessfulTestWithMethodLock.class);
52-
// TODO check concurrent execution
78+
5379
assertThat(executionEvents.stream().filter(event(test(), finishedSuccessfully())::matches)).hasSize(3);
5480
assertThat(getThreadNames(executionEvents)).hasSize(3);
5581
}
5682

5783
@Test
5884
void successfulTestWithClassLock() {
5985
List<ExecutionEvent> executionEvents = execute(SuccessfulTestWithClassLock.class);
60-
// TODO check sequential execution
86+
87+
assertThat(executionEvents.stream().filter(event(test(), finishedSuccessfully())::matches)).hasSize(3);
88+
assertThat(getThreadNames(executionEvents)).hasSize(1);
89+
}
90+
91+
@Test
92+
void testCaseWithFactory() {
93+
List<ExecutionEvent> executionEvents = execute(TestCaseWithTestFactory.class);
94+
6195
assertThat(executionEvents.stream().filter(event(test(), finishedSuccessfully())::matches)).hasSize(3);
6296
assertThat(getThreadNames(executionEvents)).hasSize(1);
6397
}
6498

99+
private List<Instant> getTimestampsFor(List<ExecutionEvent> executionEvents, Condition<ExecutionEvent> condition) {
100+
// @formatter:off
101+
return executionEvents.stream()
102+
.filter(condition::matches)
103+
.map(ExecutionEvent::getTimestamp)
104+
.collect(toList());
105+
// @formatter:on
106+
}
107+
65108
private Stream<String> getThreadNames(List<ExecutionEvent> executionEvents) {
66-
return executionEvents.stream().filter(type(REPORTING_ENTRY_PUBLISHED)::matches).map(
67-
event -> event.getPayload(ReportEntry.class).orElse(null)).map(ReportEntry::getKeyValuePairs).map(
68-
keyValuePairs -> keyValuePairs.get("thread")).distinct();
109+
// @formatter:off
110+
return executionEvents.stream()
111+
.filter(type(REPORTING_ENTRY_PUBLISHED)::matches)
112+
.map(event -> event.getPayload(ReportEntry.class).orElse(null))
113+
.map(ReportEntry::getKeyValuePairs)
114+
.filter(keyValuePairs -> keyValuePairs.containsKey("thread"))
115+
.map(keyValuePairs -> keyValuePairs.get("thread"))
116+
.distinct();
117+
// @formatter:on
118+
}
119+
120+
static class SuccessfulParallelTestCase {
121+
122+
static AtomicInteger sharedResource;
123+
static CountDownLatch countDownLatch;
124+
125+
@BeforeAll
126+
static void initialize() {
127+
sharedResource = new AtomicInteger();
128+
countDownLatch = new CountDownLatch(3);
129+
}
130+
131+
@Test
132+
void firstTest(TestReporter reporter) throws Exception {
133+
incrementAndBlock(sharedResource, countDownLatch, reporter);
134+
}
135+
136+
@Test
137+
void secondTest(TestReporter reporter) throws Exception {
138+
incrementAndBlock(sharedResource, countDownLatch, reporter);
139+
}
140+
141+
@Test
142+
void thirdTest(TestReporter reporter) throws Exception {
143+
incrementAndBlock(sharedResource, countDownLatch, reporter);
144+
}
69145
}
70146

71147
static class FailingTestWithoutLock {
72148

73-
static AtomicInteger sharedResource = new AtomicInteger();
74-
static CountDownLatch countDownLatch = new CountDownLatch(3);
149+
static AtomicInteger sharedResource;
150+
static CountDownLatch countDownLatch;
151+
152+
@BeforeAll
153+
static void initialize() {
154+
sharedResource = new AtomicInteger();
155+
countDownLatch = new CountDownLatch(3);
156+
}
75157

76158
@Test
77159
void firstTest(TestReporter reporter) throws Exception {
@@ -91,8 +173,14 @@ void thirdTest(TestReporter reporter) throws Exception {
91173

92174
static class SuccessfulTestWithMethodLock {
93175

94-
static AtomicInteger sharedResource = new AtomicInteger();
95-
static CountDownLatch countDownLatch = new CountDownLatch(3);
176+
static AtomicInteger sharedResource;
177+
static CountDownLatch countDownLatch;
178+
179+
@BeforeAll
180+
static void initialize() {
181+
sharedResource = new AtomicInteger();
182+
countDownLatch = new CountDownLatch(3);
183+
}
96184

97185
@Test
98186
@UseResource("sharedResource")
@@ -116,8 +204,14 @@ void thirdTest(TestReporter reporter) throws Exception {
116204
@UseResource("sharedResource")
117205
static class SuccessfulTestWithClassLock {
118206

119-
static AtomicInteger sharedResource = new AtomicInteger();
120-
static CountDownLatch countDownLatch = new CountDownLatch(3);
207+
static AtomicInteger sharedResource;
208+
static CountDownLatch countDownLatch;
209+
210+
@BeforeAll
211+
static void initialize() {
212+
sharedResource = new AtomicInteger();
213+
countDownLatch = new CountDownLatch(3);
214+
}
121215

122216
@Test
123217
void firstTest(TestReporter reporter) throws Exception {
@@ -135,21 +229,39 @@ void thirdTest(TestReporter reporter) throws Exception {
135229
}
136230
}
137231

232+
static class TestCaseWithTestFactory {
233+
@TestFactory
234+
@SameThreadExecution
235+
Stream<DynamicTest> testFactory(TestReporter testReporter) {
236+
AtomicInteger sharedResource = new AtomicInteger(0);
237+
CountDownLatch countDownLatch = new CountDownLatch(3);
238+
return IntStream.range(0, 3).mapToObj(i -> dynamicTest("test " + i, () -> {
239+
incrementBlockAndCheck(sharedResource, countDownLatch, testReporter);
240+
}));
241+
}
242+
}
243+
138244
private static void incrementBlockAndCheck(AtomicInteger sharedResource, CountDownLatch countDownLatch,
139245
TestReporter reporter) throws InterruptedException {
246+
int value = incrementAndBlock(sharedResource, countDownLatch, reporter);
247+
assertEquals(value, sharedResource.get());
248+
}
249+
250+
private static int incrementAndBlock(AtomicInteger sharedResource, CountDownLatch countDownLatch,
251+
TestReporter reporter) throws InterruptedException {
140252
reporter.publishEntry("thread", Thread.currentThread().getName());
141253
int value = sharedResource.incrementAndGet();
142254
countDownLatch.countDown();
143255
countDownLatch.await(1, SECONDS);
144-
assertEquals(value, sharedResource.get());
256+
return value;
145257
}
146258

147259
private List<ExecutionEvent> execute(Class<?> testClass) {
148260
// @formatter:off
149261
LauncherDiscoveryRequest discoveryRequest = request()
150262
.selectors(selectClass(testClass))
151263
.configurationParameter(PARALLEL_EXECUTION_ENABLED, String.valueOf(true))
152-
.configurationParameter(PARALLEL_EXECUTION_LEVEL, String.valueOf(4))
264+
.configurationParameter(PARALLEL_EXECUTION_LEVEL, String.valueOf(3))
153265
.build();
154266
// @formatter:on
155267
return ExecutionEventRecorder.execute(new JupiterTestEngine(), discoveryRequest);

0 commit comments

Comments
 (0)