aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Ashcon Partovi <ashcon@partovi.net> 2023-05-23 22:33:32 -0700
committerGravatar GitHub <noreply@github.com> 2023-05-23 22:33:32 -0700
commitc3d402ce47e273b60e3a37164a6ba2be6e1eee5d (patch)
tree5e780162de9c723aba5126fd3276df51d9048c18
parentd9bdfcf1317f0ea14453cb2023daa07830d446d8 (diff)
downloadbun-c3d402ce47e273b60e3a37164a6ba2be6e1eee5d.tar.gz
bun-c3d402ce47e273b60e3a37164a6ba2be6e1eee5d.tar.zst
bun-c3d402ce47e273b60e3a37164a6ba2be6e1eee5d.zip
Implement `bun test --timeout` (#3040)
You can change the default per-test timeout in `bun test`: > bun test --timeout 10 The default timeout is 5000.
-rw-r--r--src/bun.js/test/jest.zig9
-rw-r--r--src/cli.zig10
-rw-r--r--src/cli/test_command.zig1
-rw-r--r--test/cli/test/bun-test.test.ts66
4 files changed, 80 insertions, 6 deletions
diff --git a/src/bun.js/test/jest.zig b/src/bun.js/test/jest.zig
index 8b06caa0f..dcc7a8149 100644
--- a/src/bun.js/test/jest.zig
+++ b/src/bun.js/test/jest.zig
@@ -35,8 +35,6 @@ const getAllocator = @import("../base.zig").getAllocator;
const JSPrivateDataPtr = @import("../base.zig").JSPrivateDataPtr;
const GetJSPrivateData = @import("../base.zig").GetJSPrivateData;
-const default_timeout = std.time.ms_per_min * 5;
-
const ZigString = JSC.ZigString;
const JSInternalPromise = JSC.JSInternalPromise;
const JSPromise = JSC.JSPromise;
@@ -360,8 +358,6 @@ pub const TestRunner = struct {
only: bool = false,
last_file: u64 = 0,
- timeout_seconds: f64 = 5.0,
-
allocator: std.mem.Allocator,
callback: *Callback = undefined,
@@ -376,6 +372,7 @@ pub const TestRunner = struct {
snapshots: Snapshots,
+ default_timeout_ms: u32 = 0,
test_timeout_timer: ?*bun.uws.Timer = null,
last_test_timeout_timer_duration: u32 = 0,
active_test_for_timeout: ?TestRunner.Test.ID = null,
@@ -3243,7 +3240,7 @@ pub const TestScope = struct {
skipped: bool = false,
is_todo: bool = false,
snapshot_count: usize = 0,
- timeout_millis: u32 = default_timeout,
+ timeout_millis: u32 = 0,
pub const Class = NewClass(
void,
@@ -3382,7 +3379,7 @@ pub const TestScope = struct {
.label = label,
.callback = function.asObjectRef(),
.parent = DescribeScope.active,
- .timeout_millis = if (arguments.len > 2) @intCast(u32, @max(args[2].coerce(i32, ctx), 0)) else default_timeout,
+ .timeout_millis = if (arguments.len > 2) @intCast(u32, @max(args[2].coerce(i32, ctx), 0)) else Jest.runner.?.default_timeout_ms,
}) catch unreachable;
if (test_elapsed_timer == null) create_tiemr: {
diff --git a/src/cli.zig b/src/cli.zig
index be8f9fe76..606e0a3cc 100644
--- a/src/cli.zig
+++ b/src/cli.zig
@@ -211,6 +211,7 @@ pub const Arguments = struct {
// TODO: update test completions
const test_only_params = [_]ParamType{
+ clap.parseParam("--timeout <NUMBER> Set the per-test timeout in milliseconds, default is 5000.") catch unreachable,
clap.parseParam("--update-snapshots Update snapshot files") catch unreachable,
clap.parseParam("--rerun-each <NUMBER> Re-run each test file <NUMBER> times, helps catch certain bugs") catch unreachable,
};
@@ -371,6 +372,14 @@ pub const Arguments = struct {
}
if (cmd == .TestCommand) {
+ if (args.option("--timeout")) |timeout_ms| {
+ if (timeout_ms.len > 0) {
+ ctx.test_options.default_timeout_ms = std.fmt.parseInt(u32, timeout_ms, 10) catch {
+ Output.prettyErrorln("<r><red>error<r>: Invalid timeout: \"{s}\"", .{timeout_ms});
+ Global.exit(1);
+ };
+ }
+ }
ctx.test_options.update_snapshots = args.flag("--update-snapshots");
if (args.option("--rerun-each")) |repeat_count| {
if (repeat_count.len > 0) {
@@ -904,6 +913,7 @@ pub const Command = struct {
};
pub const TestOptions = struct {
+ default_timeout_ms: u32 = 5 * std.time.ms_per_s,
update_snapshots: bool = false,
repeat_count: u32 = 0,
};
diff --git a/src/cli/test_command.zig b/src/cli/test_command.zig
index 3fdce584a..cf5c4fc49 100644
--- a/src/cli/test_command.zig
+++ b/src/cli/test_command.zig
@@ -414,6 +414,7 @@ pub const TestCommand = struct {
.allocator = ctx.allocator,
.log = ctx.log,
.callback = undefined,
+ .default_timeout_ms = ctx.test_options.default_timeout_ms,
.snapshots = Snapshots{
.allocator = ctx.allocator,
.update_snapshots = ctx.test_options.update_snapshots,
diff --git a/test/cli/test/bun-test.test.ts b/test/cli/test/bun-test.test.ts
new file mode 100644
index 000000000..e1a5fdd60
--- /dev/null
+++ b/test/cli/test/bun-test.test.ts
@@ -0,0 +1,66 @@
+import { join } from "node:path";
+import { tmpdir } from "node:os";
+import { mkdtempSync, writeFileSync, rmSync } from "node:fs";
+import { spawnSync } from "bun";
+import { describe, test, expect } from "bun:test";
+import { bunExe, bunEnv } from "harness";
+
+describe("bun test", () => {
+ describe("--timeout", () => {
+ test("must provide a number timeout", () => {
+ const stderr = runTest({
+ args: ["--timeout", "foo"],
+ });
+ expect(stderr).toContain("Invalid timeout");
+ });
+ test("must provide non-negative timeout", () => {
+ const stderr = runTest({
+ args: ["--timeout", "-1"],
+ });
+ expect(stderr).toContain("Invalid timeout");
+ });
+ test("timeout can be set to 1ms", () => {
+ const stderr = runTest({
+ args: ["--timeout", "1"],
+ code: `
+ import { test, expect } from "bun:test";
+ import { sleep } from "bun";
+ test("timeout", async () => {
+ await sleep(2);
+ });
+ `,
+ });
+ expect(stderr).toContain("timed out after 1ms");
+ });
+ test("timeout should default to 5000ms", () => {
+ const stderr = runTest({
+ code: `
+ import { test, expect } from "bun:test";
+ import { sleep } from "bun";
+ test("timeout", async () => {
+ await sleep(5001);
+ });
+ `,
+ });
+ expect(stderr).toContain("timed out after 5000ms");
+ });
+ });
+});
+
+function runTest({ code = "", args = [] }: { code?: string; args?: string[] }): string {
+ const dir = mkdtempSync(join(tmpdir(), "bun-test-"));
+ const path = join(dir, `bun-test-${Date.now()}.test.ts`);
+ writeFileSync(path, code);
+ try {
+ const { stderr } = spawnSync({
+ cwd: dir,
+ cmd: [bunExe(), "test", path, ...args],
+ env: bunEnv,
+ stderr: "pipe",
+ stdout: "ignore",
+ });
+ return stderr.toString();
+ } finally {
+ rmSync(path);
+ }
+}