**Problem**
When a forked test JVM exits, WorkerProxy.watch calls notifyExit right
away, and on exit code 0 that completes the run. If the response reader
has not consumed the rest of the stream yet, the remaining events, logs and
the final response are dropped, and the build passes with failing tests
unreported. Slow listeners, or several forks at once, make this likely.
**Solution**
Track when the response stream reaches end of input (the socket reader for
Tcp/Ipc, stdout for Stdio), and have the watch thread wait for it, with a
bound, before calling notifyExit. This is what sbt 1.x did by joining the
acceptor thread. The new scripted test uses a listener that sleeps per
event, and fails without this change.
Fixes#9859
Generated-by: Claude Opus 5.5
Co-authored-by: Eugene <[email protected]>
* [2.x] feat: testForkedWorker setting
**Problem**
A major bottleneck of the default setting is that concurrentRestriction
allows exactly one forked test in flight at a time,
and partly due to the restriction all test suites from a subproject runs
within the forked process.
**Solution**
This adds a new setting called testForkedWorker, which lets the build users
tune the number of concurrent worker process in flight.
I've set the default to CPU count / 3.
Within a single worker, the default parallelism is reduced to 2.
The default test grouping now splits the test suites into
testForkedWorker count.
* Use Global scope for overall worker count
Subproject scoping can be used for single vs split
* Adds math.max(..., 1)
* Avoid running setup/cleanup on an empty selection
* Fix the tag application order
* Rename key to workerMaxInstances
* Implement TestTopology
* fix: Fixes exclusive test
Note due to the limitation of the tag system, setup-clean/cleanup
may overlap.
* Check that workerMaxInstances >= 1
sbt writes two archives per module per build: the packageBin /
packageInternal jar, and the zip the disk action cache stores an output
directory as. Both deflate one entry at a time on the calling thread.
io 1.13.0 adds IO.jarParallel / IO.zipParallel, which deflate entries
concurrently and produce byte-identical archives. Switch both call sites
over, taking the executor from IO.Implicits.zipContext -- io keeps it
behind that object so the choice is explicit rather than an ambient
ExecutionContext.
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
**Problem**
Channels.newInputStream/newOutputStream share a channel-wide lock,
which would deadlock for a duplex communication.
**Solution**
This implements an alternative DuplexChannels functions that's capable
of duplex communication expected of a "socket".
This also duplicates the Java implementation to the worker app,
so we can use JDK domain socket for forked test communication.
**Problem**
Commit c8737b8e4f, "refactor: Change the test type" (PR #8181, Eugene Yokota), made Tests.Output and SuiteResult private[sbt] while changing test tasks to return TestResult. The stated rationale was to type test tasks; it did not describe restricting TestResultLogger customization.
TestResultLogger remains a public, documented extension point, but its run method accepts Tests.Output. External plugins consequently cannot implement it in source. This was noticed while working on TW-102637.
**Solution**
Restore public visibility for Tests.Output, SuiteResult, and its companion. Add a scripted external-build regression test that implements TestResultLogger, uses Output.events as Iterable[SuiteResult], runs test, and observes the logger.
---------
Co-authored-by: Codex <[email protected]>
**Problem/Solution**
This extends the idea started with TestRecap, and applies it to
both test success and failures.
1. Existing TestResultLogger trait is extended to handle the summary
rendering.
2. TestSummary enum is added to control the verbosity via
commandline option, system property, or a setting.
3. Script test captures the log.
Three independent, pre-existing retention bugs kept the classloader of a
finished in-process test run -- and the open jar handles it holds -- alive
for the rest of the server session.
- JUnitXmlTestsListener: testSuite is an InheritableThreadLocal, so
threads spawned during a run (e.g. async-framework pool workers) inherit
a copy of the suite reference that remove() cannot reach.
- TestRecap.collect: the recap is stashed on State.attributes and outlives
the command, so it must not retain live throwables.
- ClassLoaderCache: loaders evicted from delegate by clearExpiredLoaders
are unreachable from the map and never enqueued on the ReferenceQueue, so
neither clear()/close() nor the cleanup thread could ever close them.
SuiteResult now documents the retention hazard on throwables. Adds
deterministic, cross-platform tests for all three severed chains.
Fixes#2998
When test is run at the prompt in a multi-project build,
sbt aggregates <proj>/test across subprojects. Each subproject's
TestResultLogger emits its own pass/fail summary inline, but the
aggregation layer never restated which projects failed. The final
"sbt.TestsFailedException" line surfaced no project names or failing
test classes, so on CI users had to scrollback-grep through thousands
of log lines to find what broke.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
Bumps io to released 1.12.0 which contains the atomic-write changes
to IO.copyFile, IO.transfer(InputStream, File), and IO.jar/zip.
ActionCacheStore.putBlob now uses IO.transfer instead of duplicating
staging logic.
**Problem**
In sbt 2.x, forking still creates layered classloader in the worker process,
which doesn't work for some tests.
**Solution**
This provides an escape hatch to emulate the sbt 1.x semantics of
using the system classpath for testing.
Test report listeners now receive testEvent callbacks as the underlying test framework emits events, instead of only after a whole group finishes. startGroup / endGroup timing is aligned with that streaming model for both in-process and forked test runs.
No public API changes - only listener callback timing and the internal worker <-> sbt JSON-RPC protocol.
**Problem**
When the user runs testOnly with an explicit suite name (e.g. testOnly com.example.MySuite),
ScalaTest suites annotated with @DoNotDiscover were not run because sbt always passed
explicitlySpecified=false to the test framework.
**Solution**
In Tests.processOptions, when the user has specified test filters (orderedFilters.nonEmpty),
mark the filtered tests as explicitlySpecified=true with SuiteSelector so frameworks
can run @DoNotDiscover suites when explicitly requested.
**Problem**
Forked console currently pulls in full Zinc, which includes JLine.
**Solution**
This implements a lighter-weight, full Java ForkConsoleMain,
which no longer depends on JLine.
**Problem**
1. forked console is missing user code from the classpath.
2. forked console still blocks the server.
**Solution**
1. This includes proper products and classpaths to the console.
2. This also implements client-side run for console.
When a bundle upload to Central Portal fails, the error now displays the HTTP response body instead of just the status code. This provides more useful debugging information, as the response body typically contains detailed error messages from the server.
- Convert class to object with BasicTestSuite
- Use test() syntax instead of 'should ... in'
- Move RecordingLogger and extension to top-level
- Use Scala 3 colon indentation syntax
Co-authored-by: GlobalStar117 <[email protected]>
When a forked test process crashes (e.g., due to UnsupportedClassVersionError),
sbt would hang forever waiting for test results. This happened because
notifyExit only completed the promise with success, regardless of the
exit code.
This fix checks the exit code and fails the promise if the process
exited with a non-zero code, allowing sbt to properly report the failure
and exit.
Fixes#7429
**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)
```
Before this change you had to log into the sonatype account and search for the errors there.
(https://central.sonatype.com/publishing/deployments)
This was inconvenient, especially if you don't have the admin access to the account.
**Problem**
test task is typed to unit.
To distinguish test from any other tasks, we want to actually type this to something.
**Solution**
Forward TestResult to the test task.
**Problem**
I'm seeing NPE in some test-related tests like tests/junit:
[info] [error] java.lang.NullPointerException: Null output stream
[info] [error] at java.io.PrintStream.requireNonNull(PrintStream.java:79)
[info] [error] at java.io.PrintStream.<init>(PrintStream.java:151)
[info] [error] at java.io.PrintStream.<init>(PrintStream.java:135)
[info] [error] at sbt.internal.WorkerProxy.inputStream$lzyINIT1(WorkerExchange.scala:69)
[info] [error] at sbt.internal.WorkerProxy.inputStream(WorkerExchange.scala:69)
[info] [error] at sbt.internal.WorkerProxy.println(WorkerExchange.scala:77)
[info] [error] at sbt.ForkTests$.mainTestTask$$anonfun$1(ForkTests.scala:160)
This is likely coming from ProcessBuilder#run not immediately processing the passed in processIO.
**Solution**
This uses Promise to wait for the processIO to be processed.