Skip to content

Commit 2101806

Browse files
authored
Fix fallback to ForkJoinPools Java 7 constructor on Java 9 and higher
When an invalid ParallelExecutionConfiguration is provided on Java 9+ the `ForkJoinPoolHierarchicalTestExecutorService` will fall back to using the since Java 7 constructor. As a result misconfiguration goes unnoticed. Separated the exceptions thrown by finding the Java 9+ constructor from those thrown by invoking the constructor with an invalid configuration. The former can be ignored, the latter should not be. Fixes #3045.
1 parent bd145ae commit 2101806

File tree

4 files changed

+135
-12
lines changed

4 files changed

+135
-12
lines changed

documentation/src/docs/asciidoc/release-notes/index.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ authors as well as build tool and IDE vendors.
1616

1717
include::{includedir}/link-attributes.adoc[]
1818

19+
include::{basedir}/release-notes-5.10.0-M1.adoc[]
20+
1921
include::{basedir}/release-notes-5.9.1.adoc[]
2022

2123
include::{basedir}/release-notes-5.9.0.adoc[]
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
[[release-notes-5.10.0-M1️]]
2+
== 5.10.0-M1️
3+
4+
*Date of Release:* ❓
5+
6+
*Scope:* ❓
7+
8+
For a complete list of all _closed_ issues and pull requests for this release, consult the
9+
link:{junit5-repo}+/milestone/5.10.0-M1️?closed=1+[5.10.0-M1️] milestone page in the JUnit repository on
10+
GitHub.
11+
12+
13+
[[release-notes-5.10.0-M1️-junit-platform]]
14+
=== JUnit Platform
15+
16+
==== Bug Fixes
17+
18+
* Fixed fallback to ForkJoinPools Java 7 constructor on Java 9 and higher
19+
20+
==== Deprecations and Breaking Changes
21+
22+
* ❓
23+
24+
==== New Features and Improvements
25+
26+
* ❓
27+
28+
29+
[[release-notes-5.10.0-M1️-junit-jupiter]]
30+
=== JUnit Jupiter
31+
32+
==== Bug Fixes
33+
34+
* ❓
35+
36+
==== Deprecations and Breaking Changes
37+
38+
* ❓
39+
40+
==== New Features and Improvements
41+
42+
* ❓
43+
44+
45+
[[release-notes-5.10.0-M1️-junit-vintage]]
46+
=== JUnit Vintage
47+
48+
==== Bug Fixes
49+
50+
* ❓
51+
52+
==== Deprecations and Breaking Changes
53+
54+
* ❓
55+
56+
==== New Features and Improvements
57+
58+
* ❓

junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ForkJoinPoolHierarchicalTestExecutorService.java

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,16 @@
1919
import java.util.Deque;
2020
import java.util.LinkedList;
2121
import java.util.List;
22+
import java.util.Optional;
23+
import java.util.concurrent.Callable;
2224
import java.util.concurrent.ForkJoinPool;
2325
import java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory;
2426
import java.util.concurrent.ForkJoinTask;
2527
import java.util.concurrent.ForkJoinWorkerThread;
2628
import java.util.concurrent.Future;
2729
import java.util.concurrent.RecursiveAction;
2830
import java.util.concurrent.TimeUnit;
31+
import java.util.function.Function;
2932
import java.util.function.Predicate;
3033

