Helpers for writing tests.

const t = zul.testing;

test "memcpy" {
	// 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);
}

zul.testing directly re-exports some functionality from std.testing.. This is to minimize the number of cases where both std.testing and zul.testing are needed. The following variables and functions are available under zul.testing:

pub const allocator = std.testing.allocator;

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;

arena: std.heap.ArenaAllocator

Complex tests often require their own allocation. This test-only data is particularly well suited for an ArenaAllocator.

Take care when using the arena. While it can streamline tests, it's easy to abuse. Code under test should use zul.testing.allocator (which is std.testing.allocator) so that leaks can be properly detected. Consider this slightly modified example taken from a real readLines test:

const t = zul.testing;
test "readLines" {
	// clears the arena
	defer t.reset();

	const aa = t.arena.allocator();
	const path = try std.fs.cwd().realpathAlloc(aa, "tests/sample");

	var out: [30]u8 = undefined;
	var it = try readLines(path, &out, .{});
	defer it.deinit();

	try t.expectEqual("Consider Phlebas", it.next().?);
	try t.expectEqual("Old Man's War", it.next().?);
	try t.expectEqual(null, it.next());
}

path is clearly data that belongs to the test and its lifecycle isn't something that our function under test, readLines, should be concerned with. If however, readLInes took some type of ownership over path, then zul.testing.allocator should have been used.

Asserts that expected is within delta of actual. Unlike the std.testing.expectApproxEq* functions, expectDelta works with both integers and floats.

Similar to std.testing.expectEqual, but will coerce expected to actual and can be used to compare strings.

Resets the zul.testing.arena. Typically called in a defer atop the test when the zul.testing.arena is used.

const t = zul.testing;
test "random example" {
	// Some random functions use the zul.testing.arena allocator
	// so we need to free that
	defer t.reset();

	// create a random integer
	const min = t.Random.intRange(u32, 0, 10);
	const max = t.Random.intRange(u32, min, min + 10);

	// create a random []u8 between min and max length (inclusive)
	// created using zul.testing.arena
	var d1 = t.Random.bytes(min, max);

	// fill buf with random bytes, returns a slice which
	// is between min and buf.len in length (inclusive)
	var buf: [10]u8 = undefined;
	var d2 = t.Random.fillAtLeast(&buf, min);

Helpers to generate random data.

Populates a []u8 with random bytes. The created []u8 will be between min and max bytes in length (inclusive). It is created using the zul.testing.arena so reset should be called.

Fill buf with random bytes. Because this only fills buf, overwriting any previous data, and doesn't allocate, in a tight loop, it can be much faster than bytes.

Fill buf with random bytes. Returns a slice that is between min and buf.len bytes in length (inclusive). Because this only fills buf, overwriting any previous data, and doesn't allocate, in a tight loop, it can be much faster than bytes.

Returns an integer of type T that is between min and max inclusive. This is a wrapper around std.RandomintRangeAtMost.

Returns a randomly seeded std.Random instance.