bun test: only run process.on('exit') listeners when node:test APIs were used
What does this PR do?
Reverts the user-visible half of #34444's exit-listener change. Since that PR, bun test dispatched every test file's process.on('exit') listeners once the last file finished, so a listener calling process.exit(1) failed the run even though the summary reported everything passing (this was slated to be a documented breaking change in the 1.4 notes).
Checked jest 30 and vitest 4.1 with a passing test whose file registers process.on("exit", () => process.exit(1)):
| runner | listener ran | exit code |
|---|---|---|
jest (workers and -i) | no | 0 |
vitest (forks, threads, vmForks, vmThreads) | no | 0 |
| bun 1.4 canary before this PR | yes | 1 |
jest gives test files a copy of process; vitest tears its workers down itself. Since bun test runs every file in one process, one file's listener also affected the whole run.
Now the end-of-run on_exit() still runs (profilers, deferred flushes, cleanup hooks) but skips the listener dispatch unless one of these opted in:
- a
node:testAPI was called on the main thread —test/describe/hooks/mock.*/assert.registerall reachjsFileGeneration, which marks the runner. Merely importingnode:test, or a Worker using it, doesn't count. BUN_TEST_DRAIN_EVENT_LOOP=1, the existing opt-inscripts/runner.node.mjssets for vendored node tests (a few of those run underbun testwithoutnode:testand checkcommon.mustCall()counts from an exit listener).
Both keep the vendored node tests and node:test's run() children working. A test that calls process.exit() itself still runs listeners, as before. --parallel workers now go through the same gated on_exit() instead of straight to global_exit().
test/js/node/util/node-inspect-tests/parallel/util-inspect.test.js verified its own mustCall() wrappers from an exit listener; that check is now an afterAll.
How did you verify your code works?
test/cli/test/bun-test.test.ts— replaced the two tests from #34444 with eleven covering: bun:test file, globals-style file, import-onlynode:test, and Worker-onlynode:testdon't run listeners and exit 0 even when the listener callsprocess.exit(1); explicitprocess.exit(3)from a test still runs them; a file registering anode:testtest runs them and can fail the run;BUN_TEST_DRAIN_EVENT_LOOP=1runs them for a bun:test file;--parallelruns for both kinds. The "not run" cases fail on the current canary and pass with this branch; the Worker case was also checked against a build with the main-thread guard removed.util-inspect.test.jspasses, and fails when amustCall()wrapper is never invoked.test/js/node/test_runner/node-test.test.tspasses (45/45); the vendoredtest-events-add-abort-listener,test-file-write-stream5,test-worker-arraybuffer-zerofill,test-runner-mockingstill pass withBUN_TEST_DRAIN_EVENT_LOOP=1.test/cli/test/parallel.test.ts: 34/35 locally; the one failure (unique JEST_WORKER_ID) reproduces on this debug build without the worker change and passes on the release canary — debug-build timing, unrelated.