-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Rewrite test/describe, add test.concurrent #22534
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
src/cli/test_command.zig
Outdated
_ = file_start; | ||
_ = file_end; | ||
_ = is_last; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
were these meant to be used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These used to be used
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
♻️ Duplicate comments (13)
src/bun.js/DeprecatedStrong.zig (3)
71-75
: Optional.swap() inherits the swap() GC window.This forwards to
Strong.swap()
, so fixingStrong.swap()
will fix this too. No change needed here after the upstream fix.
26-30
: Fix GC window and avoid double-protect in swap().You unprotect the old value before the new value is protected, creating a gap where neither is protected. Also, calling
.init()
after protectingnext
would double‑protect it. Protectnext
first, tear down the current state manually, then install the new state without.init()
.pub fn swap(this: *Strong, safety_gpa: std.mem.Allocator, next: jsc.JSValue) jsc.JSValue { - const prev = this.#raw; - this.deinit(); - this.* = .init(safety_gpa, next); - return prev; + const prev = this.#raw; + + // 1) Close GC window by protecting the new value first. + next.protect(); + + // 2) Tear down current protection/safety (equivalent to deinit, but after next is protected). + this.#raw.unprotect(); + if (enable_safety) if (this.#safety) |safety| { + bun.assert(@intFromEnum(safety.ptr.*.#raw) == 0xAEBCFA); + safety.ptr.*.#raw = @enumFromInt(0xFFFFFF); + bun.assert(safety.ref_count == 1); + safety.gpa.destroy(safety.ptr); + }; + + // 3) Install new state without calling .init() to avoid double-protect. + const new_safety: Safety = if (enable_safety) + .{ .ptr = bun.create(safety_gpa, Strong, .{ .#raw = @enumFromInt(0xAEBCFA), .#safety = null }), .gpa = safety_gpa, .ref_count = 1 }; + this.* = .{ .#raw = next, .#safety = new_safety }; + return prev; }
41-51
: unref(): use pointer binding and clear #safety to avoid UAF.Same copy-mutation bug as in ref(). Additionally, after destroying
safety.ptr
,this.#safety
still points at freed memory.pub fn unref(this: *Strong) void { this.#raw.unprotect(); - if (enable_safety) if (this.#safety) |safety| { - if (safety.ref_count == 1) { + if (enable_safety) if (this.#safety) |*safety| { + if (safety.ref_count == 1) { bun.assert(@intFromEnum(safety.ptr.*.#raw) == 0xAEBCFA); safety.ptr.*.#raw = @enumFromInt(0xFFFFFF); safety.gpa.destroy(safety.ptr); - return; + this.#safety = null; // prevent dangling reference + return; } safety.ref_count -= 1; }; }test/js/bun/test/bun_test.fixture.ts (2)
155-159
: Test assertion count with timeout is contradictoryThis always times out before the assertion can run; the expect.assertions(1) never gets verified. Adjust timeout/sleep so the assertion executes.
test("expect.assertions combined with timeout", async () => { expect.assertions(1); - await Bun.sleep(100); -}, 1); + await Bun.sleep(1); + expect(true).toBe(true); +}, 10);
247-248
: Empty test intentionally triggers afterEach failure — mark as failing for clarityMake intent explicit so CI output is clearer.
- test("fails when completion is not incremented", () => {}); + test.failing("expected to fail: completion not incremented", () => {});src/bun.js/bindings/JSValue.zig (1)
2375-2379
: Wrap throwable host calls; don’t expose raw C++ functionsbindings.cpp uses throw scopes here; call through fromJSHostCall{Generic} to propagate exceptions, and add brief docs.
- pub fn bind(this: JSValue, globalObject: *JSGlobalObject, bindThisArg: JSValue, name: *const bun.String, length: f64, args: []JSValue) bun.JSError!JSValue { - return bun.cpp.Bun__JSValue__bind(this, globalObject, bindThisArg, name, length, args.ptr, args.len); - } - pub const setPrototypeDirect = bun.cpp.Bun__JSValue__setPrototypeDirect; + /// Bind a callable to a receiver. May throw on allocation or if not callable. + pub fn bind( + this: JSValue, + globalObject: *JSGlobalObject, + bindThisArg: JSValue, + name: *const bun.String, + length: f64, + args: []JSValue, + ) bun.JSError!JSValue { + return bun.jsc.fromJSHostCallGeneric( + globalObject, + @src(), + bun.cpp.Bun__JSValue__bind, + .{ this, globalObject, bindThisArg, name, length, args.ptr, args.len }, + ); + } + /// Set object prototype directly. May throw. + pub fn setPrototypeDirect(this: JSValue, globalObject: *JSGlobalObject, proto: JSValue) bun.JSError!void { + return bun.jsc.fromJSHostCallGeneric(globalObject, @src(), bun.cpp.Bun__JSValue__setPrototypeDirect, .{ this, globalObject, proto }); + }#!/bin/bash # Verify C++ sides can throw; if so, wrappers are required. rg -n -C3 'Bun__JSValue__bind|Bun__JSValue__setPrototypeDirect' src/bun.js/bindings/bindings.cpp | rg -n 'DECLARE_THROW_SCOPE|RETURN_IF_EXCEPTION|propagate'src/bun.js/api/Timer/EventLoopTimer.zig (1)
220-224
: Use explicit @alignCast + @as for parent pointer; add a compile-time guard.
@fieldParentPtr("timer", self)
should follow the established pattern with@alignCast
+ explicit cast to avoid misalignment UB and improve type safety. Also assert thatBunTest.timer
exists and isEventLoopTimer
.Apply this diff:
- var container_strong = jsc.Jest.bun_test.BunTestPtr.cloneFromRawUnsafe(@fieldParentPtr("timer", self)); - defer container_strong.deinit(); - return jsc.Jest.bun_test.BunTest.bunTestTimeoutCallback(container_strong, now, vm); + comptime { + const BT = jsc.Jest.bun_test.BunTest; + if (!@hasField(BT, "timer")) @compileError("BunTest must have a 'timer' field"); + if (@FieldType(BT, "timer") != Self) @compileError("BunTest.timer must be EventLoopTimer"); + } + const bt = @as(*jsc.Jest.bun_test.BunTest, @alignCast(@fieldParentPtr("timer", self))); + var container_strong = jsc.Jest.bun_test.BunTestPtr.cloneFromRawUnsafe(bt); + defer container_strong.deinit(); + return jsc.Jest.bun_test.BunTest.bunTestTimeoutCallback(container_strong, now, vm);src/bun.js/bindings/bindings.cpp (2)
6760-6775
: Require callable target and sanitize length before creating bound functionCreating a JSBoundFunction from a non-callable target risks assertion/crash in JSC; throw a TypeError if not callable.
Clamp and cast the double length to a non-negative unsigned to avoid UB/overflow from negative values.Apply:
CPP_DECL [[ZIG_EXPORT(zero_is_throw)]] JSC::EncodedJSValue Bun__JSValue__bind(JSC::EncodedJSValue functionToBindEncoded, JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue bindThisArgEncoded, const BunString* name, double length, JSC::EncodedJSValue* args, size_t args_len) { auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); JSC::JSValue value = JSC::JSValue::decode(functionToBindEncoded); - if (!value.isCallable() || !value.isObject()) { - throwTypeError(globalObject, scope, "bind() called on non-callable"_s); + if (!value.isCallable()) { + throwTypeError(globalObject, scope, "bind() target must be callable"_s); RELEASE_AND_RETURN(scope, {}); } SourceCode bindSourceCode = makeSource("bind"_s, SourceOrigin(), SourceTaintedOrigin::Untainted); - JSC::JSObject* valueObject = value.getObject(); + JSC::JSObject* valueObject = value.getObject(); JSC::JSValue bound = JSC::JSValue::decode(bindThisArgEncoded); - auto boundFunction = JSBoundFunction::create(globalObject->vm(), globalObject, valueObject, bound, ArgList(args, args_len), length, jsString(globalObject->vm(), name->toWTFString()), bindSourceCode); + const unsigned lengthUnsigned = length < 0 ? 0u : static_cast<unsigned>(length); + auto nameStr = name ? name->toWTFString() : WTF::emptyString(); + auto boundFunction = JSBoundFunction::create(globalObject->vm(), globalObject, valueObject, bound, ArgList(args, args_len), lengthUnsigned, jsString(globalObject->vm(), nameStr), bindSourceCode); + RETURN_IF_EXCEPTION(scope, {}); RELEASE_AND_RETURN(scope, JSC::JSValue::encode(boundFunction)); }
6777-6787
: Guard inputs to avoid null deref and enforce prototype typeCalling setPrototypeDirect on a non-object will dereference a null JSObject. Also ensure prototype is object or null.
Apply:
CPP_DECL [[ZIG_EXPORT(check_slow)]] void Bun__JSValue__setPrototypeDirect(JSC::EncodedJSValue valueEncoded, JSC::EncodedJSValue prototypeEncoded, JSC::JSGlobalObject* globalObject) { auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); JSC::JSValue value = JSC::JSValue::decode(valueEncoded); JSC::JSValue prototype = JSC::JSValue::decode(prototypeEncoded); - JSC::JSObject* valueObject = value.getObject(); - valueObject->setPrototypeDirect(globalObject->vm(), prototype); - RELEASE_AND_RETURN(scope, ); - return; + if (!value.isObject()) { + throwTypeError(globalObject, scope, "setPrototypeDirect() target must be an object"_s); + RELEASE_AND_RETURN(scope, ); + } + if (!(prototype.isNull() || prototype.isObject())) { + throwTypeError(globalObject, scope, "prototype must be an object or null"_s); + RELEASE_AND_RETURN(scope, ); + } + JSC::JSObject* valueObject = value.getObject(); + valueObject->setPrototypeDirect(globalObject->vm(), prototype); + RELEASE_AND_RETURN(scope, ); }src/bun.js/test/Collection.zig (1)
86-97
: Fix: scope reset uses the wrong value (no-op today).
runOneCompleted
should restoreactive_scope
fromdata.collection.active_scope
, but it assignsprev_scope = this.active_scope
, making the reset a no-op.Apply:
- const prev_scope: *DescribeScope = switch (data) { - .collection => this.active_scope, + const prev_scope: *DescribeScope = switch (data) { + .collection => |c| c.active_scope, else => { bun.assert(false); // this probably can't happen return; }, };src/bun.js/test/DoneCallback.zig (2)
1-3
: [Duplicate] Add struct declaration and document ref field.Same concern as previously noted: wrap fields in a struct and document ref ownership/lifetime.
1-3
: Blocker: Missing struct wrapper for DoneCallback fields.The file declares fields at top-level; these must live inside a struct. Wrap them and export the type to avoid undefined type usage.
Apply:
-/// value = not called yet. null = done already called, no-op. -ref: ?*bun_test.BunTest.RefData, -called: bool = false, +pub const DoneCallback = struct { + /// value = not called yet. null = done already called, no-op. + ref: ?*bun_test.BunTest.RefData, + called: bool = false, +};src/bun.js/test/ScopeFunctions.zig (1)
106-117
: Pre-allocate and avoid OOM crashing in.each
spread.Propagate allocation errors instead of
handleOom
and useappendAssumeCapacity
after reserving.Apply:
- // Spread array as args_list (matching Jest & Vitest) - bun.handleOom(args_list.ensureUnusedCapacity(try item.getLength(globalThis))); + // Spread array as args_list (matching Jest & Vitest) + const item_len = try item.getLength(globalThis); + try args_list.ensureUnusedCapacity(item_len); var item_iter = try item.arrayIterator(globalThis); var idx: usize = 0; while (try item_iter.next()) |array_item| : (idx += 1) { - bun.handleOom(args_list.append(.init(bunTest.gpa, array_item))); + args_list.appendAssumeCapacity(.init(bunTest.gpa, array_item)); }
🧹 Nitpick comments (13)
src/bun.js/DeprecatedStrong.zig (1)
35-39
: Optional guard (non-blocking): skip protect/unprotect for non-cells.If
#raw
can be non-cell (viainitNonCell
/.zero
), consider early-returning inref()
/unref()
to avoid no-op JSC calls.pub fn ref(this: *Strong) void { + if (!this.#raw.isCell()) return; this.#raw.protect(); if (enable_safety) if (this.#safety) |*safety| { safety.ref_count += 1; }; } pub fn unref(this: *Strong) void { + if (!this.#raw.isCell()) return; this.#raw.unprotect(); if (enable_safety) if (this.#safety) |*safety| { if (safety.ref_count == 1) { bun.assert(@intFromEnum(safety.ptr.*.#raw) == 0xAEBCFA); safety.ptr.*.#raw = @enumFromInt(0xFFFFFF); safety.gpa.destroy(safety.ptr); this.#safety = null; return; } safety.ref_count -= 1; }; }Also applies to: 41-43
test/harness.ts (2)
906-919
: Unused option: archsThe archs option is accepted but never used. Either enforce an architecture allowlist (skip or todo when not matched) or drop the parameter to avoid dead API.
1000-1003
: Include supported images in error for faster triageAugment the error with the known keys to guide test authors.
- throw new Error( - `Image "${image}" is not configured in docker-compose.yml. All test containers must use docker-compose.`, - ); + throw new Error( + `Image "${image}" is not configured in docker-compose.yml. All test containers must use docker-compose. Known images: ${Object.keys(services).join(", ")}` + );test/js/bun/test/bun_test.fixture.ts (1)
95-98
: Fix typo in comment“thisa” → “this”.
- // this is a technical limitation of not using async context. in the future, we could allow thisa + // this is a technical limitation of not using async context. In the future, we could allow this.src/bun.js/test/Collection.zig (1)
110-122
: Consider using move semantics instead of copy+clear patternWhile the current implementation correctly calls
deinit()
on items from failed scopes (line 116), the pattern of copying items and clearing the source list could be more idiomatic. Consider using pop/move semantics to make ownership transfer clearer.- // append queued callbacks, in reverse order because items will be pop()ed from the end - var i: usize = this.current_scope_callback_queue.items.len; - while (i > 0) { - i -= 1; - const item = &this.current_scope_callback_queue.items[i]; - if (item.new_scope.failed) { // if there was an error in the describe callback, don't run any describe callbacks in this scope - item.deinit(); - } else { - bun.handleOom(this.describe_callback_queue.append(item.*)); - } - } - this.current_scope_callback_queue.clearRetainingCapacity(); + // Move queued callbacks (reverse order preserved by processing from end). + while (this.current_scope_callback_queue.popOrNull()) |item| { + if (item.new_scope.failed) { // do not queue callbacks from a failed scope + var moved = item; + moved.deinit(); + } else { + bun.handleOom(this.describe_callback_queue.append(item)); + } + }src/bun.js/test/DoneCallback.zig (1)
23-29
: Initialize all fields explicitly.Ensure called defaults to false on init for clarity.
Apply:
- done_callback.* = .{ .ref = null }; + done_callback.* = .{ .ref = null, .called = false };src/bun.js/test/Order.zig (1)
125-135
: Edge-case: extending previous group requires contiguous sequences only.Guard is good. Consider asserting contiguity to catch planner bugs in debug builds.
Apply:
if (this.previous_group_was_concurrent) { // extend the previous group to include this sequence - if (previous_group.tryExtend(sequences_start, sequences_end)) return; + if (previous_group.tryExtend(sequences_start, sequences_end)) return + else bun.debugAssert(previous_group.sequence_end == sequences_start); }src/cli/test_command.zig (2)
42-61
: Cosmetic: reset styles for skip glyphs.Two branches leave
<d>
active. Close with<r>
for consistent formatting.Apply:
- .skip => Output.prettyFmt("<r><yellow>»<d>", emoji_or_color), + .skip => Output.prettyFmt("<r><yellow>»<r>", emoji_or_color), .todo => Output.prettyFmt("<r><magenta>✎<r>", emoji_or_color), }, else => switch (status.basicResult()) { .pending => Output.prettyFmt("<r><d>(pending)<r>", emoji_or_color), .pass => Output.prettyFmt("<r><green>(pass)<r>", emoji_or_color), .fail => Output.prettyFmt("<r><red>(fail)<r>", emoji_or_color), - .skip => Output.prettyFmt("<r><yellow>(skip)<d>", emoji_or_color), + .skip => Output.prettyFmt("<r><yellow>(skip)<r>", emoji_or_color), .todo => Output.prettyFmt("<r><magenta>(todo)<r>", emoji_or_color), },
715-833
: JUnit nesting logic: ensure suites stack stays in sync under errors.When closing/creating suites, failures in
beginTestSuiteWithLine
/endTestSuite
could desync the stack. Consider guarding with try/errdefer and short-circuiting on OOM to avoid malformed XML.src/bun.js/test/Execution.zig (1)
192-219
: Timeout kill policy: limit to single-sequence groups only.Pragmatic safeguard; align with concurrent semantics. Consider logging which test was terminated for easier debugging.
src/bun.js/test/ScopeFunctions.zig (2)
116-123
: Avoid handleOom for single append too.Propagate instead of crashing.
Apply:
- bun.handleOom(args_list.append(.init(bunTest.gpa, item))); + try args_list.append(.init(bunTest.gpa, item));
164-242
: Enforce collection-phase only usage with clearer errors.Messages are good. Consider including file/scope name for easier triage.
src/bun.js/test/bun_test.zig (1)
398-416
: Timer callback: ensure exceptions don’t suppress re-arming.You correctly disarm and re-run; the err paths call
onUncaughtException
. Consider a finally-style re-arm check if future changes early-return beforerun()
.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
test/js/bun/test/__snapshots__/bun_test.fixture.ts.snap
is excluded by!**/*.snap
📒 Files selected for processing (19)
src/allocators/allocation_scope.zig
(1 hunks)src/bun.js/DeprecatedStrong.zig
(1 hunks)src/bun.js/Strong.zig
(1 hunks)src/bun.js/api/Timer/EventLoopTimer.zig
(5 hunks)src/bun.js/bindings/JSValue.zig
(1 hunks)src/bun.js/bindings/bindings.cpp
(1 hunks)src/bun.js/bindings/generated_classes_list.zig
(1 hunks)src/bun.js/test/Collection.zig
(1 hunks)src/bun.js/test/DoneCallback.zig
(1 hunks)src/bun.js/test/Execution.zig
(1 hunks)src/bun.js/test/Order.zig
(1 hunks)src/bun.js/test/ScopeFunctions.zig
(1 hunks)src/bun.js/test/bun_test.zig
(1 hunks)src/bun.js/test/debug.zig
(1 hunks)src/bun.js/test/expect.zig
(14 hunks)src/cli/test_command.zig
(15 hunks)test/harness.ts
(4 hunks)test/js/bun/test/bun_test.fixture.ts
(1 hunks)test/js/bun/test/bun_test.test.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/bun.js/Strong.zig
- src/bun.js/bindings/generated_classes_list.zig
🧰 Additional context used
📓 Path-based instructions (16)
test/**
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
Place all tests under the test/ directory
Files:
test/js/bun/test/bun_test.fixture.ts
test/js/bun/test/bun_test.test.ts
test/harness.ts
test/js/**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
Place JavaScript and TypeScript tests under test/js/
Files:
test/js/bun/test/bun_test.fixture.ts
test/js/bun/test/bun_test.test.ts
test/js/bun/**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
Place Bun API tests under test/js/bun/, separated by category (e.g., test/js/bun/glob/)
Files:
test/js/bun/test/bun_test.fixture.ts
test/js/bun/test/bun_test.test.ts
test/**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
test/**/*.{js,ts}
: Write tests in JavaScript or TypeScript using Bun’s Jest-style APIs (test, describe, expect) and run with bun test
Prefer data-driven tests (e.g., test.each) to reduce boilerplate
Use shared utilities from test/harness.ts where applicable
Files:
test/js/bun/test/bun_test.fixture.ts
test/js/bun/test/bun_test.test.ts
test/harness.ts
test/js/**
📄 CodeRabbit inference engine (test/CLAUDE.md)
Organize unit tests for specific features under
test/js/
by module
Files:
test/js/bun/test/bun_test.fixture.ts
test/js/bun/test/bun_test.test.ts
**/*.{js,ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Format JavaScript/TypeScript files with Prettier (bun run prettier)
Files:
test/js/bun/test/bun_test.fixture.ts
test/js/bun/test/bun_test.test.ts
test/harness.ts
test/**/*.test.ts
📄 CodeRabbit inference engine (test/CLAUDE.md)
test/**/*.test.ts
: Name test files*.test.ts
and usebun:test
Do not write flaky tests: never wait for arbitrary time; wait for conditions instead
Never hardcode port numbers in tests; useport: 0
to get a random port
When spawning Bun in tests, usebunExe()
andbunEnv
fromharness
Preferasync/await
in tests; for a single callback, usePromise.withResolvers()
Do not set explicit test timeouts; rely on Bun’s built-in timeouts
UsetempDir
/tempDirWithFiles
fromharness
for temporary files and directories in tests
For large/repetitive strings in tests, preferBuffer.alloc(count, fill).toString()
over"A".repeat(count)
Import common test utilities fromharness
(e.g.,bunExe
,bunEnv
,tempDirWithFiles
,tmpdirSync
, platform checks, GC helpers)
In error tests, assert non-zero exit codes for failing processes and usetoThrow
for synchronous errors
Usedescribe
blocks for grouping,describe.each
for parameterized tests, snapshots withtoMatchSnapshot
, and lifecycle hooks (beforeAll
,beforeEach
,afterEach
); track resources for cleanup inafterEach
Useusing
/await using
with Bun resources (e.g., Bun.listen/connect/spawn/serve) to ensure cleanup in tests
Files:
test/js/bun/test/bun_test.test.ts
test/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
test/**/*.test.{ts,tsx}
: Test files must be placed under test/ and end with .test.ts or .test.tsx
In tests, always use port: 0; do not hardcode ports or use custom random port functions
In tests, use normalizeBunSnapshot when asserting snapshots
Never write tests that merely assert absence of "panic" or "uncaught exception" in output
Avoid shell commands (e.g., find, grep) in tests; use Bun.Glob and built-ins instead
Prefer snapshot tests over exact stdout equality assertions
Files:
test/js/bun/test/bun_test.test.ts
src/**/*.zig
📄 CodeRabbit inference engine (.cursor/rules/building-bun.mdc)
Implement debug logs in Zig using
const log = bun.Output.scoped(.${SCOPE}, false);
and invokinglog("...", .{})
Files:
src/bun.js/test/debug.zig
src/bun.js/test/Order.zig
src/bun.js/test/Collection.zig
src/bun.js/test/ScopeFunctions.zig
src/bun.js/test/expect.zig
src/bun.js/DeprecatedStrong.zig
src/cli/test_command.zig
src/bun.js/test/Execution.zig
src/bun.js/bindings/JSValue.zig
src/bun.js/api/Timer/EventLoopTimer.zig
src/bun.js/test/bun_test.zig
src/allocators/allocation_scope.zig
src/bun.js/test/DoneCallback.zig
**/*.zig
📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)
**/*.zig
: Declare the extern C symbol in Zig and export a Zig-friendly alias for use
Wrap the Bun____toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue
**/*.zig
: Format Zig files with zig-format (bun run zig-format)
In Zig, manage memory carefully with allocators and use defer for cleanup
Files:
src/bun.js/test/debug.zig
src/bun.js/test/Order.zig
src/bun.js/test/Collection.zig
src/bun.js/test/ScopeFunctions.zig
src/bun.js/test/expect.zig
src/bun.js/DeprecatedStrong.zig
src/cli/test_command.zig
src/bun.js/test/Execution.zig
src/bun.js/bindings/JSValue.zig
src/bun.js/api/Timer/EventLoopTimer.zig
src/bun.js/test/bun_test.zig
src/allocators/allocation_scope.zig
src/bun.js/test/DoneCallback.zig
src/bun.js/**/*.zig
📄 CodeRabbit inference engine (.cursor/rules/zig-javascriptcore-classes.mdc)
src/bun.js/**/*.zig
: In Zig binding structs, expose generated bindings via pub const js = JSC.Codegen.JS and re-export toJS/fromJS/fromJSDirect
Constructors and prototype methods should return bun.JSError!JSC.JSValue to integrate Zig error handling with JS exceptions
Use parameter name globalObject (not ctx) and accept (*JSC.JSGlobalObject, *JSC.CallFrame) in binding methods/constructors
Implement getters as get(this, globalObject) returning JSC.JSValue and matching the .classes.ts interface
Provide deinit() for resource cleanup and finalize() that calls deinit(); use bun.destroy(this) or appropriate destroy pattern
Access JS call data via CallFrame (argument(i), argumentCount(), thisValue()) and throw errors with globalObject.throw(...)
For properties marked cache: true, use the generated Zig accessors (NameSetCached/GetCached) to work with GC-owned values
In finalize() for objects holding JS references, release them using .deref() before destroy
Files:
src/bun.js/test/debug.zig
src/bun.js/test/Order.zig
src/bun.js/test/Collection.zig
src/bun.js/test/ScopeFunctions.zig
src/bun.js/test/expect.zig
src/bun.js/DeprecatedStrong.zig
src/bun.js/test/Execution.zig
src/bun.js/bindings/JSValue.zig
src/bun.js/api/Timer/EventLoopTimer.zig
src/bun.js/test/bun_test.zig
src/bun.js/test/DoneCallback.zig
**/*.{cpp,h}
📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)
**/*.{cpp,h}
: When exposing a JS class with public Constructor and Prototype, define three C++ types: class Foo : public JSC::DestructibleObject (if it has C++ fields), class FooPrototype : public JSC::JSNonFinalObject, and class FooConstructor : public JSC::InternalFunction
If the class has C++ data members, inherit from JSC::DestructibleObject and provide proper destruction; if it has no C++ fields (only JS properties), avoid a class and use JSC::constructEmptyObject(vm, structure) with putDirectOffset
Prefer placing the subspaceFor implementation in the .cpp file rather than the header when possible
Files:
src/bun.js/bindings/bindings.cpp
**/*.cpp
📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)
**/*.cpp
: Include "root.h" at the top of C++ binding files to satisfy lints
Define prototype properties using a const HashTableValue array and declare accessors/functions with JSC_DECLARE_* macros
Prototype classes should subclass JSC::JSNonFinalObject, provide create/createStructure, DECLARE_INFO, finishCreation that reifies static properties, and set mayBePrototype on the Structure
Custom getters should use JSC_DEFINE_CUSTOM_GETTER, jsDynamicCast to validate this, and throwThisTypeError on mismatch
Custom setters should use JSC_DEFINE_CUSTOM_SETTER, validate this via jsDynamicCast, and store via WriteBarrier/set semantics
Prototype functions should use JSC_DEFINE_HOST_FUNCTION, validate this with jsDynamicCast, and return encoded JSValue
Constructors should subclass JSC::InternalFunction, return internalFunctionSpace in subspaceFor, set the prototype property as non-configurable/non-writable, and provide create/createStructure
Provide a setup function that builds the Prototype, Constructor, and Structure, and assigns them to the LazyClassStructure initializer
Use the cached Structure via globalObject->m_.get(globalObject) when constructing instances
Expose constructors to Zig via an extern "C" function that returns the constructor from the LazyClassStructure
Provide an extern "C" Bun____toJS function that creates an instance using the cached Structure and returns an EncodedJSValue
Files:
src/bun.js/bindings/bindings.cpp
**/*.{cpp,cc,cxx,h,hpp,hxx}
📄 CodeRabbit inference engine (CLAUDE.md)
Format C/C++ sources/headers with clang-format (bun run clang-format)
Files:
src/bun.js/bindings/bindings.cpp
src/bun.js/bindings/**/*.cpp
📄 CodeRabbit inference engine (CLAUDE.md)
src/bun.js/bindings/**/*.cpp
: For JS classes with a public constructor, implement Foo, FooPrototype, and FooConstructor (JSC patterns)
Define class properties using HashTableValue arrays in JSC bindings
Add iso subspaces for classes with C++ fields in JSC bindings
Cache structures in ZigGlobalObject for JSC-bound classes
Files:
src/bun.js/bindings/bindings.cpp
src/{**/js_*.zig,bun.js/api/**/*.zig}
📄 CodeRabbit inference engine (.cursor/rules/registering-bun-modules.mdc)
Use bun.JSError!JSValue for proper error propagation in JS-exposed Zig functions
Files:
src/bun.js/api/Timer/EventLoopTimer.zig
🧠 Learnings (58)
📓 Common learnings
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.803Z
Learning: Applies to test/**/*.{js,ts} : Write tests in JavaScript or TypeScript using Bun’s Jest-style APIs (test, describe, expect) and run with bun test
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : When spawning Bun in tests, use `bunExe()` and `bunEnv` from `harness`
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.803Z
Learning: Applies to test/cli/**/*.{js,ts} : When testing Bun as a CLI, use spawn with bunExe() and bunEnv from harness, and capture stdout/stderr via pipes
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.803Z
Learning: Applies to test/js/bun/**/*.{js,ts} : Place Bun API tests under test/js/bun/, separated by category (e.g., test/js/bun/glob/)
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : Name test files `*.test.ts` and use `bun:test`
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-08T04:44:59.101Z
Learning: For platform-specific changes, run bun run zig:check-all to compile on all platforms
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : Do not set explicit test timeouts; rely on Bun’s built-in timeouts
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : Import common test utilities from `harness` (e.g., `bunExe`, `bunEnv`, `tempDirWithFiles`, `tmpdirSync`, platform checks, GC helpers)
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-08-30T00:13:36.815Z
Learning: Applies to src/bun.js/**/*.zig : Constructors and prototype methods should return bun.JSError!JSC.JSValue to integrate Zig error handling with JS exceptions
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-08-30T00:13:36.815Z
Learning: Applies to src/bun.js/**/*.zig : In Zig binding structs, expose generated bindings via pub const js = JSC.Codegen.JS<ClassName> and re-export toJS/fromJS/fromJSDirect
📚 Learning: 2025-08-30T00:12:56.803Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.803Z
Learning: Applies to test/**/*.{js,ts} : Write tests in JavaScript or TypeScript using Bun’s Jest-style APIs (test, describe, expect) and run with bun test
Applied to files:
test/js/bun/test/bun_test.fixture.ts
test/js/bun/test/bun_test.test.ts
src/bun.js/test/expect.zig
src/cli/test_command.zig
src/bun.js/test/Execution.zig
src/bun.js/api/Timer/EventLoopTimer.zig
src/bun.js/test/bun_test.zig
📚 Learning: 2025-09-03T17:10:13.486Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*-fixture.ts : Name test fixture files that are spawned by tests with the suffix `-fixture.ts`
Applied to files:
test/js/bun/test/bun_test.fixture.ts
📚 Learning: 2025-08-30T00:12:56.803Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.803Z
Learning: Applies to test/js/bun/**/*.{js,ts} : Place Bun API tests under test/js/bun/, separated by category (e.g., test/js/bun/glob/)
Applied to files:
test/js/bun/test/bun_test.fixture.ts
test/js/bun/test/bun_test.test.ts
src/cli/test_command.zig
src/bun.js/test/bun_test.zig
📚 Learning: 2025-09-03T17:10:13.486Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : When spawning Bun in tests, use `bunExe()` and `bunEnv` from `harness`
Applied to files:
test/js/bun/test/bun_test.fixture.ts
test/js/bun/test/bun_test.test.ts
src/cli/test_command.zig
src/bun.js/test/Execution.zig
src/bun.js/test/bun_test.zig
test/harness.ts
📚 Learning: 2025-09-03T17:10:13.486Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : Name test files `*.test.ts` and use `bun:test`
Applied to files:
test/js/bun/test/bun_test.fixture.ts
test/js/bun/test/bun_test.test.ts
src/cli/test_command.zig
📚 Learning: 2025-09-03T17:10:13.486Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : Do not set explicit test timeouts; rely on Bun’s built-in timeouts
Applied to files:
test/js/bun/test/bun_test.fixture.ts
test/js/bun/test/bun_test.test.ts
src/cli/test_command.zig
📚 Learning: 2025-09-03T17:10:13.486Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : Use `describe` blocks for grouping, `describe.each` for parameterized tests, snapshots with `toMatchSnapshot`, and lifecycle hooks (`beforeAll`, `beforeEach`, `afterEach`); track resources for cleanup in `afterEach`
Applied to files:
test/js/bun/test/bun_test.fixture.ts
src/bun.js/test/expect.zig
📚 Learning: 2025-09-03T17:10:13.486Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : Import common test utilities from `harness` (e.g., `bunExe`, `bunEnv`, `tempDirWithFiles`, `tmpdirSync`, platform checks, GC helpers)
Applied to files:
test/js/bun/test/bun_test.fixture.ts
test/js/bun/test/bun_test.test.ts
📚 Learning: 2025-09-20T03:39:41.748Z
Learnt from: pfgithub
PR: oven-sh/bun#22534
File: test/regression/issue/21830.fixture.ts:14-63
Timestamp: 2025-09-20T03:39:41.748Z
Learning: Bun's test runner supports async describe callbacks, unlike Jest/Vitest where describe callbacks must be synchronous. The syntax `describe("name", async () => { ... })` is valid in Bun.
Applied to files:
test/js/bun/test/bun_test.fixture.ts
test/js/bun/test/bun_test.test.ts
src/bun.js/test/expect.zig
src/cli/test_command.zig
src/bun.js/test/bun_test.zig
📚 Learning: 2025-08-30T00:09:39.100Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/dev-server-tests.mdc:0-0
Timestamp: 2025-08-30T00:09:39.100Z
Learning: Applies to test/bake/dev/bundle.test.ts : bundle.test.ts should contain DevServer-specific bundling tests
Applied to files:
test/js/bun/test/bun_test.fixture.ts
📚 Learning: 2025-09-03T17:10:13.486Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : Do not write flaky tests: never wait for arbitrary time; wait for conditions instead
Applied to files:
test/js/bun/test/bun_test.fixture.ts
📚 Learning: 2025-09-03T01:30:58.001Z
Learnt from: markovejnovic
PR: oven-sh/bun#21728
File: test/js/valkey/valkey.test.ts:264-271
Timestamp: 2025-09-03T01:30:58.001Z
Learning: For test/js/valkey/valkey.test.ts PUB/SUB tests, avoid arbitrary sleeps and async-forEach. Instead, resolve a Promise from the subscriber callback when the expected number of messages is observed and await it with a bounded timeout (e.g., withTimeout + Promise.withResolvers) to account for Redis server→subscriber propagation.
Applied to files:
test/js/bun/test/bun_test.fixture.ts
📚 Learning: 2025-08-30T00:09:39.100Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/dev-server-tests.mdc:0-0
Timestamp: 2025-08-30T00:09:39.100Z
Learning: Applies to test/bake/{dev/*.test.ts,dev-and-prod.ts} : Explicitly assert client console logs with c.expectMessage; unasserted logs should not appear
Applied to files:
test/js/bun/test/bun_test.fixture.ts
📚 Learning: 2025-09-03T17:10:13.486Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : In error tests, assert non-zero exit codes for failing processes and use `toThrow` for synchronous errors
Applied to files:
test/js/bun/test/bun_test.fixture.ts
src/bun.js/test/Execution.zig
📚 Learning: 2025-09-20T00:58:38.021Z
Learnt from: markovejnovic
PR: oven-sh/bun#22568
File: test/js/valkey/valkey.test.ts:561-564
Timestamp: 2025-09-20T00:58:38.021Z
Learning: For test/js/valkey/valkey.test.ts, do not comment on synchronous throw assertions for async Redis methods (like ctx.redis.set(), ctx.redis.unsubscribe(), etc.) - Bun's Redis client implementation differs from Node.js and can throw synchronously even for async methods. The maintainer has explicitly requested to stop looking at this error pattern.
Applied to files:
test/js/bun/test/bun_test.fixture.ts
📚 Learning: 2025-09-03T17:10:13.486Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : Use `using`/`await using` with Bun resources (e.g., Bun.listen/connect/spawn/serve) to ensure cleanup in tests
Applied to files:
test/js/bun/test/bun_test.fixture.ts
src/cli/test_command.zig
📚 Learning: 2025-09-03T17:10:13.486Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : Prefer `async/await` in tests; for a single callback, use `Promise.withResolvers()`
Applied to files:
test/js/bun/test/bun_test.fixture.ts
📚 Learning: 2025-08-30T00:12:56.803Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.803Z
Learning: Applies to test/**/*.{js,ts} : Prefer data-driven tests (e.g., test.each) to reduce boilerplate
Applied to files:
test/js/bun/test/bun_test.fixture.ts
📚 Learning: 2025-09-08T04:44:59.101Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-08T04:44:59.101Z
Learning: Applies to test/**/*.test.{ts,tsx} : Never write tests that merely assert absence of "panic" or "uncaught exception" in output
Applied to files:
test/js/bun/test/bun_test.fixture.ts
📚 Learning: 2025-08-30T00:12:56.803Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.803Z
Learning: Applies to test/js/node/test/** : Vendored Node.js tests under test/js/node/test/ are exceptions and do not follow Bun’s test style
Applied to files:
test/js/bun/test/bun_test.fixture.ts
test/js/bun/test/bun_test.test.ts
src/cli/test_command.zig
📚 Learning: 2025-09-08T04:44:59.101Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-08T04:44:59.101Z
Learning: Applies to test/**/*.test.{ts,tsx} : In tests, use normalizeBunSnapshot when asserting snapshots
Applied to files:
test/js/bun/test/bun_test.test.ts
src/bun.js/test/expect.zig
📚 Learning: 2025-08-30T00:12:56.803Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.803Z
Learning: Applies to test/cli/**/*.{js,ts} : When testing Bun as a CLI, use spawn with bunExe() and bunEnv from harness, and capture stdout/stderr via pipes
Applied to files:
test/js/bun/test/bun_test.test.ts
src/cli/test_command.zig
src/bun.js/test/Execution.zig
src/bun.js/test/bun_test.zig
test/harness.ts
📚 Learning: 2025-09-08T00:41:12.052Z
Learnt from: CR
PR: oven-sh/bun#0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-09-08T00:41:12.052Z
Learning: Applies to src/bun.js/bindings/v8/test/v8/v8.test.ts : Add a corresponding test case in test/v8/v8.test.ts that invokes checkSameOutput with the new function
Applied to files:
test/js/bun/test/bun_test.test.ts
📚 Learning: 2025-09-08T04:44:59.101Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-08T04:44:59.101Z
Learning: Applies to test/**/*.test.{ts,tsx} : Avoid shell commands (e.g., find, grep) in tests; use Bun.Glob and built-ins instead
Applied to files:
test/js/bun/test/bun_test.test.ts
src/cli/test_command.zig
src/bun.js/test/Execution.zig
📚 Learning: 2025-09-08T04:44:59.101Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-08T04:44:59.101Z
Learning: Applies to test/**/*.test.{ts,tsx} : Prefer snapshot tests over exact stdout equality assertions
Applied to files:
test/js/bun/test/bun_test.test.ts
src/bun.js/test/expect.zig
📚 Learning: 2025-08-30T00:07:54.573Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/building-bun.mdc:0-0
Timestamp: 2025-08-30T00:07:54.573Z
Learning: Applies to src/**/*.zig : Implement debug logs in Zig using `const log = bun.Output.scoped(.${SCOPE}, false);` and invoking `log("...", .{})`
Applied to files:
src/bun.js/test/debug.zig
src/cli/test_command.zig
📚 Learning: 2025-08-30T00:13:36.815Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-08-30T00:13:36.815Z
Learning: Applies to src/bun.js/**/*.zig : In Zig binding structs, expose generated bindings via pub const js = JSC.Codegen.JS<ClassName> and re-export toJS/fromJS/fromJSDirect
Applied to files:
src/bun.js/test/debug.zig
src/bun.js/bindings/bindings.cpp
src/bun.js/test/ScopeFunctions.zig
src/bun.js/DeprecatedStrong.zig
src/cli/test_command.zig
src/bun.js/bindings/JSValue.zig
src/bun.js/test/bun_test.zig
src/bun.js/test/DoneCallback.zig
📚 Learning: 2025-09-08T04:44:59.101Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-08T04:44:59.101Z
Learning: Applies to **/*.zig : In Zig, manage memory carefully with allocators and use defer for cleanup
Applied to files:
src/bun.js/test/Collection.zig
src/bun.js/test/ScopeFunctions.zig
📚 Learning: 2025-08-30T00:13:36.815Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-08-30T00:13:36.815Z
Learning: Applies to src/bun.js/**/*.zig : Use parameter name globalObject (not ctx) and accept (*JSC.JSGlobalObject, *JSC.CallFrame) in binding methods/constructors
Applied to files:
src/bun.js/bindings/bindings.cpp
src/bun.js/test/ScopeFunctions.zig
src/bun.js/bindings/JSValue.zig
src/bun.js/test/DoneCallback.zig
📚 Learning: 2025-09-08T04:44:59.101Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-08T04:44:59.101Z
Learning: Applies to src/bun.js/bindings/**/*.cpp : Cache structures in ZigGlobalObject for JSC-bound classes
Applied to files:
src/bun.js/bindings/bindings.cpp
src/bun.js/bindings/JSValue.zig
src/bun.js/test/DoneCallback.zig
📚 Learning: 2025-09-08T04:44:59.101Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-08T04:44:59.101Z
Learning: Applies to src/bun.js/bindings/**/*.cpp : For JS classes with a public constructor, implement Foo, FooPrototype, and FooConstructor (JSC patterns)
Applied to files:
src/bun.js/bindings/bindings.cpp
src/bun.js/bindings/JSValue.zig
📚 Learning: 2025-09-08T04:44:59.101Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-08T04:44:59.101Z
Learning: Applies to src/bun.js/bindings/**/*.cpp : Add iso subspaces for classes with C++ fields in JSC bindings
Applied to files:
src/bun.js/bindings/bindings.cpp
📚 Learning: 2025-09-08T00:41:12.052Z
Learnt from: CR
PR: oven-sh/bun#0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-09-08T00:41:12.052Z
Learning: Applies to src/bun.js/bindings/v8/V8*.h : Ensure all public V8 API functions are marked with BUN_EXPORT for symbol visibility
Applied to files:
src/bun.js/bindings/bindings.cpp
📚 Learning: 2025-09-08T00:41:12.052Z
Learnt from: CR
PR: oven-sh/bun#0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-09-08T00:41:12.052Z
Learning: Applies to src/bun.js/bindings/v8/src/symbols.txt : Add new V8 API mangled symbols (without leading underscore) to src/symbols.txt
Applied to files:
src/bun.js/bindings/bindings.cpp
📚 Learning: 2025-08-30T00:13:36.815Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-08-30T00:13:36.815Z
Learning: Applies to src/bun.js/bindings/generated_classes_list.zig : Update src/bun.js/bindings/generated_classes_list.zig to include new classes
Applied to files:
src/bun.js/bindings/bindings.cpp
src/bun.js/test/ScopeFunctions.zig
src/bun.js/DeprecatedStrong.zig
src/bun.js/bindings/JSValue.zig
src/bun.js/test/bun_test.zig
src/bun.js/test/DoneCallback.zig
📚 Learning: 2025-09-08T00:41:12.052Z
Learnt from: CR
PR: oven-sh/bun#0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-09-08T00:41:12.052Z
Learning: Applies to src/bun.js/bindings/v8/src/symbols.dyn : Add new V8 API mangled symbols (with leading underscore and semicolons) to src/symbols.dyn
Applied to files:
src/bun.js/bindings/bindings.cpp
📚 Learning: 2025-09-08T00:41:12.052Z
Learnt from: CR
PR: oven-sh/bun#0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-09-08T00:41:12.052Z
Learning: Applies to src/bun.js/bindings/v8/**/*.{h,cpp} : Use localToJSValue() when converting V8 handles to JSC values before performing JSC operations
Applied to files:
src/bun.js/bindings/bindings.cpp
📚 Learning: 2025-08-30T00:11:00.890Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/javascriptcore-class.mdc:0-0
Timestamp: 2025-08-30T00:11:00.890Z
Learning: Applies to **/*.cpp : Prototype functions should use JSC_DEFINE_HOST_FUNCTION, validate this with jsDynamicCast, and return encoded JSValue
Applied to files:
src/bun.js/bindings/bindings.cpp
src/bun.js/bindings/JSValue.zig
📚 Learning: 2025-08-30T00:13:36.815Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-08-30T00:13:36.815Z
Learning: Applies to src/bun.js/**/*.zig : Access JS call data via CallFrame (argument(i), argumentCount(), thisValue()) and throw errors with globalObject.throw(...)
Applied to files:
src/bun.js/bindings/bindings.cpp
src/bun.js/test/ScopeFunctions.zig
src/bun.js/bindings/JSValue.zig
src/bun.js/test/bun_test.zig
📚 Learning: 2025-08-30T00:13:36.815Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-08-30T00:13:36.815Z
Learning: Applies to src/bun.js/**/*.zig : Constructors and prototype methods should return bun.JSError!JSC.JSValue to integrate Zig error handling with JS exceptions
Applied to files:
src/bun.js/bindings/bindings.cpp
src/bun.js/test/ScopeFunctions.zig
src/bun.js/DeprecatedStrong.zig
src/cli/test_command.zig
src/bun.js/test/Execution.zig
src/bun.js/bindings/JSValue.zig
src/bun.js/test/bun_test.zig
src/bun.js/test/DoneCallback.zig
📚 Learning: 2025-08-30T00:11:00.890Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/javascriptcore-class.mdc:0-0
Timestamp: 2025-08-30T00:11:00.890Z
Learning: Applies to src/bun.js/bindings/ZigGlobalObject.{h,cpp} : If only a class (no public constructor/prototype), use JSC::LazyProperty<JSGlobalObject, Structure>, initialize it in finishCreation, and visit it in visitChildren
Applied to files:
src/bun.js/bindings/bindings.cpp
src/bun.js/bindings/JSValue.zig
src/bun.js/test/DoneCallback.zig
📚 Learning: 2025-08-30T00:11:00.890Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/javascriptcore-class.mdc:0-0
Timestamp: 2025-08-30T00:11:00.890Z
Learning: Applies to src/bun.js/bindings/ZigGlobalObject.h : When there is a class, prototype, and constructor, add a JSC::LazyClassStructure field for the class to ZigGlobalObject.h
Applied to files:
src/bun.js/bindings/bindings.cpp
📚 Learning: 2025-08-30T00:11:00.890Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/javascriptcore-class.mdc:0-0
Timestamp: 2025-08-30T00:11:00.890Z
Learning: Applies to **/*.cpp : Prototype classes should subclass JSC::JSNonFinalObject, provide create/createStructure, DECLARE_INFO, finishCreation that reifies static properties, and set mayBePrototype on the Structure
Applied to files:
src/bun.js/bindings/bindings.cpp
📚 Learning: 2025-08-30T00:11:00.890Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/javascriptcore-class.mdc:0-0
Timestamp: 2025-08-30T00:11:00.890Z
Learning: Applies to **/*.zig : Wrap the Bun__<Type>__toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue
Applied to files:
src/bun.js/bindings/bindings.cpp
src/bun.js/test/ScopeFunctions.zig
src/bun.js/DeprecatedStrong.zig
src/bun.js/bindings/JSValue.zig
src/bun.js/test/DoneCallback.zig
📚 Learning: 2025-08-30T00:11:57.076Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-08-30T00:11:57.076Z
Learning: Applies to src/{**/js_*.zig,bun.js/api/**/*.zig} : Use bun.JSError!JSValue for proper error propagation in JS-exposed Zig functions
Applied to files:
src/bun.js/bindings/bindings.cpp
src/bun.js/test/ScopeFunctions.zig
src/bun.js/DeprecatedStrong.zig
src/cli/test_command.zig
src/bun.js/test/Execution.zig
src/bun.js/bindings/JSValue.zig
src/bun.js/test/bun_test.zig
src/bun.js/test/DoneCallback.zig
📚 Learning: 2025-08-30T00:11:00.890Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/javascriptcore-class.mdc:0-0
Timestamp: 2025-08-30T00:11:00.890Z
Learning: Applies to **/*.cpp : Provide an extern "C" Bun__<Type>__toJS function that creates an instance using the cached Structure and returns an EncodedJSValue
Applied to files:
src/bun.js/bindings/bindings.cpp
📚 Learning: 2025-08-30T00:13:36.815Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-08-30T00:13:36.815Z
Learning: Applies to src/bun.js/**/*.zig : In finalize() for objects holding JS references, release them using .deref() before destroy
Applied to files:
src/bun.js/test/ScopeFunctions.zig
src/bun.js/DeprecatedStrong.zig
src/bun.js/test/DoneCallback.zig
📚 Learning: 2025-08-30T00:13:36.815Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-08-30T00:13:36.815Z
Learning: Applies to src/bun.js/**/*.zig : Provide deinit() for resource cleanup and finalize() that calls deinit(); use bun.destroy(this) or appropriate destroy pattern
Applied to files:
src/bun.js/test/ScopeFunctions.zig
src/bun.js/DeprecatedStrong.zig
src/bun.js/test/bun_test.zig
src/bun.js/test/DoneCallback.zig
📚 Learning: 2025-08-30T00:11:57.076Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-08-30T00:11:57.076Z
Learning: Applies to src/**/js_*.zig : Handle reference counting correctly with ref()/deref() in JS-facing Zig code
Applied to files:
src/bun.js/test/ScopeFunctions.zig
src/bun.js/DeprecatedStrong.zig
📚 Learning: 2025-08-30T00:13:36.815Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-08-30T00:13:36.815Z
Learning: Applies to src/bun.js/**/*.zig : For properties marked cache: true, use the generated Zig accessors (<Prop>NameSetCached/GetCached) to work with GC-owned values
Applied to files:
src/bun.js/test/ScopeFunctions.zig
📚 Learning: 2025-08-30T00:11:57.076Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-08-30T00:11:57.076Z
Learning: Applies to src/**/js_*.zig : Always implement proper cleanup in deinit() and finalize() for JS-exposed types
Applied to files:
src/bun.js/DeprecatedStrong.zig
src/bun.js/test/DoneCallback.zig
📚 Learning: 2025-09-08T04:44:59.101Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-08T04:44:59.101Z
Learning: For platform-specific changes, run bun run zig:check-all to compile on all platforms
Applied to files:
src/cli/test_command.zig
📚 Learning: 2025-09-08T00:41:12.052Z
Learnt from: CR
PR: oven-sh/bun#0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-09-08T00:41:12.052Z
Learning: Applies to src/bun.js/bindings/v8/src/napi/napi.zig : Add new V8 API method mangled symbols to the V8API struct in src/napi/napi.zig for both GCC/Clang and MSVC
Applied to files:
src/bun.js/bindings/JSValue.zig
📚 Learning: 2025-08-30T00:11:57.076Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-08-30T00:11:57.076Z
Learning: Applies to src/**/js_*.zig : Implement JavaScript bindings in a Zig file named with a js_ prefix (e.g., js_smtp.zig, js_your_feature.zig)
Applied to files:
src/bun.js/bindings/JSValue.zig
src/bun.js/test/DoneCallback.zig
📚 Learning: 2025-09-08T04:44:59.101Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-08T04:44:59.101Z
Learning: Applies to src/bun.js/bindings/**/*.cpp : Define class properties using HashTableValue arrays in JSC bindings
Applied to files:
src/bun.js/bindings/JSValue.zig
📚 Learning: 2025-09-02T18:18:00.835Z
Learnt from: taylordotfish
PR: oven-sh/bun#22227
File: src/allocators/allocation_scope.zig:104-118
Timestamp: 2025-09-02T18:18:00.835Z
Learning: In AllocationScope code, accessing self.allocations from LockedState results in a compile-time error, not a runtime panic, because LockedState doesn't have an allocations field - the field exists on the History struct which is accessed via self.history.allocations.
Applied to files:
src/allocators/allocation_scope.zig
📚 Learning: 2025-09-05T19:49:26.188Z
Learnt from: markovejnovic
PR: oven-sh/bun#21728
File: src/valkey/js_valkey_functions.zig:852-867
Timestamp: 2025-09-05T19:49:26.188Z
Learning: In Bun’s Zig code, `.js_undefined` is a valid and preferred JSValue literal for “undefined” (e.g., resolving JSPromise). Do not refactor usages to `jsc.JSValue.jsUndefined()`, especially in src/valkey/js_valkey_functions.zig unsubscribe().
Applied to files:
src/bun.js/test/DoneCallback.zig
🧬 Code graph analysis (3)
test/js/bun/test/bun_test.fixture.ts (1)
src/js/node/test.ts (2)
beforeEach
(151-160)afterEach
(162-171)
test/js/bun/test/bun_test.test.ts (1)
test/harness.ts (1)
normalizeBunSnapshot
(1802-1833)
src/bun.js/bindings/bindings.cpp (2)
src/bun.js/bindings/BunString.cpp (15)
ZIG_EXPORT
(47-50)ZIG_EXPORT
(52-55)ZIG_EXPORT
(56-59)ZIG_EXPORT
(61-66)ZIG_EXPORT
(68-73)ZIG_EXPORT
(75-85)ZIG_EXPORT
(87-106)ZIG_EXPORT
(137-150)ZIG_EXPORT
(198-206)ZIG_EXPORT
(358-367)ZIG_EXPORT
(369-378)ZIG_EXPORT
(402-413)ZIG_EXPORT
(415-429)ZIG_EXPORT
(431-441)ZIG_EXPORT
(443-451)src/bun.js/bindings/JSDOMExceptionHandling.cpp (2)
throwTypeError
(220-223)throwTypeError
(220-220)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Format
pub fn ref(this: *Strong) void { | ||
this.#raw.protect(); | ||
if (enable_safety) if (this.#safety) |safety| { | ||
safety.ref_count += 1; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ref(): mutating a copy; use pointer binding for #safety.
|safety|
binds by value; safety.ref_count += 1
updates a copy and is lost. Use |*safety|
.
pub fn ref(this: *Strong) void {
this.#raw.protect();
- if (enable_safety) if (this.#safety) |safety| {
+ if (enable_safety) if (this.#safety) |*safety| {
safety.ref_count += 1;
};
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
pub fn ref(this: *Strong) void { | |
this.#raw.protect(); | |
if (enable_safety) if (this.#safety) |safety| { | |
safety.ref_count += 1; | |
}; | |
pub fn ref(this: *Strong) void { | |
this.#raw.protect(); | |
if (enable_safety) if (this.#safety) |*safety| { | |
safety.ref_count += 1; | |
}; | |
} |
🤖 Prompt for AI Agents
In src/bun.js/DeprecatedStrong.zig around lines 35 to 39, the closure `|safety|`
binds the optional by value so `safety.ref_count += 1` mutates a copy and is
lost; change the optional binding to a pointer (`|*safety|`) and then increment
the pointed-to ref_count (use the pointer binding form in the if-statement and
update the ref_count through that pointer) so the mutation affects the original
safety struct.
return runner.bun_test_root.cloneActiveFile(); | ||
} | ||
|
||
pub const DoneCallback = @import("./DoneCallback.zig"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Import pattern may cause cycles.
pub const DoneCallback = @import("./DoneCallback.zig");
combined with DoneCallback.zig referencing bun_test.DoneCallback
risks an import cycle. After adopting the struct wrapper and removing the alias in DoneCallback.zig, this should be safe. Please confirm build on all platforms.
🏁 Script executed:
#!/bin/bash
# Verify no import cycles and that generated symbols resolve
fd -t f -e zig | xargs -I{} sed -n '1,80p' {} >/dev/null
# Build headers of the two modules to force semantic check (lightweight)
rg -nP '(@import\(".*DoneCallback\.zig"\)|bun_test\.DoneCallback)' -n -C2
Length of output: 1061
Break import cycle: DoneCallback ↔ bun_test
src/bun.js/test/bun_test.zig:6 declares pub const DoneCallback = @import("./DoneCallback.zig");
while src/bun.js/test/DoneCallback.zig:45 contains const DoneCallback = bun_test.DoneCallback;
— this creates a circular import. Remove the alias in DoneCallback.zig (export the struct wrapper or have DoneCallback.zig directly expose the type) to break the cycle and verify with bun run zig:check-all
on all platforms.
🤖 Prompt for AI Agents
In src/bun.js/test/bun_test.zig around line 6, the line `pub const DoneCallback
= @import("./DoneCallback.zig");` participates in a circular import because
src/bun.js/test/DoneCallback.zig aliases `const DoneCallback =
bun_test.DoneCallback;`; to fix, remove the alias from DoneCallback.zig and
instead have DoneCallback.zig directly declare and export the callback
wrapper/type (or export the struct wrapper under a concrete name) so
bun_test.zig can import it without referencing bun_test, update any callsites to
use the exported name from DoneCallback.zig, and then run `bun run
zig:check-all` on all platforms to verify the cycle is resolved.
pub fn finalize( | ||
this: *DoneCallback, | ||
) callconv(.C) void { | ||
groupLog.begin(@src()); | ||
defer groupLog.end(); | ||
|
||
if (this.ref) |ref| ref.deref(); | ||
VirtualMachine.get().allocator.destroy(this); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Finalize should call deinit() and use bun.destroy().
Avoid direct allocator.destroy(). Ensure ref is cleared safely.
Apply:
pub fn finalize(
this: *DoneCallback,
) callconv(.C) void {
groupLog.begin(@src());
defer groupLog.end();
- if (this.ref) |ref| ref.deref();
- VirtualMachine.get().allocator.destroy(this);
+ this.deinit();
+ bun.destroy(this);
}
+
+pub fn deinit(this: *DoneCallback) void {
+ if (this.ref) |ref| ref.deref();
+ this.ref = null;
+}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
pub fn finalize( | |
this: *DoneCallback, | |
) callconv(.C) void { | |
groupLog.begin(@src()); | |
defer groupLog.end(); | |
if (this.ref) |ref| ref.deref(); | |
VirtualMachine.get().allocator.destroy(this); | |
} | |
pub fn finalize( | |
this: *DoneCallback, | |
) callconv(.C) void { | |
groupLog.begin(@src()); | |
defer groupLog.end(); | |
this.deinit(); | |
bun.destroy(this); | |
} | |
pub fn deinit(this: *DoneCallback) void { | |
if (this.ref) |ref| ref.deref(); | |
this.ref = null; | |
} |
🤖 Prompt for AI Agents
In src/bun.js/test/DoneCallback.zig around lines 9–17, the finalize
implementation should call the type's deinit, avoid direct allocator.destroy,
and clear ref safely: invoke the DoneCallback deinit on this, if this.ref is set
call ref.deref() and then clear this.ref (set to null/undefined), and finally
release the object via bun.destroy(...) instead of
VirtualMachine.get().allocator.destroy(this); ensure ordering so deinit runs
before bun.destroy.
const bun_test = jsc.Jest.bun_test; | ||
const BunTest = bun_test.BunTest; | ||
const DoneCallback = bun_test.DoneCallback; | ||
const groupLog = bun_test.debug.group; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove self-referential alias to avoid import cycles.
const DoneCallback = bun_test.DoneCallback;
creates a circular reference with bun_test importing this file.
Apply:
-const BunTest = bun_test.BunTest;
-const DoneCallback = bun_test.DoneCallback;
+const BunTest = bun_test.BunTest;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const bun_test = jsc.Jest.bun_test; | |
const BunTest = bun_test.BunTest; | |
const DoneCallback = bun_test.DoneCallback; | |
const groupLog = bun_test.debug.group; | |
const bun_test = jsc.Jest.bun_test; | |
const BunTest = bun_test.BunTest; | |
const groupLog = bun_test.debug.group; |
🤖 Prompt for AI Agents
In src/bun.js/test/DoneCallback.zig around lines 43 to 46, the line creating a
self-referential alias ("const DoneCallback = bun_test.DoneCallback;") creates
an import cycle; remove that alias and either (a) reference the type/value
directly as bun_test.DoneCallback where used, or (b) import DoneCallback from
the original module that defines it (not from bun_test) so the file no longer
re-exports/references itself. Ensure all usages are updated to the chosen
approach.
pub fn setFailureSkipTo(aor: AllOrderResult, this: *Order) void { | ||
if (aor.start == 0 and aor.end == 0) return; | ||
const skip_to = this.groups.items.len; | ||
for (this.groups.items[aor.start..aor.end]) |*group| { | ||
group.failure_skip_to = skip_to; | ||
} | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: beforeAll failure skip target points to children, not afterAll.
setFailureSkipTo()
sets failure_skip_to
to this.groups.items.len
at call-time. When invoked before scheduling afterAll
, failures skip to the first child instead of the first afterAll
.
Apply:
-pub const AllOrderResult = struct {
+pub const AllOrderResult = struct {
start: usize,
end: usize,
pub const empty: AllOrderResult = .{ .start = 0, .end = 0 };
- pub fn setFailureSkipTo(aor: AllOrderResult, this: *Order) void {
- if (aor.start == 0 and aor.end == 0) return;
- const skip_to = this.groups.items.len;
+ pub fn setFailureSkipTo(aor: AllOrderResult, this: *Order, skip_to: usize) void {
+ if (aor.start == 0 and aor.end == 0) return;
for (this.groups.items[aor.start..aor.end]) |*group| {
group.failure_skip_to = skip_to;
}
}
};
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
pub fn setFailureSkipTo(aor: AllOrderResult, this: *Order) void { | |
if (aor.start == 0 and aor.end == 0) return; | |
const skip_to = this.groups.items.len; | |
for (this.groups.items[aor.start..aor.end]) |*group| { | |
group.failure_skip_to = skip_to; | |
} | |
} | |
}; | |
pub const AllOrderResult = struct { | |
start: usize, | |
end: usize, | |
pub const empty: AllOrderResult = .{ .start = 0, .end = 0 }; | |
pub fn setFailureSkipTo(aor: AllOrderResult, this: *Order, skip_to: usize) void { | |
if (aor.start == 0 and aor.end == 0) return; | |
for (this.groups.items[aor.start..aor.end]) |*group| { | |
group.failure_skip_to = skip_to; | |
} | |
} | |
}; |
🤖 Prompt for AI Agents
In src/bun.js/test/Order.zig around lines 31-38, setFailureSkipTo currently
captures skip_to as this.groups.items.len at call-time which makes beforeAll
failures jump to the first child instead of the first afterAll; change the
calculation to point past the future afterAll entry (e.g., set skip_to to
this.groups.items.len + 1 or otherwise compute the index of the first afterAll
that will be appended) so failure_skip_to targets the afterAll group rather than
the children.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (3)
src/bun.js/test/Collection.zig (2)
110-121
: Memory management issue with queue operations.The current implementation copies items from
current_scope_callback_queue
todescribe_callback_queue
without properly transferring ownership, which causes memory leaks. The items are copied viaappend(item.*)
but the original queue is cleared without deinitializing the copied items.Based on the
defer
pattern for memory management and explicit memory freeing requirements, the loop should move items rather than copy them:- // append queued callbacks, in reverse order because items will be pop()ed from the end - var i: usize = this.current_scope_callback_queue.items.len; - while (i > 0) { - i -= 1; - const item = &this.current_scope_callback_queue.items[i]; - if (item.new_scope.failed) { // if there was an error in the describe callback, don't run any describe callbacks in this scope - item.deinit(); - } else { - bun.handleOom(this.describe_callback_queue.append(item.*)); - } - } - this.current_scope_callback_queue.clearRetainingCapacity(); + // Move queued callbacks (reverse order preserved by popping from the end). + while (this.current_scope_callback_queue.popOrNull()) |moved| { + if (moved.new_scope.failed) { // do not queue callbacks from a failed scope + var tmp = moved; + tmp.deinit(buntest.gpa); + } else { + bun.handleOom(this.describe_callback_queue.append(moved)); + } + }
86-97
: Scope reset uses incorrect value.The scope reset logic assigns
prev_scope
tothis.active_scope
, making the subsequent reset a no-op. It should read the saved scope fromdata.collection.active_scope
instead.Apply this fix:
- const prev_scope: *DescribeScope = switch (data) { - .collection => this.active_scope, + const prev_scope: *DescribeScope = switch (data) { + .collection => |c| c.active_scope, else => blk: { bun.assert(false); // this probably can't happen break :blk this.active_scope; }, };src/bun.js/test/expect/toMatchSnapshot.zig (1)
15-18
: LGTM: correct signature + inlined bunTest() guard.Fixes the previously noted wrong matcher name and removes the unused binding.
🧹 Nitpick comments (3)
src/bun.js/test/Collection.zig (1)
44-55
: Collection deinit has inconsistent allocator access.The deinit method accesses
buntest.gpa
through thebunTest()
helper, but the method signature doesn't clearly indicate this dependency. This could lead to confusion about resource ownership.Consider making the allocator dependency explicit:
-pub fn deinit(this: *Collection) void { - this.root_scope.destroy(this.bunTest().gpa); +pub fn deinit(this: *Collection, gpa: std.mem.Allocator) void { + this.root_scope.destroy(gpa); for (this.describe_callback_queue.items) |*item| { - item.deinit(); + item.deinit(gpa); } this.describe_callback_queue.deinit(); for (this.current_scope_callback_queue.items) |*item| { - item.deinit(); + item.deinit(gpa); } this.current_scope_callback_queue.deinit(); this.filter_buffer.deinit(); }src/bun.js/test/expect/toMatchSnapshot.zig (2)
30-31
: Use getSignature on all error paths for consistent UX.Empty signature ("") omits usage hints. Reuse the same signature as Line 35.
Apply:
@@ - } else { - return this.throw(globalThis, "", "\n\nMatcher error: Expected first argument to be a string or object\n", .{}); + } else { + const signature = comptime getSignature("toMatchSnapshot", "<green>properties<r><d>, <r>hint", false); + return this.throw(globalThis, signature, "\n\nMatcher error: Expected first argument to be a string or object\n", .{}); } @@ - } else { - return this.throw(globalThis, "", "\n\nMatcher error: Expected second argument to be a string\n", .{}); + } else { + const signature = comptime getSignature("toMatchSnapshot", "<green>properties<r><d>, <r>hint", false); + return this.throw(globalThis, signature, "\n\nMatcher error: Expected second argument to be a string\n", .{}); }Also applies to: 44-45
1-1
: Param name nit: prefer globalObject per guidelines.Minor naming consistency with src/bun.js/**/*.zig conventions.
-pub fn toMatchSnapshot(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) bun.JSError!JSValue { +pub fn toMatchSnapshot(this: *Expect, globalObject: *JSGlobalObject, callFrame: *CallFrame) bun.JSError!JSValue { - defer this.postMatch(globalThis); - const thisValue = callFrame.this(); + defer this.postMatch(globalObject); + const thisValue = callFrame.this(); @@ - try arguments[0].toZigString(&hint_string, globalThis); + try arguments[0].toZigString(&hint_string, globalObject); @@ - try arguments[1].toZigString(&hint_string, globalThis); + try arguments[1].toZigString(&hint_string, globalObject); @@ - const value: JSValue = try this.getValue(globalThis, thisValue, "toMatchSnapshot", "<green>properties<r><d>, <r>hint"); - return this.snapshot(globalThis, value, property_matchers, hint.slice(), "toMatchSnapshot"); + const value: JSValue = try this.getValue(globalObject, thisValue, "toMatchSnapshot", "<green>properties<r><d>, <r>hint"); + return this.snapshot(globalObject, value, property_matchers, hint.slice(), "toMatchSnapshot");
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
src/bun.js/TODO.md
(1 hunks)src/bun.js/test/Collection.zig
(1 hunks)src/bun.js/test/expect/toMatchSnapshot.zig
(1 hunks)src/cli/test_command.zig
(16 hunks)
✅ Files skipped from review due to trivial changes (1)
- src/bun.js/TODO.md
🧰 Additional context used
📓 Path-based instructions (3)
src/**/*.zig
📄 CodeRabbit inference engine (.cursor/rules/building-bun.mdc)
Implement debug logs in Zig using
const log = bun.Output.scoped(.${SCOPE}, false);
and invokinglog("...", .{})
Files:
src/bun.js/test/expect/toMatchSnapshot.zig
src/cli/test_command.zig
src/bun.js/test/Collection.zig
**/*.zig
📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)
**/*.zig
: Declare the extern C symbol in Zig and export a Zig-friendly alias for use
Wrap the Bun____toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue
**/*.zig
: Format Zig files with zig-format (bun run zig-format)
In Zig, manage memory carefully with allocators and use defer for cleanup
Files:
src/bun.js/test/expect/toMatchSnapshot.zig
src/cli/test_command.zig
src/bun.js/test/Collection.zig
src/bun.js/**/*.zig
📄 CodeRabbit inference engine (.cursor/rules/zig-javascriptcore-classes.mdc)
src/bun.js/**/*.zig
: In Zig binding structs, expose generated bindings via pub const js = JSC.Codegen.JS and re-export toJS/fromJS/fromJSDirect
Constructors and prototype methods should return bun.JSError!JSC.JSValue to integrate Zig error handling with JS exceptions
Use parameter name globalObject (not ctx) and accept (*JSC.JSGlobalObject, *JSC.CallFrame) in binding methods/constructors
Implement getters as get(this, globalObject) returning JSC.JSValue and matching the .classes.ts interface
Provide deinit() for resource cleanup and finalize() that calls deinit(); use bun.destroy(this) or appropriate destroy pattern
Access JS call data via CallFrame (argument(i), argumentCount(), thisValue()) and throw errors with globalObject.throw(...)
For properties marked cache: true, use the generated Zig accessors (NameSetCached/GetCached) to work with GC-owned values
In finalize() for objects holding JS references, release them using .deref() before destroy
Files:
src/bun.js/test/expect/toMatchSnapshot.zig
src/bun.js/test/Collection.zig
🧠 Learnings (21)
📓 Common learnings
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.803Z
Learning: Applies to test/**/*.{js,ts} : Write tests in JavaScript or TypeScript using Bun’s Jest-style APIs (test, describe, expect) and run with bun test
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : When spawning Bun in tests, use `bunExe()` and `bunEnv` from `harness`
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.803Z
Learning: Applies to test/js/bun/**/*.{js,ts} : Place Bun API tests under test/js/bun/, separated by category (e.g., test/js/bun/glob/)
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.803Z
Learning: Applies to test/cli/**/*.{js,ts} : When testing Bun as a CLI, use spawn with bunExe() and bunEnv from harness, and capture stdout/stderr via pipes
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : Name test files `*.test.ts` and use `bun:test`
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : Import common test utilities from `harness` (e.g., `bunExe`, `bunEnv`, `tempDirWithFiles`, `tmpdirSync`, platform checks, GC helpers)
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.803Z
Learning: Applies to test/**/*.{js,ts} : Use shared utilities from test/harness.ts where applicable
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : Do not set explicit test timeouts; rely on Bun’s built-in timeouts
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/dev-server-tests.mdc:0-0
Timestamp: 2025-08-30T00:09:39.100Z
Learning: Applies to test/bake/bake-harness.ts : Provide and maintain shared test utilities: devTest, prodTest, devAndProductionTest, Dev, Client, and helpers in the harness
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-08-30T00:13:36.815Z
Learning: Applies to src/bun.js/**/*.zig : Constructors and prototype methods should return bun.JSError!JSC.JSValue to integrate Zig error handling with JS exceptions
📚 Learning: 2025-09-08T04:44:59.101Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-08T04:44:59.101Z
Learning: Applies to test/**/*.test.{ts,tsx} : In tests, use normalizeBunSnapshot when asserting snapshots
Applied to files:
src/bun.js/test/expect/toMatchSnapshot.zig
📚 Learning: 2025-09-08T00:41:12.052Z
Learnt from: CR
PR: oven-sh/bun#0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-09-08T00:41:12.052Z
Learning: Applies to src/bun.js/bindings/v8/test/v8/v8.test.ts : Add a corresponding test case in test/v8/v8.test.ts that invokes checkSameOutput with the new function
Applied to files:
src/bun.js/test/expect/toMatchSnapshot.zig
📚 Learning: 2025-08-30T00:13:36.815Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-08-30T00:13:36.815Z
Learning: Applies to src/bun.js/**/*.zig : Constructors and prototype methods should return bun.JSError!JSC.JSValue to integrate Zig error handling with JS exceptions
Applied to files:
src/bun.js/test/expect/toMatchSnapshot.zig
src/cli/test_command.zig
📚 Learning: 2025-08-30T00:13:36.815Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-08-30T00:13:36.815Z
Learning: Applies to src/bun.js/**/*.zig : Use parameter name globalObject (not ctx) and accept (*JSC.JSGlobalObject, *JSC.CallFrame) in binding methods/constructors
Applied to files:
src/bun.js/test/expect/toMatchSnapshot.zig
📚 Learning: 2025-08-30T00:13:36.815Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-08-30T00:13:36.815Z
Learning: Applies to src/bun.js/**/*.zig : In Zig binding structs, expose generated bindings via pub const js = JSC.Codegen.JS<ClassName> and re-export toJS/fromJS/fromJSDirect
Applied to files:
src/bun.js/test/expect/toMatchSnapshot.zig
src/cli/test_command.zig
📚 Learning: 2025-08-30T00:13:36.815Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-08-30T00:13:36.815Z
Learning: Applies to src/bun.js/**/*.zig : Access JS call data via CallFrame (argument(i), argumentCount(), thisValue()) and throw errors with globalObject.throw(...)
Applied to files:
src/bun.js/test/expect/toMatchSnapshot.zig
📚 Learning: 2025-09-03T17:10:13.486Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : Do not set explicit test timeouts; rely on Bun’s built-in timeouts
Applied to files:
src/cli/test_command.zig
📚 Learning: 2025-08-30T00:12:56.803Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.803Z
Learning: Applies to test/**/*.{js,ts} : Write tests in JavaScript or TypeScript using Bun’s Jest-style APIs (test, describe, expect) and run with bun test
Applied to files:
src/cli/test_command.zig
📚 Learning: 2025-09-03T17:10:13.486Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : When spawning Bun in tests, use `bunExe()` and `bunEnv` from `harness`
Applied to files:
src/cli/test_command.zig
📚 Learning: 2025-08-30T00:12:56.803Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.803Z
Learning: Applies to test/cli/**/*.{js,ts} : When testing Bun as a CLI, use spawn with bunExe() and bunEnv from harness, and capture stdout/stderr via pipes
Applied to files:
src/cli/test_command.zig
📚 Learning: 2025-09-03T17:10:13.486Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : Name test files `*.test.ts` and use `bun:test`
Applied to files:
src/cli/test_command.zig
📚 Learning: 2025-09-03T17:10:13.486Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : Use `using`/`await using` with Bun resources (e.g., Bun.listen/connect/spawn/serve) to ensure cleanup in tests
Applied to files:
src/cli/test_command.zig
📚 Learning: 2025-09-08T04:44:59.101Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-08T04:44:59.101Z
Learning: Applies to test/**/*.test.{ts,tsx} : Avoid shell commands (e.g., find, grep) in tests; use Bun.Glob and built-ins instead
Applied to files:
src/cli/test_command.zig
📚 Learning: 2025-09-20T03:39:41.748Z
Learnt from: pfgithub
PR: oven-sh/bun#22534
File: test/regression/issue/21830.fixture.ts:14-63
Timestamp: 2025-09-20T03:39:41.748Z
Learning: Bun's test runner supports async describe callbacks, unlike Jest/Vitest where describe callbacks must be synchronous. The syntax `describe("name", async () => { ... })` is valid in Bun.
Applied to files:
src/cli/test_command.zig
📚 Learning: 2025-08-30T00:12:56.803Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.803Z
Learning: Applies to test/js/bun/**/*.{js,ts} : Place Bun API tests under test/js/bun/, separated by category (e.g., test/js/bun/glob/)
Applied to files:
src/cli/test_command.zig
📚 Learning: 2025-08-30T00:12:56.803Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.803Z
Learning: Applies to test/js/node/test/** : Vendored Node.js tests under test/js/node/test/ are exceptions and do not follow Bun’s test style
Applied to files:
src/cli/test_command.zig
📚 Learning: 2025-09-08T04:44:59.101Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-08T04:44:59.101Z
Learning: For platform-specific changes, run bun run zig:check-all to compile on all platforms
Applied to files:
src/cli/test_command.zig
📚 Learning: 2025-08-30T00:07:54.573Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/building-bun.mdc:0-0
Timestamp: 2025-08-30T00:07:54.573Z
Learning: Applies to src/**/*.zig : Implement debug logs in Zig using `const log = bun.Output.scoped(.${SCOPE}, false);` and invoking `log("...", .{})`
Applied to files:
src/cli/test_command.zig
📚 Learning: 2025-08-30T00:11:57.076Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-08-30T00:11:57.076Z
Learning: Applies to src/{**/js_*.zig,bun.js/api/**/*.zig} : Use bun.JSError!JSValue for proper error propagation in JS-exposed Zig functions
Applied to files:
src/cli/test_command.zig
📚 Learning: 2025-09-08T04:44:59.101Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-08T04:44:59.101Z
Learning: Applies to **/*.zig : In Zig, manage memory carefully with allocators and use defer for cleanup
Applied to files:
src/bun.js/test/Collection.zig
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Format
🔇 Additional comments (16)
src/bun.js/test/Collection.zig (4)
21-43
: LGTM!The initialization logic is well-structured, with proper setup of the root scope, queues, and allocator management. The debug logging integration adds good observability.
61-77
: LGTM!The callback enqueueing logic correctly validates the locked state, handles optional callbacks, and maintains proper scope relationships. Good use of debug logging for observability.
123-149
: LGTM!The step execution logic properly processes the describe callback queue, handles scope transitions, and integrates well with the broader test execution flow. The error handling for failed scopes is appropriate.
151-158
: LGTM!The exception handling correctly marks the active scope as failed and returns the appropriate result to show unhandled errors in the describe context.
src/cli/test_command.zig (11)
42-62
: LGTM!The status formatting function correctly maps
bun_test.Execution.Result
to appropriate display formats with proper emoji and color handling. The implementation accounts for both pending states and different display modes.
64-73
: LGTM!The test status line writing function properly handles AI agent filtering and uses compile-time status formatting for optimal performance.
379-379
: LGTM!The JUnit reporter signature update correctly uses the new
bun_test.Execution.Result
type, maintaining compatibility with the broader describe2-based execution model.
511-520
: LGTM!The timeout result handling in the JUnit reporter correctly handles all timeout variants (
fail_because_timeout
,fail_because_hook_timeout
, and their done callback variants) with proper failure reporting.
604-607
: Good parameter modernization.The
printTestLine
function signature has been updated to use modernbun_test
types (BunTest
,ExecutionSequence
,ExecutionEntry
) which aligns with the describe2-based execution model.
839-902
: LGTM!The test completion handler correctly processes different execution results, handles output buffering efficiently, and maintains proper summary statistics. The integration with file reporters is well-structured.
1303-1316
: Good integration with describe2 model.The TestRunner initialization correctly incorporates the concurrent flag and integrates with the new
bun_test_root
system, replacing the previous callback-based approach.
1748-1754
: LGTM!The test execution loop correctly integrates with the new
TestCommand.run
interface and handles error propagation appropriately.
1794-1811
: Good modernization to describe2 execution model.The file execution logic properly integrates with the new
bun_test_root
system, handles file entry/exit lifecycle, and maintains compatibility with existing repeat and reporting mechanisms.
1847-1879
: LGTM!The test execution logic correctly integrates with the new
bun_test
system, handles automatic test result queuing, and processes the event loop appropriately while tests are running. The phase-based execution model is well-implemented.
1917-1917
: LGTM!The import statement correctly references the new
bun_test.zig
module, aligning with the describe2 architecture.src/bun.js/test/expect/toMatchSnapshot.zig (1)
7-7
: LGTM: instance-based expect counter.Switching to this.incrementExpectCallCounter() matches the per-instance refactor across expect.*.
fn deinit(this: *QueuedDescribe) void { | ||
this.callback.deinit(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing allocator parameter in deinit method.
The deinit
method in QueuedDescribe
doesn't match the usage pattern where it's called with an allocator parameter (item.deinit(buntest.gpa)
on line 116).
Apply this fix to match the usage pattern:
- fn deinit(this: *QueuedDescribe) void {
+ fn deinit(this: *QueuedDescribe, gpa: std.mem.Allocator) void {
this.callback.deinit();
}
The calls to item.deinit()
on lines 47 and 51 in the main deinit
function should also be updated to pass the allocator:
- item.deinit();
+ item.deinit(buntest.gpa);
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
fn deinit(this: *QueuedDescribe) void { | |
this.callback.deinit(); | |
} | |
fn deinit(this: *QueuedDescribe, gpa: std.mem.Allocator) void { | |
this.callback.deinit(); | |
} |
🤖 Prompt for AI Agents
In src/bun.js/test/Collection.zig around lines 16-18, the QueuedDescribe.deinit
signature lacks the allocator parameter but calls use item.deinit(buntest.gpa)
elsewhere (e.g., line 116); update the method signature to accept an allocator
(e.g., deinit(this: *QueuedDescribe, allocator: *std.mem.Allocator) void) and
inside the method use that allocator for any deallocation, then update the two
calls to item.deinit() in the main deinit function (lines ~47 and ~51) to pass
the allocator argument (the same allocator variable used where items are
deinitialized elsewhere).
_ = this.bunTest() orelse { | ||
const signature = comptime getSignature("toMatchSnapshot", "", true); | ||
return this.throw(globalThis, signature, "\n\n<b>Matcher error<r>: Snapshot matchers cannot be used outside of a test\n", .{}); | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Enforce “unsupported in concurrent tests” (verify).
PR states toMatchSnapshot is unsupported in concurrent tests. If enforcement isn’t centralized, add a guard here based on the active test’s concurrency flag.
Run to confirm where concurrency is enforced:
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find concurrency flags and snapshot handling
rg -nP -C3 '(test\.concurrent|--concurrent|concurrent\s*:|isConcurrent|concurrent=)' src test
rg -nP -C3 'toMatchSnapshot|toMatchInlineSnapshot' src
# Inspect Expect/Test types for concurrency metadata
rg -nP -C5 '\b(struct|const)\s+(Test|BunTest|Expect)\b' src | sed -n '1,200p'
Length of output: 34997
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "---- Locate snapshot-related symbols ----"
rg -n --hidden -S 'pub fn (snapshot|inlineSnapshot)|pub const Snapshots|this\.snapshot\(|toMatchSnapshot' src || true
echo
echo "---- Check for 'concurrent' checks inside snapshot/expect matchers ----"
rg -n --hidden -S 'concurrent' src/bun.js/test/snapshot.zig src/bun.js/test/expect/*.zig || true
echo
echo "---- Show src/bun.js/test/expect/toMatchSnapshot.zig (full) ----"
nl -ba src/bun.js/test/expect/toMatchSnapshot.zig | sed -n '1,240p'
echo
echo "---- Show src/bun.js/test/expect/toMatchInlineSnapshot.zig (full) ----"
nl -ba src/bun.js/test/expect/toMatchInlineSnapshot.zig | sed -n '1,240p'
echo
echo "---- Show snapshot.zig header and nearby functions (1..420) ----"
nl -ba src/bun.js/test/snapshot.zig | sed -n '1,420p'
echo "---- Show snapshot.zig (420..840) ----"
nl -ba src/bun.js/test/snapshot.zig | sed -n '420,840p'
Length of output: 1921
Enforce snapshot matchers are disallowed in concurrent tests.
toMatchSnapshot currently only checks "inside a test" (this.bunTest()) and I found no concurrency guard. Add a centralized check in pub fn snapshot(...) (src/bun.js/test/expect.zig) that inspects the active test's concurrency flag (e.g., ExecutionEntry.base.concurrent / DescribeScope.base.concurrent via the BunTest active entry) and throws via getSignature/this.throw with a message like "Snapshot matchers cannot be used in concurrent tests". If you prefer not to centralize, add the same guard immediately after the this.bunTest() orelse in src/bun.js/test/expect/toMatchSnapshot.zig (and mirror for toMatchInlineSnapshot/toThrowErrorMatchingSnapshot).
🤖 Prompt for AI Agents
In src/bun.js/test/expect.zig (pub fn snapshot(...)) and related snapshot
entrypoints, add a centralized concurrency guard: inspect the active BunTest
execution entry's concurrency flag (e.g., check ExecutionEntry.base.concurrent
or DescribeScope.base.concurrent via the BunTest active entry) and if it is
true, call getSignature with the appropriate matcher name and use
this.throw(globalThis, signature, "\n\n<b>Matcher error<r>: Snapshot matchers
cannot be used in concurrent tests\n", .{}); to abort; if you prefer not to
centralize, add the same guard immediately after the this.bunTest() orelse in
src/bun.js/test/expect/toMatchSnapshot.zig (lines ~15-18) and mirror the
identical check/message in toMatchInlineSnapshot and
toThrowErrorMatchingSnapshot so all snapshot matchers disallow concurrent tests.
### What does this PR do? #22534 made `--test-name-pattern` more logical and not start with empty ` ` (space), so fixing the built regex to make it work still for old and new bun The other main issue that that pr did was make start events for filtered out names which means it appears to rerun them all even when in reality it doesn't as they are skipped Also in theory with concurrent test, if there's an error after another started it would be assigned to the wrong test because we don't get test id's in the error event, so its just assumed its from the last started one which with parallel means it isn't correct. ### How did you verify your code works?
bun test
Fixes #8768, Fixes #14624, Fixes #20100, Fixes #19875, Fixes #14135, Fixes #20980, Fixes #21830, Fixes #5738, Fixes #19758, Fixes #12782, Fixes #5585, Fixes #9548, Might fix 5996
New features:
Concurrent tests
Concurrent tests allow running multiple async tests at the same time.
Without
.concurrent
, this test file takes 3 seconds to run because each one has to wait for the one before it to finish before it can start.With
.concurrent
, this file takes 1 second because all three sleeps can run at once.To run all tests as concurrent, pass the
--concurrent
flag when running tests.Limitations:
expect()
call counts to the test, meaningexpect.assertions()
does not functiontoMatchSnapshot
.toMatchInlineSnapshot
is still supported.beforeAll
/afterAll
will never be executed concurrently.beforeEach
/afterEach
will.Chaining
Chaining multiple describe/test qualifiers is now allowed. Previously, it would fail.
Breaking changes:
Describe ordering
Previously, describe callbacks were called immediately. Now, they are deferred until the outer callback has finished running. The previous order matched Jest. The new order is similar to Vitest, but does not match exactly.
Before, this would print
Now, this will print
Test ordering
Describes are no longer always called before tests. They are now in order.
Before, this would print
Now, this will print
Preload hooks
Previously, beforeAll in a preload ran before the first file and afterAll ran after the last file. Now, beforeAll will run at the start of each file and afterAll will run at the end of each file. This behaviour matches Jest and Vitest.
Describe failures
Current behaviour is that when an error is thrown inside a describe callback, none of the tests declared there will run. Now, describes declared inside will also not run. The new behaviour matches the behaviour of Jest and Vitest.
Before, the inner describe callback would be called and the inner test would run, although the outer test would not:
Now, the inner describe callback is not called at all.
Hook failures
Previously, a beforeAll failure would skip subsequent beforeAll()s, the test, and the afterAll. Now, a beforeAll failure skips any subsequent beforeAll()s and the test, but not the afterAll.
Previously, an async beforeEach failure would still allow the test to run. Now, an async beforeEach failure will prevent the test from running
Hook timeouts
Hooks will now time out, and can have their timeout configured in an options parameter
Hook execution order
beforeAll will now execute before the tests in the scope, rather than immediately when it is called.
test inside test
test() inside test() now errors rather than silently failing. Support for this may be added in the future.
afterAll inside test
afterAll inside a test is no longer allowed
Only inside only
Previously, an outer 'describe.only' would run all tests inside it even if there was an inner 'test.only'. Now, only the innermost only tests are executed.
With no inner only, the outer only will still run all tests:
Potential follow-up work
test("rerun me", () => { console.log("run one time!"); });
--rerun-each=3
<- this runs the first and third time but not the second time. fix.--concurrent-limit=25
test.failing
should have its own style in the same waytest.todo
passing marks as 'todo' insetead of 'passing'. right now it's✓
which is confusing.expect_calls
,assert_expect_calls
. or something.bun.jsc.Jest.Jest.runner.?.getOrPutFile(file_path).file_id;
, store the file path directly)test.failing.if(false)
errors because it can't replace mode 'failing' with mode 'skip'. this should probably be allowed instead.while(true) {}
AsyncLocalStorage