**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) <noreply@anthropic.com>
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 <GlobalStar117@users.noreply.github.com>
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.
**Problem**
1. query string wasn't passed in, so sonaRelease wasn't working
2. deployment name should be human readable
**Solution**
This fixes the query string passing by hand-crafting the URL.
This also generates human readable deployment name.
**Problem**
Sonatype is sunsetting HTTP Rest endpoint OSSRH in June 2025.
**Solution**
This implements a built-in publishing mechanism.
Kudos to David Doyle who has spearheaded Sonatype Central support via sonatype-central-client etc.