Rereun-each fix (#23168)

### What does this PR do?

Fix --rerun-each. Fixes #21409

### How did you verify your code works?

Test case

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
pfg
2025-10-02 19:12:45 -07:00
committed by GitHub
parent 55f8e8add3
commit d99d622472
2 changed files with 143 additions and 6 deletions

View File

@@ -1828,6 +1828,13 @@ pub const TestCommand = struct {
vm.onUnhandledRejection = jest.on_unhandled_rejection.onUnhandledRejection;
while (repeat_index < repeat_count) : (repeat_index += 1) {
// Clear the module cache before re-running (except for the first run)
if (repeat_index > 0) {
try vm.clearEntryPoint();
var entry = jsc.ZigString.init(file_path);
try vm.global.deleteModuleRegistryEntry(&entry);
}
var bun_test_root = &jest.Jest.runner.?.bun_test_root;
// Determine if this file should run tests concurrently based on glob pattern
const should_run_concurrent = reporter.jest.shouldFileRunConcurrently(file_id);
@@ -1838,7 +1845,10 @@ pub const TestCommand = struct {
bun.jsc.Jest.bun_test.debug.group.log("loadEntryPointForTestRunner(\"{}\")", .{std.zig.fmtEscapes(file_path)});
var promise = try vm.loadEntryPointForTestRunner(file_path);
reporter.summary().files += 1;
// Only count the file once, not once per repeat
if (repeat_index == 0) {
reporter.summary().files += 1;
}
switch (promise.status(vm.global.vm())) {
.rejected => {
@@ -1905,11 +1915,6 @@ pub const TestCommand = struct {
}
vm.global.handleRejectedPromises();
if (repeat_index > 0) {
try vm.clearEntryPoint();
var entry = jsc.ZigString.init(file_path);
try vm.global.deleteModuleRegistryEntry(&entry);
}
if (Output.is_github_action) {
Output.prettyErrorln("<r>\n::endgroup::\n", .{});

View File

@@ -0,0 +1,132 @@
import { expect, test } from "bun:test";
import { bunEnv, bunExe, tempDir } from "harness";
test("--rerun-each should run tests exactly N times", async () => {
using dir = tempDir("test-rerun-each", {
"counter.test.ts": `
import { test, expect } from "bun:test";
// Use a global counter that persists across module reloads
if (!globalThis.testRunCounter) {
globalThis.testRunCounter = 0;
}
test("should increment counter", () => {
globalThis.testRunCounter++;
console.log(\`Run #\${globalThis.testRunCounter}\`);
expect(true).toBe(true);
});
`,
});
// Test with --rerun-each=3
await using proc = Bun.spawn({
cmd: [bunExe(), "test", "counter.test.ts", "--rerun-each=3"],
env: bunEnv,
cwd: String(dir),
stderr: "pipe",
stdout: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect(exitCode).toBe(0);
// Should see "Run #1", "Run #2", "Run #3" in the output
expect(stdout).toContain("Run #1");
expect(stdout).toContain("Run #2");
expect(stdout).toContain("Run #3");
// Should NOT see "Run #4"
expect(stdout).not.toContain("Run #4");
// Should run exactly 3 tests - check stderr for test summary
const combined = stdout + stderr;
expect(combined).toMatch(/3 pass/);
// Test with --rerun-each=1 (should run once)
await using proc2 = Bun.spawn({
cmd: [bunExe(), "test", "counter.test.ts", "--rerun-each=1"],
env: bunEnv,
cwd: String(dir),
stderr: "pipe",
stdout: "pipe",
});
const [stdout2, stderr2, exitCode2] = await Promise.all([proc2.stdout.text(), proc2.stderr.text(), proc2.exited]);
expect(exitCode2).toBe(0);
const combined2 = stdout2 + stderr2;
expect(combined2).toMatch(/1 pass/);
});
test("--rerun-each should report correct file count", async () => {
using dir = tempDir("test-rerun-each-file-count", {
"test1.test.ts": `
import { test, expect } from "bun:test";
test("test in file 1", () => {
expect(true).toBe(true);
});
`,
});
// Run with --rerun-each=3
await using proc = Bun.spawn({
cmd: [bunExe(), "test", "test1.test.ts", "--rerun-each=3"],
env: bunEnv,
cwd: String(dir),
stderr: "pipe",
stdout: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect(exitCode).toBe(0);
// Should report "Ran 3 tests across 1 file" not "across 3 files"
const combined = stdout + stderr;
expect(combined).toContain("Ran 3 tests across 1 file");
expect(combined).not.toContain("across 3 files");
});
test("--rerun-each should handle test failures correctly", async () => {
using dir = tempDir("test-rerun-each-fail", {
"fail.test.ts": `
import { test, expect } from "bun:test";
if (!globalThis.failCounter) {
globalThis.failCounter = 0;
}
test("fails on second run", () => {
globalThis.failCounter++;
console.log(\`Attempt #\${globalThis.failCounter}\`);
// Fail on the second run
expect(globalThis.failCounter).not.toBe(2);
});
`,
});
await using proc = Bun.spawn({
cmd: [bunExe(), "test", "fail.test.ts", "--rerun-each=3"],
env: bunEnv,
cwd: String(dir),
stderr: "pipe",
stdout: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
// Should have non-zero exit code due to failure
expect(exitCode).not.toBe(0);
// Should see all three attempts
expect(stdout).toContain("Attempt #1");
expect(stdout).toContain("Attempt #2");
expect(stdout).toContain("Attempt #3");
// Should report 2 passes and 1 failure - check both stdout and stderr
const combined = stdout + stderr;
expect(combined).toMatch(/2 pass/);
expect(combined).toMatch(/1 fail/);
});