Skip to content

std.testing: Improve expectEqualBytes for large inputs and make expectEqualSlices use it #13723

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 1, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 46 additions & 10 deletions lib/std/testing.zig
Original file line number Diff line number Diff line change
Expand Up @@ -281,8 +281,12 @@ test "expectApproxEqRel" {
/// equal, prints diagnostics to stderr to show exactly how they are not equal,
/// then returns a test failure error.
/// If your inputs are UTF-8 encoded strings, consider calling `expectEqualStrings` instead.
/// If your inputs are slices of bytes, consider calling `expectEqualBytes` instead.
/// If your inputs are slices of bytes, consider calling `expectEqualBytes` instead (this
/// function calls `expectEqualBytes` implicitly when `T` is `u8`).
pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const T) !void {
if (T == u8) {
return expectEqualBytes(expected, actual);
}
// TODO better printing of the difference
// If the arrays are small enough we could print the whole thing
// If the child type is u8 and no weird bytes, we could print it as strings
Expand Down Expand Up @@ -556,26 +560,58 @@ test {
/// then returns a test failure error. The colorized output is optional and controlled
/// by the return of `std.debug.detectTTYConfig()`.
pub fn expectEqualBytes(expected: []const u8, actual: []const u8) !void {
std.testing.expectEqualSlices(u8, expected, actual) catch |err| {
if (std.mem.indexOfDiff(u8, actual, expected)) |diff_index| {
std.debug.print("byte slices differ. first difference occurs at offset {d} (0x{X})\n", .{ diff_index, diff_index });

// TODO: Should this be configurable by the caller?
const max_window_size: usize = 256;

// Print a maximum of max_window_size bytes of each input, starting just before the
// first difference.
var window_start: usize = 0;
if (@max(actual.len, expected.len) > max_window_size) {
window_start = std.mem.alignBackward(diff_index - @min(diff_index, 16), 16);
}
const expected_window = expected[window_start..@min(expected.len, window_start + max_window_size)];
const expected_truncated = window_start + expected_window.len < expected.len;
const actual_window = actual[window_start..@min(actual.len, window_start + max_window_size)];
const actual_truncated = window_start + actual_window.len < actual.len;

var differ = BytesDiffer{
.expected = expected,
.actual = actual,
.expected = expected_window,
.actual = actual_window,
.ttyconf = std.debug.detectTTYConfig(),
};
const stderr = std.io.getStdErr();

std.debug.print("\n============ expected this output: =============\n\n", .{});
std.debug.print("\n============ expected this output: ============= len: {} (0x{X})\n\n", .{ expected.len, expected.len });
if (window_start > 0) {
std.debug.print("... truncated, start offset: 0x{X} ...\n", .{window_start});
}
differ.write(stderr.writer()) catch {};
if (expected_truncated) {
const end_offset = window_start + expected_window.len;
const num_missing_bytes = expected.len - (window_start + expected_window.len);
std.debug.print("... truncated, end offset: 0x{X}, remaining bytes: 0x{X} ...\n", .{ end_offset, num_missing_bytes });
}

// now reverse expected/actual and print again
differ.expected = actual;
differ.actual = expected;
std.debug.print("\n============= instead found this: ==============\n\n", .{});
differ.expected = actual_window;
differ.actual = expected_window;
std.debug.print("\n============= instead found this: ============== len: {} (0x{X})\n\n", .{ actual.len, actual.len });
if (window_start > 0) {
std.debug.print("... truncated, start offset: 0x{X} ...\n", .{window_start});
}
differ.write(stderr.writer()) catch {};
if (actual_truncated) {
const end_offset = window_start + actual_window.len;
const num_missing_bytes = actual.len - (window_start + actual_window.len);
std.debug.print("... truncated, end offset: 0x{X}, remaining bytes: 0x{X} ...\n", .{ end_offset, num_missing_bytes });
}
std.debug.print("\n================================================\n\n", .{});

return err;
};
return error.TestExpectedEqual;
}
}

const BytesDiffer = struct {
Expand Down