aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2023-03-19 19:07:56 -0700
committerGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2023-03-19 19:07:56 -0700
commita83c5c996f46ae861962c8e8bf7042a8fa11130b (patch)
treec25496deb4cc04f19d8a50551a40c3c99b910e73
parentb053dffca75848647286088e519e1e5a174e9d2e (diff)
downloadbun-a83c5c996f46ae861962c8e8bf7042a8fa11130b.tar.gz
bun-a83c5c996f46ae861962c8e8bf7042a8fa11130b.tar.zst
bun-a83c5c996f46ae861962c8e8bf7042a8fa11130b.zip
[bun test] Implement `--rerun-each` flag to run each test N times
-rw-r--r--src/cli.zig10
-rw-r--r--src/cli/test_command.zig92
2 files changed, 64 insertions, 38 deletions
diff --git a/src/cli.zig b/src/cli.zig
index 9b04a3588..1418d1b2e 100644
--- a/src/cli.zig
+++ b/src/cli.zig
@@ -217,6 +217,7 @@ pub const Arguments = struct {
// TODO: update test completions
const test_only_params = [_]ParamType{
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,
};
const build_params_public = public_params ++ build_only_params;
@@ -376,6 +377,14 @@ pub const Arguments = struct {
if (cmd == .TestCommand) {
ctx.test_options.update_snapshots = args.flag("--update-snapshots");
+ if (args.option("--rerun-each")) |repeat_count| {
+ if (repeat_count.len > 0) {
+ ctx.test_options.repeat_count = std.fmt.parseInt(u32, repeat_count, 10) catch |e| {
+ Output.prettyErrorln("--rerun-each expects a number: {s}", .{@errorName(e)});
+ Global.exit(1);
+ };
+ }
+ }
}
ctx.args.absolute_working_dir = cwd;
@@ -871,6 +880,7 @@ pub const Command = struct {
pub const TestOptions = struct {
update_snapshots: bool = false,
+ repeat_count: u32 = 0,
};
pub const Context = struct {
diff --git a/src/cli/test_command.zig b/src/cli/test_command.zig
index e88121e39..11f57b0ad 100644
--- a/src/cli/test_command.zig
+++ b/src/cli/test_command.zig
@@ -70,6 +70,7 @@ pub const CommandLineReporter = struct {
last_dot: u32 = 0,
summary: Summary = Summary{},
prev_file: u64 = 0,
+ repeat_count: u32 = 1,
failures_to_repeat_buf: std.ArrayListUnmanaged(u8) = .{},
skips_to_repeat_buf: std.ArrayListUnmanaged(u8) = .{},
@@ -393,6 +394,7 @@ pub const TestCommand = struct {
.onTestFail = CommandLineReporter.handleTestFail,
.onTestSkip = CommandLineReporter.handleTestSkip,
};
+ reporter.repeat_count = @max(ctx.test_options.repeat_count, 1);
reporter.jest.callback = &reporter.callback;
jest.Jest.runner = &reporter.jest;
@@ -623,56 +625,70 @@ pub const TestCommand = struct {
Output.flush();
vm.main_hash = @truncate(u32, bun.hash(resolution.path_pair.primary.text));
- var promise = try vm.loadEntryPoint(resolution.path_pair.primary.text);
-
- switch (promise.status(vm.global.vm())) {
- .Rejected => {
- var result = promise.result(vm.global.vm());
- vm.runErrorHandler(result, null);
- reporter.summary.fail += 1;
- return;
- },
- else => {},
- }
+ var repeat_count = reporter.repeat_count;
+ var repeat_index: u32 = 0;
+ while (repeat_index < repeat_count) : (repeat_index += 1) {
+ var promise = try vm.loadEntryPoint(resolution.path_pair.primary.text);
+
+ switch (promise.status(vm.global.vm())) {
+ .Rejected => {
+ var result = promise.result(vm.global.vm());
+ vm.runErrorHandler(result, null);
+ reporter.summary.fail += 1;
+ return;
+ },
+ else => {},
+ }
- {
- vm.global.vm().drainMicrotasks();
- var count = vm.unhandled_error_counter;
- vm.global.handleRejectedPromises();
- while (vm.unhandled_error_counter > count) {
- count = vm.unhandled_error_counter;
+ {
vm.global.vm().drainMicrotasks();
+ var count = vm.unhandled_error_counter;
vm.global.handleRejectedPromises();
+ while (vm.unhandled_error_counter > count) {
+ count = vm.unhandled_error_counter;
+ vm.global.vm().drainMicrotasks();
+ vm.global.handleRejectedPromises();
+ }
+ vm.global.vm().doWork();
}
- vm.global.vm().doWork();
- }
- var modules: []*jest.DescribeScope = reporter.jest.files.items(.module_scope)[file_start..];
- for (modules) |module| {
- vm.onUnhandledRejectionCtx = null;
- vm.onUnhandledRejection = jest.TestRunnerTask.onUnhandledRejection;
- module.runTests(JSC.JSValue.zero, vm.global);
- vm.eventLoop().tick();
+ const file_end = reporter.jest.files.len;
+ for (file_start..file_end) |module_id| {
+ const module = reporter.jest.files.items(.module_scope)[module_id];
- var prev_unhandled_count = vm.unhandled_error_counter;
- while (vm.active_tasks > 0) {
- if (!jest.Jest.runner.?.has_pending_tests) jest.Jest.runner.?.drain();
+ vm.onUnhandledRejectionCtx = null;
+ vm.onUnhandledRejection = jest.TestRunnerTask.onUnhandledRejection;
+ module.runTests(JSC.JSValue.zero, vm.global);
vm.eventLoop().tick();
- while (jest.Jest.runner.?.has_pending_tests) {
- vm.eventLoop().autoTick();
- if (!jest.Jest.runner.?.has_pending_tests) break;
+ var prev_unhandled_count = vm.unhandled_error_counter;
+ while (vm.active_tasks > 0) {
+ if (!jest.Jest.runner.?.has_pending_tests) jest.Jest.runner.?.drain();
vm.eventLoop().tick();
- }
- while (prev_unhandled_count < vm.unhandled_error_counter) {
- vm.global.handleRejectedPromises();
- prev_unhandled_count = vm.unhandled_error_counter;
+ while (jest.Jest.runner.?.has_pending_tests) {
+ vm.eventLoop().autoTick();
+ if (!jest.Jest.runner.?.has_pending_tests) break;
+ vm.eventLoop().tick();
+ }
+
+ while (prev_unhandled_count < vm.unhandled_error_counter) {
+ vm.global.handleRejectedPromises();
+ prev_unhandled_count = vm.unhandled_error_counter;
+ }
}
+ _ = vm.global.vm().runGC(false);
+ }
+
+ vm.global.vm().clearMicrotaskCallback();
+ vm.global.handleRejectedPromises();
+ if (repeat_index > 0) {
+ vm.clearEntryPoint();
+ var entry = JSC.ZigString.init(resolution.path_pair.primary.text);
+ vm.global.deleteModuleRegistryEntry(&entry);
+ Output.prettyErrorln("<r>{s} <d>[RUN {d:0>4}]:<r>\n", .{ resolution.path_pair.primary.name.filename, repeat_index + 1 });
+ Output.flush();
}
- _ = vm.global.vm().runGC(false);
}
- vm.global.vm().clearMicrotaskCallback();
- vm.global.handleRejectedPromises();
}
};