feature. See also
. The project being documented here (as the example) is the Zig library itself.
Run.evalZigTest
fn evalZigTest(
run: *Run,
run_index: Configuration.Step.Index,
maker: *Maker,
progress_node: std.Progress.Node,
spawn_options: process.SpawnOptions,
fuzz_context: ?FuzzContext,
) !void
File
Code
fn evalZigTest(
run: *Run,
run_index: Configuration.Step.Index,
maker: *Maker,
progress_node: std.Progress.Node,
spawn_options: process.SpawnOptions,
fuzz_context: ?FuzzContext,
) !void {
if (fuzz_context != null) {
try evalFuzzTest(run, run_index, progress_node, spawn_options, fuzz_context.?);
return;
}
const graph = maker.graph;
const gpa = maker.gpa;
const io = graph.io;
const step = maker.stepByIndex(run_index);
step.result_peak_rss = 0;
var test_results: Step.TestResults = .{
.test_count = 0,
.skip_count = 0,
.fail_count = 0,
.crash_count = 0,
.timeout_count = 0,
.leak_count = 0,
.log_err_count = 0,
};
var test_metadata: ?TestMetadata = null;
while (true) {
var child = try process.spawn(io, spawn_options);
var multi_reader_buffer: Io.File.MultiReader.Buffer(2) = undefined;
var multi_reader: Io.File.MultiReader = undefined;
multi_reader.init(gpa, io, multi_reader_buffer.toStreams(), &.{ child.stdout.?, child.stderr.? });
var child_killed = false;
defer if (!child_killed) {
child.kill(io);
multi_reader.deinit();
step.result_peak_rss = @max(
step.result_peak_rss,
child.resource_usage_statistics.getMaxRss() orelse 0,
);
};
switch (try waitZigTest(
graph.arena,
run,
run_index,
maker,
&child,
progress_node,
&multi_reader,
&test_metadata,
&test_results,
)) {
.write_failed => |err| {
// all available stderr to make our error output as useful as possible.
const stderr_fr = multi_reader.fileReader(1);
while (stderr_fr.interface.fillMore()) |_| {} else |e| switch (e) {
error.ReadFailed => return stderr_fr.err.?,
error.EndOfStream => {},
}
step.takeResultStderr(gpa, try multi_reader.toOwnedSlice(1));
child.stdin.?.close(io);
child.stdin = null;
multi_reader.deinit();
child_killed = true;
const term = try child.wait(io);
step.result_peak_rss = @max(
step.result_peak_rss,
child.resource_usage_statistics.getMaxRss() orelse 0,
);
// Fail immediately without populating `s.test_results`.
return step.fail(maker, "unable to write stdin ({t}); test process unexpectedly {f}", .{
err, fmtTerm(term),
});
},
.no_poll => |no_poll| {
// a crash of some kind. Either way, the child will terminate by itself -- wait for it.
const stderr_owned = try multi_reader.toOwnedSlice(1);
var keep_stderr_owned = false;
defer if (!keep_stderr_owned) gpa.free(stderr_owned);
child.stdin.?.close(io);
child.stdin = null;
multi_reader.deinit();
child_killed = true;
const term = try child.wait(io);
step.result_peak_rss = @max(
step.result_peak_rss,
child.resource_usage_statistics.getMaxRss() orelse 0,
);
if (no_poll.active_test_index) |test_index| {
// test, and continue to the next test.
test_metadata.?.ns_per_test[test_index] = no_poll.ns_elapsed;
test_results.crash_count += 1;
try step.addError(maker, "'{s}' {f}{s}{s}", .{
test_metadata.?.testName(test_index),
fmtTerm(term),
if (stderr_owned.len != 0) " with stderr:\n" else "",
std.mem.trim(u8, stderr_owned, "\n"),
});
continue;
}
step.takeResultStderr(gpa, stderr_owned);
keep_stderr_owned = true;
const tests_done = test_metadata != null and test_metadata.?.next_index == std.math.maxInt(u32);
if (!tests_done or !termMatches(.{ .exited = 0 }, term)) {
// Fail immediately without populating `s.test_results`.
return step.fail(maker, "test process unexpectedly {f}", .{fmtTerm(term)});
}
step.test_results = test_results;
if (test_metadata) |tm| {
run.cached_test_metadata = tm.toCachedTestMetadata();
if (maker.web_server) |ws| {
if (graph.time_report) {
ws.updateTimeReportRunTest(
run_index,
&run.cached_test_metadata.?,
tm.ns_per_test,
);
}
}
}
return;
},
.timeout => |timeout| {
const stderr_owned = try multi_reader.toOwnedSlice(1);
var keep_stderr_owned = false;
defer if (!keep_stderr_owned) gpa.free(stderr_owned);
if (timeout.active_test_index) |test_index| {
// the next test.
test_metadata.?.ns_per_test[test_index] = timeout.ns_elapsed;
test_results.timeout_count += 1;
try step.addError(maker, "'{s}' timed out after {f}{s}{s}", .{
test_metadata.?.testName(test_index),
Io.Duration{ .nanoseconds = timeout.ns_elapsed },
if (stderr_owned.len != 0) " with stderr:\n" else "",
std.mem.trim(u8, stderr_owned, "\n"),
});
continue;
}
step.takeResultStderr(gpa, stderr_owned);
keep_stderr_owned = true;
// is broken! Fail immediately without populating `s.test_results`.
return step.fail(maker, "test runner failed to respond for {f}", .{
Io.Duration{ .nanoseconds = timeout.ns_elapsed },
});
},
}
comptime unreachable;
}
}