1818import static org .junit .jupiter .api .DynamicTest .dynamicTest ;
1919import static org .junit .jupiter .api .parallel .ExecutionMode .CONCURRENT ;
2020import static org .junit .jupiter .api .parallel .ExecutionMode .SAME_THREAD ;
21+ import static org .junit .jupiter .api .parallel .ResourceAccessMode .READ_WRITE ;
2122import static org .junit .jupiter .engine .Constants .DEFAULT_CLASSES_EXECUTION_MODE_PROPERTY_NAME ;
2223import static org .junit .jupiter .engine .Constants .DEFAULT_PARALLEL_EXECUTION_MODE ;
2324import static org .junit .jupiter .engine .Constants .PARALLEL_CONFIG_FIXED_MAX_POOL_SIZE_PROPERTY_NAME ;
2425import static org .junit .jupiter .engine .Constants .PARALLEL_CONFIG_FIXED_PARALLELISM_PROPERTY_NAME ;
2526import static org .junit .jupiter .engine .Constants .PARALLEL_CONFIG_STRATEGY_PROPERTY_NAME ;
2627import static org .junit .jupiter .engine .Constants .PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME ;
2728import static org .junit .platform .commons .util .CollectionUtils .getOnlyElement ;
29+ import static org .junit .platform .engine .support .hierarchical .ExclusiveResource .GLOBAL_KEY ;
2830import static org .junit .platform .testkit .engine .EventConditions .container ;
2931import static org .junit .platform .testkit .engine .EventConditions .event ;
3032import static org .junit .platform .testkit .engine .EventConditions .finishedSuccessfully ;
6567import org .junit .jupiter .api .parallel .Execution ;
6668import org .junit .jupiter .api .parallel .Isolated ;
6769import org .junit .jupiter .api .parallel .ResourceLock ;
70+ import org .junit .jupiter .params .ParameterizedTest ;
71+ import org .junit .jupiter .params .provider .ValueSource ;
6872import org .junit .platform .engine .TestDescriptor ;
6973import org .junit .platform .engine .discovery .ClassSelector ;
7074import org .junit .platform .engine .discovery .DiscoverySelectors ;
7377import org .junit .platform .testkit .engine .EngineExecutionResults ;
7478import org .junit .platform .testkit .engine .EngineTestKit ;
7579import org .junit .platform .testkit .engine .Event ;
80+ import org .junit .platform .testkit .engine .Events ;
7681
7782/**
7883 * @since 1.3
@@ -82,7 +87,7 @@ class ParallelExecutionIntegrationTests {
8287
8388@ Test
8489void successfulParallelTest (TestReporter reporter ) {
85- var events = executeConcurrently (3 , SuccessfulParallelTestCase .class );
90+ var events = executeConcurrentlySuccessfully (3 , SuccessfulParallelTestCase .class ). list ( );
8691
8792var startedTimestamps = getTimestampsFor (events , event (test (), started ()));
8893var finishedTimestamps = getTimestampsFor (events , event (test (), finishedSuccessfully ()));
@@ -98,29 +103,29 @@ void successfulParallelTest(TestReporter reporter) {
98103
99104@ Test
100105void failingTestWithoutLock () {
101- var events = executeConcurrently (3 , FailingWithoutLockTestCase .class );
106+ var events = executeConcurrently (3 , FailingWithoutLockTestCase .class ). list () ;
102107assertThat (events .stream ().filter (event (test (), finishedWithFailure ())::matches )).hasSize (2 );
103108}
104109
105110@ Test
106111void successfulTestWithMethodLock () {
107- var events = executeConcurrently (3 , SuccessfulWithMethodLockTestCase .class );
112+ var events = executeConcurrentlySuccessfully (3 , SuccessfulWithMethodLockTestCase .class ). list ( );
108113
109114assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (3 );
110115assertThat (ThreadReporter .getThreadNames (events )).hasSize (3 );
111116}
112117
113118@ Test
114119void successfulTestWithClassLock () {
115- var events = executeConcurrently (3 , SuccessfulWithClassLockTestCase .class );
120+ var events = executeConcurrentlySuccessfully (3 , SuccessfulWithClassLockTestCase .class ). list ( );
116121
117122assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (3 );
118123assertThat (ThreadReporter .getThreadNames (events )).hasSize (1 );
119124}
120125
121126@ Test
122127void testCaseWithFactory () {
123- var events = executeConcurrently (3 , TestCaseWithTestFactory .class );
128+ var events = executeConcurrentlySuccessfully (3 , TestCaseWithTestFactory .class ). list ( );
124129
125130assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (3 );
126131assertThat (ThreadReporter .getThreadNames (events )).hasSize (1 );
@@ -133,7 +138,7 @@ void customContextClassLoader() {
133138var smilingLoader = new URLClassLoader ("(-:" , new URL [0 ], ClassLoader .getSystemClassLoader ());
134139currentThread .setContextClassLoader (smilingLoader );
135140try {
136- var events = executeConcurrently (3 , SuccessfulWithMethodLockTestCase .class );
141+ var events = executeConcurrentlySuccessfully (3 , SuccessfulWithMethodLockTestCase .class ). list ( );
137142
138143assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (3 );
139144assertThat (ThreadReporter .getThreadNames (events )).hasSize (3 );
@@ -146,23 +151,24 @@ void customContextClassLoader() {
146151
147152@ RepeatedTest (10 )
148153void mixingClassAndMethodLevelLocks () {
149- var events = executeConcurrently (4 , TestCaseWithSortedLocks .class , TestCaseWithUnsortedLocks .class );
154+ var events = executeConcurrentlySuccessfully (4 , TestCaseWithSortedLocks .class ,
155+ TestCaseWithUnsortedLocks .class ).list ();
150156
151157assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (6 );
152158assertThat (ThreadReporter .getThreadNames (events ).count ()).isLessThanOrEqualTo (2 );
153159}
154160
155161@ RepeatedTest (10 )
156162void locksOnNestedTests () {
157- var events = executeConcurrently (3 , TestCaseWithNestedLocks .class );
163+ var events = executeConcurrentlySuccessfully (3 , TestCaseWithNestedLocks .class ). list ( );
158164
159165assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (6 );
160166assertThat (ThreadReporter .getThreadNames (events )).hasSize (1 );
161167}
162168
163169@ Test
164170void afterHooksAreCalledAfterConcurrentDynamicTestsAreFinished () {
165- var events = executeConcurrently (3 , ConcurrentDynamicTestCase .class );
171+ var events = executeConcurrentlySuccessfully (3 , ConcurrentDynamicTestCase .class ). list ( );
166172
167173assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (1 );
168174var timestampedEvents = ConcurrentDynamicTestCase .events ;
@@ -175,14 +181,14 @@ void afterHooksAreCalledAfterConcurrentDynamicTestsAreFinished() {
175181 */
176182@ Test
177183void threadInterruptedByUserCode () {
178- var events = executeConcurrently (3 , InterruptedThreadTestCase .class );
184+ var events = executeConcurrentlySuccessfully (3 , InterruptedThreadTestCase .class ). list ( );
179185
180186assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (4 );
181187}
182188
183189@ Test
184190void executesTestTemplatesWithResourceLocksInSameThread () {
185- var events = executeConcurrently (2 , ConcurrentTemplateTestCase .class );
191+ var events = executeConcurrentlySuccessfully (2 , ConcurrentTemplateTestCase .class ). list ( );
186192
187193assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (10 );
188194assertThat (ThreadReporter .getThreadNames (events )).hasSize (1 );
@@ -228,30 +234,22 @@ void executesMethodsInParallelIfEnabledViaConfigurationParameter() {
228234
229235@ Test
230236void canRunTestsIsolatedFromEachOther () {
231- var events = executeConcurrently (2 , IsolatedTestCase .class );
232-
233- assertThat (events .stream ().filter (event (test (), finishedWithFailure ())::matches )).isEmpty ();
237+ executeConcurrentlySuccessfully (2 , IsolatedTestCase .class );
234238}
235239
236240@ Test
237241void canRunTestsIsolatedFromEachOtherWithNestedCases () {
238- var events = executeConcurrently (4 , NestedIsolatedTestCase .class );
239-
240- assertThat (events .stream ().filter (event (test (), finishedWithFailure ())::matches )).isEmpty ();
242+ executeConcurrentlySuccessfully (4 , NestedIsolatedTestCase .class );
241243}
242244
243245@ Test
244246void 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 );
248248}
249249
250250@ RepeatedTest (10 )
251251void 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 );
255253}
256254
257255@ Test
@@ -262,9 +260,8 @@ void runsIsolatedTestsLastToMaximizeParallelism() {
262260);
263261Class <?>[] testClasses = { IsolatedTestCase .class , SuccessfulParallelTestCase .class };
264262var 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 ));
268265
269266List <Event > parallelTestMethodEvents = events .reportingEntryPublished () //
270267.filter (e -> e .getTestDescriptor ().getSource () //
@@ -283,6 +280,15 @@ void runsIsolatedTestsLastToMaximizeParallelism() {
283280assertThat (isolatedClassStart ).isAfterOrEqualTo (parallelClassFinish );
284281}
285282
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+
286292@ Isolated ("testing" )
287293static class IsolatedTestCase {
288294static AtomicInteger sharedResource ;
@@ -355,6 +361,122 @@ void b() throws Exception {
355361}
356362}
357363
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+
358480static class IndependentClasses {
359481static AtomicInteger sharedResource = new AtomicInteger ();
360482static CountDownLatch countDownLatch = new CountDownLatch (4 );
@@ -416,11 +538,21 @@ private List<Instant> getTimestampsFor(List<Event> events, Condition<Event> cond
416538// @formatter:on
417539}
418540
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 ) {
420553Map <String , String > configParams = Map .of (DEFAULT_PARALLEL_EXECUTION_MODE , "concurrent" );
421554return executeWithFixedParallelism (parallelism , configParams , testClasses ) //
422- .allEvents () //
423- .list ();
555+ .allEvents ();
424556}
425557
426558private EngineExecutionResults executeWithFixedParallelism (int parallelism , Map <String , String > configParams ,
0 commit comments