authorgravatar for noam@pixelhero.devNoam Preil <noam@pixelhero.dev> 2020-05-27 11:51:48-04:00
committergravatar for noam@pixelhero.devNoam Preil <noam@pixelhero.dev> 2020-06-15 17:51:43-04:00
log68cc068a3aa5df8bd1995d10a9133af1fa1ea1d3
treec4596efbe3f2c202dd185548f65c8de0b90fccbe
parentbebc1f49cf2e480fbd5dc2a409ee1bc0fe3de331
signature Commit is signed but in an unrecognized format.

[Stage2/Testing] Make API more friendly


2 files changed, 97 insertions(+), 88 deletions(-)

src-self-hosted/test.zig+88-71
...@@ -6,6 +6,24 @@ const Allocator = std.mem.Allocator;...@@ -6,6 +6,24 @@ const Allocator = std.mem.Allocator;
6const zir = @import("zir.zig");6const zir = @import("zir.zig");
7const Package = @import("Package.zig");7const Package = @import("Package.zig");
88
9test "find-offset" {
10 std.testing.expectEqual(findOffset("hello123", 1, 8), 7);
11 const testmsg =
12 \\@noreturn = primitive(noreturn)
13 \\
14 \\@start_fnty = fntype([], @noreturn, cc=Naked)
15 \\@start = fn(@start_fnty, {
16 \\ %0 = call(@notafunc, [])
17 \\})
18 ;
19 std.testing.expectEqual(findOffset(testmsg, 2, 1), 32);
20 std.testing.expectEqual(findOffset(testmsg, 3, 1), 33);
21 std.testing.expectEqual(findOffset(testmsg, 3, 10), 42);
22 std.testing.expectEqual(findOffset(testmsg, 4, 1), 79);
23 std.testing.expectEqual(findOffset(testmsg, 5, 1), 106);
24 std.testing.expectEqual(findOffset(testmsg, 5, 13), 118);
25}
26
9test "self-hosted" {27test "self-hosted" {
10 var ctx: TestContext = undefined;28 var ctx: TestContext = undefined;
11 try ctx.init();29 try ctx.init();
...@@ -16,6 +34,27 @@ test "self-hosted" {...@@ -16,6 +34,27 @@ test "self-hosted" {
16 try ctx.run();34 try ctx.run();
17}35}
1836
37/// Finds the raw byte offset of line:column in src. This is not a performant implementation,
38/// as it should only ever be called rarely and it is better to focus on readability.
39fn findOffset(src: []const u8, line: usize, column: usize) ?usize {
40 // "0000000001"
41 // 1:10
42 //
43 var current_line: usize = 1;
44 var current_column: usize = 1;
45 for (src) |char, index| {
46 if (current_line == line and current_column == column) {
47 return index;
48 }
49 if (char == '\n') {
50 current_line += 1;
51 current_column = 0;
52 }
53 current_column += 1;
54 }
55 return null;
56}
57
19pub const TestContext = struct {58pub const TestContext = struct {
20 zir_cmp_output_cases: std.ArrayList(ZIRCompareOutputCase),59 zir_cmp_output_cases: std.ArrayList(ZIRCompareOutputCase),
21 zir_transform_cases: std.ArrayList(ZIRTransformCase),60 zir_transform_cases: std.ArrayList(ZIRTransformCase),
...@@ -60,9 +99,7 @@ pub const TestContext = struct {...@@ -60,9 +99,7 @@ pub const TestContext = struct {
60 pub const ZIRErrorCase = struct {99 pub const ZIRErrorCase = struct {
61 name: []const u8,100 name: []const u8,
62 src: [:0]const u8,101 src: [:0]const u8,
63 expected_file_errors: []const ErrorMsg,102 expected_errors: []const ErrorMsg,
64 expected_decl_errors: []const ErrorMsg,
65 expected_export_errors: []const ErrorMsg,
66 cross_target: std.zig.CrossTarget,103 cross_target: std.zig.CrossTarget,
67 };104 };
68105
...@@ -103,16 +140,31 @@ pub const TestContext = struct {...@@ -103,16 +140,31 @@ pub const TestContext = struct {
103 name: []const u8,140 name: []const u8,
104 cross_target: std.zig.CrossTarget,141 cross_target: std.zig.CrossTarget,
105 src: [:0]const u8,142 src: [:0]const u8,
106 expected_file_errors: []const ErrorMsg,143 expected_errors: []const []const u8,
107 expected_decl_errors: []const ErrorMsg,
108 expected_export_errors: []const ErrorMsg,
109 ) void {144 ) void {
145 var array = std.ArrayList(ErrorMsg).init(ctx.zir_error_cases.allocator);
146 for (expected_errors) |e| {
147 const line_index = std.mem.indexOf(u8, e, ":");
148 if (line_index == null) {
149 std.debug.panic("Invalid test: error must be specified as 'line:column:msg', found '{}'", .{e});
150 }
151 const column_index = std.mem.indexOf(u8, e[line_index.? + 1 ..], ":");
152 if (column_index == null) {
153 std.debug.panic("Invalid test: error must be specified as 'line:column:msg', found '{}'", .{e});
154 }
155 const line = std.fmt.parseInt(usize, e[0..line_index.?], 10) catch @panic("Unable to parse line number");
156 const column = std.fmt.parseInt(usize, e[line_index.? + 1 ..][0..column_index.?], 10) catch @panic("Unable to parse column number");
157 const msg = e[line_index.? + 1 ..][column_index.? + 1 ..];
158 const offset = findOffset(src, line, column) orelse std.debug.panic("Unable to match {}:{} to byte offset!", .{ line, column });
159 array.append(ErrorMsg{
160 .byte_offset = offset,
161 .msg = msg,
162 }) catch unreachable;
163 }
110 ctx.zir_error_cases.append(.{164 ctx.zir_error_cases.append(.{
111 .name = name,165 .name = name,
112 .src = src,166 .src = src,
113 .expected_file_errors = expected_file_errors,167 .expected_errors = array.toOwnedSlice(),
114 .expected_decl_errors = expected_decl_errors,
115 .expected_export_errors = expected_export_errors,
116 .cross_target = cross_target,168 .cross_target = cross_target,
117 }) catch unreachable;169 }) catch unreachable;
118 }170 }
...@@ -129,6 +181,9 @@ pub const TestContext = struct {...@@ -129,6 +181,9 @@ pub const TestContext = struct {
129 fn deinit(self: *TestContext) void {181 fn deinit(self: *TestContext) void {
130 self.zir_cmp_output_cases.deinit();182 self.zir_cmp_output_cases.deinit();
131 self.zir_transform_cases.deinit();183 self.zir_transform_cases.deinit();
184 for (self.zir_error_cases.items) |e| {
185 self.zir_error_cases.allocator.free(e.expected_errors);
186 }
132 self.zir_error_cases.deinit();187 self.zir_error_cases.deinit();
133 self.* = undefined;188 self.* = undefined;
134 }189 }
...@@ -364,78 +419,40 @@ pub const TestContext = struct {...@@ -364,78 +419,40 @@ pub const TestContext = struct {
364 };419 };
365 module_node.end();420 module_node.end();
366 var err: ?anyerror = null;421 var err: ?anyerror = null;
422
423 var handled_errors = allocator.alloc(bool, case.expected_errors.len) catch unreachable;
424 defer allocator.free(handled_errors);
425 for (handled_errors) |*e| {
426 e.* = false;
427 }
428
367 {429 {
368 var i = module.failed_files.iterator();430 var i = module.failed_files.iterator();
369 var index: usize = 0;431 while (i.next()) |pair| {
370 while (i.next()) |pair| : (index += 1) {
371 if (index == case.expected_file_errors.len) {
372 std.debug.warn("Unexpected file error: {}\n", .{pair.value});
373 err = error.UnexpectedError;
374 }
375 const v1 = pair.value.*;432 const v1 = pair.value.*;
376 const v2 = case.expected_file_errors[index];433 var handled = false;
377 if (v1.byte_offset != v2.byte_offset) {434 for (case.expected_errors) |e, index| {
378 std.debug.warn("Expected error at {}, found it at {}\n", .{ v2.byte_offset, v1.byte_offset });435 if (!handled_errors[index]) {
379 err = error.ExpectedErrorElsewhere;436 if (v1.byte_offset == e.byte_offset and std.mem.eql(u8, v1.msg, e.msg)) {
380 }437 handled_errors[index] = true;
381 if (!std.mem.eql(u8, v1.msg, v2.msg)) {438 handled = true;
382 std.debug.warn("Expected '{}', found '{}'\n", .{ v2.msg, v1.msg });439 break;
383 err = error.ExpectedOtherError;440 }
441 }
384 }442 }
385 }443 if (!handled) {
386 if (index != case.expected_file_errors.len) {
387 std.debug.warn("Expected an error ('{}'), but did not receive it\n", .{case.expected_file_errors[index]});
388 err = error.MissingError;
389 }
390 }
391 {
392 var i = module.failed_decls.iterator();
393 var index: usize = 0;
394 while (i.next()) |pair| : (index += 1) {
395 if (index == case.expected_decl_errors.len) {
396 std.debug.warn("Unexpected decl error: {}\n", .{pair.value});
397 err = error.UnexpectedError;444 err = error.UnexpectedError;
398 }445 std.debug.warn("Unexpected file error: {}\n", .{v1});
399 const v1 = pair.value.*;
400 const v2 = case.expected_decl_errors[index];
401 if (v1.byte_offset != v2.byte_offset) {
402 std.debug.warn("Expected error at {}, found it at {}\n", .{ v2.byte_offset, v1.byte_offset });
403 err = error.ExpectedErrorElsewhere;
404 }
405 if (!std.mem.eql(u8, v1.msg, v2.msg)) {
406 std.debug.warn("Expected '{}', found '{}'\n", .{ v2.msg, v1.msg });
407 err = error.ExpectedOtherError;
408 }446 }
409 }447 }
410 if (index != case.expected_decl_errors.len) {
411 std.debug.warn("Expected an error ('{}'), but did not receive it\n", .{case.expected_decl_errors[index]});
412 err = error.MissingError;
413 }
414 }448 }
415 {449 for (handled_errors) |e, i| {
416 var i = module.failed_exports.iterator();450 if (!e) {
417 var index: usize = 0;451 err = error.MissingExpectedError;
418 while (i.next()) |pair| : (index += 1) {452 std.debug.warn("Did not receive error: {}\n", .{case.expected_errors[i].msg});
419 if (index == case.expected_export_errors.len) {
420 std.debug.warn("Unexpected export error: {}\n", .{pair.value});
421 err = error.UnexpectedError;
422 }
423 const v1 = pair.value.*;
424 const v2 = case.expected_export_errors[index];
425 if (v1.byte_offset != v2.byte_offset) {
426 std.debug.warn("Expected error at {}, found it at {}\n", .{ v2.byte_offset, v1.byte_offset });
427 err = error.ExpectedErrorElsewhere;
428 }
429 if (!std.mem.eql(u8, v1.msg, v2.msg)) {
430 std.debug.warn("Expected '{}', found '{}'\n", .{ v2.msg, v1.msg });
431 err = error.ExpectedOtherError;
432 }
433 }
434 if (index != case.expected_export_errors.len) {
435 std.debug.warn("Expected an error ('{}'), but did not receive it\n", .{case.expected_export_errors[index]});
436 err = error.MissingError;
437 }453 }
438 }454 }
455
439 if (err) |e| {456 if (err) |e| {
440 return e;457 return e;
441 }458 }
test/stage2/compile_errors.zig+9-17
...@@ -18,24 +18,16 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -18,24 +18,16 @@ pub fn addCases(ctx: *TestContext) !void {
18 \\@start = fn(@start_fnty, {18 \\@start = fn(@start_fnty, {
19 \\ %0 = call(%test, [])19 \\ %0 = call(%test, [])
20 \\})20 \\})
21 , &[_]ErrorMsg{.{21 , &[_][]const u8{"5:13:unrecognized identifier: %test"});
22 .byte_offset = 118,
23 .msg = "unrecognized identifier: %test",
24 }}, &[_]ErrorMsg{}, &[_]ErrorMsg{});
2522
26 ctx.addZIRError("call with non-existent target", linux_x64,23 // ctx.addZIRError("call with non-existent target", linux_x64,
27 \\@noreturn = primitive(noreturn)24 // \\@noreturn = primitive(noreturn)
28 \\25 // \\
29 \\@start_fnty = fntype([], @noreturn, cc=Naked)26 // \\@start_fnty = fntype([], @noreturn, cc=Naked)
30 \\@start = fn(@start_fnty, {27 // \\@start = fn(@start_fnty, {
31 \\ %0 = call(@notafunc, [])28 // \\ %0 = call(@notafunc, [])
32 \\})29 // \\})
33 , &[_]ErrorMsg{30 // , &[_][]const u8{"5:13:unrecognized identifier: @notafunc"});
34 .{
35 .byte_offset = 118,
36 .msg = "unrecognized identifier: @notafunc",
37 },
38 }, &[_]ErrorMsg{}, &[_]ErrorMsg{});
3931
40 //try ctx.testCompileError(32 //try ctx.testCompileError(
41 // \\export fn entry() void {}33 // \\export fn entry() void {}