18
18
import static org .junit .jupiter .api .DynamicTest .dynamicTest ;
19
19
import static org .junit .jupiter .api .parallel .ExecutionMode .CONCURRENT ;
20
20
import static org .junit .jupiter .api .parallel .ExecutionMode .SAME_THREAD ;
21
+ import static org .junit .jupiter .api .parallel .ResourceAccessMode .READ_WRITE ;
21
22
import static org .junit .jupiter .engine .Constants .DEFAULT_CLASSES_EXECUTION_MODE_PROPERTY_NAME ;
22
23
import static org .junit .jupiter .engine .Constants .DEFAULT_PARALLEL_EXECUTION_MODE ;
23
24
import static org .junit .jupiter .engine .Constants .PARALLEL_CONFIG_FIXED_MAX_POOL_SIZE_PROPERTY_NAME ;
24
25
import static org .junit .jupiter .engine .Constants .PARALLEL_CONFIG_FIXED_PARALLELISM_PROPERTY_NAME ;
25
26
import static org .junit .jupiter .engine .Constants .PARALLEL_CONFIG_STRATEGY_PROPERTY_NAME ;
26
27
import static org .junit .jupiter .engine .Constants .PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME ;
27
28
import static org .junit .platform .commons .util .CollectionUtils .getOnlyElement ;
29
+ import static org .junit .platform .engine .support .hierarchical .ExclusiveResource .GLOBAL_KEY ;
28
30
import static org .junit .platform .testkit .engine .EventConditions .container ;
29
31
import static org .junit .platform .testkit .engine .EventConditions .event ;
30
32
import static org .junit .platform .testkit .engine .EventConditions .finishedSuccessfully ;
65
67
import org .junit .jupiter .api .parallel .Execution ;
66
68
import org .junit .jupiter .api .parallel .Isolated ;
67
69
import org .junit .jupiter .api .parallel .ResourceLock ;
70
+ import org .junit .jupiter .params .ParameterizedTest ;
71
+ import org .junit .jupiter .params .provider .ValueSource ;
68
72
import org .junit .platform .engine .TestDescriptor ;
69
73
import org .junit .platform .engine .discovery .ClassSelector ;
70
74
import org .junit .platform .engine .discovery .DiscoverySelectors ;
73
77
import org .junit .platform .testkit .engine .EngineExecutionResults ;
74
78
import org .junit .platform .testkit .engine .EngineTestKit ;
75
79
import org .junit .platform .testkit .engine .Event ;
80
+ import org .junit .platform .testkit .engine .Events ;
76
81
77
82
/**
78
83
* @since 1.3
@@ -82,7 +87,7 @@ class ParallelExecutionIntegrationTests {
82
87
83
88
@ Test
84
89
void successfulParallelTest (TestReporter reporter ) {
85
- var events = executeConcurrently (3 , SuccessfulParallelTestCase .class );
90
+ var events = executeConcurrentlySuccessfully (3 , SuccessfulParallelTestCase .class ). list ( );
86
91
87
92
var startedTimestamps = getTimestampsFor (events , event (test (), started ()));
88
93
var finishedTimestamps = getTimestampsFor (events , event (test (), finishedSuccessfully ()));
@@ -98,29 +103,29 @@ void successfulParallelTest(TestReporter reporter) {
98
103
99
104
@ Test
100
105
void failingTestWithoutLock () {
101
- var events = executeConcurrently (3 , FailingWithoutLockTestCase .class );
106
+ var events = executeConcurrently (3 , FailingWithoutLockTestCase .class ). list () ;
102
107
assertThat (events .stream ().filter (event (test (), finishedWithFailure ())::matches )).hasSize (2 );
103
108
}
104
109
105
110
@ Test
106
111
void successfulTestWithMethodLock () {
107
- var events = executeConcurrently (3 , SuccessfulWithMethodLockTestCase .class );
112
+ var events = executeConcurrentlySuccessfully (3 , SuccessfulWithMethodLockTestCase .class ). list ( );
108
113
109
114
assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (3 );
110
115
assertThat (ThreadReporter .getThreadNames (events )).hasSize (3 );
111
116
}
112
117
113
118
@ Test
114
119
void successfulTestWithClassLock () {
115
- var events = executeConcurrently (3 , SuccessfulWithClassLockTestCase .class );
120
+ var events = executeConcurrentlySuccessfully (3 , SuccessfulWithClassLockTestCase .class ). list ( );
116
121
117
122
assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (3 );
118
123
assertThat (ThreadReporter .getThreadNames (events )).hasSize (1 );
119
124
}
120
125
121
126
@ Test
122
127
void testCaseWithFactory () {
123
- var events = executeConcurrently (3 , TestCaseWithTestFactory .class );
128
+ var events = executeConcurrentlySuccessfully (3 , TestCaseWithTestFactory .class ). list ( );
124
129
125
130
assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (3 );
126
131
assertThat (ThreadReporter .getThreadNames (events )).hasSize (1 );
@@ -133,7 +138,7 @@ void customContextClassLoader() {
133
138
var smilingLoader = new URLClassLoader ("(-:" , new URL [0 ], ClassLoader .getSystemClassLoader ());
134
139
currentThread .setContextClassLoader (smilingLoader );
135
140
try {
136
- var events = executeConcurrently (3 , SuccessfulWithMethodLockTestCase .class );
141
+ var events = executeConcurrentlySuccessfully (3 , SuccessfulWithMethodLockTestCase .class ). list ( );
137
142
138
143
assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (3 );
139
144
assertThat (ThreadReporter .getThreadNames (events )).hasSize (3 );
@@ -146,23 +151,24 @@ void customContextClassLoader() {
146
151
147
152
@ RepeatedTest (10 )
148
153
void mixingClassAndMethodLevelLocks () {
149
- var events = executeConcurrently (4 , TestCaseWithSortedLocks .class , TestCaseWithUnsortedLocks .class );
154
+ var events = executeConcurrentlySuccessfully (4 , TestCaseWithSortedLocks .class ,
155
+ TestCaseWithUnsortedLocks .class ).list ();
150
156
151
157
assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (6 );
152
158
assertThat (ThreadReporter .getThreadNames (events ).count ()).isLessThanOrEqualTo (2 );
153
159
}
154
160
155
161
@ RepeatedTest (10 )
156
162
void locksOnNestedTests () {
157
- var events = executeConcurrently (3 , TestCaseWithNestedLocks .class );
163
+ var events = executeConcurrentlySuccessfully (3 , TestCaseWithNestedLocks .class ). list ( );
158
164
159
165
assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (6 );
160
166
assertThat (ThreadReporter .getThreadNames (events )).hasSize (1 );
161
167
}
162
168
163
169
@ Test
164
170
void afterHooksAreCalledAfterConcurrentDynamicTestsAreFinished () {
165
- var events = executeConcurrently (3 , ConcurrentDynamicTestCase .class );
171
+ var events = executeConcurrentlySuccessfully (3 , ConcurrentDynamicTestCase .class ). list ( );
166
172
167
173
assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (1 );
168
174
var timestampedEvents = ConcurrentDynamicTestCase .events ;
@@ -175,14 +181,14 @@ void afterHooksAreCalledAfterConcurrentDynamicTestsAreFinished() {
175
181
*/
176
182
@ Test
177
183
void threadInterruptedByUserCode () {
178
- var events = executeConcurrently (3 , InterruptedThreadTestCase .class );
184
+ var events = executeConcurrentlySuccessfully (3 , InterruptedThreadTestCase .class ). list ( );
179
185
180
186
assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (4 );
181
187
}
182
188
183
189
@ Test
184
190
void executesTestTemplatesWithResourceLocksInSameThread () {
185
- var events = executeConcurrently (2 , ConcurrentTemplateTestCase .class );
191
+ var events = executeConcurrentlySuccessfully (2 , ConcurrentTemplateTestCase .class ). list ( );
186
192
187
193
assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (10 );
188
194
assertThat (ThreadReporter .getThreadNames (events )).hasSize (1 );
@@ -228,30 +234,22 @@ void executesMethodsInParallelIfEnabledViaConfigurationParameter() {
228
234
229
235
@ Test
230
236
void canRunTestsIsolatedFromEachOther () {
231
- var events = executeConcurrently (2 , IsolatedTestCase .class );
232
-
233
- assertThat (events .stream ().filter (event (test (), finishedWithFailure ())::matches )).isEmpty ();
237
+ executeConcurrentlySuccessfully (2 , IsolatedTestCase .class );
234
238
}
235
239
236
240
@ Test
237
241
void canRunTestsIsolatedFromEachOtherWithNestedCases () {
238
- var events = executeConcurrently (4 , NestedIsolatedTestCase .class );
239
-
240
- assertThat (events .stream ().filter (event (test (), finishedWithFailure ())::matches )).isEmpty ();
242
+ executeConcurrentlySuccessfully (4 , NestedIsolatedTestCase .class );
241
243
}
242
244
243
245
@ Test
244
246
void canRunTestsIsolatedFromEachOtherAcrossClasses () {
245
- var events = executeConcurrently (4 , IndependentClasses .A .class , IndependentClasses .B .class );
246
-
247
- assertThat (events .stream ().filter (event (test (), finishedWithFailure ())::matches )).isEmpty ();
247
+ executeConcurrentlySuccessfully (4 , IndependentClasses .A .class , IndependentClasses .B .class );
248
248
}
249
249
250
250
@ RepeatedTest (10 )
251
251
void canRunTestsIsolatedFromEachOtherAcrossClassesWithOtherResourceLocks () {
252
- var events = executeConcurrently (4 , IndependentClasses .B .class , IndependentClasses .C .class );
253
-
254
- assertThat (events .stream ().filter (event (test (), finishedWithFailure ())::matches )).isEmpty ();
252
+ executeConcurrentlySuccessfully (4 , IndependentClasses .B .class , IndependentClasses .C .class );
255
253
}
256
254
257
255
@ Test
@@ -262,9 +260,8 @@ void runsIsolatedTestsLastToMaximizeParallelism() {
262
260
);
263
261
Class <?>[] testClasses = { IsolatedTestCase .class , SuccessfulParallelTestCase .class };
264
262
var events = executeWithFixedParallelism (3 , configParams , testClasses ) //
265
- .allEvents ();
266
-
267
- assertThat (events .stream ().filter (event (test (), finishedWithFailure ())::matches )).isEmpty ();
263
+ .allEvents () //
264
+ .assertStatistics (it -> it .failed (0 ));
268
265
269
266
List <Event > parallelTestMethodEvents = events .reportingEntryPublished () //
270
267
.filter (e -> e .getTestDescriptor ().getSource () //
@@ -283,6 +280,15 @@ void runsIsolatedTestsLastToMaximizeParallelism() {
283
280
assertThat (isolatedClassStart ).isAfterOrEqualTo (parallelClassFinish );
284
281
}
285
282
283
+ @ ParameterizedTest
284
+ @ ValueSource (classes = { IsolatedMethodFirstTestCase .class , IsolatedMethodLastTestCase .class ,
285
+ IsolatedNestedMethodFirstTestCase .class , IsolatedNestedMethodLastTestCase .class })
286
+ void canRunTestsIsolatedFromEachOtherWhenDeclaredOnMethodLevel (Class <?> testClass ) {
287
+ List <Event > events = executeConcurrentlySuccessfully (1 , testClass ).list ();
288
+
289
+ assertThat (ThreadReporter .getThreadNames (events )).hasSize (1 );
290
+ }
291
+
286
292
@ Isolated ("testing" )
287
293
static class IsolatedTestCase {
288
294
static AtomicInteger sharedResource ;
@@ -355,6 +361,122 @@ void b() throws Exception {
355
361
}
356
362
}
357
363
364
+ @ ExtendWith (ThreadReporter .class )
365
+ static class IsolatedMethodFirstTestCase {
366
+
367
+ static AtomicInteger sharedResource ;
368
+ static CountDownLatch countDownLatch ;
369
+
370
+ @ BeforeAll
371
+ static void initialize () {
372
+ sharedResource = new AtomicInteger ();
373
+ countDownLatch = new CountDownLatch (2 );
374
+ }
375
+
376
+ @ Test
377
+ @ ResourceLock (value = GLOBAL_KEY , mode = READ_WRITE ) // effectively @Isolated
378
+ void test1 () throws InterruptedException {
379
+ incrementBlockAndCheck (sharedResource , countDownLatch );
380
+ }
381
+
382
+ @ Test
383
+ @ ResourceLock (value = "b" , mode = READ_WRITE )
384
+ void test2 () throws InterruptedException {
385
+ incrementBlockAndCheck (sharedResource , countDownLatch );
386
+ }
387
+ }
388
+
389
+ @ ExtendWith (ThreadReporter .class )
390
+ static class IsolatedMethodLastTestCase {
391
+
392
+ static AtomicInteger sharedResource ;
393
+ static CountDownLatch countDownLatch ;
394
+
395
+ @ BeforeAll
396
+ static void initialize () {
397
+ sharedResource = new AtomicInteger ();
398
+ countDownLatch = new CountDownLatch (2 );
399
+ }
400
+
401
+ @ Test
402
+ @ ResourceLock (value = "b" , mode = READ_WRITE )
403
+ void test1 () throws InterruptedException {
404
+ incrementBlockAndCheck (sharedResource , countDownLatch );
405
+ }
406
+
407
+ @ Test
408
+ @ ResourceLock (value = GLOBAL_KEY , mode = READ_WRITE ) // effectively @Isolated
409
+ void test2 () throws InterruptedException {
410
+ incrementBlockAndCheck (sharedResource , countDownLatch );
411
+ }
412
+ }
413
+
414
+ @ ExtendWith (ThreadReporter .class )
415
+ static class IsolatedNestedMethodFirstTestCase {
416
+
417
+ static AtomicInteger sharedResource ;
418
+ static CountDownLatch countDownLatch ;
419
+
420
+ @ BeforeAll
421
+ static void initialize () {
422
+ sharedResource = new AtomicInteger ();
423
+ countDownLatch = new CountDownLatch (2 );
424
+ }
425
+
426
+ @ Nested
427
+ class Test1 {
428
+
429
+ @ Test
430
+ @ ResourceLock (value = GLOBAL_KEY , mode = READ_WRITE ) // effectively @Isolated
431
+ void test1 () throws InterruptedException {
432
+ incrementBlockAndCheck (sharedResource , countDownLatch );
433
+ }
434
+ }
435
+
436
+ @ Nested
437
+ class Test2 {
438
+
439
+ @ Test
440
+ @ ResourceLock (value = "b" , mode = READ_WRITE )
441
+ void test2 () throws InterruptedException {
442
+ incrementBlockAndCheck (sharedResource , countDownLatch );
443
+ }
444
+ }
445
+ }
446
+
447
+ @ ExtendWith (ThreadReporter .class )
448
+ static class IsolatedNestedMethodLastTestCase {
449
+
450
+ static AtomicInteger sharedResource ;
451
+ static CountDownLatch countDownLatch ;
452
+
453
+ @ BeforeAll
454
+ static void initialize () {
455
+ sharedResource = new AtomicInteger ();
456
+ countDownLatch = new CountDownLatch (2 );
457
+ }
458
+
459
+ @ Nested
460
+ class Test1 {
461
+
462
+ @ Test
463
+ @ ResourceLock (value = "b" , mode = READ_WRITE )
464
+ void test1 () throws InterruptedException {
465
+ incrementBlockAndCheck (sharedResource , countDownLatch );
466
+ }
467
+ }
468
+
469
+ @ Nested
470
+ class Test2 {
471
+
472
+ @ Test
473
+ @ ResourceLock (value = GLOBAL_KEY , mode = READ_WRITE ) // effectively @Isolated
474
+ void test2 () throws InterruptedException {
475
+ incrementBlockAndCheck (sharedResource , countDownLatch );
476
+ }
477
+ }
478
+ }
479
+
358
480
static class IndependentClasses {
359
481
static AtomicInteger sharedResource = new AtomicInteger ();
360
482
static CountDownLatch countDownLatch = new CountDownLatch (4 );
@@ -416,11 +538,21 @@ private List<Instant> getTimestampsFor(List<Event> events, Condition<Event> cond
416
538
// @formatter:on
417
539
}
418
540
419
- private List <Event > executeConcurrently (int parallelism , Class <?>... testClasses ) {
541
+ private Events executeConcurrentlySuccessfully (int parallelism , Class <?>... testClasses ) {
542
+ var events = executeConcurrently (parallelism , testClasses );
543
+ try {
544
+ return events .assertStatistics (it -> it .failed (0 ));
545
+ }
546
+ catch (AssertionError error ) {
547
+ events .debug ();
548
+ throw error ;
549
+ }
550
+ }
551
+
552
+ private Events executeConcurrently (int parallelism , Class <?>... testClasses ) {
420
553
Map <String , String > configParams = Map .of (DEFAULT_PARALLEL_EXECUTION_MODE , "concurrent" );
421
554
return executeWithFixedParallelism (parallelism , configParams , testClasses ) //
422
- .allEvents () //
423
- .list ();
555
+ .allEvents ();
424
556
}
425
557
426
558
private EngineExecutionResults executeWithFixedParallelism (int parallelism , Map <String , String > configParams ,
0 commit comments