authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-01-03 14:51:11+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-01-03 14:52:11+00:00
logd024d9f00566968fd3aa85e0e6a0e8907016977c
treeafd1f92145e999b66704f2a2549ce99cf5438c81
parentc9fa8e46df21cdf66b291fb3dfdcef1b6a1d3cab
signaturelock-open Commit is signed but in an unrecognized format.

std.Build: crashes in the test runner are fatal errors


1 files changed, 45 insertions(+), 44 deletions(-)

lib/std/Build/Step/Run.zig+45-44
...@@ -1539,10 +1539,6 @@ fn runCommand(...@@ -1539,10 +1539,6 @@ fn runCommand(
1539 }1539 }
1540}1540}
15411541
1542const EvalZigTestResult = struct {
1543 test_results: Step.TestResults,
1544 test_metadata: ?TestMetadata,
1545};
1546const EvalGenericResult = struct {1542const EvalGenericResult = struct {
1547 term: std.process.Child.Term,1543 term: std.process.Child.Term,
1548 stdout: ?[]const u8,1544 stdout: ?[]const u8,
...@@ -1605,21 +1601,8 @@ fn spawnChildAndCollect(...@@ -1605,21 +1601,8 @@ fn spawnChildAndCollect(
16051601
1606 if (run.stdio == .zig_test) {1602 if (run.stdio == .zig_test) {
1607 var timer = try std.time.Timer.start();1603 var timer = try std.time.Timer.start();
1608 const res = try evalZigTest(run, &child, options, fuzz_context);1604 defer run.step.result_duration_ns = timer.read();
1609 run.step.result_duration_ns = timer.read();1605 try evalZigTest(run, &child, options, fuzz_context);
1610 run.step.test_results = res.test_results;
1611 if (res.test_metadata) |tm| {
1612 run.cached_test_metadata = tm.toCachedTestMetadata();
1613 if (options.web_server) |ws| {
1614 if (b.graph.time_report) {
1615 ws.updateTimeReportRunTest(
1616 run,
1617 &run.cached_test_metadata.?,
1618 tm.ns_per_test,
1619 );
1620 }
1621 }
1622 }
1623 return null;1606 return null;
1624 } else {1607 } else {
1625 const inherit = child.stdout_behavior == .Inherit or child.stderr_behavior == .Inherit;1608 const inherit = child.stdout_behavior == .Inherit or child.stderr_behavior == .Inherit;
...@@ -1675,7 +1658,7 @@ fn evalZigTest(...@@ -1675,7 +1658,7 @@ fn evalZigTest(
1675 child: *std.process.Child,1658 child: *std.process.Child,
1676 options: Step.MakeOptions,1659 options: Step.MakeOptions,
1677 fuzz_context: ?FuzzContext,1660 fuzz_context: ?FuzzContext,
1678) !EvalZigTestResult {1661) !void {
1679 const step_owner = run.step.owner;1662 const step_owner = run.step.owner;
1680 const gpa = step_owner.allocator;1663 const gpa = step_owner.allocator;
1681 const arena = step_owner.allocator;1664 const arena = step_owner.allocator;
...@@ -1684,18 +1667,16 @@ fn evalZigTest(...@@ -1684,18 +1667,16 @@ fn evalZigTest(
1684 // We will update this every time a child runs.1667 // We will update this every time a child runs.
1685 run.step.result_peak_rss = 0;1668 run.step.result_peak_rss = 0;
16861669
1687 var result: EvalZigTestResult = .{1670 var test_results: Step.TestResults = .{
1688 .test_results = .{1671 .test_count = 0,
1689 .test_count = 0,1672 .skip_count = 0,
1690 .skip_count = 0,1673 .fail_count = 0,
1691 .fail_count = 0,1674 .crash_count = 0,
1692 .crash_count = 0,1675 .timeout_count = 0,
1693 .timeout_count = 0,1676 .leak_count = 0,
1694 .leak_count = 0,1677 .log_err_count = 0,
1695 .log_err_count = 0,
1696 },
1697 .test_metadata = null,
1698 };1678 };
1679 var test_metadata: ?TestMetadata = null;
16991680
1700 while (true) {1681 while (true) {
1701 try child.spawn(io);1682 try child.spawn(io);
...@@ -1721,8 +1702,8 @@ fn evalZigTest(...@@ -1721,8 +1702,8 @@ fn evalZigTest(
1721 options,1702 options,
1722 fuzz_context,1703 fuzz_context,
1723 &poller,1704 &poller,
1724 &result.test_metadata,1705 &test_metadata,
1725 &result.test_results,1706 &test_results,
1726 )) {1707 )) {
1727 .write_failed => |err| {1708 .write_failed => |err| {
1728 // The runner unexpectedly closed a stdio pipe, which means a crash. Make sure we've captured1709 // The runner unexpectedly closed a stdio pipe, which means a crash. Make sure we've captured
...@@ -1741,8 +1722,9 @@ fn evalZigTest(...@@ -1741,8 +1722,9 @@ fn evalZigTest(
1741 child.resource_usage_statistics.getMaxRss() orelse 0,1722 child.resource_usage_statistics.getMaxRss() orelse 0,
1742 );1723 );
17431724
1744 try run.step.addError("unable to write stdin ({t}); test process unexpectedly {f}", .{ err, fmtTerm(term) });1725 // The individual unit test results are irrelevant: the test runner itself broke!
1745 return result;1726 // Fail immediately without populating `s.test_results`.
1727 return run.step.fail("unable to write stdin ({t}); test process unexpectedly {f}", .{ err, fmtTerm(term) });
1746 },1728 },
1747 .no_poll => |no_poll| {1729 .no_poll => |no_poll| {
1748 // This might be a success (we requested exit and the child dutifully closed stdout) or1730 // This might be a success (we requested exit and the child dutifully closed stdout) or
...@@ -1764,10 +1746,10 @@ fn evalZigTest(...@@ -1764,10 +1746,10 @@ fn evalZigTest(
1764 if (no_poll.active_test_index) |test_index| {1746 if (no_poll.active_test_index) |test_index| {
1765 // A test was running, so this is definitely a crash. Report it against that1747 // A test was running, so this is definitely a crash. Report it against that
1766 // test, and continue to the next test.1748 // test, and continue to the next test.
1767 result.test_metadata.?.ns_per_test[test_index] = no_poll.ns_elapsed;1749 test_metadata.?.ns_per_test[test_index] = no_poll.ns_elapsed;
1768 result.test_results.crash_count += 1;1750 test_results.crash_count += 1;
1769 try run.step.addError("'{s}' {f}{s}{s}", .{1751 try run.step.addError("'{s}' {f}{s}{s}", .{
1770 result.test_metadata.?.testName(test_index),1752 test_metadata.?.testName(test_index),
1771 fmtTerm(term),1753 fmtTerm(term),
1772 if (stderr_owned.len != 0) " with stderr:\n" else "",1754 if (stderr_owned.len != 0) " with stderr:\n" else "",
1773 std.mem.trim(u8, stderr_owned, "\n"),1755 std.mem.trim(u8, stderr_owned, "\n"),
...@@ -1777,11 +1759,28 @@ fn evalZigTest(...@@ -1777,11 +1759,28 @@ fn evalZigTest(
17771759
1778 // Report an error if the child terminated uncleanly or if we were still trying to run more tests.1760 // Report an error if the child terminated uncleanly or if we were still trying to run more tests.
1779 run.step.result_stderr = stderr_owned;1761 run.step.result_stderr = stderr_owned;
1780 const tests_done = result.test_metadata != null and result.test_metadata.?.next_index == std.math.maxInt(u32);1762 const tests_done = test_metadata != null and test_metadata.?.next_index == std.math.maxInt(u32);
1781 if (!tests_done or !termMatches(.{ .Exited = 0 }, term)) {1763 if (!tests_done or !termMatches(.{ .Exited = 0 }, term)) {
1782 try run.step.addError("test process unexpectedly {f}", .{fmtTerm(term)});1764 // The individual unit test results are irrelevant: the test runner itself broke!
1765 // Fail immediately without populating `s.test_results`.
1766 return run.step.fail("test process unexpectedly {f}", .{fmtTerm(term)});
1767 }
1768
1769 // We're done with all of the tests! Commit the test results and return.
1770 run.step.test_results = test_results;
1771 if (test_metadata) |tm| {
1772 run.cached_test_metadata = tm.toCachedTestMetadata();
1773 if (options.web_server) |ws| {
1774 if (run.step.owner.graph.time_report) {
1775 ws.updateTimeReportRunTest(
1776 run,
1777 &run.cached_test_metadata.?,
1778 tm.ns_per_test,
1779 );
1780 }
1781 }
1783 }1782 }
1784 return result;1783 return;
1785 },1784 },
1786 .timeout => |timeout| {1785 .timeout => |timeout| {
1787 const stderr = poller.reader(.stderr).buffered();1786 const stderr = poller.reader(.stderr).buffered();
...@@ -1789,10 +1788,10 @@ fn evalZigTest(...@@ -1789,10 +1788,10 @@ fn evalZigTest(
1789 if (timeout.active_test_index) |test_index| {1788 if (timeout.active_test_index) |test_index| {
1790 // A test was running. Report the timeout against that test, and continue on to1789 // A test was running. Report the timeout against that test, and continue on to
1791 // the next test.1790 // the next test.
1792 result.test_metadata.?.ns_per_test[test_index] = timeout.ns_elapsed;1791 test_metadata.?.ns_per_test[test_index] = timeout.ns_elapsed;
1793 result.test_results.timeout_count += 1;1792 test_results.timeout_count += 1;
1794 try run.step.addError("'{s}' timed out after {D}{s}{s}", .{1793 try run.step.addError("'{s}' timed out after {D}{s}{s}", .{
1795 result.test_metadata.?.testName(test_index),1794 test_metadata.?.testName(test_index),
1796 timeout.ns_elapsed,1795 timeout.ns_elapsed,
1797 if (stderr.len != 0) " with stderr:\n" else "",1796 if (stderr.len != 0) " with stderr:\n" else "",
1798 std.mem.trim(u8, stderr, "\n"),1797 std.mem.trim(u8, stderr, "\n"),
...@@ -1801,6 +1800,8 @@ fn evalZigTest(...@@ -1801,6 +1800,8 @@ fn evalZigTest(
1801 }1800 }
1802 // Just log an error and let the child be killed.1801 // Just log an error and let the child be killed.
1803 run.step.result_stderr = try arena.dupe(u8, stderr);1802 run.step.result_stderr = try arena.dupe(u8, stderr);
1803 // The individual unit test results in `results` are irrelevant: the test runner
1804 // is broken! Fail immediately without populating `s.test_results`.
1804 return run.step.fail("test runner failed to respond for {D}", .{timeout.ns_elapsed});1805 return run.step.fail("test runner failed to respond for {D}", .{timeout.ns_elapsed});
1805 },1806 },
1806 }1807 }