authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-09-14 02:10:39-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-09-26 19:08:50-04:00
log207c883bfbdc3099997087869234d6e109bdb0cc
tree1634f621d3ab09fbf3a76d7b6828379789b20b21
parentf63e8e802f5dc2aebe888c0051f685cab6b2571e

runner: implement skip commands


3 files changed, 153 insertions(+), 79 deletions(-)

test/src/runner.zig+94-46
......@@ -8,8 +8,24 @@ const Args = struct {
88 zig_file: ?std.Io.File,
99 lib_dir: ?std.Io.Dir,
1010 src_dir: ?std.Io.Dir,
11 targets: std.ArrayList(u32),
12 target_bytes: std.ArrayList(u8),
13
14 fn deinit(args: *Args, gpa: std.mem.Allocator) void {
15 args.targets.deinit(gpa);
16 args.target_bytes.deinit(gpa);
17 }
18
19 fn addTarget(args: *Args, gpa: std.mem.Allocator, target: []const u8) std.mem.Allocator.Error!void {
20 try args.target_bytes.ensureUnusedCapacity(gpa, target.len + 1);
21 try args.targets.append(gpa, @intCast(args.target_bytes.items.len));
22 args.target_bytes.appendSliceAssumeCapacity(target);
23 args.target_bytes.appendAssumeCapacity(0);
24 }
1125};
1226pub fn main(init: std.process.Init) !void {
27 const arena = init.arena.allocator();
28
1329 var stdin_buffer: [512]u8 = undefined;
1430 var stdout_buffer: [512]u8 = undefined;
1531 var stderr_buffer: [512]u8 = undefined;
......@@ -20,14 +36,19 @@ pub fn main(init: std.process.Init) !void {
2036 .in = &stdin.interface,
2137 .out = &stdout.interface,
2238 };
23 try server.serveStringMessage(.zig_version, @import("builtin").zig_version_string);
39
2440 var args: Args = .{
2541 .@"test" = null,
2642 .manifest_file = null,
2743 .zig_file = null,
2844 .lib_dir = null,
2945 .src_dir = null,
46 .targets = .empty,
47 .target_bytes = .empty,
3048 };
49 defer args.deinit(init.gpa);
50
51 try server.serveStringMessage(.zig_version, @import("builtin").zig_version_string);
3152 while (true) {
3253 const hdr = try server.receiveMessage();
3354 switch (hdr.tag) {
......@@ -35,13 +56,11 @@ pub fn main(init: std.process.Init) !void {
3556 std.debug.print("unsupported message: {t}\n", .{hdr.tag});
3657 std.process.exit(1);
3758 },
38 .exit => {
39 std.process.exit(0);
40 },
59 .exit => std.process.exit(0),
4160 .args => {
42 const args_body = try init.arena.allocator().alloc(u8, hdr.bytes_len);
61 const args_body = try arena.alloc(u8, hdr.bytes_len);
4362 try server.in.readSliceAll(args_body);
44 var state: enum { positional, zig, lib, src } = .positional;
63 var state: enum { positional, zig, lib, src, target } = .positional;
4564 var input_dir: std.zig.Server.Message.InputDir = .cwd;
4665 var args_body_offset: usize = 0;
4766 while (args_body.len - args_body_offset > 0) {
......@@ -53,7 +72,18 @@ pub fn main(init: std.process.Init) !void {
5372 const end = std.mem.findScalarPos(u8, args_body, args_body_offset, 0).?;
5473 const string = args_body[args_body_offset..end];
5574 args_body_offset = end + 1;
56 _ = string;
75 switch (state) {
76 .positional => if (std.mem.eql(u8, string, "--target")) {
77 state = .target;
78 continue;
79 } else if (std.mem.cutPrefix(u8, string, "--target=")) |target| {
80 try args.addTarget(init.gpa, target);
81 },
82 .zig => unreachable,
83 .lib => unreachable,
84 .src => unreachable,
85 .target => try args.addTarget(init.gpa, string),
86 }
5787 },
5888 .prefix => {
5989 const end = std.mem.findScalarPos(u8, args_body, args_body_offset, 0).?;
......@@ -109,6 +139,7 @@ pub fn main(init: std.process.Init) !void {
109139 std.debug.assert(args.src_dir == null);
110140 args.src_dir = dir;
111141 },
142 .target => unreachable,
112143 }
113144 },
114145 .input_file, .input_file_content, .output_file => {
......@@ -129,22 +160,30 @@ pub fn main(init: std.process.Init) !void {
129160 std.debug.assert(args.zig_file == null);
130161 args.zig_file = file;
131162 },
132 .lib, .src => unreachable,
163 .lib => unreachable,
164 .src => unreachable,
165 .target => unreachable,
133166 }
134167 },
135168 }
136169 state = .positional;
137170 }
138171 },
139 .query_test_metadata => try server.serveTestMetadata(.{
140 .names = &.{0},
141 .expected_panic_msgs = &.{0},
142 .string_bytes = "runner\x00",
143 }),
172 .query_test_metadata => {
173 const expected_panic_msgs = try arena.alloc(u32, args.targets.items.len);
174 @memset(expected_panic_msgs, 0);
175 try server.serveTestMetadata(.{
176 .names = args.targets.items,
177 .expected_panic_msgs = expected_panic_msgs,
178 .string_bytes = args.target_bytes.items,
179 });
180 },
144181 .run_test => {
145182 const test_index = try server.receiveBody_u32();
183 const target =
184 std.mem.sliceTo(args.target_bytes.items[args.targets.items[test_index]..], 0);
146185 try server.serveBodylessMessage(.test_started);
147 const test_result = runTest(init.io, &stderr.interface, &server, &args);
186 const test_result = runTest(init.io, &stderr.interface, &server, &args, target);
148187 try stderr.flush();
149188 try server.serveTestResults(.{
150189 .index = test_index,
......@@ -170,11 +209,13 @@ fn runTest(
170209 stderr: *std.Io.Writer,
171210 server: *std.zig.Server,
172211 args: *const Args,
173) anyerror!void {
212 target: []const u8,
213) !void {
174214 const manifest_file = args.manifest_file orelse return error.MissingManifestArg;
175215 const src_dir = args.src_dir orelse return error.MissingSrcDir;
176216 var manifest_buffer: [512]u8 = undefined;
177217 var manifest_fr = manifest_file.reader(io, &manifest_buffer);
218 var allow_skip = true;
178219 var skip_delimiter = false;
179220 var update_mtime = std.Io.Clock.real.now(io);
180221 var update_num: usize = 0;
......@@ -188,7 +229,7 @@ fn runTest(
188229 continue;
189230 }
190231 const cmd = std.meta.stringToEnum(
191 enum { obj, exe, lib, write, delete, update, check },
232 enum { skip, obj, exe, lib, write, delete, update, check },
192233 cmd_str,
193234 ) orelse return error.UnknownCommand;
194235 var maybe_arg = line_it.next();
......@@ -198,38 +239,40 @@ fn runTest(
198239 dr: DelimitedReader,
199240 fr: std.Io.File.Reader,
200241 } = undefined;
201 const contents_r: *std.Io.Reader = contents_r: {
202 switch (cmd) {
203 else => {},
204 .delete => break :contents_r .ending,
205 }
206
207 const contents_path = contents_path: {
208 const line_rest = line_it.rest();
209 if (line_rest.len > 0) break :contents_path line_rest;
210 const contents_path = maybe_arg orelse break :contents_r .ending;
211 maybe_arg = null;
212 break :contents_path contents_path;
213 };
214 if (std.mem.eql(u8, contents_path, "{")) {
215 contents_impl = .{ .dr = .init(&manifest_fr.interface, "#}", &.{}) };
216 skip_delimiter = true;
217 break :contents_r &contents_impl.dr.interface;
218 }
242 const contents_r: *std.Io.Reader = contents_r: switch (cmd) {
243 .skip, .delete => {
244 if (line_it.next()) |_| return error.UnexpectedArg;
245 break :contents_r .ending;
246 },
247 else => {
248 const contents_path = contents_path: {
249 const line_rest = line_it.rest();
250 if (line_rest.len > 0) break :contents_path line_rest;
251 const contents_path = maybe_arg orelse break :contents_r .ending;
252 maybe_arg = null;
253 break :contents_path contents_path;
254 };
255 if (std.mem.eql(u8, contents_path, "{")) {
256 contents_impl = .{ .dr = .init(&manifest_fr.interface, "#}", &.{}) };
257 skip_delimiter = true;
258 break :contents_r &contents_impl.dr.interface;
259 }
219260
220 const @"test" = args.@"test" orelse return error.ContentsFileInFileTest;
221 try server.serveMessageHeader(.{
222 .tag = .discovered_inputs,
223 .bytes_len = @intCast(@sizeOf(std.zig.Server.Message.InputDir) + contents_path.len + 1),
224 });
225 try server.out.writeInt(u32, @backingInt(@"test".input_dir), .little);
226 try server.out.writeAll(contents_path);
227 try server.out.writeByte(0);
228 try server.out.flush();
261 const @"test" = args.@"test" orelse return error.ContentsFileInFileTest;
262 try server.serveMessageHeader(.{
263 .tag = .discovered_inputs,
264 .bytes_len = @intCast(@sizeOf(std.zig.Server.Message.InputDir) +
265 contents_path.len + 1),
266 });
267 try server.out.writeInt(u32, @backingInt(@"test".input_dir), .little);
268 try server.out.writeAll(contents_path);
269 try server.out.writeByte(0);
270 try server.out.flush();
229271
230 contents_file = try @"test".dir.openFile(io, contents_path, .{});
231 contents_impl = .{ .fr = contents_file.?.reader(io, &.{}) };
232 break :contents_r &contents_impl.fr.interface;
272 contents_file = try @"test".dir.openFile(io, contents_path, .{});
273 contents_impl = .{ .fr = contents_file.?.reader(io, &.{}) };
274 break :contents_r &contents_impl.fr.interface;
275 },
233276 };
234277 switch (cmd) {
235278 else => {
......@@ -237,6 +280,10 @@ fn runTest(
237280 _ = try contents_r.streamRemaining(stderr);
238281 try stderr.writeAll("\n}\n");
239282 },
283 .skip => if (!allow_skip)
284 return error.NonInitialSkipCommand // #skip must appear first
285 else if (std.mem.eql(u8, target, maybe_arg orelse return error.MissingArg))
286 return error.SkipZigTest,
240287 .write => {
241288 const file = try src_dir.createFile(io, maybe_arg orelse return error.MissingArg, .{});
242289 defer file.close(io);
......@@ -262,6 +309,7 @@ fn runTest(
262309 },
263310 }
264311 std.debug.assert(try contents_r.discardRemaining() == 0);
312 if (cmd != .skip) allow_skip = false;
265313 } else |err| switch (err) {
266314 else => |e| return e,
267315 error.EndOfStream => if (skip_delimiter) return error.MissingDelimiter,
test/tests.zig+3-3
......@@ -2214,9 +2214,9 @@ const IncrementalTarget = struct {
22142214
22152215/// These are passed to `incr-check` as `<target>-<backend>` strings.
22162216///
2217/// If only one specific test is failing on a target, instead of entirely disabling the target here,
2218/// you can skip the target for that specific test only by adding a line like this to the manifest:
2219/// #skip_target=x86_64-linux-selfhosted
2217/// If only one specific test is failing on a target, instead of entirely disabling the target here, you
2218/// can skip the target for that specific test only by adding a line like this to top of the manifest:
2219/// #skip x86_64-linux-selfhosted
22202220const incremental_targets = &[_]IncrementalTarget{
22212221 // Avoid adding more CBE or LLVM targets without good reason: they're a lot slower than others
22222222 // to run due to the output (C source code or LLVM IR) being built non-incrementally (by Clang
tools/runner-check.zig+56-30
......@@ -5,6 +5,8 @@ pub fn main(init: std.process.Init) !void {
55 var test_path_arg: ?[]const u8 = null;
66 var zig_path_arg: ?[]const u8 = null;
77 var lib_path_arg: ?[]const u8 = null;
8 var target_args: std.ArrayList([]const u8) = .empty;
9 defer target_args.deinit(init.gpa);
810 var keep_src = false;
911
1012 var arg_it = try init.minimal.args.iterateAllocator(arena);
......@@ -18,6 +20,13 @@ pub fn main(init: std.process.Init) !void {
1820 lib_path_arg = arg_it.next() orelse fail("missing arg after '{s}'", .{arg});
1921 } else if (std.mem.cutPrefix(u8, arg, "--lib=")) |lib_path| {
2022 lib_path_arg = lib_path;
23 } else if (std.mem.eql(u8, arg, "--target")) {
24 try target_args.append(
25 init.gpa,
26 try arena.dupe(u8, arg_it.next() orelse fail("missing arg after '{s}'", .{arg})),
27 );
28 } else if (std.mem.cutPrefix(u8, arg, "--target=")) |target| {
29 try target_args.append(init.gpa, try arena.dupe(u8, target));
2130 } else if (std.mem.eql(u8, arg, "--keep-src")) {
2231 keep_src = true;
2332 } else {
......@@ -72,45 +81,47 @@ pub fn main(init: std.process.Init) !void {
7281 .out = &stdin_writer.interface,
7382 };
7483 {
84 var args: std.ArrayList(u8) = .empty;
85 defer args.deinit(init.gpa);
7586 const Arg = std.zig.Client.Message.Arg;
76 var args: std.ArrayList(u8) = .initBuffer(try arena.alloc(u8, 0 +
77 @sizeOf(Arg) + @sizeOf(std.Io.File.Handle) +
78 @sizeOf(Arg) + "--zig=\x00".len +
79 @sizeOf(Arg) + @sizeOf(std.Io.File.Handle) +
80 @sizeOf(Arg) + "--lib=\x00".len +
81 @sizeOf(Arg) + @sizeOf(std.Io.Dir.Handle) +
82 @sizeOf(Arg) + "--src=\x00".len +
83 @sizeOf(Arg) + @sizeOf(std.Io.Dir.Handle)));
84
85 args.appendAssumeCapacity(@backingInt(@as(Arg, switch ((try test_file.stat(init.io)).kind) {
87
88 try args.append(init.gpa, @backingInt(@as(Arg, switch ((try test_file.stat(init.io)).kind) {
8689 else => unreachable,
8790 .file => .input_file,
8891 .directory => .input_dir,
8992 })));
90 args.appendSliceAssumeCapacity(@ptrCast(&test_file.handle));
93 try args.appendSlice(init.gpa, @ptrCast(&test_file.handle));
94
95 try args.append(init.gpa, @backingInt(Arg.prefix));
96 try args.appendSlice(init.gpa, "--zig=");
97 try args.append(init.gpa, 0);
9198
92 args.appendAssumeCapacity(@backingInt(Arg.prefix));
93 args.appendSliceAssumeCapacity("--zig=");
94 args.appendAssumeCapacity(0);
99 try args.append(init.gpa, @backingInt(Arg.input_file));
100 try args.appendSlice(init.gpa, @ptrCast(&zig_file.handle));
95101
96 args.appendAssumeCapacity(@backingInt(Arg.input_file));
97 args.appendSliceAssumeCapacity(@ptrCast(&zig_file.handle));
102 try args.append(init.gpa, @backingInt(Arg.prefix));
103 try args.appendSlice(init.gpa, "--lib=");
104 try args.append(init.gpa, 0);
98105
99 args.appendAssumeCapacity(@backingInt(Arg.prefix));
100 args.appendSliceAssumeCapacity("--lib=");
101 args.appendAssumeCapacity(0);
106 try args.append(init.gpa, @backingInt(Arg.input_dir));
107 try args.appendSlice(init.gpa, @ptrCast(&lib_dir.handle));
102108
103 args.appendAssumeCapacity(@backingInt(Arg.input_dir));
104 args.appendSliceAssumeCapacity(@ptrCast(&lib_dir.handle));
109 try args.append(init.gpa, @backingInt(Arg.prefix));
110 try args.appendSlice(init.gpa, "--src=");
111 try args.append(init.gpa, 0);
105112
106 args.appendAssumeCapacity(@backingInt(Arg.prefix));
107 args.appendSliceAssumeCapacity("--src=");
108 args.appendAssumeCapacity(0);
113 try args.append(init.gpa, @backingInt(Arg.output_dir));
114 try args.appendSlice(init.gpa, @ptrCast(&src_dir.handle));
109115
110 args.appendAssumeCapacity(@backingInt(Arg.output_dir));
111 args.appendSliceAssumeCapacity(@ptrCast(&src_dir.handle));
116 for (target_args.items) |target_arg| {
117 try args.append(init.gpa, @backingInt(Arg.string));
118 try args.appendSlice(init.gpa, "--target");
119 try args.append(init.gpa, 0);
112120
113 std.debug.assert(args.unusedCapacitySlice().len == 0);
121 try args.append(init.gpa, @backingInt(Arg.string));
122 try args.appendSlice(init.gpa, target_arg);
123 try args.append(init.gpa, 0);
124 }
114125
115126 try client.serveMessageHeader(.{
116127 .tag = .args,
......@@ -118,6 +129,16 @@ pub fn main(init: std.process.Init) !void {
118129 });
119130 try client.out.writeAll(args.items);
120131
132 var metadata: struct {
133 index: u32,
134 len: u32,
135
136 fn next(m: *@This()) ?u32 {
137 if (m.len - m.index == 0) return null;
138 defer m.index += 1;
139 return m.index;
140 }
141 } = .{ .index = 0, .len = 0 };
121142 try client.serveBodylessMessage(.query_test_metadata);
122143 while (true) {
123144 const hdr = try client.receiveMessage();
......@@ -132,12 +153,17 @@ pub fn main(init: std.process.Init) !void {
132153 );
133154 },
134155 .test_metadata => {
135 try client.in.discardAll(hdr.bytes_len);
136 try client.serveRunTest(0);
156 const tm_hdr =
157 try client.in.takeStruct(std.zig.Server.Message.TestMetadata, .little);
158 try client.in.discardAll(
159 hdr.bytes_len - @sizeOf(std.zig.Server.Message.TestMetadata),
160 );
161 metadata = .{ .index = 0, .len = tm_hdr.tests_len };
162 try client.serveRunTest(metadata.next() orelse break);
137163 },
138164 .test_results => {
139165 try client.in.discardAll(hdr.bytes_len);
140 break;
166 try client.serveRunTest(metadata.next() orelse break);
141167 },
142168 }
143169 }