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
8489 void successfulParallelTest (TestReporter reporter ) {
85- var events = executeConcurrently (3 , SuccessfulParallelTestCase .class );
90+ var events = executeConcurrentlySuccessfully (3 , SuccessfulParallelTestCase .class ). list ( );
8691
8792 var startedTimestamps = getTimestampsFor (events , event (test (), started ()));
8893 var finishedTimestamps = getTimestampsFor (events , event (test (), finishedSuccessfully ()));
@@ -98,29 +103,29 @@ void successfulParallelTest(TestReporter reporter) {
98103
99104 @ Test
100105 void failingTestWithoutLock () {
101- var events = executeConcurrently (3 , FailingWithoutLockTestCase .class );
106+ var events = executeConcurrently (3 , FailingWithoutLockTestCase .class ). list () ;
102107 assertThat (events .stream ().filter (event (test (), finishedWithFailure ())::matches )).hasSize (2 );
103108 }
104109
105110 @ Test
106111 void successfulTestWithMethodLock () {
107- var events = executeConcurrently (3 , SuccessfulWithMethodLockTestCase .class );
112+ var events = executeConcurrentlySuccessfully (3 , SuccessfulWithMethodLockTestCase .class ). list ( );
108113
109114 assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (3 );
110115 assertThat (ThreadReporter .getThreadNames (events )).hasSize (3 );
111116 }
112117
113118 @ Test
114119 void successfulTestWithClassLock () {
115- var events = executeConcurrently (3 , SuccessfulWithClassLockTestCase .class );
120+ var events = executeConcurrentlySuccessfully (3 , SuccessfulWithClassLockTestCase .class ). list ( );
116121
117122 assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (3 );
118123 assertThat (ThreadReporter .getThreadNames (events )).hasSize (1 );
119124 }
120125
121126 @ Test
122127 void testCaseWithFactory () {
123- var events = executeConcurrently (3 , TestCaseWithTestFactory .class );
128+ var events = executeConcurrentlySuccessfully (3 , TestCaseWithTestFactory .class ). list ( );
124129
125130 assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (3 );
126131 assertThat (ThreadReporter .getThreadNames (events )).hasSize (1 );
@@ -133,7 +138,7 @@ void customContextClassLoader() {
133138 var smilingLoader = new URLClassLoader ("(-:" , new URL [0 ], ClassLoader .getSystemClassLoader ());
134139 currentThread .setContextClassLoader (smilingLoader );
135140 try {
136- var events = executeConcurrently (3 , SuccessfulWithMethodLockTestCase .class );
141+ var events = executeConcurrentlySuccessfully (3 , SuccessfulWithMethodLockTestCase .class ). list ( );
137142
138143 assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (3 );
139144 assertThat (ThreadReporter .getThreadNames (events )).hasSize (3 );
@@ -146,23 +151,24 @@ void customContextClassLoader() {
146151
147152 @ RepeatedTest (10 )
148153 void mixingClassAndMethodLevelLocks () {
149- var events = executeConcurrently (4 , TestCaseWithSortedLocks .class , TestCaseWithUnsortedLocks .class );
154+ var events = executeConcurrentlySuccessfully (4 , TestCaseWithSortedLocks .class ,
155+ TestCaseWithUnsortedLocks .class ).list ();
150156
151157 assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (6 );
152158 assertThat (ThreadReporter .getThreadNames (events ).count ()).isLessThanOrEqualTo (2 );
153159 }
154160
155161 @ RepeatedTest (10 )
156162 void locksOnNestedTests () {
157- var events = executeConcurrently (3 , TestCaseWithNestedLocks .class );
163+ var events = executeConcurrentlySuccessfully (3 , TestCaseWithNestedLocks .class ). list ( );
158164
159165 assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (6 );
160166 assertThat (ThreadReporter .getThreadNames (events )).hasSize (1 );
161167 }
162168
163169 @ Test
164170 void afterHooksAreCalledAfterConcurrentDynamicTestsAreFinished () {
165- var events = executeConcurrently (3 , ConcurrentDynamicTestCase .class );
171+ var events = executeConcurrentlySuccessfully (3 , ConcurrentDynamicTestCase .class ). list ( );
166172
167173 assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (1 );
168174 var timestampedEvents = ConcurrentDynamicTestCase .events ;
@@ -175,14 +181,14 @@ void afterHooksAreCalledAfterConcurrentDynamicTestsAreFinished() {
175181 */
176182 @ Test
177183 void threadInterruptedByUserCode () {
178- var events = executeConcurrently (3 , InterruptedThreadTestCase .class );
184+ var events = executeConcurrentlySuccessfully (3 , InterruptedThreadTestCase .class ). list ( );
179185
180186 assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (4 );
181187 }
182188
183189 @ Test
184190 void executesTestTemplatesWithResourceLocksInSameThread () {
185- var events = executeConcurrently (2 , ConcurrentTemplateTestCase .class );
191+ var events = executeConcurrentlySuccessfully (2 , ConcurrentTemplateTestCase .class ). list ( );
186192
187193 assertThat (events .stream ().filter (event (test (), finishedSuccessfully ())::matches )).hasSize (10 );
188194 assertThat (ThreadReporter .getThreadNames (events )).hasSize (1 );
@@ -228,30 +234,22 @@ void executesMethodsInParallelIfEnabledViaConfigurationParameter() {
228234
229235 @ Test
230236 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 );
234238 }
235239
236240 @ Test
237241 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 );
241243 }
242244
243245 @ Test
244246 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 );
248248 }
249249
250250 @ RepeatedTest (10 )
251251 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 );
255253 }
256254
257255 @ Test
@@ -262,9 +260,8 @@ void runsIsolatedTestsLastToMaximizeParallelism() {
262260 );
263261 Class <?>[] testClasses = { IsolatedTestCase .class , SuccessfulParallelTestCase .class };
264262 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 ));
268265
269266 List <Event > parallelTestMethodEvents = events .reportingEntryPublished () //
270267 .filter (e -> e .getTestDescriptor ().getSource () //
@@ -283,6 +280,15 @@ void runsIsolatedTestsLastToMaximizeParallelism() {
283280 assertThat (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" )
287293 static class IsolatedTestCase {
288294 static 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+
358480 static class IndependentClasses {
359481 static AtomicInteger sharedResource = new AtomicInteger ();
360482 static 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 ) {
420553 Map <String , String > configParams = Map .of (DEFAULT_PARALLEL_EXECUTION_MODE , "concurrent" );
421554 return executeWithFixedParallelism (parallelism , configParams , testClasses ) //
422- .allEvents () //
423- .list ();
555+ .allEvents ();
424556 }
425557
426558 private EngineExecutionResults executeWithFixedParallelism (int parallelism , Map <String , String > configParams ,
0 commit comments