Skip to content

make some tests faster - #33622

Merged
Jarred-Sumner merged 10 commits into
mainfrom
jarred/cant
Jul 7, 2026
Merged

Jarred-Sumner merged 10 commits into
mainfrom
jarred/cant

Conversation

@Jarred-Sumner

@Jarred-Sumner Jarred-Sumner commented Jul 7, 2026 •

Copy link
Copy Markdown
Collaborator

What does this PR do?

Speeds up the test suite across ~60 files without changing what is being asserted, by:

  • Concurrency: test.concurrent / describe.concurrent where tests spawn independent subprocesses or do I/O with no shared mutable state (bundler, upgrade, workspaces, multi-run, no-orphans, spawn, websocket, serve-epoll, structured-clone, proxy-stress, FormData, grpc-js idle-timer, password, type-export, and more).
  • Smaller workloads that preserve the assertion: brotli leak test at quality 2 instead of 11, ansi256 fuzz sweeps ~13k equivalence-class inputs on ASAN instead of 2^24, zlib streaming encode pushes 8 random 1 MB chunks instead of 50 PRNG-filled chunks, TLS keepalive leak fixture with smaller NUM/WARMUP, sqlite long-identifier test at 100K chars instead of 50M, security-provider large-payload fixture served from memory.
  • N-API addon build fast path: test/napi/node-napi-tests/harness.ts now compiles the trivial binding.gyp shape directly with clang when the node-headers cache is present, falling back to node-gyp configure build otherwise. napi.test.ts / napi-value-ffi / napi-finalizer-delete-ref skip bun install when the built .node is already fresh. The isolate-finalizer addon moves to its own fixture directory so 30205.test.ts no longer rebuilds all of napi-app.
  • Docker test scheduling: the prestart map moves to test/docker/prestart-map.mjs (consumable from plain Node) and scripts/runner.node.mjs sorts docker-backed test files toward the end of each shard so container cold start overlaps with the non-docker tests that run first. test/docker/coordinator.ts caches the last healthy ServiceInfo per service and re-validates it with a cheap TCP probe instead of paying the full compose up --wait path on every request.
  • Poll-for-condition instead of fixed sleeps: grpc-js outlier-detection, bun-serve-file pump-stall check, etc.
  • Misc: tonic test caches the protoc download and cargo target dir, next-build installs with PUPPETEER_SKIP_DOWNLOAD and builds both variants concurrently, test-performance-eventloopdelay spins for 100ms instead of 1s.

How did you verify your code works?

CI. On linux-x64-asan-debian-13 (build #69715 vs the median of 5 recent PR builds), the 60 comparable changed files go from 1303s to 1103s total (-15.3%); the longest shard on that lane was 692s vs 718-932s across the baseline builds. Largest per-file wins: websocket-syscall-fault -38s, FormData -29s, proxy-stress-concurrent -25s, node/net/handle-leak -25s, zlib/leak -20s, fetch-backpressure -20s.

A few files in the first run report slower because all 60+ touched files are sorted to the very front of their shard (position 3-5/~227), where they contend with the shard's bun install step and cold docker-compose. Those are measurement artifacts of touching the file, not of the change itself; the same files run at normal positions once this merges.

Three changes from the original commit were reverted in e19cfca after failing in CI:

  • bun-security-scanner-matrix-runner.ts: toMatchSnapshot() is rejected inside test.concurrent (1440+ snapshot entries here cannot be made inline).
  • test/js/node/test/parallel/test-dns-setserver-when-querying.js and test-stream-iter-broadcast-from.js: restored to upstream per the parallel/ convention; the dns rewrite also exposed a null deref in dns.rs remove_timer on ASAN that is out of scope here.

@robobun

robobun commented Jul 7, 2026 •

Copy link
Copy Markdown
Collaborator
Updated 7:27 AM PT - Jul 7th, 2026

❌ @robobun, your commit 584bb9f has 2 failures in Build #69787 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 33622

That installs a local version of the PR into your bun-33622 executable, so you can run:

bun-33622 --bun

@Jarred-Sumner

Copy link
Copy Markdown
Collaborator Author

@robobun adopt this PR and check the end result is CI tests for linux finish faster for the changed tests than they did in other recent builds

@robobun

robobun commented Jul 7, 2026 •

Copy link
Copy Markdown
Collaborator

Adopted. Build #69787 (584bb9f, all linux+windows lanes complete): zero error-level test failures. The only failed job is the recurring darwin buildkite-agent artifact download timed out after 120s infra issue on the darwin-aarch64-26-* agent pool (fourth consecutive build, independent of this diff).

lane (shards) longest shard sum vs baseline #69493
linux-x64-debian (20) 196s 3339s 308s / 3811s (-36% / -12%)
linux-x64-asan (20) 556s 8934s 718s / 11312s (-23% / -21%)
win-x64 (8) 598s 4037s 701s / 4521s (-15% / -11%)
win-aarch64 (8) 761s 5103s 865s / 5979s (-12% / -15%)

linux-x64-debian shard spread is now 146s..196s (50s) vs baseline 86s..308s (222s).

7 retry-recovered flakes in this build, all at or below their baseline rate across 10 recent builds; none of the flakes the earlier commits introduced (test-repl-close ×2, bun-install-proxy ×4, dns-tcp-bidirectional-poll ×3, bun-upgrade ETXTBSY, zlib brotli, bundler_compile_autoload) reappeared after 584bb9f.

Stack: within-file concurrency + LPT bin-pack sharding over test/expected-durations.json (default/asan/windows) + two-phase runner (serial tests, then js/{node,bun}/test/parallel/ N-wide) + docker coordinator caching + N-API fast-path. scripts/update-test-durations.mjs regenerates the table; .buildkite/update-test-durations.yml for a weekly schedule.

Ready for review; the darwin lane needs that agent looked at.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2c3a0c9f-3e95-4c12-a959-371501ff19dc

📥 Commits

Reviewing files that changed from the base of the PR and between cdb060d and 584bb9f.

📒 Files selected for processing (6)
  • scripts/runner.node.mjs
  • scripts/update-test-durations.mjs
  • test/bundler/bundler_compile_autoload.test.ts
  • test/docker/prestart-map.mjs
  • test/expected-durations.json
  • test/js/node/zlib/zlib.test.js
💤 Files with no reviewable changes (1)
  • test/docker/prestart-map.mjs

Walkthrough

This PR overhauls the test runner and CI infrastructure to schedule tests via deterministic LPT bin-packing using historical duration data, introduces a shared Docker prestart-map consumed by both the runner and coordinator, adds a duration-tracking pipeline, and converts large swaths of test suites to run concurrently while adjusting numerous timeouts, iteration counts, and payload sizes. It also refactors N-API addon build harnesses to add a fast compile path and relocates/reduces regression test fixtures.

Changes

Test Runner and Docker Infrastructure

Layer / File(s) Summary
Prestart map, runner scheduling, and coordinator wiring
test/docker/prestart-map.mjs, scripts/runner.node.mjs, test/docker/coordinator.ts, test/tsconfig.json
Adds a shared prefix-to-service map consumed by the runner (for LPT bin-packing shards and docker-last ordering, plus a new concurrent test scheduling path) and by the coordinator (for cached/probe-based service reuse).
Valkey test enablement gating
test/js/valkey/test-utils.ts
Gates docker info probing behind BUN_DOCKER_COORDINATOR/BUN_TEST_SERVICE_redis_unified env checks.

Test Duration Tracking Pipeline

Layer / File(s) Summary
update-test-durations script and CI pipeline
scripts/update-test-durations.mjs, .buildkite/update-test-durations.yml, .gitattributes, .prettierignore
Adds a script that parses Buildkite job logs to compute per-test median durations, a scheduled pipeline step to regenerate and upload test/expected-durations.json, and ignore rules for the generated file.

Test Suite Concurrency and Timing

Layer / File(s) Summary
Bundler and CLI autoload/outfile tests
test/bundler/bundler_compile_autoload.test.ts, test/bundler/cli.test.ts
Relocates CLI autoload tests and extracts shared compile/exec helpers for --outfile binary tests.
Install, registry, proxy, and init test setup
test/cli/install/bun-install-registry.test.ts, test/cli/install/bun-install-security-provider.test.ts, test/cli/install/simple-dummy-registry.ts, test/cli/init/init.test.ts, test/cli/install/test-dev-peer-dependency-priority.test.ts
Reduces loop iterations, switches tarball staging to in-memory bytes, adds dispose support, and primes install caches.
bun-workspaces concurrency overhaul
test/cli/install/bun-workspaces.test.ts
Introduces a concurrency-gated setupTest() helper providing isolated dirs/env per test, converting most tests to test.concurrent.
multi-run and no-orphans CLI conversions
test/cli/run/multi-run.test.ts, test/cli/run/no-orphans.test.ts
Converts inline Bun scripts to shell commands and switches suites to concurrent execution with adjusted timeouts.
Next.js integration build parallelization
test/integration/next-pages/test/next-build.test.ts
Runs dependency installs asynchronously and builds temp dirs concurrently.
HTTP, serve, websocket, and fetch concurrency
test/js/bun/dns/..., test/js/bun/http/..., test/js/bun/css/color.test.ts, test/js/web/websocket/...
Converts numerous suites to concurrent execution and tunes timeouts, payload sizes, and stall-detection thresholds.
Spawn, perf, shell, password, FormData concurrency
test/js/bun/perf/linker-order.test.ts, test/js/bun/shell/leak.test.ts, test/js/bun/spawn/*, test/js/bun/util/password.test.ts, test/js/web/html/FormData.test.ts
Adds batched/concurrent execution and ASAN-aware iteration scaling.
TypeScript type-export async migration
test/js/bun/typescript/type-export.test.ts
Rewrites subprocess execution to async with a compile concurrency throttle.
Node.js compatibility concurrency and leak tuning
test/js/node/net/handle-leak.test.ts, test/js/node/process/*, test/js/node/test/sequential/*, test/js/node/tls/node-tls-cert.test.ts, test/js/node/worker_threads/*, test/js/node/zlib/*
Converts suites to concurrent, reduces measurement iterations, and randomizes zlib test input.
SQL, MySQL, Postgres, SQLite, Valkey timing
test/js/sql/*, test/js/valkey/*
Tightens connect/idle/lifetime timeouts and shortens generated fixture sizes.
gRPC idle-timer, outlier-detection, tonic tests
test/js/third_party/grpc-js/*
Introduces runWithClient helper for concurrency, polling-based outlier waits, and cached protoc/Cargo builds.
Fetch body-stream and structured-clone async tests
test/js/web/fetch/*, test/js/web/workers/structured-clone.test.ts
Refactors typed-array construction and migrates cloning tests to async with warm/cold cross-process helpers.

N-API Addon Build Fixture Overhaul

Layer / File(s) Summary
N-API build harness fast-path and async runner
test/napi/node-napi-tests/harness.ts, test/napi/node-napi-tests/test/.../do.test.ts
Adds direct-compile fast path, header caching, and shared-build-promise concurrent test execution.
napi-finalizer-delete-ref, napi-value-ffi, napi.test.ts freshness checks
test/napi/napi-finalizer-delete-ref.test.ts, test/napi/napi-value-ffi.test.ts, test/napi/napi.test.ts, test/napi/uv.test.ts
Skips redundant builds when artifacts are already fresh.
Isolate finalizer addon fixture relocation
test/napi/napi-app/binding.gyp, test/regression/issue/30205-napi-app/*, test/regression/issue/30205.test.ts
Moves the isolate finalizer addon into a dedicated fixture directory.

Regression Test Refactors

Layer / File(s) Summary
Issue 22157 and 26298 consolidation/concurrency
test/regression/issue/22157.test.ts, test/regression/issue/26298.test.ts
Reuses a shared compiled binary across scenarios and converts a suite to concurrent execution.

Possibly related PRs

  • oven-sh/bun#29873: Both PRs modify bun-workspaces.test.ts around workspace-relative local tarball behavior.
  • oven-sh/bun#32028: Both PRs adjust sql-connect-error-reporting.test.ts connect/retry timing expectations.
  • oven-sh/bun#32033: Both PRs modify Docker-dependent test scheduling using the shared prestartMap.
🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is related to the PR, but it's too generic to describe the main changes clearly. Use a concise, specific title that names the primary change, such as test-suite concurrency and runner speedups.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description matches the template and includes both the PR purpose and verification details.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
test/js/node/process/dlopen-duplicate-load.test.ts (2)

96-234: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Consider test.concurrent for these subprocess-spawning tests.

Every test here spawns an independent Bun.spawn subprocess against a shared, read-only addonPath, matching the repo convention that subprocess/file-I/O tests are good test.concurrent candidates — directly aligned with this PR's "make tests faster" objective.

Based on learnings, "treat a use of describe.concurrent / test.concurrent as a concurrency guideline only when the tests spawn subprocesses and/or perform filesystem I/O," which applies here since all five tests spawn subprocesses.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/js/node/process/dlopen-duplicate-load.test.ts` around lines 96 - 234,
These subprocess-based tests in process.dlopen duplicate loads and
process.dlopen with non-object exports are independent and should run
concurrently to match the repo’s test-speedup pattern. Update the individual
test cases under the describe blocks to use test.concurrent, since each one
spawns its own Bun.spawn process and only reads the shared addonPath without
mutating shared state. Keep the existing assertions and structure, but apply
concurrency at the test declaration level for the duplicate-load and
non-object-exports cases.

Source: Learnings


114-233: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Extract a shared spawn helper; add the missing stderr diagnostic on failure.

The Bun.spawn + Promise.all([...stdout, stderr, exited]) block is duplicated five times, along with the addonPath.replace(/\\/g, "\\\\") escaping. None of the five tests surface stderr when the subprocess fails — only stdout/exitCode are asserted, so a crash/failure won't show diagnostic output in the failure diff.

♻️ Proposed helper extraction
+  const escapedAddonPath = addonPath.replace(/\\/g, "\\\\");
+
+  async function runDlopenScript(testScript: string) {
+    await using proc = Bun.spawn({
+      cmd: [bunExe(), "-e", testScript],
+      env: bunEnv,
+      stdout: "pipe",
+      stderr: "pipe",
+    });
+    const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
+    if (exitCode !== 0) {
+      expect(stderr).toBe("");
+    }
+    return { stdout, exitCode };
+  }

Then each test becomes:

-      await using proc = Bun.spawn({
-        cmd: [bunExe(), "-e", testScript],
-        env: bunEnv,
-        stdout: "pipe",
-        stderr: "pipe",
-      });
-      const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
+      const { stdout, exitCode } = await runDlopenScript(testScript);

Based on learnings, "when you spawn a subprocess in a bun:test and you assert on its exit code, follow the CLAUDE.md house style: write if (exitCode !== 0) { expect(stderr).toBe(\"\"); } immediately before expect(exitCode).toBe(0)."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/js/node/process/dlopen-duplicate-load.test.ts` around lines 114 - 233,
The subprocess setup in these `process.dlopen` tests is duplicated and the
failure path never reports `stderr`, making debugging hard. Extract the repeated
`Bun.spawn`/`Promise.all` pattern into a shared helper and centralize the
`addonPath` escaping there, then update the tests that assert `exitCode` in the
`dlopen` suite to include the house-style `stderr` check before asserting
success so failures surface diagnostics; use the existing `Bun.spawn`,
`proc.exited`, and `addonPath.replace` patterns to locate the shared setup.

Source: Learnings

test/js/bun/spawn/spawn.test.ts (1)

168-213: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Reduce the concurrent Bun batch under ASAN/debug. This spawns 50 bunExe() processes per batch; lower batchSize to 5 when isASAN || isDebug is true so this test stays within the default timeout on slower debug+ASAN runs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/js/bun/spawn/spawn.test.ts` around lines 168 - 213, Reduce the
concurrency in the spawn stress test by making the batch size conditional in the
runPair loop: keep the current 25 for normal runs, but lower it to 5 when isASAN
|| isDebug is true so the Promise.all batching in spawn.test.ts finishes within
the default timeout on slower environments.

Source: Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/bundler/cli.test.ts`:
- Around line 47-53: The temp directory cleanup in testCompileAndExec only
happens after testCompile and testExec succeed, so a failed assertion can skip
fs.rmSync and leak the tmp dir. Update testCompileAndExec to always clean up by
moving the fs.rmSync(baseDir, ...) call into a finally block around the existing
testCompile/testExec flow, keeping the same happy-path behavior while ensuring
cleanup on failure.
- Around line 37-46: The shared testExec helper in cli.test.ts is too strict by
asserting stderr is exactly empty, which can flake on ASAN/debug builds. Update
the helper used by both --outfile tests to avoid toBeEmpty() on stderr and
instead tolerate benign warnings while still checking the process exit status
from Bun.spawn. Keep the change localized to testExec so both callers benefit
from the relaxed stderr handling.

In `@test/cli/install/bun-security-scanner-matrix-runner.ts`:
- Line 570: The `runSecurityScannerTest()` flow still uses `toMatchSnapshot()`
for `sortedPackages` and `sortedTarballs` inside `test.concurrent`, which Bun
cannot handle. Update the `test.concurrent(testName, ...)` usage in
`bun-security-scanner-matrix-runner.ts` so these snapshot assertions run
sequentially, or बदल them to `toMatchInlineSnapshot()` within
`runSecurityScannerTest()` to keep the concurrent test safe.

In `@test/cli/install/bun-upgrade.test.ts`:
- Around line 217-226: The bun asset-name matrix is duplicated in both the
staging and digest tests; extract the nested-loop generation into a shared
module-level helper or constant in bun-upgrade.test.ts and reuse it from both
test cases. Keep the shared symbol close to the existing assetNames logic so the
two tests reference the same source of truth instead of building the list
independently.

In `@test/docker/coordinator.ts`:
- Around line 27-34: The `prestartMap` cast in `coordinator.ts` only changes
TypeScript types and does not validate the service names loaded from
`prestart-map.mjs`, so typos can slip through and fail silently during the
prestart loop. Add a small runtime assertion at module load around
`prestartMapRaw` that checks every mapped entry against the known `ServiceName`
set, and make it throw immediately on any unknown value. Keep the validation
close to the `prestartMap` initialization so bad entries fail fast before
`ensure()` or the `.catch(() => {})` path can hide them.

In `@test/js/node/test/parallel/test-stream-iter-broadcast-from.js`:
- Around line 103-105: Revert the change in the test-stream-iter-broadcast-from
test so it matches the upstream Node fixture exactly; the edit to the long-delay
timer in the generator should be removed from this read-only compatibility test.
If Bun needs different behavior, keep the Node-owned test unchanged and move any
local accommodation into a Bun-owned test instead, preserving the existing
structure around the generator and cancel/unblock logic.

In `@test/js/node/zlib/zlib.test.js`:
- Around line 258-260: The chunk-generation loops in the zlib tests are
zero-filling buffers before immediately overwriting them with random data.
Update both `readStream.push(randomFillSync(Buffer.alloc(...)))` call sites in
the Brotli and Zstd chunk-generation loops to use `Buffer.allocUnsafe` so the
buffers are not redundantly initialized before `randomFillSync` fills them.

In `@test/js/third_party/grpc-js/test-tonic.test.ts`:
- Around line 39-57: The tonic cache in getProtocZipBytes is never evicted, so
long-lived CI agents will keep accumulating stale protoc/Cargo artifacts under
cacheDir. Add a simple pruning or TTL strategy around the cacheDir/target
contents used by this test helper, and keep the existing cachedPath reuse
behavior while ensuring old entries are periodically removed.

In `@test/js/valkey/test-utils.ts`:
- Around line 13-17: The Valkey test enablement logic in isEnabled is
duplicating the Docker service key instead of deriving it from the shared
prestart map source of truth. Update the BUN_TEST_SERVICE_redis_unified check to
consume the same redis_unified key used by prestartMap in
test/docker/prestart-map.mjs, so any service rename stays in sync and the docker
info short-circuit remains accurate.

In `@test/napi/napi-finalizer-delete-ref.test.ts`:
- Around line 21-52: The fast path in the napi finalizer delete-ref test always
runs the node-gyp build, even when the addon output is already fresh, so add the
same freshness check used in needsInstall() from napi.test.ts and
napi-value-ffi.test.ts before invoking spawnSync for node-gyp. Gate the
configure/build path in the test’s beforeAll logic on addonPath being missing or
stale relative to the relevant sources, and only fall back to the full
install/rebuild path when the fast path does not produce the target.

In `@test/napi/napi-value-ffi.test.ts`:
- Around line 31-63: The staleness check in needsInstall() is incomplete
compared with the sibling helper in napi.test.ts because it only compares the
built .node files against each addon’s .c source. Update needsInstall() in
napi-value-ffi.test.ts to also consider binding.gyp and package.json mtimes when
deciding whether to run bun install, so changes to build config or package
metadata correctly trigger a rebuild.

In `@test/napi/node-napi-tests/harness.ts`:
- Around line 86-89: The comment in harness.ts is too long and must be reduced
to 3 lines or fewer. Shorten the explanatory block near the node-gyp
optimization logic in the harness setup so it keeps only the essential rationale
and fallback behavior, while preserving the meaning around the direct compiler
invocation and false return path.
- Around line 90-144: The per-target direct compile path in tryBuildFast can
fail before child.exited is reached if spawn() throws during setup, which
currently bypasses the node-gyp fallback. Wrap the compiler invocation and await
logic in a try/catch inside the targets.map callback, and return false on any
spawn or setup error so buildWithNodeGyp() still gets a chance to run. Keep the
fallback behavior consistent with the existing exit-code failure handling around
child.exitCode and the stderr warning.

---

Outside diff comments:
In `@test/js/bun/spawn/spawn.test.ts`:
- Around line 168-213: Reduce the concurrency in the spawn stress test by making
the batch size conditional in the runPair loop: keep the current 25 for normal
runs, but lower it to 5 when isASAN || isDebug is true so the Promise.all
batching in spawn.test.ts finishes within the default timeout on slower
environments.

In `@test/js/node/process/dlopen-duplicate-load.test.ts`:
- Around line 96-234: These subprocess-based tests in process.dlopen duplicate
loads and process.dlopen with non-object exports are independent and should run
concurrently to match the repo’s test-speedup pattern. Update the individual
test cases under the describe blocks to use test.concurrent, since each one
spawns its own Bun.spawn process and only reads the shared addonPath without
mutating shared state. Keep the existing assertions and structure, but apply
concurrency at the test declaration level for the duplicate-load and
non-object-exports cases.
- Around line 114-233: The subprocess setup in these `process.dlopen` tests is
duplicated and the failure path never reports `stderr`, making debugging hard.
Extract the repeated `Bun.spawn`/`Promise.all` pattern into a shared helper and
centralize the `addonPath` escaping there, then update the tests that assert
`exitCode` in the `dlopen` suite to include the house-style `stderr` check
before asserting success so failures surface diagnostics; use the existing
`Bun.spawn`, `proc.exited`, and `addonPath.replace` patterns to locate the
shared setup.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 06540f36-4b74-45af-a153-2cf45ea552cf

📥 Commits

Reviewing files that changed from the base of the PR and between 2bffc92 and 5017b79.

⛔ Files ignored due to path filters (1)
  • test/regression/issue/30205-napi-app/bun.lock is excluded by !**/*.lock
📒 Files selected for processing (79)
  • scripts/runner.node.mjs
  • test/bundler/bundler_compile_autoload.test.ts
  • test/bundler/cli.test.ts
  • test/cli/init/init.test.ts
  • test/cli/install/bun-install-lifecycle-scripts.test.ts
  • test/cli/install/bun-install-proxy.test.ts
  • test/cli/install/bun-install-registry.test.ts
  • test/cli/install/bun-install-security-provider.test.ts
  • test/cli/install/bun-security-scanner-matrix-runner.ts
  • test/cli/install/bun-upgrade.test.ts
  • test/cli/install/bun-workspaces.test.ts
  • test/cli/install/simple-dummy-registry.ts
  • test/cli/install/test-dev-peer-dependency-priority.test.ts
  • test/cli/run/multi-run.test.ts
  • test/cli/run/no-orphans.test.ts
  • test/docker/coordinator.ts
  • test/docker/prestart-map.mjs
  • test/integration/next-pages/test/next-build.test.ts
  • test/js/bun/css/color.test.ts
  • test/js/bun/dns/resolve-dns.test.ts
  • test/js/bun/http/bun-serve-args.test.ts
  • test/js/bun/http/bun-serve-file.test.ts
  • test/js/bun/http/bun-server.test.ts
  • test/js/bun/http/http-server-chunking.test.ts
  • test/js/bun/http/proxy-stress-concurrent.test.ts
  • test/js/bun/http/serve-epoll-add-fail.test.ts
  • test/js/bun/http/serve-http3.test.ts
  • test/js/bun/http/tls-keepalive-leak-fixture.js
  • test/js/bun/http/tls-keepalive.test.ts
  • test/js/bun/perf/linker-order.test.ts
  • test/js/bun/shell/leak.test.ts
  • test/js/bun/spawn/spawn-socketpair-shutdown.test.ts
  • test/js/bun/spawn/spawn.test.ts
  • test/js/bun/typescript/type-export.test.ts
  • test/js/bun/util/password.test.ts
  • test/js/node/net/handle-leak.test.ts
  • test/js/node/process/dlopen-duplicate-load.test.ts
  • test/js/node/process/process-stdin.test.ts
  • test/js/node/test/parallel/test-dns-setserver-when-querying.js
  • test/js/node/test/parallel/test-stream-iter-broadcast-from.js
  • test/js/node/test/sequential/test-performance-eventloopdelay.js
  • test/js/node/tls/node-tls-cert.test.ts
  • test/js/node/worker_threads/worker_heap_snapshot_gc.test.ts
  • test/js/node/zlib/leak.test.ts
  • test/js/node/zlib/zlib.test.js
  • test/js/sql/sql-connect-error-reporting.test.ts
  • test/js/sql/sql-mysql.test.ts
  • test/js/sql/sql-postgres-datetime-roundtrip.test.ts
  • test/js/sql/sql.test.ts
  • test/js/sql/sqlite-sql.test.ts
  • test/js/third_party/grpc-js/test-idle-timer.test.ts
  • test/js/third_party/grpc-js/test-outlier-detection.test.ts
  • test/js/third_party/grpc-js/test-tonic.test.ts
  • test/js/third_party/prisma/prisma.test.ts
  • test/js/valkey/reliability/protocol-handling.test.ts
  • test/js/valkey/test-utils.ts
  • test/js/valkey/unit/basic-operations.test.ts
  • test/js/web/fetch/body-stream.test.ts
  • test/js/web/fetch/fetch-backpressure.test.ts
  • test/js/web/html/FormData.test.ts
  • test/js/web/websocket/autobahn.test.ts
  • test/js/web/websocket/websocket-syscall-fault.test.ts
  • test/js/web/workers/structured-clone.test.ts
  • test/napi/napi-app/binding.gyp
  • test/napi/napi-finalizer-delete-ref.test.ts
  • test/napi/napi-value-ffi.test.ts
  • test/napi/napi.test.ts
  • test/napi/node-napi-tests/harness.ts
  • test/napi/node-napi-tests/test/js-native-api/test_constructor/do.test.ts
  • test/napi/node-napi-tests/test/js-native-api/test_general/do.test.ts
  • test/napi/uv.test.ts
  • test/regression/issue/22157.test.ts
  • test/regression/issue/26298.test.ts
  • test/regression/issue/30205-napi-app/.gitignore
  • test/regression/issue/30205-napi-app/binding.gyp
  • test/regression/issue/30205-napi-app/isolate_finalizer_addon.c
  • test/regression/issue/30205-napi-app/package.json
  • test/regression/issue/30205.test.ts
  • test/tsconfig.json
💤 Files with no reviewable changes (2)
  • test/napi/napi-app/binding.gyp
  • test/js/bun/shell/leak.test.ts

Comment thread test/bundler/cli.test.ts Resolved
Comment thread test/bundler/cli.test.ts Resolved
Comment thread test/cli/install/bun-upgrade.test.ts Resolved
Comment thread test/docker/coordinator.ts Resolved
Comment thread test/js/valkey/test-utils.ts Resolved
Comment thread test/napi/napi-value-ffi.test.ts Resolved
Comment thread test/napi/node-napi-tests/harness.ts Resolved
Comment thread test/napi/node-napi-tests/harness.ts Resolved
…e test edits

- bun-security-scanner-matrix-runner.ts: revert to serial test() with shared
  registry. Bun's test runner rejects toMatchSnapshot() inside test.concurrent
  and this file has 1440+ snapshot entries that cannot be converted inline.
- test-dns-setserver-when-querying.js: revert to upstream. The stub-server
  approach fails with ECONNREFUSED on the dgram socket when c-ares closes its
  ephemeral port after dns.setServers(), and on ASAN it exposed a null deref
  in dns.rs remove_timer. Files under test/js/node/test/parallel/ are upstream
  fixtures.
- test-stream-iter-broadcast-from.js: revert to upstream for the same reason.
- napi harness tryBuildFast: catch spawn() throwing (missing compiler) so the
  node-gyp fallback still runs instead of the whole test file erroring out.
@Jarred-Sumner

Copy link
Copy Markdown
Collaborator Author

@robobun can we try making only some of the known safe tests parallel? Look at test runs for the files that are quick where spawn overhead of the test itself is the main factor and let's run those in parallel. We can allowlist these test files in another test/*.txt file and then we run the parallel safe ones together? What do you think of the right way to approach parallelizing tests? You can look at timestamps from flushed BuildKite logs along with the test reported timings themselves

@robobun

robobun commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Ran the numbers on linux-x64-debian-13 build #69493 using the BuildKite _bk;t= timestamps for per-file wall clock plus bun test's own [Xms]:

bucket files wall sum notes
test/js/node/test/{parallel,sequential} 2,818 309s p50 48ms, p90 103ms, pure bun <file> startup
bun-test files <300ms 1,504 126s p50 90ms
bun-test files 300ms-1s 230 133s
bun-test files 1-5s 205 438s
bun-test files >5s 124 2,181s 76% of the lane
vendor/ 128 17s

Two observations:

  1. Spawn overhead per file is small. wall - bun_reported has a median of 5ms; bun test's [Xms] already includes almost everything. So "overhead-dominated" really means "the test file itself is quick" — the <300ms bucket.
  2. The 124 slow files are 76% of the lane. File-level parallelism can shave the other 24%, but the long pole stays the long pole.

Estimated savings from 4-way file parallelism inside each of the 20 shards:

  • node/test/parallel: ~15.4s/shard serial → ~3.8s, saves ~11s/shard
  • quick bun-test (<300ms): ~6.3s/shard → ~1.5s, saves ~5s/shard
  • total: ~16s/shard (~320s of machine time), vs a ~150-220s typical shard

On approach, runner.node.mjs already has --parallel (wraps every runTest in pLimit(availableParallelism())). Rather than building a whole new thing I'd extend that:

  • Tier 1 (parallel by default, no allowlist): test/js/node/test/parallel/, test/js/bun/test/parallel/, vendor/. They are parallel by naming convention and Node's own CI runs them that way. This is where most of the win is (~11s/shard) and needs zero curation.
  • Tier 2 (allowlisted bun-test files): a test/runner-parallel-safe.txt listing quick bun-test files that only use port: 0 + tempDir + spawned subprocesses with no RSS/timing assertions and no process.chdir. I can auto-generate a starter list (~1,200 files <300ms) by grepping each for process.chdir|memoryUsage.rss|hardcoded port|snapshot and then it gets hand-pruned.
  • Everything else: serial as today.

The runner change is then: when walking filteredTests, group each maximal run of tier-1/tier-2 files and feed that group through pLimit(n) (n = 4 or availableParallelism()/shards-aware), keeping slow/unsafe files one-at-a-time between groups. This keeps ordering stable for retry/annotation logic and only changes concurrency within safe runs.

An alternative I considered: batching N quick files into one bun test a.ts b.ts c.ts call (eliminates spawn entirely). That saves more per file but a crash in one file kills the batch and failure attribution in the runner gets messy, so I'd start with process-level parallelism.

Want me to implement tier 1 (node-parallel/vendor) in this PR and open a separate one for the allowlist mechanism, or do both here? Tier 1 is a ~20-line runner change with no new files.

@Jarred-Sumner

Copy link
Copy Markdown
Collaborator Author

Tier 1

@Jarred-Sumner

Copy link
Copy Markdown
Collaborator Author

@robobun how do we get the wall clock time down to like 4 mins or less with half the shards?

These ~2.8k files are parallel-safe by naming convention (Node's own CI
runs test/parallel concurrently) and have a ~50ms median dominated by
bun startup. A separate pLimit(min(4, nproc)) for these paths overlaps
that startup without changing the serial contract for every other file.

TEST_SERIAL_ID is now set for every Node-style test so test/common/tmpdir.js
creates distinct .tmp.<index> directories; previously every file used
.tmp.0 which only worked because they ran one at a time.

Local: 63 test-buffer-* files 5.0s serial -> 1.6s at width 4.
@robobun

robobun commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Tier 1 pushed in 6c5559b (local: 63 test-buffer-* files 5.0s serial → 1.6s at width 4; added TEST_SERIAL_ID so common/tmpdir.js does not collide).

On 4 min wall clock with 10 shards (linux-x64-debian-13, build #69493 timings, 4880 files, 3175s serial total):

config longest shard note
today: 20 shards, round-robin, serial 268s status quo
20 shards, round-robin, +tier-1 width-4 259s tier 1 alone
20 shards, LPT bin-pack, serial 184s time-aware sharding alone
20 shards, LPT + tier-1 169s
10 shards, round-robin, serial 429s halving shards today
10 shards, round-robin, +tier-1 406s
10 shards, LPT, serial 343s
10 shards, LPT + tier-1 313s
10 shards, LPT + tier-1 + shrink >5s files by 35% ~240s target

(Assumes ~25s/shard of fixed overhead: clone + bun install + artifact download, from log timestamps.)

So three levers, in descending leverage:

  1. Time-aware sharding (LPT bin-packing by historical per-file time instead of index % shards): 268s→184s at 20 shards, 429s→343s at 10. Biggest single win and orthogonal to everything else. Needs a test/expected-durations.json checked in (regenerated from BuildKite periodically) that getRelevantTests() reads to bin-pack instead of round-robin.
  2. Shrink the 124 files >5s (they are 2181s = 69% of the lane). This is what this PR already does via within-file test.concurrent and smaller workloads. Hitting 240s@10 needs roughly a 35% cut across that bucket. Top targets remaining: bundler_compile 107s, type-export 92s, bake/dev/{hot,css,bundle} 183s combined, bun-install 59s, test-tonic 58s, fetch-leak 50s, in-process-cron 48s.
  3. Tier 1 runner parallelism (just pushed): modest on its own (~10-15s/shard) but free and composable.

Hard floor: the single slowest file (bundler_compile.test.ts 107s) plus the 25s fixed overhead is 132s, so one shard can never go below that until that file is split or internally concurrent. Getting to 4 min at 10 shards is reachable with (1)+(3) plus about a third off the >5s bucket; getting much below that needs the top few monster files broken up.

Want me to add the LPT bin-packing (lever 1) to this PR as well? It is a contained runner change: generate test/expected-durations.json once from a recent build, then replace the i % maxShards loop with longest-processing-time-first assignment.

@Jarred-Sumner

Copy link
Copy Markdown
Collaborator Author

@robobun yes and can we delineate between Asan and non Asan and also write a weekly BuildKite thing that only runs on oven-sh/bun that submits a PR updating this file? Or maybe we only store it as an artifact in a recurring BuildKite job itself?

Replaces index % maxShards with longest-processing-time-first assignment
driven by test/expected-durations.json, a per-file median wall-clock table
with separate default and asan columns. Every shard deterministically
computes the same assignment and picks its own bin; unknown files fall
back to the table median so new tests still spread evenly.

On linux-x64-debian-13 timings, 10 shards go from a 497s longest /
159s shortest round-robin spread to 315s / 315s with LPT (asan:
1294s..877s -> 1062s flat).

scripts/update-test-durations.mjs regenerates the table from the last N
complete Buildkite runs of the release + asan linux-x64 lanes; the
checked-in file was generated from builds 69524/69519/69515.
.buildkite/update-test-durations.yml is a standalone pipeline for a
weekly schedule that reruns the script and uploads the result as an
artifact.
@robobun

robobun commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Done in cccae2f:

  • test/expected-durations.json (458 KB, 4755 files) with {path: {default, asan}} medians from three recent complete builds.
  • scripts/update-test-durations.mjs regenerates it from the last N Buildkite runs of the release + asan linux-x64 lanes.
  • scripts/runner.node.mjs: sharding is now LPT bin-packing over that table instead of i % maxShards. Picks the asan column when --step contains asan, otherwise default. Unknown files use the table median so new tests still spread. Every shard computes the identical deterministic assignment independently.
  • .buildkite/update-test-durations.yml: standalone pipeline step gated on build.source == "schedule" || "ui" that runs the script and buildkite-agent artifact uploads the json. Attach a weekly schedule to it in the Buildkite UI (it is intentionally not wired into ci.mjs so it never runs on PRs). When the committed file drifts, download the latest artifact and commit it.

Simulated balance on the current test set:

lane 10 shards round-robin 10 shards LPT
default 159s..497s (spread 338s) 315s..315s
asan 877s..1294s (spread 417s) 1062s..1062s

I went with artifact-upload for the recurring job rather than auto-PR; the runner reads the checked-in copy, so the artifact is for humans to pull when rebalancing, not a runtime dependency. Happy to switch to auto-PR if you want it fully hands-off, but that needs a bot token with push rights wired into the scheduled pipeline.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/runner.node.mjs`:
- Around line 589-595: `limitFor` in `scripts/runner.node.mjs` should honor the
global `--parallel` limiter before any test-specific limiter selection. Update
the logic around `parallelSafeLimit`/`limit` so that when `parallelism` is
enabled, both parallel-safe and non-parallel-safe tests route through the same
global width, preserving the “supersedes this” contract. Keep
`isParallelSafeTest` and `limitFor` as the main entry points, but make the
limiter choice depend on the active parallel mode rather than always allowing
`parallelSafeLimit`.

In `@scripts/update-test-durations.mjs`:
- Around line 108-130: The collect() worker currently lets a single failed api()
call for a job log abort the entire update-test-durations run, losing all
previously gathered samples. Update the worker inside collect() to catch errors
per job when fetching builds/${build}/jobs/${j.id}/log.txt, log or skip
transient failures like 429/5xx, and continue processing the remaining jobs
instead of letting Promise.all fail the whole batch.
- Around line 46-52: The Buildkite request helper api() currently uses fetch
without any timeout or AbortSignal, so a stalled request can hang the scheduled
job indefinitely. Update the api() helper to create and pass an AbortSignal with
a reasonable timeout, and ensure the timeout is cleared/handled around the
existing fetch and response error handling so requests fail fast instead of
waiting forever.
- Around line 93-106: The source build selection in findSourceBuilds() is
pulling builds from every branch, which can skew the sampled durations. Update
the builds API request in that function to filter to the default branch (for
example, add branch=main or the pipeline’s default branch) so only
representative builds are considered before collecting picked build numbers.
- Around line 58-82: parseLog currently hardcodes the group-header and
end-marker formats from startGroup(), so it can silently return wrong durations
if scripts/utils.mjs::startGroup() or scripts/runner.node.mjs changes. Tighten
this contract by either sharing the same formatter/parser helper between
parseLog and startGroup, or by adding explicit validation in parseLog for the
expected marker shape before computing durations. Keep the fix centered around
parseLog and the startGroup output format so changes fail clearly instead of
producing bad data.

In `@test/napi/node-napi-tests/harness.ts`:
- Around line 134-141: The direct compile fallback path in harness.ts waits on
child.exited before reading child.stderr, which can deadlock if the stderr pipe
fills. Update the direct compile logic and buildWithNodeGyp to start draining
stderr in parallel with the child process execution, then await both the exit
and the stderr read before deciding whether to fall back. Use the child,
child.exited, and child.stderr handling in these functions as the main places to
fix.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 52860a36-0eda-45a4-8275-af547c3a347d

📥 Commits

Reviewing files that changed from the base of the PR and between 5017b79 and 88f5a73.

📒 Files selected for processing (7)
  • .buildkite/update-test-durations.yml
  • .gitattributes
  • .prettierignore
  • scripts/runner.node.mjs
  • scripts/update-test-durations.mjs
  • test/expected-durations.json
  • test/napi/node-napi-tests/harness.ts

Comment thread scripts/runner.node.mjs Resolved
Comment thread scripts/update-test-durations.mjs Resolved
Comment thread scripts/update-test-durations.mjs Resolved
Comment thread scripts/update-test-durations.mjs Resolved
Comment thread scripts/update-test-durations.mjs Resolved
Comment thread test/napi/node-napi-tests/harness.ts Resolved
robobun added 2 commits July 7, 2026 07:33
…derr concurrently, harden duration generator

- runner: when --parallel is set, route parallel-safe tests through the
  same pLimit instance as everything else instead of a second limiter of
  the same width (two limiters at width N can admit 2N processes).
- napi harness: drain stderr concurrently with child.exited in both
  tryBuildFast and buildWithNodeGyp so a verbose failing compile cannot
  fill the pipe buffer and wedge.
- update-test-durations: 60s AbortSignal timeout on API requests; skip a
  single shard log on transient fetch failure instead of aborting the
  whole run; fail loudly if <1000 paths are parsed so a log-format change
  cannot silently produce an empty table. Not filtering to branch=main
  because main builds skip the test-bun steps (documented in-source).
4-wide surfaced new retry-flakes on the Windows lanes in tight-timeout
and stdin-write node/test/parallel tests (test-repl-close EPIPE,
test-https-proxy-request*, test-https-timeout) that did not flake on
seven recent baseline builds. Process creation is heavier on Windows and
the shards there are fewer (8 vs 20), so 4 concurrent bun processes
contend enough to push those races over. Posix stays at 4.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/runner.node.mjs (1)

2121-2135: 🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

Read test/expected-durations.json here. cwd resolves to the repo root, so join(cwd, "expected-durations.json") misses the file written by scripts/update-test-durations.mjs and falls back to index sharding, undoing the LPT balancing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/runner.node.mjs` around lines 2121 - 2135, The expected durations
file is being read from the wrong location in the sharding setup, so the loader
in runner.node.mjs falls back to index-based sharding instead of LPT balancing.
Update the JSON read in the duration-loading block to use the
test/expected-durations.json path that matches
scripts/update-test-durations.mjs, and keep the rest of the duration parsing
logic in the same section around costOf and the durations map unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@scripts/runner.node.mjs`:
- Around line 2121-2135: The expected durations file is being read from the
wrong location in the sharding setup, so the loader in runner.node.mjs falls
back to index-based sharding instead of LPT balancing. Update the JSON read in
the duration-loading block to use the test/expected-durations.json path that
matches scripts/update-test-durations.mjs, and keep the rest of the duration
parsing logic in the same section around costOf and the durations map unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 17e0e4d6-b919-4ff4-8412-3a870e26527e

📥 Commits

Reviewing files that changed from the base of the PR and between 88f5a73 and cdb060d.

📒 Files selected for processing (3)
  • scripts/runner.node.mjs
  • scripts/update-test-durations.mjs
  • test/napi/node-napi-tests/harness.ts

The eight backend:"cli" cases each spawn a full bun build --compile link
(hundreds of MB); running them all at once drove the linux lanes to
SIGTERM at ~65s on six variants. expectBundled already forces
backend:"api" to it.serial, so describe.concurrent only overlapped the
CLI cases.
@Jarred-Sumner

Copy link
Copy Markdown
Collaborator Author

@robobun looks like there are newly flaky tests to address + windows has more test time variance (should windows gets its own section separate from default and Asan)?

…ndows durations column

runner: split test execution into two non-overlapping phases. Phase 1
runs every non-parallel-safe test through the serial limiter; phase 2
then runs js/{node,bun}/test/parallel/ through the N-wide limiter. The
previous two-independent-limiters design let up to N parallel-safe
processes run alongside a serial test, which surfaced new retry-flakes
in tight-timeout and port-bind tests (dns-tcp-bidirectional-poll,
test-https-timeout, test-repl-close) that did not flake on 10 baseline
builds.

bun-upgrade.test.ts: revert describe.concurrent. Concurrent
copyFile(bunExe(), execPath) + spawn(execPath) races to ETXTBSY on linux.

bun-install-proxy.test.ts: revert to the dedicated docker run on
hardcoded :3128. The shared ensure("squid") path flaked ConnectionClosed
4x in one build vs 2x across 10 baseline builds; dropping the prestart-map
entry too.

zlib.test.js brotli streaming: set BROTLI_PARAM_QUALITY 4 so 8 MB of
random input completes inside the 15s budget on a contended runner; the
assertion is quality-independent.

expected-durations: add a windows column (windows-x64-2019 lane). Process
spawn is ~2x slower than linux there and many tests skipIf(isWindows), so
packing with the linux timings left 150-300s of spread across the 8
Windows shards. runner.node.mjs picks the windows column when the step is
a windows lane. Regenerated from builds 69636/69628/69627.
@robobun

robobun commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Both addressed in 584bb9f:

Newly flaky tests (compared against 10 baseline builds):

  • bun-upgrade.test.ts (ETXTBSY from concurrent copyFile(bunExe())+spawn): reverted to serial
  • bun-install-proxy.test.ts (4x ConnectionClosed via shared squid, vs 2x/10 baseline): reverted to its original dedicated docker run on :3128
  • zlib.test.js brotli streaming timeout: set BROTLI_PARAM_QUALITY: 4 so 8 MB finishes inside 15s on a contended runner (assertion is quality-independent)
  • dns-tcp-bidirectional-poll, test-https-timeout, test-repl-close: these flaked because the two-limiter design let up to N node/test/parallel processes run alongside a serial test. Rewrote the execution into two non-overlapping phases: serial tests first (one at a time), then the parallel-safe batch N-wide. No more overlap.

Everything else in #69770's flaky list (update_interactive, bun-install, bun-install-registry, webview-chrome, cron, dev-and-prod) flakes at the same or higher rate across 10 baseline builds.

Windows durations column: yes, added. expected-durations.json now has {default, asan, windows} per file; the runner picks windows on Windows lanes. Windows process spawn is ~2x linux (e.g. test-buffer-alloc.js: 49ms linux / 87ms windows / 240ms asan) and a lot of tests skipIf(isWindows), so packing with linux timings left 164-308s of spread across the 8 Windows shards even after LPT. Regenerated from builds 69636/69628/69627.

For reference, #69770 measured wall clock (20 shards linux, 8 shards windows) vs baseline #69493:

lane longest shard sum
linux-x64-debian 308s → 228s 3811s → 3150s
linux-x64-asan 718s → 517s 11312s → 8261s
win-x64 701s → 559s 4521s → 3872s
win-aarch64 865s → 772s 5979s → 5061s

@Jarred-Sumner
Jarred-Sumner merged commit c464531 into main Jul 7, 2026
75 of 78 checks passed
@Jarred-Sumner
Jarred-Sumner deleted the jarred/cant branch July 7, 2026 19:24
@robobun

robobun commented Jul 26, 2026

Copy link
Copy Markdown
Collaborator

Heads up: the N-wide phase this introduced for js/*/test/parallel/ ended up overlapping test-docker-build-debian.ts with test-buffer-constants.js on shard 19, and the 2 GiB string allocation gets OOM-killed on the 8 GiB no-swap x64 runners. #35944 routes both through the serial phase.

@robobun

robobun commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

Since the tests in process-stdin.test.ts run concurrently, the one-chunk snapshot of "stdin with 'readable' event handler should receive data when paused" splits into two chunks on Windows (the second write() leaves after the other tests' Bun.spawn calls). #42260 makes that test independent of chunk boundaries.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants