Something went wrong. Try again.
zig utility library forked from karlseguin.tngl.sh/zul
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213const std = @import("std");const builtin = @import("builtin");
// std.testing.expectEqual won't coerce expected to actual, which is a problem// when expected is frequently a comptime.// https://github.com/ziglang/zig/issues/4437pub fn expectEqual(expected: anytype, actual: anytype) !void { const type_actual: std.lang.Type = @typeInfo(@TypeOf(actual)); switch (type_actual) { .array => |arr| if (arr.child == u8) { return std.testing.expectEqualStrings(expected, &actual); }, .pointer => |ptr| if (ptr.child == u8) { return std.testing.expectEqualStrings(expected, actual); } else if (comptime isStringArray(ptr.child)) { return std.testing.expectEqualStrings(expected, actual); } else if (ptr.child == []u8 or ptr.child == []const u8) { return expectStrings(expected, actual); }, .@"struct" => |structType| { inline for (structType.field_names) |field| { try expectEqual(@field(expected, field), @field(actual, field)); } return; }, .@"union" => |union_info| { if (union_info.tag_type == null) { @compileError("Unable to compare untagged union values"); } const Tag = std.meta.Tag(@TypeOf(expected));
const expectedTag = @as(Tag, expected); const actualTag = @as(Tag, actual); try expectEqual(expectedTag, actualTag);
inline for (union_info.field_names) |fld| { if (std.mem.eql(u8, fld.name, @tagName(actualTag))) { try expectEqual(@field(expected, fld), @field(actual, fld)); return; } } unreachable; }, else => {}, } return std.testing.expectEqual(@as(@TypeOf(actual), expected), actual);}
fn expectStrings(expected: []const []const u8, actual: anytype) !void { try t.expectEqual(expected.len, actual.len); for (expected, actual) |e, a| { try std.testing.expectEqualStrings(e, a); }}
fn isStringArray(comptime T: type) bool { if (!is(.array)(T) and !isPtrTo(.array)(T)) { return false; } return std.meta.Elem(T) == u8;}
pub const TraitFn = fn (type) bool;pub fn is(comptime id: std.builtin.TypeId) TraitFn { const Closure = struct { pub fn trait(comptime T: type) bool { return id == @typeInfo(T); } }; return Closure.trait;}
pub fn isPtrTo(comptime id: std.builtin.TypeId) TraitFn { const Closure = struct { pub fn trait(comptime T: type) bool { if (!comptime isSingleItemPtr(T)) return false; return id == @typeInfo(std.meta.Child(T)); } }; return Closure.trait;}
pub fn isSingleItemPtr(comptime T: type) bool { if (comptime is(.pointer)(T)) { return @typeInfo(T).pointer.size == .one; } return false;}
pub fn expectDelta(expected: anytype, actual: anytype, delta: anytype) !void { var diff = expected - actual; if (diff < 0) { diff = -diff; } if (diff <= delta) { return; }
print("Expected {} to be within {} of {}. Actual diff: {}", .{ expected, delta, actual, diff }); return error.NotWithinDelta;}
pub fn print(comptime fmt: []const u8, args: anytype) void { if (@inComptime()) { @compileError(std.fmt.comptimePrint(fmt, args)); } else { std.debug.print(fmt, args); }}
// Re-expose these as-is so that more cases can rely on zul.testing exclusively.// Else, it's a pain to have both std.testing and zul.testing in a test.pub const expect = std.testing.expect;pub const expectFmt = std.testing.expectFmt;pub const expectError = std.testing.expectError;pub const expectEqualSlices = std.testing.expectEqualSlices;pub const expectEqualStrings = std.testing.expectEqualStrings;pub const expectEqualSentinel = std.testing.expectEqualSentinel;pub const expectApproxEqAbs = std.testing.expectApproxEqAbs;pub const expectApproxEqRel = std.testing.expectApproxEqRel;
pub const io = std.testing.io;
pub const allocator = std.testing.allocator;pub var arena = std.heap.ArenaAllocator.init(allocator);
pub fn reset() void { _ = arena.reset(.free_all);}
pub const Random = struct { var instance: ?std.Random.DefaultPrng = null;
pub fn bytes(min: usize, max: usize) []u8 { var r = random(); const l = r.intRangeAtMost(usize, min, max); const buf = arena.allocator().alloc(u8, l) catch unreachable; r.bytes(buf); return buf; }
pub fn fill(buf: []u8) void { var r = random(); r.bytes(buf); }
pub fn fillAtLeast(buf: []u8, min: usize) []u8 { var r = random(); const l = r.intRangeAtMost(usize, min, buf.len); r.bytes(buf[0..l]); return buf; }
pub fn intRange(comptime T: type, min: T, max: T) T { var r = random(); return r.intRangeAtMost(T, min, max); }
pub fn random() std.Random { if (instance == null) { var seed: u64 = undefined; io.random(std.mem.asBytes(&seed)); instance = std.Random.DefaultPrng.init(seed); } return instance.?.random(); }};
const t = @This();test "testing.rand: bytes" { defer t.reset(); for (0..10) |_| { const bytes = Random.bytes(4, 8); try t.expectEqual(true, bytes.len >= 4 and bytes.len <= 8); }}
test "testing.rand: fillAtLeast" { var buf: [10]u8 = undefined;
for (0..10) |_| { const bytes = Random.fillAtLeast(&buf, 7); try t.expectEqual(true, bytes.len >= 7 and bytes.len <= 10); }}
test "testing.rand: intRange" { for (0..10) |_| { const value = Random.intRange(u16, 3, 6); try t.expectEqual(true, value >= 3 and value <= 6); }}
test "testing: doc example" { // clear's the arena allocator defer t.reset();
// In addition to exposing std.testing.allocator as zul.testing.allocator // zul.testing.arena is an ArenaAllocator. An ArenaAllocator can // make managing test-specific allocations a lot simpler. // Just stick a `defer zul.testing.reset()` atop your test. var buf = try t.arena.allocator().alloc(u8, 5);
// unlike std.testing.expectEqual, zul's expectEqual // will coerce expected to actual's type, so this is valid: try t.expectEqual(5, buf.len);
@memcpy(buf[0..5], "hello");
// zul's expectEqual also works with strings. try t.expectEqual("hello", buf);}