diff options
author | 2023-03-19 19:07:56 -0700 | |
---|---|---|
committer | 2023-03-19 19:07:56 -0700 | |
commit | a83c5c996f46ae861962c8e8bf7042a8fa11130b (patch) | |
tree | c25496deb4cc04f19d8a50551a40c3c99b910e73 | |
parent | b053dffca75848647286088e519e1e5a174e9d2e (diff) | |
download | bun-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.zig | 10 | ||||
-rw-r--r-- | src/cli/test_command.zig | 92 |
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(); } }; |