authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-09-03 10:09:26-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-09-03 10:09:26-04:00
logbdac2ba9dd2efc4ec1b6ecbb743539f469a09102
tree8390117f094bc8297e1d62c6924592a689863240
parentd74b8567cf6a81550831a9ea02f2cebcb4db9846
parentaba67ecf4475a72035b60dbc0284c2076600f2e1
signaturelock-open Commit is signed but in an unrecognized format.

Merge branch 'mikdusan-issue.2485'

closes #2544

3 files changed, 486 insertions(+), 1 deletions(-)

build.zig+2-1
......@@ -138,12 +138,13 @@ pub fn build(b: *Builder) !void {
138138
139139 test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));
140140 test_step.dependOn(tests.addStandaloneTests(b, test_filter, modes));
141 test_step.dependOn(tests.addStackTraceTests(b, test_filter, modes));
141142 test_step.dependOn(tests.addCliTests(b, test_filter, modes));
142 test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes));
143143 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes));
144144 test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes));
145145 test_step.dependOn(tests.addTranslateCTests(b, test_filter));
146146 test_step.dependOn(tests.addGenHTests(b, test_filter));
147 test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes));
147148 test_step.dependOn(docs_step);
148149}
149150
test/stack_traces.zig created+274
......@@ -0,0 +1,274 @@
1const builtin = @import("builtin");
2const std = @import("std");
3const os = std.os;
4const tests = @import("tests.zig");
5
6pub fn addCases(cases: *tests.StackTracesContext) void {
7 const source_return =
8 \\const std = @import("std");
9 \\
10 \\pub fn main() !void {
11 \\ return error.TheSkyIsFalling;
12 \\}
13 ;
14 const source_try_return =
15 \\const std = @import("std");
16 \\
17 \\fn foo() !void {
18 \\ return error.TheSkyIsFalling;
19 \\}
20 \\
21 \\pub fn main() !void {
22 \\ try foo();
23 \\}
24 ;
25 const source_try_try_return_return =
26 \\const std = @import("std");
27 \\
28 \\fn foo() !void {
29 \\ try bar();
30 \\}
31 \\
32 \\fn bar() !void {
33 \\ return make_error();
34 \\}
35 \\
36 \\fn make_error() !void {
37 \\ return error.TheSkyIsFalling;
38 \\}
39 \\
40 \\pub fn main() !void {
41 \\ try foo();
42 \\}
43 ;
44 switch (builtin.os) {
45 .linux => {
46 cases.addCase(
47 "return",
48 source_return,
49 [_][]const u8{
50 // debug
51 \\error: TheSkyIsFalling
52 \\source.zig:4:5: [address] in main (test)
53 \\
54 ,
55 // release-safe
56 \\error: TheSkyIsFalling
57 \\source.zig:4:5: [address] in std.special.posixCallMainAndExit (test)
58 \\
59 ,
60 // release-fast
61 \\error: TheSkyIsFalling
62 \\
63 ,
64 // release-small
65 \\error: TheSkyIsFalling
66 \\
67 },
68 );
69 cases.addCase(
70 "try return",
71 source_try_return,
72 [_][]const u8{
73 // debug
74 \\error: TheSkyIsFalling
75 \\source.zig:4:5: [address] in foo (test)
76 \\source.zig:8:5: [address] in main (test)
77 \\
78 ,
79 // release-safe
80 \\error: TheSkyIsFalling
81 \\source.zig:4:5: [address] in std.special.posixCallMainAndExit (test)
82 \\source.zig:8:5: [address] in std.special.posixCallMainAndExit (test)
83 \\
84 ,
85 // release-fast
86 \\error: TheSkyIsFalling
87 \\
88 ,
89 // release-small
90 \\error: TheSkyIsFalling
91 \\
92 },
93 );
94 cases.addCase(
95 "try try return return",
96 source_try_try_return_return,
97 [_][]const u8{
98 // debug
99 \\error: TheSkyIsFalling
100 \\source.zig:12:5: [address] in make_error (test)
101 \\source.zig:8:5: [address] in bar (test)
102 \\source.zig:4:5: [address] in foo (test)
103 \\source.zig:16:5: [address] in main (test)
104 \\
105 ,
106 // release-safe
107 \\error: TheSkyIsFalling
108 \\source.zig:12:5: [address] in std.special.posixCallMainAndExit (test)
109 \\source.zig:8:5: [address] in std.special.posixCallMainAndExit (test)
110 \\source.zig:4:5: [address] in std.special.posixCallMainAndExit (test)
111 \\source.zig:16:5: [address] in std.special.posixCallMainAndExit (test)
112 \\
113 ,
114 // release-fast
115 \\error: TheSkyIsFalling
116 \\
117 ,
118 // release-small
119 \\error: TheSkyIsFalling
120 \\
121 },
122 );
123 },
124 .macosx => {
125 cases.addCase(
126 "return",
127 source_return,
128 [_][]const u8{
129 // debug
130 \\error: TheSkyIsFalling
131 \\source.zig:4:5: [address] in _main.0 (test.o)
132 \\
133 ,
134 // release-safe
135 \\error: TheSkyIsFalling
136 \\source.zig:4:5: [address] in _main (test.o)
137 \\
138 ,
139 // release-fast
140 \\error: TheSkyIsFalling
141 \\
142 ,
143 // release-small
144 \\error: TheSkyIsFalling
145 \\
146 },
147 );
148 cases.addCase(
149 "try return",
150 source_try_return,
151 [_][]const u8{
152 // debug
153 \\error: TheSkyIsFalling
154 \\source.zig:4:5: [address] in _foo (test.o)
155 \\source.zig:8:5: [address] in _main.0 (test.o)
156 \\
157 ,
158 // release-safe
159 \\error: TheSkyIsFalling
160 \\source.zig:4:5: [address] in _main (test.o)
161 \\source.zig:8:5: [address] in _main (test.o)
162 \\
163 ,
164 // release-fast
165 \\error: TheSkyIsFalling
166 \\
167 ,
168 // release-small
169 \\error: TheSkyIsFalling
170 \\
171 },
172 );
173 cases.addCase(
174 "try try return return",
175 source_try_try_return_return,
176 [_][]const u8{
177 // debug
178 \\error: TheSkyIsFalling
179 \\source.zig:12:5: [address] in _make_error (test.o)
180 \\source.zig:8:5: [address] in _bar (test.o)
181 \\source.zig:4:5: [address] in _foo (test.o)
182 \\source.zig:16:5: [address] in _main.0 (test.o)
183 \\
184 ,
185 // release-safe
186 \\error: TheSkyIsFalling
187 \\source.zig:12:5: [address] in _main (test.o)
188 \\source.zig:8:5: [address] in _main (test.o)
189 \\source.zig:4:5: [address] in _main (test.o)
190 \\source.zig:16:5: [address] in _main (test.o)
191 \\
192 ,
193 // release-fast
194 \\error: TheSkyIsFalling
195 \\
196 ,
197 // release-small
198 \\error: TheSkyIsFalling
199 \\
200 },
201 );
202 },
203 .windows => {
204 cases.addCase(
205 "return",
206 source_return,
207 [_][]const u8{
208 // debug
209 \\error: TheSkyIsFalling
210 \\source.zig:4:5: [address] in main (test.obj)
211 \\
212 ,
213 // release-safe
214 // --disabled-- results in segmenetation fault
215 "",
216 // release-fast
217 \\error: TheSkyIsFalling
218 \\
219 ,
220 // release-small
221 \\error: TheSkyIsFalling
222 \\
223 },
224 );
225 cases.addCase(
226 "try return",
227 source_try_return,
228 [_][]const u8{
229 // debug
230 \\error: TheSkyIsFalling
231 \\source.zig:4:5: [address] in foo (test.obj)
232 \\source.zig:8:5: [address] in main (test.obj)
233 \\
234 ,
235 // release-safe
236 // --disabled-- results in segmenetation fault
237 "",
238 // release-fast
239 \\error: TheSkyIsFalling
240 \\
241 ,
242 // release-small
243 \\error: TheSkyIsFalling
244 \\
245 },
246 );
247 cases.addCase(
248 "try try return return",
249 source_try_try_return_return,
250 [_][]const u8{
251 // debug
252 \\error: TheSkyIsFalling
253 \\source.zig:12:5: [address] in make_error (test.obj)
254 \\source.zig:8:5: [address] in bar (test.obj)
255 \\source.zig:4:5: [address] in foo (test.obj)
256 \\source.zig:16:5: [address] in main (test.obj)
257 \\
258 ,
259 // release-safe
260 // --disabled-- results in segmenetation fault
261 "",
262 // release-fast
263 \\error: TheSkyIsFalling
264 \\
265 ,
266 // release-small
267 \\error: TheSkyIsFalling
268 \\
269 },
270 );
271 },
272 else => {},
273 }
274}
test/tests.zig+210
......@@ -16,6 +16,7 @@ const LibExeObjStep = build.LibExeObjStep;
1616
1717const compare_output = @import("compare_output.zig");
1818const standalone = @import("standalone.zig");
19const stack_traces = @import("stack_traces.zig");
1920const compile_errors = @import("compile_errors.zig");
2021const assemble_and_link = @import("assemble_and_link.zig");
2122const runtime_safety = @import("runtime_safety.zig");
......@@ -57,6 +58,21 @@ pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8, modes:
5758 return cases.step;
5859}
5960
61pub fn addStackTraceTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step {
62 const cases = b.allocator.create(StackTracesContext) catch unreachable;
63 cases.* = StackTracesContext{
64 .b = b,
65 .step = b.step("test-stack-traces", "Run the stack trace tests"),
66 .test_index = 0,
67 .test_filter = test_filter,
68 .modes = modes,
69 };
70
71 stack_traces.addCases(cases);
72
73 return cases.step;
74}
75
6076pub fn addRuntimeSafetyTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step {
6177 const cases = b.allocator.create(CompareOutputContext) catch unreachable;
6278 cases.* = CompareOutputContext{
......@@ -549,6 +565,200 @@ pub const CompareOutputContext = struct {
549565 }
550566};
551567
568pub const StackTracesContext = struct {
569 b: *build.Builder,
570 step: *build.Step,
571 test_index: usize,
572 test_filter: ?[]const u8,
573 modes: []const Mode,
574
575 const Expect = [@typeInfo(Mode).Enum.fields.len][]const u8;
576
577 pub fn addCase(
578 self: *StackTracesContext,
579 name: []const u8,
580 source: []const u8,
581 expect: Expect,
582 ) void {
583 const b = self.b;
584
585 const source_pathname = fs.path.join(
586 b.allocator,
587 [_][]const u8{ b.cache_root, "source.zig" },
588 ) catch unreachable;
589
590 for (self.modes) |mode| {
591 const expect_for_mode = expect[@enumToInt(mode)];
592 if (expect_for_mode.len == 0) continue;
593
594 const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {} ({})", "stack-trace", name, @tagName(mode)) catch unreachable;
595 if (self.test_filter) |filter| {
596 if (mem.indexOf(u8, annotated_case_name, filter) == null) continue;
597 }
598
599 const exe = b.addExecutable("test", source_pathname);
600 exe.setBuildMode(mode);
601
602 const write_source = b.addWriteFile(source_pathname, source);
603 exe.step.dependOn(&write_source.step);
604
605 const run_and_compare = RunAndCompareStep.create(
606 self,
607 exe,
608 annotated_case_name,
609 mode,
610 expect_for_mode,
611 );
612
613 self.step.dependOn(&run_and_compare.step);
614 }
615 }
616
617 const RunAndCompareStep = struct {
618 step: build.Step,
619 context: *StackTracesContext,
620 exe: *LibExeObjStep,
621 name: []const u8,
622 mode: Mode,
623 expect_output: []const u8,
624 test_index: usize,
625
626 pub fn create(
627 context: *StackTracesContext,
628 exe: *LibExeObjStep,
629 name: []const u8,
630 mode: Mode,
631 expect_output: []const u8,
632 ) *RunAndCompareStep {
633 const allocator = context.b.allocator;
634 const ptr = allocator.create(RunAndCompareStep) catch unreachable;
635 ptr.* = RunAndCompareStep{
636 .step = build.Step.init("StackTraceCompareOutputStep", allocator, make),
637 .context = context,
638 .exe = exe,
639 .name = name,
640 .mode = mode,
641 .expect_output = expect_output,
642 .test_index = context.test_index,
643 };
644 ptr.step.dependOn(&exe.step);
645 context.test_index += 1;
646 return ptr;
647 }
648
649 fn make(step: *build.Step) !void {
650 const self = @fieldParentPtr(RunAndCompareStep, "step", step);
651 const b = self.context.b;
652
653 const full_exe_path = self.exe.getOutputPath();
654 var args = ArrayList([]const u8).init(b.allocator);
655 defer args.deinit();
656 args.append(full_exe_path) catch unreachable;
657
658 warn("Test {}/{} {}...", self.test_index + 1, self.context.test_index, self.name);
659
660 const child = std.ChildProcess.init(args.toSliceConst(), b.allocator) catch unreachable;
661 defer child.deinit();
662
663 child.stdin_behavior = .Ignore;
664 child.stdout_behavior = .Pipe;
665 child.stderr_behavior = .Pipe;
666 child.env_map = b.env_map;
667
668 child.spawn() catch |err| debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
669
670 var stdout = Buffer.initNull(b.allocator);
671 var stderr = Buffer.initNull(b.allocator);
672
673 var stdout_file_in_stream = child.stdout.?.inStream();
674 var stderr_file_in_stream = child.stderr.?.inStream();
675
676 stdout_file_in_stream.stream.readAllBuffer(&stdout, max_stdout_size) catch unreachable;
677 stderr_file_in_stream.stream.readAllBuffer(&stderr, max_stdout_size) catch unreachable;
678
679 const term = child.wait() catch |err| {
680 debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
681 };
682
683 switch (term) {
684 .Exited => |code| {
685 const expect_code: u32 = 1;
686 if (code != expect_code) {
687 warn("Process {} exited with error code {} but expected code {}\n", full_exe_path, code, expect_code);
688 printInvocation(args.toSliceConst());
689 return error.TestFailed;
690 }
691 },
692 .Signal => |signum| {
693 warn("Process {} terminated on signal {}\n", full_exe_path, signum);
694 printInvocation(args.toSliceConst());
695 return error.TestFailed;
696 },
697 .Stopped => |signum| {
698 warn("Process {} stopped on signal {}\n", full_exe_path, signum);
699 printInvocation(args.toSliceConst());
700 return error.TestFailed;
701 },
702 .Unknown => |code| {
703 warn("Process {} terminated unexpectedly with error code {}\n", full_exe_path, code);
704 printInvocation(args.toSliceConst());
705 return error.TestFailed;
706 },
707 }
708
709 // process result
710 // - keep only basename of source file path
711 // - replace address with symbolic string
712 // - skip empty lines
713 const got: []const u8 = got_result: {
714 var buf = try Buffer.initSize(b.allocator, 0);
715 defer buf.deinit();
716 var bytes = stderr.toSliceConst();
717 if (bytes.len != 0 and bytes[bytes.len - 1] == '\n') bytes = bytes[0 .. bytes.len - 1];
718 var it = mem.separate(bytes, "\n");
719 process_lines: while (it.next()) |line| {
720 if (line.len == 0) continue;
721 const delims = [_][]const u8{ ":", ":", ":", " in " };
722 var marks = [_]usize{0} ** 4;
723 // offset search past `[drive]:` on windows
724 var pos: usize = if (builtin.os == .windows) 2 else 0;
725 for (delims) |delim, i| {
726 marks[i] = mem.indexOfPos(u8, line, pos, delim) orelse {
727 try buf.append(line);
728 try buf.append("\n");
729 continue :process_lines;
730 };
731 pos = marks[i] + delim.len;
732 }
733 pos = mem.lastIndexOfScalar(u8, line[0..marks[0]], fs.path.sep) orelse {
734 try buf.append(line);
735 try buf.append("\n");
736 continue :process_lines;
737 };
738 try buf.append(line[pos + 1 .. marks[2] + delims[2].len]);
739 try buf.append(" [address]");
740 try buf.append(line[marks[3]..]);
741 try buf.append("\n");
742 }
743 break :got_result buf.toOwnedSlice();
744 };
745
746 if (!mem.eql(u8, self.expect_output, got)) {
747 warn(
748 \\
749 \\========= Expected this output: =========
750 \\{}
751 \\================================================
752 \\{}
753 \\
754 , self.expect_output, got);
755 return error.TestFailed;
756 }
757 warn("OK\n");
758 }
759 };
760};
761
552762pub const CompileErrorContext = struct {
553763 b: *build.Builder,
554764 step: *build.Step,