[2.x] Add testForkedParallelism setting for forked test thread count (#8453)

**Problems**

When running forked tests, sbt uses `Runtime.getRuntime().availableProcessors()` to determine the thread pool size, ignoring `concurrentRestrictions`. This is inconsistent with non-forked parallel tests.

**Expectations**

Users should be able to control the number of parallel test threads in forked mode, similar to how `concurrentRestrictions` works for non-forked tests.

**Notes**

Added a new setting `testForkedParallelism` that allows explicit control:

```scala
testForkedParallelism := Some(2)  // Use 2 threads
testForkedParallelism := None     // Use availableProcessors() (default)
```
This commit is contained in:
MkDev11
2026-01-09 12:43:50 -05:00
committed by GitHub
parent 7320b7176a
commit 061145e67b
6 changed files with 66 additions and 27 deletions
@@ -311,12 +311,13 @@ public class ForkTestMain {
this.originalOut.flush();
}
private ExecutorService executorService(final boolean parallel) {
private ExecutorService executorService(final boolean parallel, final Integer parallelism) {
if (parallel) {
final int nbThreads = Runtime.getRuntime().availableProcessors();
final int nbThreads =
(parallelism != null && parallelism > 0)
? parallelism
: Runtime.getRuntime().availableProcessors();
logDebug("Create a test executor with a thread pool of " + nbThreads + " threads.");
// more options later...
// TODO we might want to configure the blocking queue with size #proc
return Executors.newFixedThreadPool(nbThreads);
} else {
logDebug("Create a single-thread test executor");
@@ -326,7 +327,7 @@ public class ForkTestMain {
private void runTests(TestInfo info, ClassLoader classLoader) throws Exception {
Thread.currentThread().setContextClassLoader(classLoader);
final ExecutorService executor = executorService(info.parallel);
final ExecutorService executor = executorService(info.parallel, info.parallelism);
final TaskDef[] tests = info.taskDefs.toArray(new TaskDef[] {});
final int nFrameworks = info.testRunners.size();
final Logger[] loggers = {remoteLogger(info.ansiCodesSupported)};
@@ -33,6 +33,7 @@ public class TestInfo implements Serializable {
public final RunInfo.NativeRunInfo nativeRunInfo;
public final boolean ansiCodesSupported;
public final boolean parallel;
public final Integer parallelism;
public final ArrayList<TaskDef> taskDefs;
public final ArrayList<TestRunner> testRunners;
@@ -42,6 +43,7 @@ public class TestInfo implements Serializable {
RunInfo.NativeRunInfo nativeRunInfo,
boolean ansiCodesSupported,
boolean parallel,
Integer parallelism,
ArrayList<TaskDef> taskDefs,
ArrayList<TestRunner> testRunners) {
this.jvm = jvm;
@@ -49,6 +51,7 @@ public class TestInfo implements Serializable {
this.nativeRunInfo = nativeRunInfo;
this.ansiCodesSupported = ansiCodesSupported;
this.parallel = parallel;
this.parallelism = parallelism;
this.taskDefs = taskDefs;
this.testRunners = testRunners;
}