3134
import org.apiguardian.api.API;
@@ -81,18 +84,32 @@ private static ParallelExecutionConfiguration createConfiguration(ConfigurationP
8184

8285
private ForkJoinPool createForkJoinPool(ParallelExecutionConfiguration configuration) {
8386
ForkJoinWorkerThreadFactory threadFactory = new WorkerThreadFactory();
84-
return Try.call(() -> {
85-
// Try to use constructor available in Java >= 9
86-
Constructor<ForkJoinPool> constructor = ForkJoinPool.class.getDeclaredConstructor(Integer.TYPE,
87-
ForkJoinWorkerThreadFactory.class, UncaughtExceptionHandler.class, Boolean.TYPE, Integer.TYPE,
88-
Integer.TYPE, Integer.TYPE, Predicate.class, Long.TYPE, TimeUnit.class);
89-
return constructor.newInstance(configuration.getParallelism(), threadFactory, null, false,
90-
configuration.getCorePoolSize(), configuration.getMaxPoolSize(), configuration.getMinimumRunnable(),
91-
configuration.getSaturatePredicate(), configuration.getKeepAliveSeconds(), TimeUnit.SECONDS);
92-
}).orElseTry(() -> {
93-
// Fallback for Java 8
94-
return new ForkJoinPool(configuration.getParallelism(), threadFactory, null, false);
95-
}).getOrThrow(cause -> new JUnitException("Failed to create ForkJoinPool", cause));
87+
// Try to use constructor available in Java >= 9
88+
Callable<ForkJoinPool> constructorInvocation = sinceJava9Constructor() //
89+
.map(sinceJava9ConstructorInvocation(configuration, threadFactory))
90+
// Fallback for Java 8
91+
.orElse(sinceJava7ConstructorInvocation(configuration, threadFactory));
92+
return Try.call(constructorInvocation) //
93+
.getOrThrow(cause -> new JUnitException("Failed to create ForkJoinPool", cause));
94+
}
95+
96+
private static Optional<Constructor<ForkJoinPool>> sinceJava9Constructor() {
97+
return Try.call(() -> ForkJoinPool.class.getDeclaredConstructor(Integer.TYPE, ForkJoinWorkerThreadFactory.class,
98+
UncaughtExceptionHandler.class, Boolean.TYPE, Integer.TYPE, Integer.TYPE, Integer.TYPE, Predicate.class,
99+
Long.TYPE, TimeUnit.class)) //
100+
.toOptional();
101+
}
102+
103+
private static Function<Constructor<ForkJoinPool>, Callable<ForkJoinPool>> sinceJava9ConstructorInvocation(
104+
ParallelExecutionConfiguration configuration, ForkJoinWorkerThreadFactory threadFactory) {
105+
return constructor -> () -> constructor.newInstance(configuration.getParallelism(), threadFactory, null, false,
106+
configuration.getCorePoolSize(), configuration.getMaxPoolSize(), configuration.getMinimumRunnable(),
107+
configuration.getSaturatePredicate(), configuration.getKeepAliveSeconds(), TimeUnit.SECONDS);
108+
}
109+
110+
private static Callable<ForkJoinPool> sinceJava7ConstructorInvocation(ParallelExecutionConfiguration configuration,
111+
ForkJoinWorkerThreadFactory threadFactory) {
112+
return () -> new ForkJoinPool(configuration.getParallelism(), threadFactory, null, false);
96113
}
97114

98115
@Override
@@ -199,6 +216,7 @@ static class WorkerThreadFactory implements ForkJoinPool.ForkJoinWorkerThreadFac
199216
public ForkJoinWorkerThread newThread(ForkJoinPool pool) {
200217
return new WorkerThread(pool, contextClassLoader);
201218
}
219+
202220
}
203221

204222
static class WorkerThread extends ForkJoinWorkerThread {
@@ -207,6 +225,7 @@ static class WorkerThread extends ForkJoinWorkerThread {
207225
super(pool);
208226
setContextClassLoader(contextClassLoader);
209227
}
228+
210229
}
211230

212231
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2015-2022 the original author or authors.
3+
*
4+
* All rights reserved. This program and the accompanying materials are
5+
* made available under the terms of the Eclipse Public License v2.0 which
6+
* accompanies this distribution and is available at
7+
*
8+
* https://www.eclipse.org/legal/epl-v20.html
9+
*/
10+
11+
package org.junit.platform.engine.support.hierarchical;
12+
13+
import static org.assertj.core.api.Assertions.assertThat;
14+
import static org.junit.jupiter.api.Assertions.assertThrows;
15+
import static org.mockito.Mockito.when;
16+
17+
import org.junit.jupiter.api.Test;
18+
import org.junit.jupiter.api.extension.ExtendWith;
19+
import org.junit.platform.commons.JUnitException;
20+
import org.mockito.Mock;
21+
import org.mockito.junit.jupiter.MockitoExtension;
22+
23+
@ExtendWith(MockitoExtension.class)
24+
class ForkJoinPoolHierarchicalTestExecutorServiceTests {
25+
26+
@Mock
27+
ParallelExecutionConfiguration configuration;
28+
29+
@Test
30+
void exceptionsFromInvalidConfigurationAreNotSwallowed() {
31+
when(configuration.getParallelism()).thenReturn(2);
32+
when(configuration.getMaxPoolSize()).thenReturn(1); // invalid, should be > parallelism
33+
when(configuration.getCorePoolSize()).thenReturn(1);
34+
when(configuration.getMinimumRunnable()).thenReturn(1);
35+
when(configuration.getSaturatePredicate()).thenReturn(__ -> true);
36+
when(configuration.getKeepAliveSeconds()).thenReturn(0);
37+
38+
JUnitException exception = assertThrows(JUnitException.class,
39+
() -> new ForkJoinPoolHierarchicalTestExecutorService(configuration));
40+
assertThat(exception).hasMessage("Failed to create ForkJoinPool");
41+
assertThat(exception).rootCause().isInstanceOf(IllegalArgumentException.class);
42+
}
43+
44+
}

0 commit comments

Comments
 (0)