11
11
package org .junit .platform .engine .support .hierarchical ;
12
12
13
13
import static java .util .concurrent .TimeUnit .SECONDS ;
14
+ import static java .util .stream .Collectors .toList ;
14
15
import static org .assertj .core .api .Assertions .assertThat ;
15
16
import static org .junit .jupiter .api .Assertions .assertEquals ;
17
+ import static org .junit .jupiter .api .DynamicTest .dynamicTest ;
16
18
import static org .junit .jupiter .engine .Constants .PARALLEL_EXECUTION_ENABLED ;
17
19
import static org .junit .jupiter .engine .Constants .PARALLEL_EXECUTION_LEVEL ;
18
20
import static org .junit .platform .engine .discovery .DiscoverySelectors .selectClass ;
19
21
import static org .junit .platform .engine .test .event .ExecutionEvent .Type .REPORTING_ENTRY_PUBLISHED ;
20
22
import static org .junit .platform .engine .test .event .ExecutionEventConditions .event ;
21
23
import static org .junit .platform .engine .test .event .ExecutionEventConditions .finishedSuccessfully ;
22
24
import static org .junit .platform .engine .test .event .ExecutionEventConditions .finishedWithFailure ;
25
+ import static org .junit .platform .engine .test .event .ExecutionEventConditions .started ;
23
26
import static org .junit .platform .engine .test .event .ExecutionEventConditions .test ;
24
27
import static org .junit .platform .engine .test .event .ExecutionEventConditions .type ;
25
28
import static org .junit .platform .launcher .core .LauncherDiscoveryRequestBuilder .request ;
26
29
30
+ import java .time .Instant ;
27
31
import java .util .List ;
28
32
import java .util .concurrent .CountDownLatch ;
29
33
import java .util .concurrent .atomic .AtomicInteger ;
34
+ import java .util .stream .IntStream ;
30
35
import java .util .stream .Stream ;
31
36
37
+ import org .assertj .core .api .Condition ;
38
+ import org .junit .jupiter .api .BeforeAll ;
39
+ import org .junit .jupiter .api .DynamicTest ;
32
40
import org .junit .jupiter .api .Test ;
41
+ import org .junit .jupiter .api .TestFactory ;
33
42
import org .junit .jupiter .api .TestReporter ;
34
43
import org .junit .jupiter .engine .JupiterTestEngine ;
44
+ import org .junit .platform .commons .annotation .SameThreadExecution ;
35
45
import org .junit .platform .commons .annotation .UseResource ;
36
46
import org .junit .platform .engine .reporting .ReportEntry ;
37
47
import org .junit .platform .engine .test .event .ExecutionEvent ;
40
50
41
51
class ParallelExecutionTests {
42
52
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
+
43
69
@ Test
44
70
void failingTestWithoutLock () {
45
71
List <ExecutionEvent > executionEvents = execute (FailingTestWithoutLock .class );
@@ -49,29 +75,85 @@ void failingTestWithoutLock() {
49
75
@ Test
50
76
void successfulTestWithMethodLock () {
51
77
List <ExecutionEvent > executionEvents = execute (SuccessfulTestWithMethodLock .class );
52
- // TODO check concurrent execution
78
+
53
79
assertThat (executionEvents .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (3 );
54
80
assertThat (getThreadNames (executionEvents )).hasSize (3 );
55
81
}
56
82
57
83
@ Test
58
84
void successfulTestWithClassLock () {
59
85
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
+
61
95
assertThat (executionEvents .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (3 );
62
96
assertThat (getThreadNames (executionEvents )).hasSize (1 );
63
97
}
64
98
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
+
65
108
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
+ }
69
145
}
70
146
71
147
static class FailingTestWithoutLock {
72
148
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
+ }
75
157
76
158
@ Test
77
159
void firstTest (TestReporter reporter ) throws Exception {
@@ -91,8 +173,14 @@ void thirdTest(TestReporter reporter) throws Exception {
91
173
92
174
static class SuccessfulTestWithMethodLock {
93
175
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
+ }
96
184
97
185
@ Test
98
186
@ UseResource ("sharedResource" )
@@ -116,8 +204,14 @@ void thirdTest(TestReporter reporter) throws Exception {
116
204
@ UseResource ("sharedResource" )
117
205
static class SuccessfulTestWithClassLock {
118
206
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
+ }
121
215
122
216
@ Test
123
217
void firstTest (TestReporter reporter ) throws Exception {
@@ -135,21 +229,39 @@ void thirdTest(TestReporter reporter) throws Exception {
135
229
}
136
230
}
137
231
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
+
138
244
private static void incrementBlockAndCheck (AtomicInteger sharedResource , CountDownLatch countDownLatch ,
139
245
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 {
140
252
reporter .publishEntry ("thread" , Thread .currentThread ().getName ());
141
253
int value = sharedResource .incrementAndGet ();
142
254
countDownLatch .countDown ();
143
255
countDownLatch .await (1 , SECONDS );
144
- assertEquals ( value , sharedResource . get ()) ;
256
+ return value ;
145
257
}
146
258
147
259
private List <ExecutionEvent > execute (Class <?> testClass ) {
148
260
// @formatter:off
149
261
LauncherDiscoveryRequest discoveryRequest = request ()
150
262
.selectors (selectClass (testClass ))
151
263
.configurationParameter (PARALLEL_EXECUTION_ENABLED , String .valueOf (true ))
152
- .configurationParameter (PARALLEL_EXECUTION_LEVEL , String .valueOf (4 ))
264
+ .configurationParameter (PARALLEL_EXECUTION_LEVEL , String .valueOf (3 ))
153
265
.build ();
154
266
// @formatter:on
155
267
return ExecutionEventRecorder .execute (new JupiterTestEngine (), discoveryRequest );
0 commit comments