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
signaturelock-open 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 @@
11const std = @import("std");
22const link = @import("link.zig");
33const Module = @import("Module.zig");
4const ErrorMsg = Module.ErrorMsg;
54const Allocator = std.mem.Allocator;
65const zir = @import("zir.zig");
76const 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
278test "self-hosted" {
289 var ctx: TestContext = undefined;
2910 try ctx.init();
......@@ -34,26 +15,11 @@ test "self-hosted" {
3415 try ctx.run();
3516}
3617
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}
18const ErrorMsg = struct {
19 msg: []const u8,
20 line: u32,
21 column: u32,
22};
5723
5824pub const TestContext = struct {
5925 // TODO: remove these. They are deprecated.
......@@ -115,7 +81,7 @@ pub const TestContext = struct {
11581 cross_target: std.zig.CrossTarget,
11682 };
11783
118 pub const ZIRStageType = enum {
84 pub const ZIRUpdateType = enum {
11985 /// A transformation stage transforms the input ZIR and tests against
12086 /// the expected output
12187 Transformation,
......@@ -129,7 +95,7 @@ pub const TestContext = struct {
12995 Compiles,
13096 };
13197
132 pub const ZIRStage = struct {
98 pub const ZIRUpdate = struct {
13399 /// The input to the current stage. We simulate an incremental update
134100 /// with the file's contents changed to this value each stage.
135101 ///
......@@ -138,7 +104,7 @@ pub const TestContext = struct {
138104 /// you can keep it mostly consistent, with small changes, testing the
139105 /// effects of the incremental compilation.
140106 src: [:0]const u8,
141 case: union(ZIRStageType) {
107 case: union(ZIRUpdateType) {
142108 /// The expected output ZIR
143109 Transformation: []const u8,
144110 /// A slice containing the expected errors *in sequential order*.
......@@ -175,14 +141,14 @@ pub const TestContext = struct {
175141 /// such as QEMU is required for tests to complete.
176142 ///
177143 target: std.zig.CrossTarget,
178 stages: []ZIRStage,
144 stages: []ZIRUpdate,
179145 };
180146
181147 pub fn addZIRCase(
182148 ctx: *TestContext,
183149 name: []const u8,
184150 target: std.zig.CrossTarget,
185 stages: []ZIRStage,
151 stages: []ZIRUpdate,
186152 ) !void {
187153 const case = .{
188154 .name = name,
......@@ -233,21 +199,34 @@ pub const TestContext = struct {
233199 ) void {
234200 var array = std.ArrayList(ErrorMsg).init(ctx.zir_error_cases.allocator);
235201 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, ":");
237209 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});
239211 }
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, ":");
241215 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!");
243224 }
244 const line = std.fmt.parseInt(usize, e[0..line_index.?], 10) catch @panic("Unable to parse line number");
245 const column = std.fmt.parseInt(usize, e[line_index.? + 1 ..][0..column_index.?], 10) catch @panic("Unable to parse column number");
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,
225
226 array.append(.{
250227 .msg = msg,
228 .line = line - 1,
229 .column = column - 1,
251230 }) catch unreachable;
252231 }
253232 ctx.zir_error_cases.append(.{
......@@ -264,6 +243,7 @@ pub const TestContext = struct {
264243 .zir_cmp_output_cases = std.ArrayList(ZIRCompareOutputCase).init(allocator),
265244 .zir_transform_cases = std.ArrayList(ZIRTransformCase).init(allocator),
266245 .zir_error_cases = std.ArrayList(ZIRErrorCase).init(allocator),
246 .zir_cases = std.ArrayList(ZIRCase).init(allocator),
267247 };
268248 }
269249
......@@ -274,6 +254,7 @@ pub const TestContext = struct {
274254 self.zir_error_cases.allocator.free(e.expected_errors);
275255 }
276256 self.zir_error_cases.deinit();
257 self.zir_cases.deinit();
277258 self.* = undefined;
278259 }
279260
......@@ -343,9 +324,9 @@ pub const TestContext = struct {
343324
344325 for (case.stages) |s| {
345326 // 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);
349330 stage_node.activate();
350331 defer stage_node.end();
351332
......@@ -592,74 +573,33 @@ pub const TestContext = struct {
592573 e.* = false;
593574 }
594575
595 // TODO: check the input error list in sequential order, manually
596 // incrementing indices when needed. This would allow deduplicating the
597 // following three blocks into one, and the restriction it imposes on
598 // test writers is one that naturally flows anyways.
599 {
600 var i = module.failed_files.iterator();
601 while (i.next()) |pair| {
602 const v1 = pair.value.*;
603 var handled = false;
604 for (case.expected_errors) |e, index| {
605 if (!handled_errors[index]) {
606 if (v1.byte_offset == e.byte_offset and std.mem.eql(u8, v1.msg, e.msg)) {
607 handled_errors[index] = true;
608 handled = true;
609 break;
610 }
576 var all_errors = try module.getAllErrorsAlloc();
577 defer all_errors.deinit(allocator);
578 for (all_errors.list) |e| {
579 var handled = false;
580 for (case.expected_errors) |ex, i| {
581 if (e.line == ex.line and e.column == ex.column and std.mem.eql(u8, ex.msg, e.msg)) {
582 if (handled_errors[i]) {
583 err = error.ErrorReceivedMultipleTimes;
584 std.debug.warn("Received error multiple times: {}\n", .{e.msg});
585 } else {
586 handled_errors[i] = true;
587 handled = true;
611588 }
612 }
613 if (!handled) {
614 err = error.UnexpectedError;
615 std.debug.warn("Unexpected file error: {}\n", .{v1});
589 break;
616590 }
617591 }
618 }
619 {
620 var i = module.failed_decls.iterator();
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 }
592 if (!handled) {
593 err = error.ErrorNotExpected;
594 std.debug.warn("Received an unexpected error: {}:{}: {}\n", .{ e.line, e.column, e.msg });
657595 }
658596 }
597
659598 for (handled_errors) |e, i| {
660599 if (!e) {
661600 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 });
663603 }
664604 }
665605
test/stage2/compile_errors.zig+2-2
......@@ -18,7 +18,7 @@ pub fn addCases(ctx: *TestContext) !void {
1818 \\@start = fn(@start_fnty, {
1919 \\ %0 = call(%test, [])
2020 \\})
21 , &[_][]const u8{"5:13:unrecognized identifier: %test"});
21 , &[_][]const u8{"error: 5:13: unrecognized identifier: %test"});
2222
2323 // TODO: fix this test
2424 // ctx.addZIRError("call with non-existent target", linux_x64,
......@@ -45,7 +45,7 @@ pub fn addCases(ctx: *TestContext) !void {
4545 \\@0 = str("_start")
4646 \\@1 = ref(@0)
4747 \\@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
5050 //try ctx.testCompileError(
5151 // \\export fn entry() void {}