| ... | @@ -281,8 +281,12 @@ test "expectApproxEqRel" { | ... | @@ -281,8 +281,12 @@ test "expectApproxEqRel" { |
| 281 | /// equal, prints diagnostics to stderr to show exactly how they are not equal, | 281 | /// equal, prints diagnostics to stderr to show exactly how they are not equal, |
| 282 | /// then returns a test failure error. | 282 | /// then returns a test failure error. |
| 283 | /// If your inputs are UTF-8 encoded strings, consider calling `expectEqualStrings` instead. | 283 | /// If your inputs are UTF-8 encoded strings, consider calling `expectEqualStrings` instead. |
| 284 | /// If your inputs are slices of bytes, consider calling `expectEqualBytes` instead. | 284 | /// If your inputs are slices of bytes, consider calling `expectEqualBytes` instead (this |
| | 285 | /// function calls `expectEqualBytes` implicitly when `T` is `u8`). |
| 285 | pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const T) !void { | 286 | pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const T) !void { |
| | 287 | if (T == u8) { |
| | 288 | return expectEqualBytes(expected, actual); |
| | 289 | } |
| 286 | // TODO better printing of the difference | 290 | // TODO better printing of the difference |
| 287 | // If the arrays are small enough we could print the whole thing | 291 | // If the arrays are small enough we could print the whole thing |
| 288 | // If the child type is u8 and no weird bytes, we could print it as strings | 292 | // If the child type is u8 and no weird bytes, we could print it as strings |
| ... | @@ -556,26 +560,58 @@ test { | ... | @@ -556,26 +560,58 @@ test { |
| 556 | /// then returns a test failure error. The colorized output is optional and controlled | 560 | /// then returns a test failure error. The colorized output is optional and controlled |
| 557 | /// by the return of `std.debug.detectTTYConfig()`. | 561 | /// by the return of `std.debug.detectTTYConfig()`. |
| 558 | pub fn expectEqualBytes(expected: []const u8, actual: []const u8) !void { | 562 | pub fn expectEqualBytes(expected: []const u8, actual: []const u8) !void { |
| 559 | std.testing.expectEqualSlices(u8, expected, actual) catch |err| { | 563 | if (std.mem.indexOfDiff(u8, actual, expected)) |diff_index| { |
| | 564 | std.debug.print("byte slices differ. first difference occurs at offset {d} (0x{X})\n", .{ diff_index, diff_index }); |
| | 565 | |
| | 566 | // TODO: Should this be configurable by the caller? |
| | 567 | const max_window_size: usize = 256; |
| | 568 | |
| | 569 | // Print a maximum of max_window_size bytes of each input, starting just before the |
| | 570 | // first difference. |
| | 571 | var window_start: usize = 0; |
| | 572 | if (@max(actual.len, expected.len) > max_window_size) { |
| | 573 | window_start = std.mem.alignBackward(diff_index - @min(diff_index, 16), 16); |
| | 574 | } |
| | 575 | const expected_window = expected[window_start..@min(expected.len, window_start + max_window_size)]; |
| | 576 | const expected_truncated = window_start + expected_window.len < expected.len; |
| | 577 | const actual_window = actual[window_start..@min(actual.len, window_start + max_window_size)]; |
| | 578 | const actual_truncated = window_start + actual_window.len < actual.len; |
| | 579 | |
| 560 | var differ = BytesDiffer{ | 580 | var differ = BytesDiffer{ |
| 561 | .expected = expected, | 581 | .expected = expected_window, |
| 562 | .actual = actual, | 582 | .actual = actual_window, |
| 563 | .ttyconf = std.debug.detectTTYConfig(), | 583 | .ttyconf = std.debug.detectTTYConfig(), |
| 564 | }; | 584 | }; |
| 565 | const stderr = std.io.getStdErr(); | 585 | const stderr = std.io.getStdErr(); |
| 566 | | 586 | |
| 567 | std.debug.print("\n============ expected this output: =============\n\n", .{}); | 587 | std.debug.print("\n============ expected this output: ============= len: {} (0x{X})\n\n", .{ expected.len, expected.len }); |
| | 588 | if (window_start > 0) { |
| | 589 | std.debug.print("... truncated, start offset: 0x{X} ...\n", .{window_start}); |
| | 590 | } |
| 568 | differ.write(stderr.writer()) catch {}; | 591 | differ.write(stderr.writer()) catch {}; |
| | 592 | if (expected_truncated) { |
| | 593 | const end_offset = window_start + expected_window.len; |
| | 594 | const num_missing_bytes = expected.len - (window_start + expected_window.len); |
| | 595 | std.debug.print("... truncated, end offset: 0x{X}, remaining bytes: 0x{X} ...\n", .{ end_offset, num_missing_bytes }); |
| | 596 | } |
| 569 | | 597 | |
| 570 | // now reverse expected/actual and print again | 598 | // now reverse expected/actual and print again |
| 571 | differ.expected = actual; | 599 | differ.expected = actual_window; |
| 572 | differ.actual = expected; | 600 | differ.actual = expected_window; |
| 573 | std.debug.print("\n============= instead found this: ==============\n\n", .{}); | 601 | std.debug.print("\n============= instead found this: ============== len: {} (0x{X})\n\n", .{ actual.len, actual.len }); |
| | 602 | if (window_start > 0) { |
| | 603 | std.debug.print("... truncated, start offset: 0x{X} ...\n", .{window_start}); |
| | 604 | } |
| 574 | differ.write(stderr.writer()) catch {}; | 605 | differ.write(stderr.writer()) catch {}; |
| | 606 | if (actual_truncated) { |
| | 607 | const end_offset = window_start + actual_window.len; |
| | 608 | const num_missing_bytes = actual.len - (window_start + actual_window.len); |
| | 609 | std.debug.print("... truncated, end offset: 0x{X}, remaining bytes: 0x{X} ...\n", .{ end_offset, num_missing_bytes }); |
| | 610 | } |
| 575 | std.debug.print("\n================================================\n\n", .{}); | 611 | std.debug.print("\n================================================\n\n", .{}); |
| 576 | | 612 | |
| 577 | return err; | 613 | return error.TestExpectedEqual; |
| 578 | }; | 614 | } |
| 579 | } | 615 | } |
| 580 | | 616 | |
| 581 | const BytesDiffer = struct { | 617 | const BytesDiffer = struct { |