feature. See also
. The project being documented here (as the example) is the Zig library itself.
test_runner.mainServer
fn mainServer(init: std.process.Init.Minimal) !void
File
Code
fn mainServer(init: std.process.Init.Minimal) !void {
@disableInstrumentation();
stdin_reader = .initStreaming(.stdin(), runner_threaded_io, &stdin_buffer);
stdout_writer = .initStreaming(.stdout(), runner_threaded_io, &stdout_buffer);
var server = try std.zig.Server.init(.{
.in = &stdin_reader.interface,
.out = &stdout_writer.interface,
.zig_version = builtin.zig_version_string,
});
while (true) {
const hdr = try server.receiveMessage();
switch (hdr.tag) {
.exit => {
return std.process.exit(0);
},
.query_test_metadata => {
testing.allocator_instance = .init(std.heap.page_allocator, .{});
defer if (testing.allocator_instance.deinit() != 0) {
@panic("internal test runner memory leak");
};
var string_bytes: std.ArrayList(u8) = .empty;
defer string_bytes.deinit(testing.allocator);
try string_bytes.append(testing.allocator, 0);
const test_fns = builtin.test_functions;
const names = try testing.allocator.alloc(u32, test_fns.len);
defer testing.allocator.free(names);
const expected_panic_msgs = try testing.allocator.alloc(u32, test_fns.len);
defer testing.allocator.free(expected_panic_msgs);
for (test_fns, names, expected_panic_msgs) |test_fn, *name, *expected_panic_msg| {
name.* = @intCast(string_bytes.items.len);
try string_bytes.ensureUnusedCapacity(testing.allocator, test_fn.name.len + 1);
string_bytes.appendSliceAssumeCapacity(test_fn.name);
string_bytes.appendAssumeCapacity(0);
expected_panic_msg.* = 0;
}
try server.serveTestMetadata(.{
.names = names,
.expected_panic_msgs = expected_panic_msgs,
.string_bytes = string_bytes.items,
});
},
.run_test => {
testing.environ = init.environ;
testing.allocator_instance = .init(std.heap.page_allocator, .{
.canary = 0xc3a701ba,
.check_write_after_free = true,
});
testing.io_instance = .init(testing.allocator, .{
.argv0 = .init(init.args),
.environ = init.environ,
});
log_err_count = 0;
const index = try server.receiveBody_u32();
const test_fn = builtin.test_functions[index];
is_fuzz_test = false;
try server.serveStringMessage(.test_started, &.{});
const TestResults = std.zig.Server.Message.TestResults;
const status: TestResults.Status = if (test_fn.func()) |v| s: {
v;
break :s .pass;
} else |err| switch (err) {
error.SkipZigTest => .skip,
else => s: {
if (@errorReturnTrace()) |trace| {
std.debug.dumpErrorReturnTrace(trace);
}
break :s .fail;
},
};
testing.io_instance.deinit();
const leak_count = testing.allocator_instance.deinit();
try server.serveTestResults(.{
.index = index,
.flags = .{
.status = status,
.fuzz = is_fuzz_test,
.log_err_count = std.math.lossyCast(
@FieldType(TestResults.Flags, "log_err_count"),
log_err_count,
),
.leak_count = std.math.lossyCast(
@FieldType(TestResults.Flags, "leak_count"),
leak_count,
),
},
});
},
.start_fuzzing => {
// since they are not present.
if (!builtin.fuzz) unreachable;
var gpa_instance: std.heap.SafeAllocator = .init(std.heap.page_allocator, .{});
defer if (gpa_instance.deinit() != 0) {
@panic("internal test runner memory leak");
};
const gpa = gpa_instance.allocator();
var io_instance: Io.Threaded = .init(gpa, .{
.argv0 = .init(init.args),
.environ = init.environ,
});
defer io_instance.deinit();
const io = io_instance.io();
const mode: fuzz_abi.LimitKind = @fromBackingInt(@intCast(try server.receiveBody_u8()));
const amount_or_instance = try server.receiveBody_u64();
const main_instance = mode == .iterations or amount_or_instance == 0;
if (main_instance) {
const coverage = fuzz_abi.fuzzer_coverage();
try server.serveCoverageIdMessage(
coverage.id,
coverage.runs,
coverage.unique,
coverage.seen,
);
}
const n_tests: u32 = try server.receiveBody_u32();
const test_indexes = try gpa.alloc(u32, n_tests);
defer gpa.free(test_indexes);
fuzz_runner = .{
.indexes = test_indexes,
.server = &server,
.gpa = gpa,
.io = io,
.input_poller = undefined,
};
{
var large_name_buf: std.ArrayList(u8) = .empty;
defer large_name_buf.deinit(gpa);
for (test_indexes) |*i| {
const name_len = try server.receiveBody_u32();
const name = if (name_len <= server.in.buffer.len)
try server.in.take(name_len)
else large_name: {
try large_name_buf.resize(gpa, name_len);
try server.in.readSliceAll(large_name_buf.items);
break :large_name large_name_buf.items;
};
for (0.., builtin.test_functions) |test_i, test_fn| {
if (std.mem.eql(u8, name, test_fn.name)) {
i.* = @intCast(test_i);
break;
}
} else {
panic("fuzz test {s} no longer exists", .{name});
}
if (main_instance) {
const relocated_entry_addr = @intFromPtr(builtin.test_functions[i.*].func);
const entry_addr = fuzz_abi.fuzzer_unslide_address(relocated_entry_addr);
try server.serveU64Message(.fuzz_start_addr, entry_addr);
}
}
}
fuzz_abi.fuzzer_main(n_tests, testing.random_seed, mode, amount_or_instance);
assert(mode != .forever);
std.process.exit(0);
},
else => {
std.debug.print("unsupported message: {x}\n", .{@backingInt(hdr.tag)});
std.process.exit(1);
},
}
}
}