authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2026-08-25 02:46:14+02:00
committergravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2026-08-25 02:46:14+02:00
log40ad0920b37564a4c119fa6fa385b76a9c2b0c21
treeda95a55454700a826cab0a1a5af6bca10c4ea392
parent857d8ac650b3154bfd320911f8d8fd0bd7ed9ea7
parent960469dc8a3c132c95e51bad304a23811675472f

Merge pull request 'std.testing: various improvements' (#36138) from K4/zig:pub into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/36138 Reviewed-by: Ryan Liptak <squeek502@noreply.codeberg.org>

1 files changed, 135 insertions(+), 109 deletions(-)

lib/std/testing.zig+135-109
...@@ -18,10 +18,7 @@ var failing_allocator_instance = FailingAllocator.init(base_allocator_instance.a...@@ -18,10 +18,7 @@ var failing_allocator_instance = FailingAllocator.init(base_allocator_instance.a
18var base_allocator_instance = std.heap.FixedBufferAllocator.init("");18var base_allocator_instance = std.heap.FixedBufferAllocator.init("");
1919
20pub var allocator_instance: std.heap.SafeAllocator = undefined;20pub var allocator_instance: std.heap.SafeAllocator = undefined;
21pub const allocator = if (builtin.is_test)21pub const allocator = if (builtin.is_test) allocator_instance.allocator() else @compileError("not testing");
22 allocator_instance.allocator()
23else
24 @compileError("not testing");
2522
26pub var io_instance: Io.Threaded = undefined;23pub var io_instance: Io.Threaded = undefined;
27pub const io = if (builtin.is_test) io_instance.io() else @compileError("not testing");24pub const io = if (builtin.is_test) io_instance.io() else @compileError("not testing");
...@@ -42,7 +39,8 @@ pub const backend_can_print = switch (builtin.zig_backend) {...@@ -42,7 +39,8 @@ pub const backend_can_print = switch (builtin.zig_backend) {
42 else => true,39 else => true,
43};40};
4441
45fn print(comptime fmt: []const u8, args: anytype) void {42/// Helper function for printing test failure information.
43pub fn failPrint(comptime fmt: []const u8, args: anytype) void {
46 if (@inComptime()) {44 if (@inComptime()) {
47 @compileError(std.fmt.comptimePrint(fmt, args));45 @compileError(std.fmt.comptimePrint(fmt, args));
48 } else if (backend_can_print) {46 } else if (backend_can_print) {
...@@ -50,23 +48,6 @@ fn print(comptime fmt: []const u8, args: anytype) void {...@@ -50,23 +48,6 @@ fn print(comptime fmt: []const u8, args: anytype) void {
50 }48 }
51}49}
5250
53/// This function is intended to be used only in tests. It prints diagnostics to stderr
54/// and then returns a test failure error when actual_error_union is not expected_error.
55pub fn expectError(expected_error: anyerror, actual_error_union: anytype) !void {
56 if (actual_error_union) |actual_payload| {
57 print("expected error.{s}, found {any}\n", .{ @errorName(expected_error), actual_payload });
58 return error.TestExpectedError;
59 } else |actual_error| {
60 if (expected_error != actual_error) {
61 print("expected error.{s}, found error.{s}\n", .{
62 @errorName(expected_error),
63 @errorName(actual_error),
64 });
65 return error.TestUnexpectedError;
66 }
67 }
68}
69
70/// This function is intended to be used only in tests. When the two values are not51/// This function is intended to be used only in tests. When the two values are not
71/// equal, prints diagnostics to stderr to show exactly how they are not equal,52/// equal, prints diagnostics to stderr to show exactly how they are not equal,
72/// then returns a test failure error.53/// then returns a test failure error.
...@@ -92,7 +73,7 @@ fn expectEqualInner(comptime T: type, expected: T, actual: T) !void {...@@ -92,7 +73,7 @@ fn expectEqualInner(comptime T: type, expected: T, actual: T) !void {
9273
93 .type => {74 .type => {
94 if (actual != expected) {75 if (actual != expected) {
95 print("expected type {s}, found type {s}\n", .{ @typeName(expected), @typeName(actual) });76 failPrint("expected type {s}, found type {s}\n", .{ @typeName(expected), @typeName(actual) });
96 return error.TestExpectedEqual;77 return error.TestExpectedEqual;
97 }78 }
98 },79 },
...@@ -108,7 +89,7 @@ fn expectEqualInner(comptime T: type, expected: T, actual: T) !void {...@@ -108,7 +89,7 @@ fn expectEqualInner(comptime T: type, expected: T, actual: T) !void {
108 .error_set,89 .error_set,
109 => {90 => {
110 if (actual != expected) {91 if (actual != expected) {
111 print("expected {any}, found {any}\n", .{ expected, actual });92 failPrint("expected {any}, found {any}\n", .{ expected, actual });
112 return error.TestExpectedEqual;93 return error.TestExpectedEqual;
113 }94 }
114 },95 },
...@@ -117,17 +98,17 @@ fn expectEqualInner(comptime T: type, expected: T, actual: T) !void {...@@ -117,17 +98,17 @@ fn expectEqualInner(comptime T: type, expected: T, actual: T) !void {
117 switch (pointer.size) {98 switch (pointer.size) {
118 .one, .many, .c => {99 .one, .many, .c => {
119 if (actual != expected) {100 if (actual != expected) {
120 print("expected {*}, found {*}\n", .{ expected, actual });101 failPrint("expected {*}, found {*}\n", .{ expected, actual });
121 return error.TestExpectedEqual;102 return error.TestExpectedEqual;
122 }103 }
123 },104 },
124 .slice => {105 .slice => {
125 if (actual.ptr != expected.ptr) {106 if (actual.ptr != expected.ptr) {
126 print("expected slice ptr {*}, found {*}\n", .{ expected.ptr, actual.ptr });107 failPrint("expected slice ptr {*}, found {*}\n", .{ expected.ptr, actual.ptr });
127 return error.TestExpectedEqual;108 return error.TestExpectedEqual;
128 }109 }
129 if (actual.len != expected.len) {110 if (actual.len != expected.len) {
130 print("expected slice len {}, found {}\n", .{ expected.len, actual.len });111 failPrint("expected slice len {}, found {}\n", .{ expected.len, actual.len });
131 return error.TestExpectedEqual;112 return error.TestExpectedEqual;
132 }113 }
133 },114 },
...@@ -188,12 +169,12 @@ fn expectEqualInner(comptime T: type, expected: T, actual: T) !void {...@@ -188,12 +169,12 @@ fn expectEqualInner(comptime T: type, expected: T, actual: T) !void {
188 if (actual) |actual_payload| {169 if (actual) |actual_payload| {
189 try expectEqual(expected_payload, actual_payload);170 try expectEqual(expected_payload, actual_payload);
190 } else {171 } else {
191 print("expected {any}, found null\n", .{expected_payload});172 failPrint("expected {any}, found null\n", .{expected_payload});
192 return error.TestExpectedEqual;173 return error.TestExpectedEqual;
193 }174 }
194 } else {175 } else {
195 if (actual) |actual_payload| {176 if (actual) |actual_payload| {
196 print("expected null, found {any}\n", .{actual_payload});177 failPrint("expected null, found {any}\n", .{actual_payload});
197 return error.TestExpectedEqual;178 return error.TestExpectedEqual;
198 }179 }
199 }180 }
...@@ -204,12 +185,12 @@ fn expectEqualInner(comptime T: type, expected: T, actual: T) !void {...@@ -204,12 +185,12 @@ fn expectEqualInner(comptime T: type, expected: T, actual: T) !void {
204 if (actual) |actual_payload| {185 if (actual) |actual_payload| {
205 try expectEqual(expected_payload, actual_payload);186 try expectEqual(expected_payload, actual_payload);
206 } else |actual_err| {187 } else |actual_err| {
207 print("expected {any}, found {}\n", .{ expected_payload, actual_err });188 failPrint("expected {any}, found {}\n", .{ expected_payload, actual_err });
208 return error.TestExpectedEqual;189 return error.TestExpectedEqual;
209 }190 }
210 } else |expected_err| {191 } else |expected_err| {
211 if (actual) |actual_payload| {192 if (actual) |actual_payload| {
212 print("expected {}, found {any}\n", .{ expected_err, actual_payload });193 failPrint("expected {}, found {any}\n", .{ expected_err, actual_payload });
213 return error.TestExpectedEqual;194 return error.TestExpectedEqual;
214 } else |actual_err| {195 } else |actual_err| {
215 try expectEqual(expected_err, actual_err);196 try expectEqual(expected_err, actual_err);
...@@ -219,7 +200,7 @@ fn expectEqualInner(comptime T: type, expected: T, actual: T) !void {...@@ -219,7 +200,7 @@ fn expectEqualInner(comptime T: type, expected: T, actual: T) !void {
219 }200 }
220}201}
221202
222test "expectEqual.union(enum)" {203test "expectEqual union(enum)" {
223 const T = union(enum) {204 const T = union(enum) {
224 a: i32,205 a: i32,
225 b: f32,206 b: f32,
...@@ -268,20 +249,6 @@ test "expectEqual null" {...@@ -268,20 +249,6 @@ test "expectEqual null" {
268 try expectEqual(a, b);249 try expectEqual(a, b);
269}250}
270251
271/// This function is intended to be used only in tests. When the formatted result of the template
272/// and its arguments does not equal the expected text, it prints diagnostics to stderr to show how
273/// they are not equal, then returns an error. It depends on `expectEqualStrings` for printing
274/// diagnostics.
275pub fn expectFmt(expected: []const u8, comptime template: []const u8, args: anytype) !void {
276 if (@inComptime()) {
277 var buffer: [std.fmt.count(template, args)]u8 = undefined;
278 return expectEqualStrings(expected, try std.fmt.bufPrint(&buffer, template, args));
279 }
280 const actual = try std.fmt.allocPrint(allocator, template, args);
281 defer allocator.free(actual);
282 return expectEqualStrings(expected, actual);
283}
284
285/// This function is intended to be used only in tests. When the actual value is252/// This function is intended to be used only in tests. When the actual value is
286/// not approximately equal to the expected value, prints diagnostics to stderr253/// not approximately equal to the expected value, prints diagnostics to stderr
287/// to show exactly how they are not equal, then returns a test failure error.254/// to show exactly how they are not equal, then returns a test failure error.
...@@ -296,7 +263,7 @@ pub inline fn expectApproxEqAbs(expected: anytype, actual: anytype, tolerance: a...@@ -296,7 +263,7 @@ pub inline fn expectApproxEqAbs(expected: anytype, actual: anytype, tolerance: a
296fn expectApproxEqAbsInner(comptime T: type, expected: T, actual: T, tolerance: T) !void {263fn expectApproxEqAbsInner(comptime T: type, expected: T, actual: T, tolerance: T) !void {
297 switch (@typeInfo(T)) {264 switch (@typeInfo(T)) {
298 .float => if (!math.approxEqAbs(T, expected, actual, tolerance)) {265 .float => if (!math.approxEqAbs(T, expected, actual, tolerance)) {
299 print("actual {}, not within absolute tolerance {} of expected {}\n", .{ actual, tolerance, expected });266 failPrint("actual {}, not within absolute tolerance {} of expected {}\n", .{ actual, tolerance, expected });
300 return error.TestExpectedApproxEqAbs;267 return error.TestExpectedApproxEqAbs;
301 },268 },
302269
...@@ -332,7 +299,7 @@ pub inline fn expectApproxEqRel(expected: anytype, actual: anytype, tolerance: a...@@ -332,7 +299,7 @@ pub inline fn expectApproxEqRel(expected: anytype, actual: anytype, tolerance: a
332fn expectApproxEqRelInner(comptime T: type, expected: T, actual: T, tolerance: T) !void {299fn expectApproxEqRelInner(comptime T: type, expected: T, actual: T, tolerance: T) !void {
333 switch (@typeInfo(T)) {300 switch (@typeInfo(T)) {
334 .float => if (!math.approxEqRel(T, expected, actual, tolerance)) {301 .float => if (!math.approxEqRel(T, expected, actual, tolerance)) {
335 print("actual {}, not within relative tolerance {} of expected {}\n", .{ actual, tolerance, expected });302 failPrint("actual {}, not within relative tolerance {} of expected {}\n", .{ actual, tolerance, expected });
336 return error.TestExpectedApproxEqRel;303 return error.TestExpectedApproxEqRel;
337 },304 },
338305
...@@ -358,7 +325,7 @@ test expectApproxEqRel {...@@ -358,7 +325,7 @@ test expectApproxEqRel {
358}325}
359326
360/// This function is intended to be used only in tests. When the two slices are327/// This function is intended to be used only in tests. When the two slices are
361/// not equal, prints diagnostics to stderr to show exactly how they are not328/// not equal, it prints diagnostics to stderr to show exactly how they are not
362/// equal (with the differences highlighted in red), then returns a test329/// equal (with the differences highlighted in red), then returns a test
363/// failure error.330/// failure error.
364pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const T) !void {331pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const T) !void {
...@@ -553,7 +520,7 @@ const BytesDiffer = struct {...@@ -553,7 +520,7 @@ const BytesDiffer = struct {
553 }520 }
554};521};
555522
556test {523test expectEqualSlices {
557 try expectEqualSlices(u8, "foo\x00", "foo\x00");524 try expectEqualSlices(u8, "foo\x00", "foo\x00");
558 try expectEqualSlices(u16, &[_]u16{ 100, 200, 300, 400 }, &[_]u16{ 100, 200, 300, 400 });525 try expectEqualSlices(u16, &[_]u16{ 100, 200, 300, 400 }, &[_]u16{ 100, 200, 300, 400 });
559 const E = enum { foo, bar };526 const E = enum { foo, bar };
...@@ -567,8 +534,10 @@ test {...@@ -567,8 +534,10 @@ test {
567 );534 );
568}535}
569536
570/// This function is intended to be used only in tests. Checks that two slices or two arrays are equal,537/// This function is intended to be used only in tests. When the two slices or two arrays are not equal,
571/// including that their sentinel (if any) are the same. Will error if given another type.538/// or their sentinel (if any) are not the same, it prints diagnostics to stderr to show exactly how
539/// they are not equal (with the differences highlighted in red), then returns a test failure error.
540/// It partially depends on `expectEquaSlices` for printing diagnostics.
572pub fn expectEqualSentinel(comptime T: type, comptime sentinel: T, expected: [:sentinel]const T, actual: [:sentinel]const T) !void {541pub fn expectEqualSentinel(comptime T: type, comptime sentinel: T, expected: [:sentinel]const T, actual: [:sentinel]const T) !void {
573 try expectEqualSlices(T, expected, actual);542 try expectEqualSlices(T, expected, actual);
574543
...@@ -599,12 +568,12 @@ pub fn expectEqualSentinel(comptime T: type, comptime sentinel: T, expected: [:s...@@ -599,12 +568,12 @@ pub fn expectEqualSentinel(comptime T: type, comptime sentinel: T, expected: [:s
599 };568 };
600569
601 if (!std.meta.eql(sentinel, expected_value_sentinel)) {570 if (!std.meta.eql(sentinel, expected_value_sentinel)) {
602 print("expectEqualSentinel: 'expected' sentinel in memory is different from its type sentinel. type sentinel {}, in memory sentinel {}\n", .{ sentinel, expected_value_sentinel });571 failPrint("expectEqualSentinel: 'expected' sentinel in memory is different from its type sentinel. type sentinel {}, in memory sentinel {}\n", .{ sentinel, expected_value_sentinel });
603 return error.TestExpectedEqual;572 return error.TestExpectedEqual;
604 }573 }
605574
606 if (!std.meta.eql(sentinel, actual_value_sentinel)) {575 if (!std.meta.eql(sentinel, actual_value_sentinel)) {
607 print("expectEqualSentinel: 'actual' sentinel in memory is different from its type sentinel. type sentinel {}, in memory sentinel {}\n", .{ sentinel, actual_value_sentinel });576 failPrint("expectEqualSentinel: 'actual' sentinel in memory is different from its type sentinel. type sentinel {}, in memory sentinel {}\n", .{ sentinel, actual_value_sentinel });
608 return error.TestExpectedEqual;577 return error.TestExpectedEqual;
609 }578 }
610}579}
...@@ -654,6 +623,48 @@ pub fn tmpDir(opts: Io.Dir.OpenOptions) TmpDir {...@@ -654,6 +623,48 @@ pub fn tmpDir(opts: Io.Dir.OpenOptions) TmpDir {
654 };623 };
655}624}
656625
626/// This function is intended to be used only in tests. When `actual_error_union` is not
627/// `expected_error`, it prints diagnostics to stderr, then returns a test failure error.
628pub fn expectError(expected_error: anyerror, actual_error_union: anytype) !void {
629 if (actual_error_union) |actual_payload| {
630 failPrint("expected error.{s}, found {any}\n", .{ @errorName(expected_error), actual_payload });
631 return error.TestExpectedError;
632 } else |actual_error| {
633 if (expected_error != actual_error) {
634 failPrint("expected error.{s}, found error.{s}\n", .{
635 @errorName(expected_error),
636 @errorName(actual_error),
637 });
638 return error.TestUnexpectedError;
639 }
640 }
641}
642
643fn returnErrorUnion() !u8 {
644 return error.Expected;
645}
646
647test expectError {
648 const actualErrorUnion = returnErrorUnion();
649 try expectError(error.Expected, actualErrorUnion);
650}
651
652/// This function is intended to be used only in tests. When the formatted result of the template
653/// and its arguments does not equal the expected text, it prints diagnostics to stderr to show how
654/// they are not equal, then returns an error. It depends on `expectEqualStrings` for printing
655/// diagnostics.
656pub fn expectFmt(expected: []const u8, comptime template: []const u8, args: anytype) !void {
657 if (@inComptime()) {
658 var buffer: [std.fmt.count(template, args)]u8 = undefined;
659 return expectEqualStrings(expected, try std.fmt.bufPrint(&buffer, template, args));
660 }
661 const actual = try std.fmt.allocPrint(allocator, template, args);
662 defer allocator.free(actual);
663 return expectEqualStrings(expected, actual);
664}
665
666// This function is intended to be used only in test. When the two strings are not equal,
667/// it prints diagnostics to stderr to show how they are not equal, then returns an error.
657pub fn expectEqualStrings(expected: []const u8, actual: []const u8) !void {668pub fn expectEqualStrings(expected: []const u8, actual: []const u8) !void {
658 if (std.mem.findDiff(u8, actual, expected)) |diff_index| {669 if (std.mem.findDiff(u8, actual, expected)) |diff_index| {
659 if (@inComptime()) {670 if (@inComptime()) {
...@@ -661,28 +672,34 @@ pub fn expectEqualStrings(expected: []const u8, actual: []const u8) !void {...@@ -661,28 +672,34 @@ pub fn expectEqualStrings(expected: []const u8, actual: []const u8) !void {
661 expected, actual, diff_index,672 expected, actual, diff_index,
662 }));673 }));
663 }674 }
664 print("\n====== expected this output: =========\n", .{});675 failPrint("\n====== expected this output: =========\n", .{});
665 printWithVisibleNewlines(expected);676 failPrintWithVisibleNewlines(expected);
666 print("\n======== instead found this: =========\n", .{});677 failPrint("\n======== instead found this: =========\n", .{});
667 printWithVisibleNewlines(actual);678 failPrintWithVisibleNewlines(actual);
668 print("\n======================================\n", .{});679 failPrint("\n======================================\n", .{});
669680
670 var diff_line_number: usize = 1;681 var diff_line_number: usize = 1;
671 for (expected[0..diff_index]) |value| {682 for (expected[0..diff_index]) |value| {
672 if (value == '\n') diff_line_number += 1;683 if (value == '\n') diff_line_number += 1;
673 }684 }
674 print("First difference occurs on line {d}:\n", .{diff_line_number});685 failPrint("First difference occurs on line {d}:\n", .{diff_line_number});
675686
676 print("expected:\n", .{});687 failPrint("expected:\n", .{});
677 printIndicatorLine(expected, diff_index);688 failPrintIndicatorLine(expected, diff_index);
678689
679 print("found:\n", .{});690 failPrint("found:\n", .{});
680 printIndicatorLine(actual, diff_index);691 failPrintIndicatorLine(actual, diff_index);
681692
682 return error.TestExpectedEqual;693 return error.TestExpectedEqual;
683 }694 }
684}695}
685696
697test expectEqualStrings {
698 try expectEqualStrings("foo", "foo");
699}
700
701/// This function is intended to be used only in test. When the start of `actual` and `expected_starts_with`
702/// are not equal, it prints diagnostics to stderr to show how they are not equal, then returns an error.
686pub fn expectStringStartsWith(actual: []const u8, expected_starts_with: []const u8) !void {703pub fn expectStringStartsWith(actual: []const u8, expected_starts_with: []const u8) !void {
687 if (std.mem.startsWith(u8, actual, expected_starts_with))704 if (std.mem.startsWith(u8, actual, expected_starts_with))
688 return;705 return;
...@@ -692,17 +709,23 @@ pub fn expectStringStartsWith(actual: []const u8, expected_starts_with: []const...@@ -692,17 +709,23 @@ pub fn expectStringStartsWith(actual: []const u8, expected_starts_with: []const
692 else709 else
693 actual;710 actual;
694711
695 print("\n====== expected to start with: =========\n", .{});712 failPrint("\n====== expected to start with: =========\n", .{});
696 printWithVisibleNewlines(expected_starts_with);713 failPrintWithVisibleNewlines(expected_starts_with);
697 print("\n====== instead started with: ===========\n", .{});714 failPrint("\n====== instead started with: ===========\n", .{});
698 printWithVisibleNewlines(shortened_actual);715 failPrintWithVisibleNewlines(shortened_actual);
699 print("\n========= full output: ==============\n", .{});716 failPrint("\n========= full output: ==============\n", .{});
700 printWithVisibleNewlines(actual);717 failPrintWithVisibleNewlines(actual);
701 print("\n======================================\n", .{});718 failPrint("\n======================================\n", .{});
702719
703 return error.TestExpectedStartsWith;720 return error.TestExpectedStartsWith;
704}721}
705722
723test expectStringStartsWith {
724 try expectStringStartsWith("foobar", "foo");
725}
726
727/// This function is intended to be used only in test. When the end of `actual` and `expected_ends_with`
728/// are not equal, it prints diagnostics to stderr to show how they are not equal, then returns an error.
706pub fn expectStringEndsWith(actual: []const u8, expected_ends_with: []const u8) !void {729pub fn expectStringEndsWith(actual: []const u8, expected_ends_with: []const u8) !void {
707 if (std.mem.endsWith(u8, actual, expected_ends_with))730 if (std.mem.endsWith(u8, actual, expected_ends_with))
708 return;731 return;
...@@ -712,17 +735,21 @@ pub fn expectStringEndsWith(actual: []const u8, expected_ends_with: []const u8)...@@ -712,17 +735,21 @@ pub fn expectStringEndsWith(actual: []const u8, expected_ends_with: []const u8)
712 else735 else
713 actual;736 actual;
714737
715 print("\n====== expected to end with: =========\n", .{});738 failPrint("\n====== expected to end with: =========\n", .{});
716 printWithVisibleNewlines(expected_ends_with);739 failPrintWithVisibleNewlines(expected_ends_with);
717 print("\n====== instead ended with: ===========\n", .{});740 failPrint("\n====== instead ended with: ===========\n", .{});
718 printWithVisibleNewlines(shortened_actual);741 failPrintWithVisibleNewlines(shortened_actual);
719 print("\n========= full output: ==============\n", .{});742 failPrint("\n========= full output: ==============\n", .{});
720 printWithVisibleNewlines(actual);743 failPrintWithVisibleNewlines(actual);
721 print("\n======================================\n", .{});744 failPrint("\n======================================\n", .{});
722745
723 return error.TestExpectedEndsWith;746 return error.TestExpectedEndsWith;
724}747}
725748
749test expectStringEndsWith {
750 try expectStringEndsWith("foobar", "bar");
751}
752
726/// This function is intended to be used only in tests. When the two values are not753/// This function is intended to be used only in tests. When the two values are not
727/// deeply equal, prints diagnostics to stderr to show exactly how they are not equal,754/// deeply equal, prints diagnostics to stderr to show exactly how they are not equal,
728/// then returns a test failure error.755/// then returns a test failure error.
...@@ -757,7 +784,7 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe...@@ -757,7 +784,7 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe
757784
758 .type => {785 .type => {
759 if (actual != expected) {786 if (actual != expected) {
760 print("expected type {s}, found type {s}\n", .{ @typeName(expected), @typeName(actual) });787 failPrint("expected type {s}, found type {s}\n", .{ @typeName(expected), @typeName(actual) });
761 return error.TestExpectedEqual;788 return error.TestExpectedEqual;
762 }789 }
763 },790 },
...@@ -773,7 +800,7 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe...@@ -773,7 +800,7 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe
773 .error_set,800 .error_set,
774 => {801 => {
775 if (actual != expected) {802 if (actual != expected) {
776 print("expected {any}, found {any}\n", .{ expected, actual });803 failPrint("expected {any}, found {any}\n", .{ expected, actual });
777 return error.TestExpectedEqual;804 return error.TestExpectedEqual;
778 }805 }
779 },806 },
...@@ -783,7 +810,7 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe...@@ -783,7 +810,7 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe
783 // We have no idea what is behind those pointers, so the best we can do is `==` check.810 // We have no idea what is behind those pointers, so the best we can do is `==` check.
784 .c, .many => {811 .c, .many => {
785 if (actual != expected) {812 if (actual != expected) {
786 print("expected {*}, found {*}\n", .{ expected, actual });813 failPrint("expected {*}, found {*}\n", .{ expected, actual });
787 return error.TestExpectedEqual;814 return error.TestExpectedEqual;
788 }815 }
789 },816 },
...@@ -792,7 +819,7 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe...@@ -792,7 +819,7 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe
792 switch (@typeInfo(pointer.child)) {819 switch (@typeInfo(pointer.child)) {
793 .@"fn", .@"opaque" => {820 .@"fn", .@"opaque" => {
794 if (actual != expected) {821 if (actual != expected) {
795 print("expected {*}, found {*}\n", .{ expected, actual });822 failPrint("expected {*}, found {*}\n", .{ expected, actual });
796 return error.TestExpectedEqual;823 return error.TestExpectedEqual;
797 }824 }
798 },825 },
...@@ -801,13 +828,13 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe...@@ -801,13 +828,13 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe
801 },828 },
802 .slice => {829 .slice => {
803 if (expected.len != actual.len) {830 if (expected.len != actual.len) {
804 print("Slice len not the same, expected {d}, found {d}\n", .{ expected.len, actual.len });831 failPrint("Slice len not the same, expected {d}, found {d}\n", .{ expected.len, actual.len });
805 return error.TestExpectedEqual;832 return error.TestExpectedEqual;
806 }833 }
807 var i: usize = 0;834 var i: usize = 0;
808 while (i < expected.len) : (i += 1) {835 while (i < expected.len) : (i += 1) {
809 expectEqualDeep(expected[i], actual[i]) catch |e| {836 expectEqualDeep(expected[i], actual[i]) catch |e| {
810 print("index {d} incorrect. expected {any}, found {any}\n", .{837 failPrint("index {d} incorrect. expected {any}, found {any}\n", .{
811 i, expected[i], actual[i],838 i, expected[i], actual[i],
812 });839 });
813 return e;840 return e;
...@@ -819,13 +846,13 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe...@@ -819,13 +846,13 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe
819846
820 .array => {847 .array => {
821 if (expected.len != actual.len) {848 if (expected.len != actual.len) {
822 print("Array len not the same, expected {d}, found {d}\n", .{ expected.len, actual.len });849 failPrint("Array len not the same, expected {d}, found {d}\n", .{ expected.len, actual.len });
823 return error.TestExpectedEqual;850 return error.TestExpectedEqual;
824 }851 }
825 var i: usize = 0;852 var i: usize = 0;
826 while (i < expected.len) : (i += 1) {853 while (i < expected.len) : (i += 1) {
827 expectEqualDeep(expected[i], actual[i]) catch |e| {854 expectEqualDeep(expected[i], actual[i]) catch |e| {
828 print("index {d} incorrect. expected {any}, found {any}\n", .{855 failPrint("index {d} incorrect. expected {any}, found {any}\n", .{
829 i, expected[i], actual[i],856 i, expected[i], actual[i],
830 });857 });
831 return e;858 return e;
...@@ -835,12 +862,12 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe...@@ -835,12 +862,12 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe
835862
836 .vector => |info| {863 .vector => |info| {
837 if (info.len != @typeInfo(@TypeOf(actual)).vector.len) {864 if (info.len != @typeInfo(@TypeOf(actual)).vector.len) {
838 print("Vector len not the same, expected {d}, found {d}\n", .{ info.len, @typeInfo(@TypeOf(actual)).vector.len });865 failPrint("Vector len not the same, expected {d}, found {d}\n", .{ info.len, @typeInfo(@TypeOf(actual)).vector.len });
839 return error.TestExpectedEqual;866 return error.TestExpectedEqual;
840 }867 }
841 inline for (0..info.len) |i| {868 inline for (0..info.len) |i| {
842 expectEqualDeep(expected[i], actual[i]) catch |e| {869 expectEqualDeep(expected[i], actual[i]) catch |e| {
843 print("index {d} incorrect. expected {any}, found {any}\n", .{870 failPrint("index {d} incorrect. expected {any}, found {any}\n", .{
844 i, expected[i], actual[i],871 i, expected[i], actual[i],
845 });872 });
846 return e;873 return e;
...@@ -851,7 +878,7 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe...@@ -851,7 +878,7 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe
851 .@"struct" => |structType| {878 .@"struct" => |structType| {
852 inline for (structType.field_names) |field_name| {879 inline for (structType.field_names) |field_name| {
853 expectEqualDeep(@field(expected, field_name), @field(actual, field_name)) catch |e| {880 expectEqualDeep(@field(expected, field_name), @field(actual, field_name)) catch |e| {
854 print("Field {s} incorrect. expected {any}, found {any}\n", .{ field_name, @field(expected, field_name), @field(actual, field_name) });881 failPrint("Field {s} incorrect. expected {any}, found {any}\n", .{ field_name, @field(expected, field_name), @field(actual, field_name) });
855 return e;882 return e;
856 };883 };
857 }884 }
...@@ -882,12 +909,12 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe...@@ -882,12 +909,12 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe
882 if (actual) |actual_payload| {909 if (actual) |actual_payload| {
883 try expectEqualDeep(expected_payload, actual_payload);910 try expectEqualDeep(expected_payload, actual_payload);
884 } else {911 } else {
885 print("expected {any}, found null\n", .{expected_payload});912 failPrint("expected {any}, found null\n", .{expected_payload});
886 return error.TestExpectedEqual;913 return error.TestExpectedEqual;
887 }914 }
888 } else {915 } else {
889 if (actual) |actual_payload| {916 if (actual) |actual_payload| {
890 print("expected null, found {any}\n", .{actual_payload});917 failPrint("expected null, found {any}\n", .{actual_payload});
891 return error.TestExpectedEqual;918 return error.TestExpectedEqual;
892 }919 }
893 }920 }
...@@ -898,12 +925,12 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe...@@ -898,12 +925,12 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe
898 if (actual) |actual_payload| {925 if (actual) |actual_payload| {
899 try expectEqualDeep(expected_payload, actual_payload);926 try expectEqualDeep(expected_payload, actual_payload);
900 } else |actual_err| {927 } else |actual_err| {
901 print("expected {any}, found {any}\n", .{ expected_payload, actual_err });928 failPrint("expected {any}, found {any}\n", .{ expected_payload, actual_err });
902 return error.TestExpectedEqual;929 return error.TestExpectedEqual;
903 }930 }
904 } else |expected_err| {931 } else |expected_err| {
905 if (actual) |actual_payload| {932 if (actual) |actual_payload| {
906 print("expected {any}, found {any}\n", .{ expected_err, actual_payload });933 failPrint("expected {any}, found {any}\n", .{ expected_err, actual_payload });
907 return error.TestExpectedEqual;934 return error.TestExpectedEqual;
908 } else |actual_err| {935 } else |actual_err| {
909 try expectEqualDeep(expected_err, actual_err);936 try expectEqualDeep(expected_err, actual_err);
...@@ -999,7 +1026,8 @@ test "expectEqualDeep composite type" {...@@ -999,7 +1026,8 @@ test "expectEqualDeep composite type" {
999 );1026 );
1000}1027}
10011028
1002fn printIndicatorLine(source: []const u8, indicator_index: usize) void {1029/// Helper function for printing test failure information.
1030pub fn failPrintIndicatorLine(source: []const u8, indicator_index: usize) void {
1003 const line_begin_index = if (std.mem.findScalarLast(u8, source[0..indicator_index], '\n')) |line_begin|1031 const line_begin_index = if (std.mem.findScalarLast(u8, source[0..indicator_index], '\n')) |line_begin|
1004 line_begin + 11032 line_begin + 1
1005 else1033 else
...@@ -1009,33 +1037,31 @@ fn printIndicatorLine(source: []const u8, indicator_index: usize) void {...@@ -1009,33 +1037,31 @@ fn printIndicatorLine(source: []const u8, indicator_index: usize) void {
1009 else1037 else
1010 source.len;1038 source.len;
10111039
1012 printLine(source[line_begin_index..line_end_index]);1040 failPrintLine(source[line_begin_index..line_end_index]);
1013 for (line_begin_index..indicator_index) |_|1041 for (line_begin_index..indicator_index) |_|
1014 print(" ", .{});1042 failPrint(" ", .{});
1015 if (indicator_index >= source.len)1043 if (indicator_index >= source.len)
1016 print("^ (end of string)\n", .{})1044 failPrint("^ (end of string)\n", .{})
1017 else1045 else
1018 print("^ ('\\x{x:0>2}')\n", .{source[indicator_index]});1046 failPrint("^ ('\\x{x:0>2}')\n", .{source[indicator_index]});
1019}1047}
10201048
1021fn printWithVisibleNewlines(source: []const u8) void {1049/// Helper function for printing test failure information.
1050pub fn failPrintWithVisibleNewlines(source: []const u8) void {
1022 var i: usize = 0;1051 var i: usize = 0;
1023 while (std.mem.findScalar(u8, source[i..], '\n')) |nl| : (i += nl + 1) {1052 while (std.mem.findScalar(u8, source[i..], '\n')) |nl| : (i += nl + 1) {
1024 printLine(source[i..][0..nl]);1053 failPrintLine(source[i..][0..nl]);
1025 }1054 }
1026 print("{s}␃\n", .{source[i..]}); // End of Text symbol (ETX)1055 failPrint("{s}␃\n", .{source[i..]}); // End of Text symbol (ETX)
1027}1056}
10281057
1029fn printLine(line: []const u8) void {1058/// Helper function for printing test failure information.
1059pub fn failPrintLine(line: []const u8) void {
1030 if (line.len != 0) switch (line[line.len - 1]) {1060 if (line.len != 0) switch (line[line.len - 1]) {
1031 ' ', '\t' => return print("{s}⏎\n", .{line}), // Return symbol1061 ' ', '\t' => return failPrint("{s}⏎\n", .{line}), // Return symbol
1032 else => {},1062 else => {},
1033 };1063 };
1034 print("{s}\n", .{line});1064 failPrint("{s}\n", .{line});
1035}
1036
1037test {
1038 try expectEqualStrings("foo", "foo");
1039}1065}
10401066
1041/// Exhaustively check that allocation failures within `test_fn` are handled without1067/// Exhaustively check that allocation failures within `test_fn` are handled without
...@@ -1140,7 +1166,7 @@ pub fn checkAllAllocationFailures(...@@ -1140,7 +1166,7 @@ pub fn checkAllAllocationFailures(
1140 } else |err| switch (err) {1166 } else |err| switch (err) {
1141 error.OutOfMemory => {1167 error.OutOfMemory => {
1142 if (failing_allocator_inst.allocated_bytes != failing_allocator_inst.freed_bytes) {1168 if (failing_allocator_inst.allocated_bytes != failing_allocator_inst.freed_bytes) {
1143 print(1169 failPrint(
1144 "\nfail_index: {d}/{d}\nallocated bytes: {d}\nfreed bytes: {d}\nallocations: {d}\ndeallocations: {d}\nallocation that was made to fail: {f}",1170 "\nfail_index: {d}/{d}\nallocated bytes: {d}\nfreed bytes: {d}\nallocations: {d}\ndeallocations: {d}\nallocation that was made to fail: {f}",
1145 .{1171 .{
1146 fail_index,1172 fail_index,