authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-06-27 21:54:11-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-06-27 21:54:11-04:00
logac6bf53069d3dccc3236cee05d5da026642ee4d4
tree3298c620be500345bf275f3098b0c52011d18ba5
parent0cfe8e5d6ff06eed0cde6aed0c009a58ceffc395
parent80b70470c016ad0d1db47d0edc4d48f1f75af258
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

stage2: clean up test harness, implement symbol collision detection (#5708)

* Clean up test harness * Stage2/Testing: Add convenience wrappers * Add a `compiles` wrapper case * fix incremental compilation after error * exported symbol collision detection * function redefinition detection for Zig code * handle missing function names * Stage2/Testing: Simplify incremental compilation tests * Stage2/Testing: Update documentation * Stage2/TestHarness: Improve progress reporting * Disable test * Improve Tranform failure output

6 files changed, 345 insertions(+), 122 deletions(-)

src-self-hosted/Module.zig+38-6
......@@ -33,6 +33,9 @@ bin_file_path: []const u8,
3333/// Decl pointers to details about them being exported.
3434/// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table.
3535decl_exports: std.AutoHashMap(*Decl, []*Export),
36/// We track which export is associated with the given symbol name for quick
37/// detection of symbol collisions.
38symbol_exports: std.StringHashMap(*Export),
3639/// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl
3740/// is modified. Note that the key of this table is not the Decl being exported, but the Decl that
3841/// is performing the export of another Decl.
......@@ -777,6 +780,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
777780 .optimize_mode = options.optimize_mode,
778781 .decl_table = DeclTable.init(gpa),
779782 .decl_exports = std.AutoHashMap(*Decl, []*Export).init(gpa),
783 .symbol_exports = std.StringHashMap(*Export).init(gpa),
780784 .export_owners = std.AutoHashMap(*Decl, []*Export).init(gpa),
781785 .failed_decls = std.AutoHashMap(*Decl, *ErrorMsg).init(gpa),
782786 .failed_files = std.AutoHashMap(*Scope, *ErrorMsg).init(gpa),
......@@ -834,6 +838,7 @@ pub fn deinit(self: *Module) void {
834838 }
835839 self.export_owners.deinit();
836840 }
841 self.symbol_exports.deinit();
837842 self.root_scope.destroy(allocator);
838843 self.* = undefined;
839844}
......@@ -1732,8 +1737,10 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
17321737 for (decls) |src_decl, decl_i| {
17331738 if (src_decl.cast(ast.Node.FnProto)) |fn_proto| {
17341739 // We will create a Decl for it regardless of analysis status.
1735 const name_tok = fn_proto.name_token orelse
1736 @panic("TODO handle missing function name in the parser");
1740 const name_tok = fn_proto.name_token orelse {
1741 @panic("TODO missing function name");
1742 };
1743
17371744 const name_loc = tree.token_locs[name_tok];
17381745 const name = tree.tokenSliceLoc(name_loc);
17391746 const name_hash = root_scope.fullyQualifiedNameHash(name);
......@@ -1743,10 +1750,16 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
17431750 // Update the AST Node index of the decl, even if its contents are unchanged, it may
17441751 // have been re-ordered.
17451752 decl.src_index = decl_i;
1746 deleted_decls.removeAssertDiscard(decl);
1747 if (!srcHashEql(decl.contents_hash, contents_hash)) {
1748 try self.markOutdatedDecl(decl);
1749 decl.contents_hash = contents_hash;
1753 if (deleted_decls.remove(decl) == null) {
1754 decl.analysis = .sema_failure;
1755 const err_msg = try ErrorMsg.create(self.allocator, tree.token_locs[name_tok].start, "redefinition of '{}'", .{decl.name});
1756 errdefer err_msg.destroy(self.allocator);
1757 try self.failed_decls.putNoClobber(decl, err_msg);
1758 } else {
1759 if (!srcHashEql(decl.contents_hash, contents_hash)) {
1760 try self.markOutdatedDecl(decl);
1761 decl.contents_hash = contents_hash;
1762 }
17501763 }
17511764 } else {
17521765 const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash);
......@@ -1895,6 +1908,10 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void {
18951908 }
18961909
18971910 self.bin_file.deleteExport(exp.link);
1911 if (self.failed_exports.remove(exp)) |entry| {
1912 entry.value.destroy(self.allocator);
1913 }
1914 _ = self.symbol_exports.remove(exp.options.name);
18981915 self.allocator.destroy(exp);
18991916 }
19001917 self.allocator.free(kv.value);
......@@ -2130,6 +2147,7 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const
21302147 .Fn => {},
21312148 else => return self.fail(scope, src, "unable to export type '{}'", .{typed_value.ty}),
21322149 }
2150
21332151 try self.decl_exports.ensureCapacity(self.decl_exports.size + 1);
21342152 try self.export_owners.ensureCapacity(self.export_owners.size + 1);
21352153
......@@ -2165,6 +2183,20 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const
21652183 de_gop.kv.value[de_gop.kv.value.len - 1] = new_export;
21662184 errdefer de_gop.kv.value = self.allocator.shrink(de_gop.kv.value, de_gop.kv.value.len - 1);
21672185
2186 if (self.symbol_exports.get(symbol_name)) |_| {
2187 try self.failed_exports.ensureCapacity(self.failed_exports.size + 1);
2188 self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create(
2189 self.allocator,
2190 src,
2191 "exported symbol collision: {}",
2192 .{symbol_name},
2193 ));
2194 // TODO: add a note
2195 new_export.status = .failed;
2196 return;
2197 }
2198
2199 try self.symbol_exports.putNoClobber(symbol_name, new_export);
21682200 self.bin_file.updateDeclExports(self, exported_decl, de_gop.kv.value) catch |err| switch (err) {
21692201 error.OutOfMemory => return error.OutOfMemory,
21702202 else => {
src-self-hosted/test.zig+246-90
......@@ -21,9 +21,10 @@ const ErrorMsg = struct {
2121};
2222
2323pub const TestContext = struct {
24 zir_cases: std.ArrayList(Case),
24 /// TODO: find a way to treat cases as individual tests (shouldn't show "1 test passed" if there are 200 cases)
25 cases: std.ArrayList(Case),
2526
26 pub const ZIRUpdate = struct {
27 pub const Update = struct {
2728 /// The input to the current update. We simulate an incremental update
2829 /// with the file's contents changed to this value each update.
2930 ///
......@@ -33,35 +34,43 @@ pub const TestContext = struct {
3334 /// effects of the incremental compilation.
3435 src: [:0]const u8,
3536 case: union(enum) {
36 /// A transformation update transforms the input ZIR and tests against
37 /// A transformation update transforms the input and tests against
3738 /// the expected output ZIR.
3839 Transformation: [:0]const u8,
3940 /// An error update attempts to compile bad code, and ensures that it
4041 /// fails to compile, and for the expected reasons.
4142 /// A slice containing the expected errors *in sequential order*.
4243 Error: []const ErrorMsg,
43 /// An execution update compiles and runs the input ZIR, feeding in
44 /// provided input and ensuring that the stdout match what is expected.
44 /// An execution update compiles and runs the input, testing the
45 /// stdout against the expected results
46 /// This is a slice containing the expected message.
4547 Execution: []const u8,
4648 },
4749 };
4850
49 /// A Case consists of a set of *updates*. A update can transform ZIR,
50 /// compile it, ensure that compilation fails, and more. The same Module is
51 /// used for each update, so each update's source is treated as a single file
52 /// being updated by the test harness and incrementally compiled.
51 pub const TestType = enum {
52 Zig,
53 ZIR,
54 };
55
56 /// A Case consists of a set of *updates*. The same Module is used for each
57 /// update, so each update's source is treated as a single file being
58 /// updated by the test harness and incrementally compiled.
5359 pub const Case = struct {
60 /// The name of the test case. This is shown if a test fails, and
61 /// otherwise ignored.
5462 name: []const u8,
55 /// The platform the ZIR targets. For non-native platforms, an emulator
63 /// The platform the test targets. For non-native platforms, an emulator
5664 /// such as QEMU is required for tests to complete.
5765 target: std.zig.CrossTarget,
58 updates: std.ArrayList(ZIRUpdate),
66 /// In order to be able to run e.g. Execution updates, this must be set
67 /// to Executable.
5968 output_mode: std.builtin.OutputMode,
60 /// Either ".zir" or ".zig"
61 extension: [4]u8,
69 updates: std.ArrayList(Update),
70 extension: TestType,
6271
63 /// Adds a subcase in which the module is updated with new ZIR, and the
64 /// resulting ZIR is validated.
72 /// Adds a subcase in which the module is updated with `src`, and the
73 /// resulting ZIR is validated against `result`.
6574 pub fn addTransform(self: *Case, src: [:0]const u8, result: [:0]const u8) void {
6675 self.updates.append(.{
6776 .src = src,
......@@ -69,6 +78,8 @@ pub const TestContext = struct {
6978 }) catch unreachable;
7079 }
7180
81 /// Adds a subcase in which the module is updated with `src`, compiled,
82 /// run, and the output is tested against `result`.
7283 pub fn addCompareOutput(self: *Case, src: [:0]const u8, result: []const u8) void {
7384 self.updates.append(.{
7485 .src = src,
......@@ -76,31 +87,31 @@ pub const TestContext = struct {
7687 }) catch unreachable;
7788 }
7889
79 /// Adds a subcase in which the module is updated with invalid ZIR, and
80 /// ensures that compilation fails for the expected reasons.
81 ///
82 /// Errors must be specified in sequential order.
90 /// Adds a subcase in which the module is updated with `src`, which
91 /// should contain invalid input, and ensures that compilation fails
92 /// for the expected reasons, given in sequential order in `errors` in
93 /// the form `:line:column: error: message`.
8394 pub fn addError(self: *Case, src: [:0]const u8, errors: []const []const u8) void {
8495 var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch unreachable;
8596 for (errors) |e, i| {
8697 if (e[0] != ':') {
87 std.debug.panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n", .{});
98 @panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n");
8899 }
89100 var cur = e[1..];
90101 var line_index = std.mem.indexOf(u8, cur, ":");
91102 if (line_index == null) {
92 std.debug.panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n", .{});
103 @panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n");
93104 }
94105 const line = std.fmt.parseInt(u32, cur[0..line_index.?], 10) catch @panic("Unable to parse line number");
95106 cur = cur[line_index.? + 1 ..];
96107 const column_index = std.mem.indexOf(u8, cur, ":");
97108 if (column_index == null) {
98 std.debug.panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n", .{});
109 @panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n");
99110 }
100111 const column = std.fmt.parseInt(u32, cur[0..column_index.?], 10) catch @panic("Unable to parse column number");
101112 cur = cur[column_index.? + 2 ..];
102113 if (!std.mem.eql(u8, cur[0..7], "error: ")) {
103 std.debug.panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n", .{});
114 @panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n");
104115 }
105116 const msg = cur[7..];
106117
......@@ -116,123 +127,245 @@ pub const TestContext = struct {
116127 }
117128 self.updates.append(.{ .src = src, .case = .{ .Error = array } }) catch unreachable;
118129 }
130
131 /// Adds a subcase in which the module is updated with `src`, and
132 /// asserts that it compiles without issue
133 pub fn compiles(self: *Case, src: [:0]const u8) void {
134 self.addError(src, &[_][]const u8{});
135 }
119136 };
120137
121 pub fn addExeZIR(
138 pub fn addExe(
122139 ctx: *TestContext,
123140 name: []const u8,
124141 target: std.zig.CrossTarget,
142 T: TestType,
125143 ) *Case {
126 const case = Case{
144 ctx.cases.append(Case{
127145 .name = name,
128146 .target = target,
129 .updates = std.ArrayList(ZIRUpdate).init(ctx.zir_cases.allocator),
147 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
130148 .output_mode = .Exe,
131 .extension = ".zir".*,
132 };
133 ctx.zir_cases.append(case) catch unreachable;
134 return &ctx.zir_cases.items[ctx.zir_cases.items.len - 1];
149 .extension = T,
150 }) catch unreachable;
151 return &ctx.cases.items[ctx.cases.items.len - 1];
135152 }
136153
137 pub fn addObjZIR(
154 /// Adds a test case for Zig input, producing an executable
155 pub fn exe(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case {
156 return ctx.addExe(name, target, .Zig);
157 }
158
159 /// Adds a test case for ZIR input, producing an executable
160 pub fn exeZIR(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case {
161 return ctx.addExe(name, target, .ZIR);
162 }
163
164 pub fn addObj(
138165 ctx: *TestContext,
139166 name: []const u8,
140167 target: std.zig.CrossTarget,
168 T: TestType,
141169 ) *Case {
142 const case = Case{
170 ctx.cases.append(Case{
143171 .name = name,
144172 .target = target,
145 .updates = std.ArrayList(ZIRUpdate).init(ctx.zir_cases.allocator),
173 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
146174 .output_mode = .Obj,
147 .extension = ".zir".*,
148 };
149 ctx.zir_cases.append(case) catch unreachable;
150 return &ctx.zir_cases.items[ctx.zir_cases.items.len - 1];
175 .extension = T,
176 }) catch unreachable;
177 return &ctx.cases.items[ctx.cases.items.len - 1];
151178 }
152179
153 pub fn addExe(
180 /// Adds a test case for Zig input, producing an object file
181 pub fn obj(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case {
182 return ctx.addObj(name, target, .Zig);
183 }
184
185 /// Adds a test case for ZIR input, producing an object file
186 pub fn objZIR(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case {
187 return ctx.addObj(name, target, .ZIR);
188 }
189
190 pub fn addCompareOutput(
154191 ctx: *TestContext,
155192 name: []const u8,
156 target: std.zig.CrossTarget,
157 ) *Case {
158 const case = Case{
159 .name = name,
160 .target = target,
161 .updates = std.ArrayList(ZIRUpdate).init(ctx.zir_cases.allocator),
162 .output_mode = .Exe,
163 .extension = ".zig".*,
164 };
165 ctx.zir_cases.append(case) catch unreachable;
166 return &ctx.zir_cases.items[ctx.zir_cases.items.len - 1];
193 T: TestType,
194 src: [:0]const u8,
195 expected_stdout: []const u8,
196 ) void {
197 ctx.addExe(name, .{}, T).addCompareOutput(src, expected_stdout);
167198 }
168199
169 pub fn addObj(
200 /// Adds a test case that compiles the Zig source given in `src`, executes
201 /// it, runs it, and tests the output against `expected_stdout`
202 pub fn compareOutput(
170203 ctx: *TestContext,
171204 name: []const u8,
172 target: std.zig.CrossTarget,
173 ) *Case {
174 const case = Case{
175 .name = name,
176 .target = target,
177 .updates = std.ArrayList(ZIRUpdate).init(ctx.zir_cases.allocator),
178 .output_mode = .Obj,
179 .extension = ".zig".*,
180 };
181 ctx.zir_cases.append(case) catch unreachable;
182 return &ctx.zir_cases.items[ctx.zir_cases.items.len - 1];
205 src: [:0]const u8,
206 expected_stdout: []const u8,
207 ) void {
208 return ctx.addCompareOutput(name, .Zig, src, expected_stdout);
183209 }
184210
185 pub fn addZIRCompareOutput(
211 /// Adds a test case that compiles the ZIR source given in `src`, executes
212 /// it, runs it, and tests the output against `expected_stdout`
213 pub fn compareOutputZIR(
186214 ctx: *TestContext,
187215 name: []const u8,
188216 src: [:0]const u8,
189217 expected_stdout: []const u8,
190218 ) void {
191 var c = ctx.addExeZIR(name, .{});
192 c.addCompareOutput(src, expected_stdout);
219 ctx.addCompareOutput(name, .ZIR, src, expected_stdout);
193220 }
194221
195 pub fn addCompareOutput(
222 pub fn addTransform(
196223 ctx: *TestContext,
197224 name: []const u8,
225 target: std.zig.CrossTarget,
226 T: TestType,
198227 src: [:0]const u8,
199 expected_stdout: []const u8,
228 result: [:0]const u8,
229 ) void {
230 ctx.addObj(name, target, T).addTransform(src, result);
231 }
232
233 /// Adds a test case that compiles the Zig given in `src` to ZIR and tests
234 /// the ZIR against `result`
235 pub fn transform(
236 ctx: *TestContext,
237 name: []const u8,
238 target: std.zig.CrossTarget,
239 src: [:0]const u8,
240 result: [:0]const u8,
200241 ) void {
201 var c = ctx.addExe(name, .{});
202 c.addCompareOutput(src, expected_stdout);
242 ctx.addTransform(name, target, .Zig, src, result);
203243 }
204244
205 pub fn addZIRTransform(
245 /// Adds a test case that cleans up the ZIR source given in `src`, and
246 /// tests the resulting ZIR against `result`
247 pub fn transformZIR(
206248 ctx: *TestContext,
207249 name: []const u8,
208250 target: std.zig.CrossTarget,
209251 src: [:0]const u8,
210252 result: [:0]const u8,
211253 ) void {
212 var c = ctx.addObjZIR(name, target);
213 c.addTransform(src, result);
254 ctx.addTransform(name, target, .ZIR, src, result);
255 }
256
257 pub fn addError(
258 ctx: *TestContext,
259 name: []const u8,
260 target: std.zig.CrossTarget,
261 T: TestType,
262 src: [:0]const u8,
263 expected_errors: []const []const u8,
264 ) void {
265 ctx.addObj(name, target, T).addError(src, expected_errors);
266 }
267
268 /// Adds a test case that ensures that the Zig given in `src` fails to
269 /// compile for the expected reasons, given in sequential order in
270 /// `expected_errors` in the form `:line:column: error: message`.
271 pub fn compileError(
272 ctx: *TestContext,
273 name: []const u8,
274 target: std.zig.CrossTarget,
275 src: [:0]const u8,
276 expected_errors: []const []const u8,
277 ) void {
278 ctx.addError(name, target, .Zig, src, expected_errors);
279 }
280
281 /// Adds a test case that ensures that the ZIR given in `src` fails to
282 /// compile for the expected reasons, given in sequential order in
283 /// `expected_errors` in the form `:line:column: error: message`.
284 pub fn compileErrorZIR(
285 ctx: *TestContext,
286 name: []const u8,
287 target: std.zig.CrossTarget,
288 src: [:0]const u8,
289 expected_errors: []const []const u8,
290 ) void {
291 ctx.addError(name, target, .ZIR, src, expected_errors);
292 }
293
294 pub fn addCompiles(
295 ctx: *TestContext,
296 name: []const u8,
297 target: std.zig.CrossTarget,
298 T: TestType,
299 src: [:0]const u8,
300 ) void {
301 ctx.addObj(name, target, T).compiles(src);
302 }
303
304 /// Adds a test case that asserts that the Zig given in `src` compiles
305 /// without any errors.
306 pub fn compiles(
307 ctx: *TestContext,
308 name: []const u8,
309 target: std.zig.CrossTarget,
310 src: [:0]const u8,
311 ) void {
312 ctx.addCompiles(name, target, .Zig, src);
313 }
314
315 /// Adds a test case that asserts that the ZIR given in `src` compiles
316 /// without any errors.
317 pub fn compilesZIR(
318 ctx: *TestContext,
319 name: []const u8,
320 target: std.zig.CrossTarget,
321 src: [:0]const u8,
322 ) void {
323 ctx.addCompiles(name, target, .ZIR, src);
214324 }
215325
216 pub fn addZIRError(
326 /// Adds a test case that first ensures that the Zig given in `src` fails
327 /// to compile for the reasons given in sequential order in
328 /// `expected_errors` in the form `:line:column: error: message`, then
329 /// asserts that fixing the source (updating with `fixed_src`) isn't broken
330 /// by incremental compilation.
331 pub fn incrementalFailure(
217332 ctx: *TestContext,
218333 name: []const u8,
219334 target: std.zig.CrossTarget,
220335 src: [:0]const u8,
221336 expected_errors: []const []const u8,
337 fixed_src: [:0]const u8,
222338 ) void {
223 var c = ctx.addObjZIR(name, target);
224 c.addError(src, expected_errors);
339 var case = ctx.addObj(name, target, .Zig);
340 case.addError(src, expected_errors);
341 case.compiles(fixed_src);
342 }
343
344 /// Adds a test case that first ensures that the ZIR given in `src` fails
345 /// to compile for the reasons given in sequential order in
346 /// `expected_errors` in the form `:line:column: error: message`, then
347 /// asserts that fixing the source (updating with `fixed_src`) isn't broken
348 /// by incremental compilation.
349 pub fn incrementalFailureZIR(
350 ctx: *TestContext,
351 name: []const u8,
352 target: std.zig.CrossTarget,
353 src: [:0]const u8,
354 expected_errors: []const []const u8,
355 fixed_src: [:0]const u8,
356 ) void {
357 var case = ctx.addObj(name, target, .ZIR);
358 case.addError(src, expected_errors);
359 case.compiles(fixed_src);
225360 }
226361
227362 fn init() TestContext {
228363 const allocator = std.heap.page_allocator;
229 return .{
230 .zir_cases = std.ArrayList(Case).init(allocator),
231 };
364 return .{ .cases = std.ArrayList(Case).init(allocator) };
232365 }
233366
234367 fn deinit(self: *TestContext) void {
235 for (self.zir_cases.items) |c| {
368 for (self.cases.items) |c| {
236369 for (c.updates.items) |u| {
237370 if (u.case == .Error) {
238371 c.updates.allocator.free(u.case.Error);
......@@ -240,26 +373,28 @@ pub const TestContext = struct {
240373 }
241374 c.updates.deinit();
242375 }
243 self.zir_cases.deinit();
376 self.cases.deinit();
244377 self.* = undefined;
245378 }
246379
247380 fn run(self: *TestContext) !void {
248381 var progress = std.Progress{};
249 const root_node = try progress.start("zir", self.zir_cases.items.len);
382 const root_node = try progress.start("tests", self.cases.items.len);
250383 defer root_node.end();
251384
252385 const native_info = try std.zig.system.NativeTargetInfo.detect(std.heap.page_allocator, .{});
253386
254 for (self.zir_cases.items) |case| {
387 for (self.cases.items) |case| {
255388 std.testing.base_allocator_instance.reset();
256389
257390 var prg_node = root_node.start(case.name, case.updates.items.len);
258391 prg_node.activate();
259392 defer prg_node.end();
260393
261 // So that we can see which test case failed when the leak checker goes off.
262 progress.refresh();
394 // So that we can see which test case failed when the leak checker goes off,
395 // or there's an internal error
396 progress.initial_delay_ns = 0;
397 progress.refresh_rate_ns = 0;
263398
264399 const info = try std.zig.system.NativeTargetInfo.detect(std.testing.allocator, case.target);
265400 try self.runOneCase(std.testing.allocator, &prg_node, case, info.target);
......@@ -267,17 +402,15 @@ pub const TestContext = struct {
267402 }
268403 }
269404
270 fn runOneCase(self: *TestContext, allocator: *Allocator, prg_node: *std.Progress.Node, case: Case, target: std.Target) !void {
405 fn runOneCase(self: *TestContext, allocator: *Allocator, root_node: *std.Progress.Node, case: Case, target: std.Target) !void {
271406 var tmp = std.testing.tmpDir(.{});
272407 defer tmp.cleanup();
273408
274 const root_name = "test_case";
275 const tmp_src_path = try std.fmt.allocPrint(allocator, "{}{}", .{ root_name, case.extension });
276 defer allocator.free(tmp_src_path);
409 const tmp_src_path = if (case.extension == .Zig) "test_case.zig" else if (case.extension == .ZIR) "test_case.zir" else unreachable;
277410 const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);
278411 defer root_pkg.destroy();
279412
280 const bin_name = try std.zig.binNameAlloc(allocator, root_name, target, case.output_mode, null);
413 const bin_name = try std.zig.binNameAlloc(allocator, "test_case", target, case.output_mode, null);
281414 defer allocator.free(bin_name);
282415
283416 var module = try Module.init(allocator, .{
......@@ -299,7 +432,7 @@ pub const TestContext = struct {
299432 defer module.deinit();
300433
301434 for (case.updates.items) |update, update_index| {
302 var update_node = prg_node.start("update", 4);
435 var update_node = root_node.start("update", 3);
303436 update_node.activate();
304437 defer update_node.end();
305438
......@@ -316,6 +449,7 @@ pub const TestContext = struct {
316449
317450 switch (update.case) {
318451 .Transformation => |expected_output| {
452 update_node.estimated_total_items = 5;
319453 var emit_node = update_node.start("emit", null);
320454 emit_node.activate();
321455 var new_zir_module = try zir.emit(allocator, module);
......@@ -329,9 +463,26 @@ pub const TestContext = struct {
329463 try new_zir_module.writeToStream(allocator, out_zir.outStream());
330464 write_node.end();
331465
332 std.testing.expectEqualSlices(u8, expected_output, out_zir.items);
466 var test_node = update_node.start("assert", null);
467 test_node.activate();
468 defer test_node.end();
469 if (expected_output.len != out_zir.items.len) {
470 std.debug.warn("{}\nTransformed ZIR length differs:\n================\nExpected:\n================\n{}\n================\nFound: {}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
471 std.process.exit(1);
472 }
473 for (expected_output) |e, i| {
474 if (out_zir.items[i] != e) {
475 if (expected_output.len != out_zir.items.len) {
476 std.debug.warn("{}\nTransformed ZIR differs:\n================\nExpected:\n================\n{}\n================\nFound: {}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
477 std.process.exit(1);
478 }
479 }
480 }
333481 },
334482 .Error => |e| {
483 var test_node = update_node.start("assert", null);
484 test_node.activate();
485 defer test_node.end();
335486 var handled_errors = try allocator.alloc(bool, e.len);
336487 defer allocator.free(handled_errors);
337488 for (handled_errors) |*h| {
......@@ -360,6 +511,7 @@ pub const TestContext = struct {
360511 }
361512 },
362513 .Execution => |expected_stdout| {
514 update_node.estimated_total_items = 4;
363515 var exec_result = x: {
364516 var exec_node = update_node.start("execute", null);
365517 exec_node.activate();
......@@ -376,6 +528,10 @@ pub const TestContext = struct {
376528 .cwd_dir = tmp.dir,
377529 });
378530 };
531 var test_node = update_node.start("test", null);
532 test_node.activate();
533 defer test_node.end();
534
379535 defer allocator.free(exec_result.stdout);
380536 defer allocator.free(exec_result.stderr);
381537 switch (exec_result.term) {
test/stage2/compare_output.zig+1-1
......@@ -17,7 +17,7 @@ pub fn addCases(ctx: *TestContext) !void {
1717 }
1818
1919 {
20 var case = ctx.addExe("hello world with updates", linux_x64);
20 var case = ctx.exe("hello world with updates", linux_x64);
2121 // Regular old hello world
2222 case.addCompareOutput(
2323 \\export fn _start() noreturn {
test/stage2/compile_errors.zig+53-18
......@@ -9,7 +9,7 @@ const linux_x64 = std.zig.CrossTarget{
99};
1010
1111pub fn addCases(ctx: *TestContext) !void {
12 ctx.addZIRError("call undefined local", linux_x64,
12 ctx.compileErrorZIR("call undefined local", linux_x64,
1313 \\@noreturn = primitive(noreturn)
1414 \\
1515 \\@start_fnty = fntype([], @noreturn, cc=Naked)
......@@ -19,7 +19,7 @@ pub fn addCases(ctx: *TestContext) !void {
1919 // TODO: address inconsistency in this message and the one in the next test
2020 , &[_][]const u8{":5:13: error: unrecognized identifier: %test"});
2121
22 ctx.addZIRError("call with non-existent target", linux_x64,
22 ctx.compileErrorZIR("call with non-existent target", linux_x64,
2323 \\@noreturn = primitive(noreturn)
2424 \\
2525 \\@start_fnty = fntype([], @noreturn, cc=Naked)
......@@ -31,7 +31,7 @@ pub fn addCases(ctx: *TestContext) !void {
3131 , &[_][]const u8{":5:13: error: decl 'notafunc' not found"});
3232
3333 // TODO: this error should occur at the call site, not the fntype decl
34 ctx.addZIRError("call naked function", linux_x64,
34 ctx.compileErrorZIR("call naked function", linux_x64,
3535 \\@noreturn = primitive(noreturn)
3636 \\
3737 \\@start_fnty = fntype([], @noreturn, cc=Naked)
......@@ -43,56 +43,91 @@ pub fn addCases(ctx: *TestContext) !void {
4343 \\@1 = export(@0, "start")
4444 , &[_][]const u8{":4:9: error: unable to call function with naked calling convention"});
4545
46 // TODO: re-enable these tests.
47 // https://github.com/ziglang/zig/issues/1364
48 // TODO: add Zig AST -> ZIR testing pipeline
46 ctx.incrementalFailureZIR("exported symbol collision", linux_x64,
47 \\@noreturn = primitive(noreturn)
48 \\
49 \\@start_fnty = fntype([], @noreturn)
50 \\@start = fn(@start_fnty, {})
51 \\
52 \\@0 = str("_start")
53 \\@1 = export(@0, "start")
54 \\@2 = export(@0, "start")
55 , &[_][]const u8{":8:13: error: exported symbol collision: _start"},
56 \\@noreturn = primitive(noreturn)
57 \\
58 \\@start_fnty = fntype([], @noreturn)
59 \\@start = fn(@start_fnty, {})
60 \\
61 \\@0 = str("_start")
62 \\@1 = export(@0, "start")
63 );
64
65 ctx.compileError("function redefinition", linux_x64,
66 \\fn entry() void {}
67 \\fn entry() void {}
68 , &[_][]const u8{":2:4: error: redefinition of 'entry'"});
69
70 //ctx.incrementalFailure("function redefinition", linux_x64,
71 // \\fn entry() void {}
72 // \\fn entry() void {}
73 //, &[_][]const u8{":2:4: error: redefinition of 'entry'"},
74 // \\fn entry() void {}
75 //);
4976
50 //try ctx.testCompileError(
77 //// TODO: need to make sure this works with other variants of export.
78 //ctx.incrementalFailure("exported symbol collision", linux_x64,
5179 // \\export fn entry() void {}
5280 // \\export fn entry() void {}
53 //, "1.zig", 2, 8, "exported symbol collision: 'entry'");
81 //, &[_][]const u8{":2:11: error: redefinition of 'entry'"},
82 // \\export fn entry() void {}
83 //);
84
85 // ctx.incrementalFailure("missing function name", linux_x64,
86 // \\fn() void {}
87 // , &[_][]const u8{":1:3: error: missing function name"},
88 // \\fn a() void {}
89 // );
5490
55 //try ctx.testCompileError(
56 // \\fn() void {}
57 //, "1.zig", 1, 1, "missing function name");
91 // TODO: re-enable these tests.
92 // https://github.com/ziglang/zig/issues/1364
5893
59 //try ctx.testCompileError(
94 //ctx.testCompileError(
6095 // \\comptime {
6196 // \\ return;
6297 // \\}
6398 //, "1.zig", 2, 5, "return expression outside function definition");
6499
65 //try ctx.testCompileError(
100 //ctx.testCompileError(
66101 // \\export fn entry() void {
67102 // \\ defer return;
68103 // \\}
69104 //, "1.zig", 2, 11, "cannot return from defer expression");
70105
71 //try ctx.testCompileError(
106 //ctx.testCompileError(
72107 // \\export fn entry() c_int {
73108 // \\ return 36893488147419103232;
74109 // \\}
75110 //, "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'");
76111
77 //try ctx.testCompileError(
112 //ctx.testCompileError(
78113 // \\comptime {
79114 // \\ var a: *align(4) align(4) i32 = 0;
80115 // \\}
81116 //, "1.zig", 2, 22, "Extra align qualifier");
82117
83 //try ctx.testCompileError(
118 //ctx.testCompileError(
84119 // \\comptime {
85120 // \\ var b: *const const i32 = 0;
86121 // \\}
87122 //, "1.zig", 2, 19, "Extra align qualifier");
88123
89 //try ctx.testCompileError(
124 //ctx.testCompileError(
90125 // \\comptime {
91126 // \\ var c: *volatile volatile i32 = 0;
92127 // \\}
93128 //, "1.zig", 2, 22, "Extra align qualifier");
94129
95 //try ctx.testCompileError(
130 //ctx.testCompileError(
96131 // \\comptime {
97132 // \\ var d: *allowzero allowzero i32 = 0;
98133 // \\}
test/stage2/test.zig+1-1
......@@ -3,5 +3,5 @@ const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
33pub fn addCases(ctx: *TestContext) !void {
44 try @import("compile_errors.zig").addCases(ctx);
55 try @import("compare_output.zig").addCases(ctx);
6 @import("zir.zig").addCases(ctx);
6 try @import("zir.zig").addCases(ctx);
77}
test/stage2/zir.zig+6-6
......@@ -8,8 +8,8 @@ const linux_x64 = std.zig.CrossTarget{
88 .os_tag = .linux,
99};
1010
11pub fn addCases(ctx: *TestContext) void {
12 ctx.addZIRTransform("referencing decls which appear later in the file", linux_x64,
11pub fn addCases(ctx: *TestContext) !void {
12 ctx.transformZIR("referencing decls which appear later in the file", linux_x64,
1313 \\@void = primitive(void)
1414 \\@fnty = fntype([], @void, cc=C)
1515 \\
......@@ -32,7 +32,7 @@ pub fn addCases(ctx: *TestContext) void {
3232 \\})
3333 \\
3434 );
35 ctx.addZIRTransform("elemptr, add, cmp, condbr, return, breakpoint", linux_x64,
35 ctx.transformZIR("elemptr, add, cmp, condbr, return, breakpoint", linux_x64,
3636 \\@void = primitive(void)
3737 \\@usize = primitive(usize)
3838 \\@fnty = fntype([], @void, cc=C)
......@@ -86,7 +86,7 @@ pub fn addCases(ctx: *TestContext) void {
8686 );
8787
8888 {
89 var case = ctx.addObjZIR("reference cycle with compile error in the cycle", linux_x64);
89 var case = ctx.objZIR("reference cycle with compile error in the cycle", linux_x64);
9090 case.addTransform(
9191 \\@void = primitive(void)
9292 \\@fnty = fntype([], @void, cc=C)
......@@ -207,7 +207,7 @@ pub fn addCases(ctx: *TestContext) void {
207207 return;
208208 }
209209
210 ctx.addZIRCompareOutput("hello world ZIR",
210 ctx.compareOutputZIR("hello world ZIR",
211211 \\@noreturn = primitive(noreturn)
212212 \\@void = primitive(void)
213213 \\@usize = primitive(usize)
......@@ -265,7 +265,7 @@ pub fn addCases(ctx: *TestContext) void {
265265 \\
266266 );
267267
268 ctx.addZIRCompareOutput("function call with no args no return value",
268 ctx.compareOutputZIR("function call with no args no return value",
269269 \\@noreturn = primitive(noreturn)
270270 \\@void = primitive(void)
271271 \\@usize = primitive(usize)