diff --git a/test/src/runner.zig b/test/src/runner.zig index 49b0bcead722760dcbc8388b66f2df2753e86045..bc9d65b0d572d0aa8d23e3650cff1d562b7d2989 100644 --- a/test/src/runner.zig +++ b/test/src/runner.zig @@ -8,8 +8,24 @@ const Args = struct { zig_file: ?std.Io.File, lib_dir: ?std.Io.Dir, src_dir: ?std.Io.Dir, + targets: std.ArrayList(u32), + target_bytes: std.ArrayList(u8), + + fn deinit(args: *Args, gpa: std.mem.Allocator) void { + args.targets.deinit(gpa); + args.target_bytes.deinit(gpa); + } + + fn addTarget(args: *Args, gpa: std.mem.Allocator, target: []const u8) std.mem.Allocator.Error!void { + try args.target_bytes.ensureUnusedCapacity(gpa, target.len + 1); + try args.targets.append(gpa, @intCast(args.target_bytes.items.len)); + args.target_bytes.appendSliceAssumeCapacity(target); + args.target_bytes.appendAssumeCapacity(0); + } }; pub fn main(init: std.process.Init) !void { + const arena = init.arena.allocator(); + var stdin_buffer: [512]u8 = undefined; var stdout_buffer: [512]u8 = undefined; var stderr_buffer: [512]u8 = undefined; @@ -20,14 +36,19 @@ pub fn main(init: std.process.Init) !void { .in = &stdin.interface, .out = &stdout.interface, }; - try server.serveStringMessage(.zig_version, @import("builtin").zig_version_string); + var args: Args = .{ .@"test" = null, .manifest_file = null, .zig_file = null, .lib_dir = null, .src_dir = null, + .targets = .empty, + .target_bytes = .empty, }; + defer args.deinit(init.gpa); + + try server.serveStringMessage(.zig_version, @import("builtin").zig_version_string); while (true) { const hdr = try server.receiveMessage(); switch (hdr.tag) { @@ -35,13 +56,11 @@ pub fn main(init: std.process.Init) !void { std.debug.print("unsupported message: {t}\n", .{hdr.tag}); std.process.exit(1); }, - .exit => { - std.process.exit(0); - }, + .exit => std.process.exit(0), .args => { - const args_body = try init.arena.allocator().alloc(u8, hdr.bytes_len); + const args_body = try arena.alloc(u8, hdr.bytes_len); try server.in.readSliceAll(args_body); - var state: enum { positional, zig, lib, src } = .positional; + var state: enum { positional, zig, lib, src, target } = .positional; var input_dir: std.zig.Server.Message.InputDir = .cwd; var args_body_offset: usize = 0; while (args_body.len - args_body_offset > 0) { @@ -53,7 +72,18 @@ pub fn main(init: std.process.Init) !void { const end = std.mem.findScalarPos(u8, args_body, args_body_offset, 0).?; const string = args_body[args_body_offset..end]; args_body_offset = end + 1; - _ = string; + switch (state) { + .positional => if (std.mem.eql(u8, string, "--target")) { + state = .target; + continue; + } else if (std.mem.cutPrefix(u8, string, "--target=")) |target| { + try args.addTarget(init.gpa, target); + }, + .zig => unreachable, + .lib => unreachable, + .src => unreachable, + .target => try args.addTarget(init.gpa, string), + } }, .prefix => { const end = std.mem.findScalarPos(u8, args_body, args_body_offset, 0).?; @@ -109,6 +139,7 @@ pub fn main(init: std.process.Init) !void { std.debug.assert(args.src_dir == null); args.src_dir = dir; }, + .target => unreachable, } }, .input_file, .input_file_content, .output_file => { @@ -129,22 +160,30 @@ pub fn main(init: std.process.Init) !void { std.debug.assert(args.zig_file == null); args.zig_file = file; }, - .lib, .src => unreachable, + .lib => unreachable, + .src => unreachable, + .target => unreachable, } }, } state = .positional; } }, - .query_test_metadata => try server.serveTestMetadata(.{ - .names = &.{0}, - .expected_panic_msgs = &.{0}, - .string_bytes = "runner\x00", - }), + .query_test_metadata => { + const expected_panic_msgs = try arena.alloc(u32, args.targets.items.len); + @memset(expected_panic_msgs, 0); + try server.serveTestMetadata(.{ + .names = args.targets.items, + .expected_panic_msgs = expected_panic_msgs, + .string_bytes = args.target_bytes.items, + }); + }, .run_test => { const test_index = try server.receiveBody_u32(); + const target = + std.mem.sliceTo(args.target_bytes.items[args.targets.items[test_index]..], 0); try server.serveBodylessMessage(.test_started); - const test_result = runTest(init.io, &stderr.interface, &server, &args); + const test_result = runTest(init.io, &stderr.interface, &server, &args, target); try stderr.flush(); try server.serveTestResults(.{ .index = test_index, @@ -170,11 +209,13 @@ fn runTest( stderr: *std.Io.Writer, server: *std.zig.Server, args: *const Args, -) anyerror!void { + target: []const u8, +) !void { const manifest_file = args.manifest_file orelse return error.MissingManifestArg; const src_dir = args.src_dir orelse return error.MissingSrcDir; var manifest_buffer: [512]u8 = undefined; var manifest_fr = manifest_file.reader(io, &manifest_buffer); + var allow_skip = true; var skip_delimiter = false; var update_mtime = std.Io.Clock.real.now(io); var update_num: usize = 0; @@ -188,7 +229,7 @@ fn runTest( continue; } const cmd = std.meta.stringToEnum( - enum { obj, exe, lib, write, delete, update, check }, + enum { skip, obj, exe, lib, write, delete, update, check }, cmd_str, ) orelse return error.UnknownCommand; var maybe_arg = line_it.next(); @@ -198,38 +239,40 @@ fn runTest( dr: DelimitedReader, fr: std.Io.File.Reader, } = undefined; - const contents_r: *std.Io.Reader = contents_r: { - switch (cmd) { - else => {}, - .delete => break :contents_r .ending, - } + const contents_r: *std.Io.Reader = contents_r: switch (cmd) { + .skip, .delete => { + if (line_it.next()) |_| return error.UnexpectedArg; + break :contents_r .ending; + }, + else => { + const contents_path = contents_path: { + const line_rest = line_it.rest(); + if (line_rest.len > 0) break :contents_path line_rest; + const contents_path = maybe_arg orelse break :contents_r .ending; + maybe_arg = null; + break :contents_path contents_path; + }; + if (std.mem.eql(u8, contents_path, "{")) { + contents_impl = .{ .dr = .init(&manifest_fr.interface, "#}", &.{}) }; + skip_delimiter = true; + break :contents_r &contents_impl.dr.interface; + } - const contents_path = contents_path: { - const line_rest = line_it.rest(); - if (line_rest.len > 0) break :contents_path line_rest; - const contents_path = maybe_arg orelse break :contents_r .ending; - maybe_arg = null; - break :contents_path contents_path; - }; - if (std.mem.eql(u8, contents_path, "{")) { - contents_impl = .{ .dr = .init(&manifest_fr.interface, "#}", &.{}) }; - skip_delimiter = true; - break :contents_r &contents_impl.dr.interface; - } + const @"test" = args.@"test" orelse return error.ContentsFileInFileTest; + try server.serveMessageHeader(.{ + .tag = .discovered_inputs, + .bytes_len = @intCast(@sizeOf(std.zig.Server.Message.InputDir) + + contents_path.len + 1), + }); + try server.out.writeInt(u32, @backingInt(@"test".input_dir), .little); + try server.out.writeAll(contents_path); + try server.out.writeByte(0); + try server.out.flush(); - const @"test" = args.@"test" orelse return error.ContentsFileInFileTest; - try server.serveMessageHeader(.{ - .tag = .discovered_inputs, - .bytes_len = @intCast(@sizeOf(std.zig.Server.Message.InputDir) + contents_path.len + 1), - }); - try server.out.writeInt(u32, @backingInt(@"test".input_dir), .little); - try server.out.writeAll(contents_path); - try server.out.writeByte(0); - try server.out.flush(); - - contents_file = try @"test".dir.openFile(io, contents_path, .{}); - contents_impl = .{ .fr = contents_file.?.reader(io, &.{}) }; - break :contents_r &contents_impl.fr.interface; + contents_file = try @"test".dir.openFile(io, contents_path, .{}); + contents_impl = .{ .fr = contents_file.?.reader(io, &.{}) }; + break :contents_r &contents_impl.fr.interface; + }, }; switch (cmd) { else => { @@ -237,6 +280,10 @@ fn runTest( _ = try contents_r.streamRemaining(stderr); try stderr.writeAll("\n}\n"); }, + .skip => if (!allow_skip) + return error.NonInitialSkipCommand // #skip must appear first + else if (std.mem.eql(u8, target, maybe_arg orelse return error.MissingArg)) + return error.SkipZigTest, .write => { const file = try src_dir.createFile(io, maybe_arg orelse return error.MissingArg, .{}); defer file.close(io); @@ -262,6 +309,7 @@ fn runTest( }, } std.debug.assert(try contents_r.discardRemaining() == 0); + if (cmd != .skip) allow_skip = false; } else |err| switch (err) { else => |e| return e, error.EndOfStream => if (skip_delimiter) return error.MissingDelimiter, diff --git a/test/tests.zig b/test/tests.zig index db2973e413b4e1babb231a79435a6cd1b8f3cd07..cd5166f48e149830f910a6f837acd83b2122512d 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -2214,9 +2214,9 @@ const IncrementalTarget = struct { /// These are passed to `incr-check` as `-` strings. /// -/// If only one specific test is failing on a target, instead of entirely disabling the target here, -/// you can skip the target for that specific test only by adding a line like this to the manifest: -/// #skip_target=x86_64-linux-selfhosted +/// If only one specific test is failing on a target, instead of entirely disabling the target here, you +/// can skip the target for that specific test only by adding a line like this to top of the manifest: +/// #skip x86_64-linux-selfhosted const incremental_targets = &[_]IncrementalTarget{ // Avoid adding more CBE or LLVM targets without good reason: they're a lot slower than others // to run due to the output (C source code or LLVM IR) being built non-incrementally (by Clang diff --git a/tools/runner-check.zig b/tools/runner-check.zig index 0291d93814f99242322930cf3722f4d10af3c418..429e1e3d42ba22399348298cac7ae17509695900 100644 --- a/tools/runner-check.zig +++ b/tools/runner-check.zig @@ -5,6 +5,8 @@ pub fn main(init: std.process.Init) !void { var test_path_arg: ?[]const u8 = null; var zig_path_arg: ?[]const u8 = null; var lib_path_arg: ?[]const u8 = null; + var target_args: std.ArrayList([]const u8) = .empty; + defer target_args.deinit(init.gpa); var keep_src = false; var arg_it = try init.minimal.args.iterateAllocator(arena); @@ -18,6 +20,13 @@ pub fn main(init: std.process.Init) !void { lib_path_arg = arg_it.next() orelse fail("missing arg after '{s}'", .{arg}); } else if (std.mem.cutPrefix(u8, arg, "--lib=")) |lib_path| { lib_path_arg = lib_path; + } else if (std.mem.eql(u8, arg, "--target")) { + try target_args.append( + init.gpa, + try arena.dupe(u8, arg_it.next() orelse fail("missing arg after '{s}'", .{arg})), + ); + } else if (std.mem.cutPrefix(u8, arg, "--target=")) |target| { + try target_args.append(init.gpa, try arena.dupe(u8, target)); } else if (std.mem.eql(u8, arg, "--keep-src")) { keep_src = true; } else { @@ -72,45 +81,47 @@ pub fn main(init: std.process.Init) !void { .out = &stdin_writer.interface, }; { + var args: std.ArrayList(u8) = .empty; + defer args.deinit(init.gpa); const Arg = std.zig.Client.Message.Arg; - var args: std.ArrayList(u8) = .initBuffer(try arena.alloc(u8, 0 + - @sizeOf(Arg) + @sizeOf(std.Io.File.Handle) + - @sizeOf(Arg) + "--zig=\x00".len + - @sizeOf(Arg) + @sizeOf(std.Io.File.Handle) + - @sizeOf(Arg) + "--lib=\x00".len + - @sizeOf(Arg) + @sizeOf(std.Io.Dir.Handle) + - @sizeOf(Arg) + "--src=\x00".len + - @sizeOf(Arg) + @sizeOf(std.Io.Dir.Handle))); - args.appendAssumeCapacity(@backingInt(@as(Arg, switch ((try test_file.stat(init.io)).kind) { + try args.append(init.gpa, @backingInt(@as(Arg, switch ((try test_file.stat(init.io)).kind) { else => unreachable, .file => .input_file, .directory => .input_dir, }))); - args.appendSliceAssumeCapacity(@ptrCast(&test_file.handle)); + try args.appendSlice(init.gpa, @ptrCast(&test_file.handle)); - args.appendAssumeCapacity(@backingInt(Arg.prefix)); - args.appendSliceAssumeCapacity("--zig="); - args.appendAssumeCapacity(0); + try args.append(init.gpa, @backingInt(Arg.prefix)); + try args.appendSlice(init.gpa, "--zig="); + try args.append(init.gpa, 0); - args.appendAssumeCapacity(@backingInt(Arg.input_file)); - args.appendSliceAssumeCapacity(@ptrCast(&zig_file.handle)); + try args.append(init.gpa, @backingInt(Arg.input_file)); + try args.appendSlice(init.gpa, @ptrCast(&zig_file.handle)); - args.appendAssumeCapacity(@backingInt(Arg.prefix)); - args.appendSliceAssumeCapacity("--lib="); - args.appendAssumeCapacity(0); + try args.append(init.gpa, @backingInt(Arg.prefix)); + try args.appendSlice(init.gpa, "--lib="); + try args.append(init.gpa, 0); - args.appendAssumeCapacity(@backingInt(Arg.input_dir)); - args.appendSliceAssumeCapacity(@ptrCast(&lib_dir.handle)); + try args.append(init.gpa, @backingInt(Arg.input_dir)); + try args.appendSlice(init.gpa, @ptrCast(&lib_dir.handle)); - args.appendAssumeCapacity(@backingInt(Arg.prefix)); - args.appendSliceAssumeCapacity("--src="); - args.appendAssumeCapacity(0); + try args.append(init.gpa, @backingInt(Arg.prefix)); + try args.appendSlice(init.gpa, "--src="); + try args.append(init.gpa, 0); - args.appendAssumeCapacity(@backingInt(Arg.output_dir)); - args.appendSliceAssumeCapacity(@ptrCast(&src_dir.handle)); + try args.append(init.gpa, @backingInt(Arg.output_dir)); + try args.appendSlice(init.gpa, @ptrCast(&src_dir.handle)); - std.debug.assert(args.unusedCapacitySlice().len == 0); + for (target_args.items) |target_arg| { + try args.append(init.gpa, @backingInt(Arg.string)); + try args.appendSlice(init.gpa, "--target"); + try args.append(init.gpa, 0); + + try args.append(init.gpa, @backingInt(Arg.string)); + try args.appendSlice(init.gpa, target_arg); + try args.append(init.gpa, 0); + } try client.serveMessageHeader(.{ .tag = .args, @@ -118,6 +129,16 @@ pub fn main(init: std.process.Init) !void { }); try client.out.writeAll(args.items); + var metadata: struct { + index: u32, + len: u32, + + fn next(m: *@This()) ?u32 { + if (m.len - m.index == 0) return null; + defer m.index += 1; + return m.index; + } + } = .{ .index = 0, .len = 0 }; try client.serveBodylessMessage(.query_test_metadata); while (true) { const hdr = try client.receiveMessage(); @@ -132,12 +153,17 @@ pub fn main(init: std.process.Init) !void { ); }, .test_metadata => { - try client.in.discardAll(hdr.bytes_len); - try client.serveRunTest(0); + const tm_hdr = + try client.in.takeStruct(std.zig.Server.Message.TestMetadata, .little); + try client.in.discardAll( + hdr.bytes_len - @sizeOf(std.zig.Server.Message.TestMetadata), + ); + metadata = .{ .index = 0, .len = tm_hdr.tests_len }; + try client.serveRunTest(metadata.next() orelse break); }, .test_results => { try client.in.discardAll(hdr.bytes_len); - break; + try client.serveRunTest(metadata.next() orelse break); }, } }