Commit daff072af2

Lachlan Easton <lachlan@lakebythewoods.xyz>
2020-03-09 03:53:20
Add visible newlines to parser_test output when there's a failure.
Also print first line that differs between expected and result.
1 parent beae932
Changed files (2)
lib/std/zig/parser_test.zig
@@ -2960,6 +2960,36 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b
     return buffer.toOwnedSlice();
 }
 
+fn printLine(line: []const u8) void {
+    warn("{}⏎\n", .{line}); // Carriage return symbol
+}
+
+fn printIndicatorLine(source: []const u8, indicator_index: usize) void {
+    const line_begin_index = if (mem.lastIndexOfScalar(u8, source[0..indicator_index], '\n')) |line_begin|
+        line_begin + 1
+    else
+        0;
+    const line_end_index = if (mem.indexOfScalar(u8, source[indicator_index..], '\n')) |line_end|
+        (indicator_index + line_end)
+    else
+        (source.len - 1);
+    printLine(source[line_begin_index..line_end_index]);
+    {
+        var i: usize = line_begin_index;
+        while (i < indicator_index) : (i += 1)
+            warn(" ", .{});
+    }
+    warn("^~~~~\n", .{});
+}
+
+fn printWithVisibleNewlines(source: []const u8) void {
+    var i: usize = 0;
+    while (mem.indexOf(u8, source[i..], "\n")) |nl| : (i += nl + 1) {
+        printLine(source[i .. i + nl]);
+    }
+    warn("{}{}\n", .{ source[i..], "\u{2403}" }); // End of Text symbol (ETX)
+}
+
 fn testTransform(source: []const u8, expected_source: []const u8) !void {
     const needed_alloc_count = x: {
         // Try it once with unlimited memory, make sure it works
@@ -2969,10 +2999,25 @@ fn testTransform(source: []const u8, expected_source: []const u8) !void {
         const result_source = try testParse(source, &failing_allocator.allocator, &anything_changed);
         if (!mem.eql(u8, result_source, expected_source)) {
             warn("\n====== expected this output: =========\n", .{});
-            warn("{}", .{expected_source});
+            printWithVisibleNewlines(expected_source);
             warn("\n======== instead found this: =========\n", .{});
-            warn("{}", .{result_source});
+            printWithVisibleNewlines(result_source);
             warn("\n======================================\n", .{});
+
+            const diff_index = mem.diffIndex(u8, result_source, expected_source);
+
+            var diff_line_number: usize = 1;
+            for (expected_source[0..diff_index]) |value| {
+                if (value == '\n') diff_line_number += 1;
+            }
+            warn("First difference occurs on line {}:\n", .{diff_line_number});
+
+            warn("expected:\n", .{});
+            printIndicatorLine(expected_source, diff_index);
+
+            warn("found:\n", .{});
+            printIndicatorLine(result_source, diff_index);
+
             return error.TestFailed;
         }
         const changes_expected = source.ptr != expected_source.ptr;
lib/std/mem.zig
@@ -492,6 +492,25 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
     return true;
 }
 
+/// Compares two slices and returns the index of the first inequality.
+/// Returns the length of the slices if they are equal.
+pub fn diffIndex(comptime T: type, a: []const T, b: []const T) usize {
+    const shortest = math.min(a.len, b.len);
+    if (a.ptr == b.ptr)
+        return shortest;
+    var index: usize = 0;
+    while (index < shortest) : (index += 1) if (a[index] != b[index]) return index;
+    return shortest;
+}
+
+test "diffIndex" {
+    testing.expectEqual(diffIndex(u8, "one", "one"), 3);
+    testing.expectEqual(diffIndex(u8, "one two", "one"), 3);
+    testing.expectEqual(diffIndex(u8, "one", "one two"), 3);
+    testing.expectEqual(diffIndex(u8, "one twx", "one two"), 6);
+    testing.expectEqual(diffIndex(u8, "xne", "one"), 0);
+}
+
 pub const toSliceConst = @compileError("deprecated; use std.mem.spanZ");
 pub const toSlice = @compileError("deprecated; use std.mem.spanZ");