Skip to content

test: run the bytecode portability builds concurrently and share them between tests - #40898

Merged
Jarred-Sumner merged 1 commit into
mainfrom
robobun/a0ea52b1/faster-bytecode-portable-test
Aug 30, 2026
Merged

Jarred-Sumner merged 1 commit into
mainfrom
robobun/a0ea52b1/faster-bytecode-portable-test

Conversation

@robobun

@robobun robobun commented Aug 29, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

  • test/bundler/bundler_bytecode_portable.test.ts takes 34s on the x64-asan lane (build #108217). Locally: 342s with the debug build, 5.6s with the release binary.
  • The snapshot test spawned its 16 bun build --bytecode processes one after another. The load and reject tests then bundled the same entries again. The libraries.js build alone takes 88s in the debug build, and the file ran it twice.

Fix

  • The snapshot test starts all 16 builds at once and encodes the in-process cases while they run. It collects the results in list order and compares the same inline snapshot.
  • build() memoizes one build per entry in a file-level temp dir. The snapshot, load and reject tests share it: 24 spawned builds per run instead of 37. A test run alone with -t still starts what it needs.
  • bundle() names the command in its stderr, exit code and output listing checks, so a failed build fails with the build error, not a fingerprint mismatch. The --compile tests require empty stderr, not not.toContain("error").
  • Verified: bun bd test test/bundler/bundler_bytecode_portable.test.ts 341.6s before, 133 to 138s after (3 runs, 22 pass). Release binary: 5.6s before, 2.8s after.

Background

  • The file pins the JSC bytecode cache bytes of a corpus so CI can compare them across platforms. The snapshot block and the corpus are byte-identical to main.
  • Each bun build --bytecode is a child process. The in-process encodes are synchronous JS: they cannot overlap each other, but they can overlap child processes.
Notes

Timing of the unchanged file, debug build with --timeout=270000 (the CI asan value): 341.6s total. Snapshot test 189.2s, process test 41.1s, concurrent group about 111s, of which the libraries.js load test took 107.6s (88s bundle plus 19s run). The 5s default timeout had to be raised: with it, the concurrent tests time out locally.

Per-build times, debug, sequential (147s total): libraries.js 88s, svelte 11.6s, happy-dom 11s, all.js 9.2s, --minify all.js 8.7s, react-dom 5.7s, the rest under 3s. The same 16 builds with Promise.all: 92s wall, that is the libraries.js build. For libraries.js, bundling without --bytecode takes 7s, so the rest is bytecode generation.

In-process encodes, debug: vm.Script typescript.js 37s, the internal modules loop 18s, the others under 1.3s. Everything in-process is about 41s and is now hidden behind the 92s of builds.

After the change, debug: snapshot test 95s, process test 20.5s, concurrent group about 20s (libraries.js load test 19.6s, run only). Three full runs: 136.4s, 133.4s, 133.8s, 22 pass each. Release binary: snapshot test 3.0s to 2.0s, process test 0.6s to 0.3s, libraries.js load test 1.9s to 0.4s.

Memory: peak RSS per build process is 30 to 90 MB with the release binary (libraries.js 551 MB, sum of all 16 about 1.2 GB) and about 340 MB under ASAN (libraries.js 1.0 GB, sum 5.7 GB). The x64 release test lane has 8 GB, the asan lanes 64 GB.

Redundant entries: none. All 16 js hashes and all 26 fingerprints in the snapshot are distinct, so no entry was removed. The redundant work was the same entry bundled by several tests.

Failure modes, probed on purpose. A missing entry fails the snapshot test and the matching load test with: error: `bun build --bytecode ./missing.js` wrote to stderr followed by the bundler's ModuleNotFound line. A changed fingerprint fails with the existing snapshot diff, which shows the entry key as context and both sha256 values, so no second message was added for it. dumpPayloads() still prints every payload on a mismatch.

bun build ignores unknown flags such as --bogus-flag (exit 0). That is unrelated and not changed here.

tsc -p test/tsconfig.json reports the same two pre-existing createCachedData errors on this file as on main.


no test proof · iteration 0 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/bundler/bundler_bytecode_portable.test.ts

@coderabbitai

coderabbitai Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

  • Run on-demand review

On-demand reviews are free for the next 22 days. After that, they cost $0.25 per reviewed file.

Or wait 45 seconds for your next included review.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ae2eee49-f5ec-4853-b17a-288ad69ff15e

📥 Commits

Reviewing files that changed from the base of the PR and between 1ab272b and 6ae2b14.

📒 Files selected for processing (1)
  • test/bundler/bundler_bytecode_portable.test.ts

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

@robobun

robobun commented Aug 29, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 4:18 AM PT - Aug 29th, 2026

❌ @robobun, your commit 6ae2b14 has 1 failures in Build #108308 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 40898

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

bun-40898 --bun

@robobun

robobun commented Aug 29, 2026 •

Copy link
Copy Markdown
Collaborator Author

Status: test-only change, ready for review.

Verified locally with bun bd test test/bundler/bundler_bytecode_portable.test.ts (debug build, --timeout=270000): 22 pass in 133 to 138s on three runs, against 341.6s before the change. With the release binary (USE_SYSTEM_BUN=1): 2.8s, against 5.6s before. The inline snapshot block and the corpus directory are byte-identical to main.

CI (build #108308), per-file time of this test, 22 pass on every lane:

  • debian 13 x64-asan: 11.4s (34s in build #108217)
  • debian 13 x64: 9.9s (11 to 12s)
  • darwin x64: 3.2s (11 to 12s)
  • windows 2019 x64: 8.6s (11 to 12s)
  • windows 11 aarch64: 19.5s (22s)
  • debian 13 aarch64 and ubuntu 25.04 aarch64: 7.5s (7 to 8s)

The build is red for a reason outside this diff: test/js/web/url/url.test.ts fails on darwin x64 on every attempt and also fails on main there (reported to main-break triage). The other failures passed on retry or when run alone (h2-conformance, fetch-leak, bun-inspector-protocol, the shell command tests on alpine x64, and others). None of them touch this file.

@claude claude 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.

LGTM — test-only speedup that shares bundler builds across tests without weakening any assertion.

What was reviewed:

  • Snapshot ordering: outputs insertion order changed (vm.Script entries now populated before bundler builds), but the inline snapshot serializer sorts keys alphabetically, so the existing snapshot still matches.
  • Shared jsc buffer in the reject tests: spoil(Buffer.from(jsc)) copies before mutating, so the three concurrent variants don't clobber each other's cached bytes.
  • Memoization: build() keys on name (unique across bundlerBuilds) and reads builds.size synchronously before set, so per-build outdirs never collide even under test.concurrent.
  • The --compile stderr check moves from not.toContain("error") to toBe("") — a strengthening, matching what bundle() already asserts.
Extended reasoning...

Overview

This PR refactors a single test file, test/bundler/bundler_bytecode_portable.test.ts, to cut its wall time roughly in half by (a) launching all bun build --bytecode child processes concurrently and doing the in-process vm.Script/SourceTextModule encoding while they run, and (b) memoizing each corpus build in a file-lifetime tempDir so the snapshot test, the per-entry load tests, the determinism test's reference build, and the .jsc rejection tests all share one build per entry instead of re-bundling. bundle() now labels its assertions with the invoked command and returns the output path; the --compile test's local build variable is renamed to compile to avoid shadowing the new helper. No production code is touched.

Security risks

None. The change is confined to test orchestration: it rearranges when child processes are spawned and where their outputs land on disk, and adds assertion labels. It introduces no new inputs, no network access, no credential handling, and no changes to what is being asserted (other than tightening one stderr check).

Level of scrutiny

Moderate — the file pins JSC bytecode fingerprints across platforms, so the main risk of a refactor here is silently weakening coverage or making the snapshot pass for the wrong reason. I checked each of those failure modes: the inline snapshot is byte-identical (key order is serializer-sorted, so the changed outputs insertion order is irrelevant); dumpPayloads() still sees every payload because fingerprint() is still called on every entry; the memoized build() uses name as its key and every bundlerBuilds entry has a distinct name; each build gets its own numbered subdirectory under buildsDir so readdirSync(outdir) still sees exactly two files; the reject tests write the shared js alongside the spoiled jsc into a fresh temp dir and copy the buffer before mutating it. The determinism test's reference now reuses the memoized default-env build of bundlerBuilds[0], which is semantically identical to the fresh bundle() it replaced. The synchronous vm encoding block means the un-awaited Promise.all cannot reject before a handler is attached, so there is no unhandled-rejection window.

Other factors

The change follows the repo's test conventions: tempDir from harness with explicit afterAll disposal for the file-lifetime dir, await using on spawned processes, stderr asserted before exit code, test.concurrent for independent subprocess tests, and {...bunEnv, ...} spreads. No CODEOWNERS entry covers this path. The PR description reports three passing bun bd test runs (22 pass each) and release-binary timings, and the bug-hunting pass exited on a dry streak with no findings.

@Jarred-Sumner
Jarred-Sumner merged commit badb2fa into main Aug 30, 2026
5 of 7 checks passed
@Jarred-Sumner
Jarred-Sumner deleted the robobun/a0ea52b1/faster-bytecode-portable-test branch August 30, 2026 09:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants