diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 5418cc34f096..1bda46ecce69 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -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 @@ -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 {