Skip to content

Instantly share code, notes, and snippets.

@nurpax
Last active June 18, 2026 10:21
Show Gist options
  • Select an option

  • Save nurpax/4afcb6e4ef3f03f0d282f7c462005f12 to your computer and use it in GitHub Desktop.

Select an option

Save nurpax/4afcb6e4ef3f03f0d282f7c462005f12 to your computer and use it in GitHub Desktop.
zig test runner
// Modded from from https://gist.github.com/karlseguin/c6bea5b35e4e8d26af6f81c22cb5d76b
// in your build.zig, you can specify a custom test runner:
// const tests = b.addTest(.{
// .target = target,
// .optimize = optimize,
// .test_runner = .{ .path = b.path("test_runner.zig"), .mode = .simple }, // add this line
// .root_source_file = b.path("src/main.zig"),
// });
//
// Tested on versions:
// - zig-0.16.0
//
// Note: doesn't support std.testing.fuzz()
const std = @import("std");
const builtin = @import("builtin");
const BORDER = "=" ** 80;
const Status = enum {
pass,
fail,
skip,
text,
};
pub fn main(process_init: std.process.Init) !void {
const fail_first = blk: {
if (process_init.environ_map.get("TEST_FAIL_FIRST")) |e| {
break :blk std.mem.eql(u8, e, "true");
}
break :blk false;
};
const filter = process_init.environ_map.get("TEST_FILTER");
const printer = Printer.init();
printer.fmt("\r\x1b[0K", .{}); // beginning of line and clear to end of line
var pass: usize = 0;
var fail: usize = 0;
var skip: usize = 0;
var leak: usize = 0;
for (builtin.test_functions) |t| {
std.testing.allocator_instance = .{};
var status = Status.pass;
if (filter) |f| {
if (std.mem.indexOf(u8, t.name, f) == null) {
continue;
}
}
printer.fmt("Testing {s}: ", .{t.name});
const result = t.func();
if (std.testing.allocator_instance.deinit() == .leak) {
leak += 1;
printer.status(.fail, "\n{s}\n\"{s}\" - Memory Leak\n{s}\n", .{ BORDER, t.name, BORDER });
}
if (result) |_| {
pass += 1;
} else |err| {
switch (err) {
error.SkipZigTest => {
skip += 1;
status = .skip;
},
else => {
status = .fail;
fail += 1;
printer.status(.fail, "\n{s}\n\"{s}\" - {s}\n{s}\n", .{ BORDER, t.name, @errorName(err), BORDER });
if (@errorReturnTrace()) |trace| {
std.debug.dumpErrorReturnTrace(trace);
}
if (fail_first) {
break;
}
},
}
}
printer.status(status, "[{s}]\n", .{@tagName(status)});
}
const total_tests = pass + fail;
const status: Status = if (fail == 0) .pass else .fail;
printer.status(status, "\n{d} of {d} test{s} passed\n", .{ pass, total_tests, if (total_tests != 1) "s" else "" });
if (skip > 0) {
printer.status(.skip, "{d} test{s} skipped\n", .{ skip, if (skip != 1) "s" else "" });
}
if (leak > 0) {
printer.status(.fail, "{d} test{s} leaked\n", .{ leak, if (leak != 1) "s" else "" });
}
std.process.exit(if (fail == 0) 0 else 1);
}
const Printer = struct {
fn init() Printer {
return .{};
}
fn fmt(self: Printer, comptime format: []const u8, args: anytype) void {
_ = self; // autofix
std.debug.print(format, args);
}
fn status(self: Printer, s: Status, comptime format: []const u8, args: anytype) void {
const color = switch (s) {
.pass => "\x1b[32m",
.fail => "\x1b[31m",
.skip => "\x1b[33m",
else => "",
};
std.debug.print("{s}", .{color});
std.debug.print(format, args);
self.fmt("\x1b[0m", .{});
}
};
@nurpax

nurpax commented Jun 18, 2026

Copy link
Copy Markdown
Author

Updated to zig 0.16.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment