mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
the current implementation only works when the struct is in a register. we use some shifting magic to get the field into the LSB, and from there, given the type provenance, the generated code should never reach into the bits beyond the bit size of the type and interact with the rest of the struct.
252 lines
8.6 KiB
Zig
252 lines
8.6 KiB
Zig
//! Default test runner for unit tests.
|
|
const std = @import("std");
|
|
const io = std.io;
|
|
const builtin = @import("builtin");
|
|
|
|
pub const std_options = .{
|
|
.logFn = log,
|
|
};
|
|
|
|
var log_err_count: usize = 0;
|
|
var cmdline_buffer: [4096]u8 = undefined;
|
|
var fba = std.heap.FixedBufferAllocator.init(&cmdline_buffer);
|
|
|
|
pub fn main() void {
|
|
if (builtin.zig_backend == .stage2_aarch64 or
|
|
builtin.zig_backend == .stage2_riscv64)
|
|
{
|
|
return mainSimple() catch @panic("test failure");
|
|
}
|
|
|
|
const args = std.process.argsAlloc(fba.allocator()) catch
|
|
@panic("unable to parse command line args");
|
|
|
|
var listen = false;
|
|
|
|
for (args[1..]) |arg| {
|
|
if (std.mem.eql(u8, arg, "--listen=-")) {
|
|
listen = true;
|
|
} else {
|
|
@panic("unrecognized command line argument");
|
|
}
|
|
}
|
|
|
|
if (listen) {
|
|
return mainServer() catch @panic("internal test runner failure");
|
|
} else {
|
|
return mainTerminal();
|
|
}
|
|
}
|
|
|
|
fn mainServer() !void {
|
|
var server = try std.zig.Server.init(.{
|
|
.gpa = fba.allocator(),
|
|
.in = std.io.getStdIn(),
|
|
.out = std.io.getStdOut(),
|
|
.zig_version = builtin.zig_version_string,
|
|
});
|
|
defer server.deinit();
|
|
|
|
while (true) {
|
|
const hdr = try server.receiveMessage();
|
|
switch (hdr.tag) {
|
|
.exit => {
|
|
return std.process.exit(0);
|
|
},
|
|
.query_test_metadata => {
|
|
std.testing.allocator_instance = .{};
|
|
defer if (std.testing.allocator_instance.deinit() == .leak) {
|
|
@panic("internal test runner memory leak");
|
|
};
|
|
|
|
var string_bytes: std.ArrayListUnmanaged(u8) = .{};
|
|
defer string_bytes.deinit(std.testing.allocator);
|
|
try string_bytes.append(std.testing.allocator, 0); // Reserve 0 for null.
|
|
|
|
const test_fns = builtin.test_functions;
|
|
const names = try std.testing.allocator.alloc(u32, test_fns.len);
|
|
defer std.testing.allocator.free(names);
|
|
const expected_panic_msgs = try std.testing.allocator.alloc(u32, test_fns.len);
|
|
defer std.testing.allocator.free(expected_panic_msgs);
|
|
|
|
for (test_fns, names, expected_panic_msgs) |test_fn, *name, *expected_panic_msg| {
|
|
name.* = @as(u32, @intCast(string_bytes.items.len));
|
|
try string_bytes.ensureUnusedCapacity(std.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 => {
|
|
std.testing.allocator_instance = .{};
|
|
log_err_count = 0;
|
|
const index = try server.receiveBody_u32();
|
|
const test_fn = builtin.test_functions[index];
|
|
var fail = false;
|
|
var skip = false;
|
|
var leak = false;
|
|
test_fn.func() catch |err| switch (err) {
|
|
error.SkipZigTest => skip = true,
|
|
else => {
|
|
fail = true;
|
|
if (@errorReturnTrace()) |trace| {
|
|
std.debug.dumpStackTrace(trace.*);
|
|
}
|
|
},
|
|
};
|
|
leak = std.testing.allocator_instance.deinit() == .leak;
|
|
try server.serveTestResults(.{
|
|
.index = index,
|
|
.flags = .{
|
|
.fail = fail,
|
|
.skip = skip,
|
|
.leak = leak,
|
|
.log_err_count = std.math.lossyCast(
|
|
@TypeOf(@as(std.zig.Server.Message.TestResults.Flags, undefined).log_err_count),
|
|
log_err_count,
|
|
),
|
|
},
|
|
});
|
|
},
|
|
|
|
else => {
|
|
std.debug.print("unsupported message: {x}", .{@intFromEnum(hdr.tag)});
|
|
std.process.exit(1);
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
fn mainTerminal() void {
|
|
const test_fn_list = builtin.test_functions;
|
|
var ok_count: usize = 0;
|
|
var skip_count: usize = 0;
|
|
var fail_count: usize = 0;
|
|
var progress = std.Progress{
|
|
.dont_print_on_dumb = true,
|
|
};
|
|
const root_node = progress.start("Test", test_fn_list.len);
|
|
const have_tty = progress.terminal != null and
|
|
(progress.supports_ansi_escape_codes or progress.is_windows_terminal);
|
|
|
|
var async_frame_buffer: []align(builtin.target.stackAlignment()) u8 = undefined;
|
|
// TODO this is on the next line (using `undefined` above) because otherwise zig incorrectly
|
|
// ignores the alignment of the slice.
|
|
async_frame_buffer = &[_]u8{};
|
|
|
|
var leaks: usize = 0;
|
|
for (test_fn_list, 0..) |test_fn, i| {
|
|
std.testing.allocator_instance = .{};
|
|
defer {
|
|
if (std.testing.allocator_instance.deinit() == .leak) {
|
|
leaks += 1;
|
|
}
|
|
}
|
|
std.testing.log_level = .warn;
|
|
|
|
var test_node = root_node.start(test_fn.name, 0);
|
|
test_node.activate();
|
|
progress.refresh();
|
|
if (!have_tty) {
|
|
std.debug.print("{d}/{d} {s}... ", .{ i + 1, test_fn_list.len, test_fn.name });
|
|
}
|
|
if (test_fn.func()) |_| {
|
|
ok_count += 1;
|
|
test_node.end();
|
|
if (!have_tty) std.debug.print("OK\n", .{});
|
|
} else |err| switch (err) {
|
|
error.SkipZigTest => {
|
|
skip_count += 1;
|
|
progress.log("SKIP\n", .{});
|
|
test_node.end();
|
|
},
|
|
else => {
|
|
fail_count += 1;
|
|
progress.log("FAIL ({s})\n", .{@errorName(err)});
|
|
if (@errorReturnTrace()) |trace| {
|
|
std.debug.dumpStackTrace(trace.*);
|
|
}
|
|
test_node.end();
|
|
},
|
|
}
|
|
}
|
|
root_node.end();
|
|
if (ok_count == test_fn_list.len) {
|
|
std.debug.print("All {d} tests passed.\n", .{ok_count});
|
|
} else {
|
|
std.debug.print("{d} passed; {d} skipped; {d} failed.\n", .{ ok_count, skip_count, fail_count });
|
|
}
|
|
if (log_err_count != 0) {
|
|
std.debug.print("{d} errors were logged.\n", .{log_err_count});
|
|
}
|
|
if (leaks != 0) {
|
|
std.debug.print("{d} tests leaked memory.\n", .{leaks});
|
|
}
|
|
if (leaks != 0 or log_err_count != 0 or fail_count != 0) {
|
|
std.process.exit(1);
|
|
}
|
|
}
|
|
|
|
pub fn log(
|
|
comptime message_level: std.log.Level,
|
|
comptime scope: @Type(.EnumLiteral),
|
|
comptime format: []const u8,
|
|
args: anytype,
|
|
) void {
|
|
if (@intFromEnum(message_level) <= @intFromEnum(std.log.Level.err)) {
|
|
log_err_count +|= 1;
|
|
}
|
|
if (@intFromEnum(message_level) <= @intFromEnum(std.testing.log_level)) {
|
|
std.debug.print(
|
|
"[" ++ @tagName(scope) ++ "] (" ++ @tagName(message_level) ++ "): " ++ format ++ "\n",
|
|
args,
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Simpler main(), exercising fewer language features, so that
|
|
/// work-in-progress backends can handle it.
|
|
pub fn mainSimple() anyerror!void {
|
|
const enable_print = false;
|
|
const print_all = false;
|
|
|
|
var passed: u64 = 0;
|
|
var skipped: u64 = 0;
|
|
var failed: u64 = 0;
|
|
const stderr = if (enable_print) std.io.getStdErr() else {};
|
|
for (builtin.test_functions) |test_fn| {
|
|
if (enable_print and print_all) {
|
|
stderr.writeAll(test_fn.name) catch {};
|
|
stderr.writeAll("... ") catch {};
|
|
}
|
|
test_fn.func() catch |err| {
|
|
if (enable_print and !print_all) {
|
|
stderr.writeAll(test_fn.name) catch {};
|
|
stderr.writeAll("... ") catch {};
|
|
}
|
|
if (err != error.SkipZigTest) {
|
|
if (enable_print) stderr.writeAll("FAIL\n") catch {};
|
|
failed += 1;
|
|
if (!enable_print) return err;
|
|
continue;
|
|
}
|
|
if (enable_print) stderr.writeAll("SKIP\n") catch {};
|
|
skipped += 1;
|
|
continue;
|
|
};
|
|
if (enable_print and print_all) stderr.writeAll("PASS\n") catch {};
|
|
passed += 1;
|
|
}
|
|
if (enable_print) {
|
|
stderr.writer().print("{} passed, {} skipped, {} failed\n", .{ passed, skipped, failed }) catch {};
|
|
if (failed != 0) std.process.exit(1);
|
|
}
|
|
}
|