authorgravatar for noam@pixelhero.devNoam Preil <noam@pixelhero.dev> 2020-06-04 15:59:33-04:00
committergravatar for noam@pixelhero.devNoam Preil <noam@pixelhero.dev> 2020-06-15 20:33:07-04:00
log6dce317fe39443278ee744e66118c6e9b6023615
tree2afa59db4e509a052efa2e43d027e38cc7a68066
parentd4fd7c6a014f4ba41f2231b58d090d72179a5bdb
signature Commit is signed but in an unrecognized format.

Stage2/Testing: Fix error tests


2 files changed, 58 insertions(+), 118 deletions(-)

src-self-hosted/test.zig+56-116
...@@ -1,29 +1,10 @@...@@ -1,29 +1,10 @@
1const std = @import("std");1const std = @import("std");
2const link = @import("link.zig");2const link = @import("link.zig");
3const Module = @import("Module.zig");3const Module = @import("Module.zig");
4const ErrorMsg = Module.ErrorMsg;
5const Allocator = std.mem.Allocator;4const Allocator = std.mem.Allocator;
6const zir = @import("zir.zig");5const zir = @import("zir.zig");
7const Package = @import("Package.zig");6const Package = @import("Package.zig");
87
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
27test "self-hosted" {8test "self-hosted" {
28 var ctx: TestContext = undefined;9 var ctx: TestContext = undefined;
29 try ctx.init();10 try ctx.init();
...@@ -34,26 +15,11 @@ test "self-hosted" {...@@ -34,26 +15,11 @@ test "self-hosted" {
34 try ctx.run();15 try ctx.run();
35}16}
3617
37/// Finds the raw byte offset of line:column in src. This is not a performant implementation,18const ErrorMsg = struct {
38/// as it should only ever be called rarely and it is better to focus on readability.19 msg: []const u8,
39fn findOffset(src: []const u8, line: usize, column: usize) ?usize {20 line: u32,
40 // "0000000001"21 column: u32,
41 // 1:1022};
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}
5723
58pub const TestContext = struct {24pub const TestContext = struct {
59 // TODO: remove these. They are deprecated.25 // TODO: remove these. They are deprecated.
...@@ -115,7 +81,7 @@ pub const TestContext = struct {...@@ -115,7 +81,7 @@ pub const TestContext = struct {
115 cross_target: std.zig.CrossTarget,81 cross_target: std.zig.CrossTarget,
116 };82 };
11783
118 pub const ZIRStageType = enum {84 pub const ZIRUpdateType = enum {
119 /// A transformation stage transforms the input ZIR and tests against85 /// A transformation stage transforms the input ZIR and tests against
120 /// the expected output86 /// the expected output
121 Transformation,87 Transformation,
...@@ -129,7 +95,7 @@ pub const TestContext = struct {...@@ -129,7 +95,7 @@ pub const TestContext = struct {
129 Compiles,95 Compiles,
130 };96 };
13197
132 pub const ZIRStage = struct {98 pub const ZIRUpdate = struct {
133 /// The input to the current stage. We simulate an incremental update99 /// The input to the current stage. We simulate an incremental update
134 /// with the file's contents changed to this value each stage.100 /// with the file's contents changed to this value each stage.
135 ///101 ///
...@@ -138,7 +104,7 @@ pub const TestContext = struct {...@@ -138,7 +104,7 @@ pub const TestContext = struct {
138 /// you can keep it mostly consistent, with small changes, testing the104 /// you can keep it mostly consistent, with small changes, testing the
139 /// effects of the incremental compilation.105 /// effects of the incremental compilation.
140 src: [:0]const u8,106 src: [:0]const u8,
141 case: union(ZIRStageType) {107 case: union(ZIRUpdateType) {
142 /// The expected output ZIR108 /// The expected output ZIR
143 Transformation: []const u8,109 Transformation: []const u8,
144 /// A slice containing the expected errors *in sequential order*.110 /// A slice containing the expected errors *in sequential order*.
...@@ -175,14 +141,14 @@ pub const TestContext = struct {...@@ -175,14 +141,14 @@ pub const TestContext = struct {
175 /// such as QEMU is required for tests to complete.141 /// such as QEMU is required for tests to complete.
176 ///142 ///
177 target: std.zig.CrossTarget,143 target: std.zig.CrossTarget,
178 stages: []ZIRStage,144 stages: []ZIRUpdate,
179 };145 };
180146
181 pub fn addZIRCase(147 pub fn addZIRCase(
182 ctx: *TestContext,148 ctx: *TestContext,
183 name: []const u8,149 name: []const u8,
184 target: std.zig.CrossTarget,150 target: std.zig.CrossTarget,
185 stages: []ZIRStage,151 stages: []ZIRUpdate,
186 ) !void {152 ) !void {
187 const case = .{153 const case = .{
188 .name = name,154 .name = name,
...@@ -233,21 +199,34 @@ pub const TestContext = struct {...@@ -233,21 +199,34 @@ pub const TestContext = struct {
233 ) void {199 ) void {
234 var array = std.ArrayList(ErrorMsg).init(ctx.zir_error_cases.allocator);200 var array = std.ArrayList(ErrorMsg).init(ctx.zir_error_cases.allocator);
235 for (expected_errors) |e| {201 for (expected_errors) |e| {
236 const line_index = std.mem.indexOf(u8, e, ":");202 var cur = e;
203 const err = cur[0..7];
204 if (!std.mem.eql(u8, err, "error: ")) {
205 std.debug.panic("Only error messages are currently supported, received {}\n", .{e});
206 }
207 cur = cur[7..];
208 var line_index = std.mem.indexOf(u8, cur, ":");
237 if (line_index == null) {209 if (line_index == null) {
238 std.debug.panic("Invalid test: error must be specified as 'line:column:msg', found '{}'", .{e});210 std.debug.panic("Invalid test: error must be specified as 'error: line:column: msg', found '{}'", .{e});
239 }211 }
240 const column_index = std.mem.indexOf(u8, e[line_index.? + 1 ..], ":");212 const line = std.fmt.parseInt(u32, cur[0..line_index.?], 10) catch @panic("Unable to parse line number");
213 cur = cur[line_index.? + 1 ..];
214 const column_index = std.mem.indexOf(u8, cur, ":");
241 if (column_index == null) {215 if (column_index == null) {
242 std.debug.panic("Invalid test: error must be specified as 'line:column:msg', found '{}'", .{e});216 std.debug.panic("Invalid test: error must be specified as 'error: line:column: msg', found '{}'", .{e});
217 }
218 const column = std.fmt.parseInt(u32, cur[0..column_index.?], 10) catch @panic("Unable to parse column number");
219 std.debug.assert(cur[column_index.? + 1] == ' ');
220 const msg = cur[column_index.? + 2 ..];
221
222 if (line == 0 or column == 0) {
223 @panic("Invalid test: error line and column must be specified starting at one!");
243 }224 }
244 const line = std.fmt.parseInt(usize, e[0..line_index.?], 10) catch @panic("Unable to parse line number");225
245 const column = std.fmt.parseInt(usize, e[line_index.? + 1 ..][0..column_index.?], 10) catch @panic("Unable to parse column number");226 array.append(.{
246 const msg = e[line_index.? + 1 ..][column_index.? + 1 ..];
247 const offset = findOffset(src, line, column) orelse std.debug.panic("Unable to match {}:{} to byte offset!", .{ line, column });
248 array.append(ErrorMsg{
249 .byte_offset = offset,
250 .msg = msg,227 .msg = msg,
228 .line = line - 1,
229 .column = column - 1,
251 }) catch unreachable;230 }) catch unreachable;
252 }231 }
253 ctx.zir_error_cases.append(.{232 ctx.zir_error_cases.append(.{
...@@ -264,6 +243,7 @@ pub const TestContext = struct {...@@ -264,6 +243,7 @@ pub const TestContext = struct {
264 .zir_cmp_output_cases = std.ArrayList(ZIRCompareOutputCase).init(allocator),243 .zir_cmp_output_cases = std.ArrayList(ZIRCompareOutputCase).init(allocator),
265 .zir_transform_cases = std.ArrayList(ZIRTransformCase).init(allocator),244 .zir_transform_cases = std.ArrayList(ZIRTransformCase).init(allocator),
266 .zir_error_cases = std.ArrayList(ZIRErrorCase).init(allocator),245 .zir_error_cases = std.ArrayList(ZIRErrorCase).init(allocator),
246 .zir_cases = std.ArrayList(ZIRCase).init(allocator),
267 };247 };
268 }248 }
269249
...@@ -274,6 +254,7 @@ pub const TestContext = struct {...@@ -274,6 +254,7 @@ pub const TestContext = struct {
274 self.zir_error_cases.allocator.free(e.expected_errors);254 self.zir_error_cases.allocator.free(e.expected_errors);
275 }255 }
276 self.zir_error_cases.deinit();256 self.zir_error_cases.deinit();
257 self.zir_cases.deinit();
277 self.* = undefined;258 self.* = undefined;
278 }259 }
279260
...@@ -343,9 +324,9 @@ pub const TestContext = struct {...@@ -343,9 +324,9 @@ pub const TestContext = struct {
343324
344 for (case.stages) |s| {325 for (case.stages) |s| {
345 // TODO: remove before committing. This is for ZLS ;)326 // TODO: remove before committing. This is for ZLS ;)
346 const stage: ZIRStage = s;327 const stage: ZIRUpdate = s;
347328
348 var stage_node = prg_node.start("stage", 4);329 var stage_node = prg_node.start("update", 4);
349 stage_node.activate();330 stage_node.activate();
350 defer stage_node.end();331 defer stage_node.end();
351332
...@@ -592,74 +573,33 @@ pub const TestContext = struct {...@@ -592,74 +573,33 @@ pub const TestContext = struct {
592 e.* = false;573 e.* = false;
593 }574 }
594575
595 // TODO: check the input error list in sequential order, manually576 var all_errors = try module.getAllErrorsAlloc();
596 // incrementing indices when needed. This would allow deduplicating the577 defer all_errors.deinit(allocator);
597 // following three blocks into one, and the restriction it imposes on578 for (all_errors.list) |e| {
598 // test writers is one that naturally flows anyways.579 var handled = false;
599 {580 for (case.expected_errors) |ex, i| {
600 var i = module.failed_files.iterator();581 if (e.line == ex.line and e.column == ex.column and std.mem.eql(u8, ex.msg, e.msg)) {
601 while (i.next()) |pair| {582 if (handled_errors[i]) {
602 const v1 = pair.value.*;583 err = error.ErrorReceivedMultipleTimes;
603 var handled = false;584 std.debug.warn("Received error multiple times: {}\n", .{e.msg});
604 for (case.expected_errors) |e, index| {585 } else {
605 if (!handled_errors[index]) {586 handled_errors[i] = true;
606 if (v1.byte_offset == e.byte_offset and std.mem.eql(u8, v1.msg, e.msg)) {587 handled = true;
607 handled_errors[index] = true;
608 handled = true;
609 break;
610 }
611 }588 }
612 }589 break;
613 if (!handled) {
614 err = error.UnexpectedError;
615 std.debug.warn("Unexpected file error: {}\n", .{v1});
616 }590 }
617 }591 }
618 }592 if (!handled) {
619 {593 err = error.ErrorNotExpected;
620 var i = module.failed_decls.iterator();594 std.debug.warn("Received an unexpected error: {}:{}: {}\n", .{ e.line, e.column, e.msg });
621 while (i.next()) |pair| {
622 const v1 = pair.value.*;
623 var handled = false;
624 for (case.expected_errors) |e, index| {
625 if (!handled_errors[index]) {
626 if (v1.byte_offset == e.byte_offset and std.mem.eql(u8, v1.msg, e.msg)) {
627 handled_errors[index] = true;
628 handled = true;
629 break;
630 }
631 }
632 }
633 if (!handled) {
634 err = error.UnexpectedError;
635 std.debug.warn("Unexpected decl error: {}\n", .{v1});
636 }
637 }
638 }
639 {
640 var i = module.failed_exports.iterator();
641 while (i.next()) |pair| {
642 const v1 = pair.value.*;
643 var handled = false;
644 for (case.expected_errors) |e, index| {
645 if (!handled_errors[index]) {
646 if (v1.byte_offset == e.byte_offset and std.mem.eql(u8, v1.msg, e.msg)) {
647 handled_errors[index] = true;
648 handled = true;
649 break;
650 }
651 }
652 }
653 if (!handled) {
654 err = error.UnexpectedError;
655 std.debug.warn("Unexpected export error: {}\n", .{v1});
656 }
657 }595 }
658 }596 }
597
659 for (handled_errors) |e, i| {598 for (handled_errors) |e, i| {
660 if (!e) {599 if (!e) {
661 err = error.MissingExpectedError;600 err = error.MissingExpectedError;
662 std.debug.warn("Did not receive error: {}\n", .{case.expected_errors[i].msg});601 const er = case.expected_errors[i];
602 std.debug.warn("Did not receive error: {}:{}: {}\n", .{ er.line, er.column, er.msg });
663 }603 }
664 }604 }
665605
test/stage2/compile_errors.zig+2-2
...@@ -18,7 +18,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -18,7 +18,7 @@ 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 , &[_][]const u8{"5:13:unrecognized identifier: %test"});21 , &[_][]const u8{"error: 5:13: unrecognized identifier: %test"});
2222
23 // TODO: fix this test23 // TODO: fix this test
24 // ctx.addZIRError("call with non-existent target", linux_x64,24 // ctx.addZIRError("call with non-existent target", linux_x64,
...@@ -45,7 +45,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -45,7 +45,7 @@ pub fn addCases(ctx: *TestContext) !void {
45 \\@0 = str("_start")45 \\@0 = str("_start")
46 \\@1 = ref(@0)46 \\@1 = ref(@0)
47 \\@2 = export(@1, @start)47 \\@2 = export(@1, @start)
48 , &[_][]const u8{"4:9:unable to call function with naked calling convention"});48 , &[_][]const u8{"error: 4:9: unable to call function with naked calling convention"});
4949
50 //try ctx.testCompileError(50 //try ctx.testCompileError(
51 // \\export fn entry() void {}51 // \\export fn entry() void {}