From 9e3bda5efffb12dd6491b4f7c92ccc89b9043c64 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 8 Dec 2025 18:24:52 -0800 Subject: [PATCH] tests: close() -> close(io) --- lib/std/Io/File/Writer.zig | 6 +- lib/std/process/Child.zig | 17 ++- test/src/convert-stack-trace.zig | 6 +- test/standalone/child_process/main.zig | 15 ++- test/standalone/cmakedefine/check.zig | 7 +- test/standalone/dirname/build.zig | 10 +- test/standalone/dirname/exists_in.zig | 9 +- test/standalone/dirname/touch.zig | 17 ++- test/standalone/entry_point/check_differ.zig | 7 +- .../glibc_compat/glibc_runtime_check.zig | 4 +- .../install_headers/check_exists.zig | 11 +- test/standalone/libfuzzer/main.zig | 10 +- test/standalone/posix/relpaths.zig | 23 ++-- test/standalone/run_cwd/check_file_exists.zig | 5 +- test/standalone/run_output_caching/main.zig | 8 +- .../run_output_paths/create_file.zig | 4 +- .../self_exe_symlink/create-symlink.zig | 6 +- test/standalone/self_exe_symlink/main.zig | 16 ++- test/standalone/simple/cat/main.zig | 6 +- test/standalone/windows_spawn/main.zig | 120 +++++++++--------- test/tests.zig | 19 +-- 21 files changed, 187 insertions(+), 139 deletions(-) diff --git a/lib/std/Io/File/Writer.zig b/lib/std/Io/File/Writer.zig index e511e62e19..d8c30ddbef 100644 --- a/lib/std/Io/File/Writer.zig +++ b/lib/std/Io/File/Writer.zig @@ -56,8 +56,9 @@ pub const WriteFileError = error{ pub const SeekError = Io.File.SeekError; -pub fn init(file: File, buffer: []u8) Writer { +pub fn init(file: File, io: Io, buffer: []u8) Writer { return .{ + .io = io, .file = file, .interface = initInterface(buffer), .mode = .positional, @@ -67,8 +68,9 @@ pub fn init(file: File, buffer: []u8) Writer { /// Positional is more threadsafe, since the global seek position is not /// affected, but when such syscalls are not available, preemptively /// initializing in streaming mode will skip a failed syscall. -pub fn initStreaming(file: File, buffer: []u8) Writer { +pub fn initStreaming(file: File, io: Io, buffer: []u8) Writer { return .{ + .io = io, .file = file, .interface = initInterface(buffer), .mode = .streaming, diff --git a/lib/std/process/Child.zig b/lib/std/process/Child.zig index 4521cd511f..dbbacc496c 100644 --- a/lib/std/process/Child.zig +++ b/lib/std/process/Child.zig @@ -435,8 +435,7 @@ pub const RunError = posix.GetCwdError || posix.ReadError || SpawnError || posix /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns. /// If it succeeds, the caller owns result.stdout and result.stderr memory. -pub fn run(args: struct { - allocator: mem.Allocator, +pub fn run(allocator: Allocator, io: Io, args: struct { argv: []const []const u8, cwd: ?[]const u8 = null, cwd_dir: ?Io.Dir = null, @@ -447,7 +446,7 @@ pub fn run(args: struct { expand_arg0: Arg0Expand = .no_expand, progress_node: std.Progress.Node = std.Progress.Node.none, }) RunError!RunResult { - var child = ChildProcess.init(args.argv, args.allocator); + var child = ChildProcess.init(args.argv, allocator); child.stdin_behavior = .Ignore; child.stdout_behavior = .Pipe; child.stderr_behavior = .Pipe; @@ -458,19 +457,19 @@ pub fn run(args: struct { child.progress_node = args.progress_node; var stdout: ArrayList(u8) = .empty; - defer stdout.deinit(args.allocator); + defer stdout.deinit(allocator); var stderr: ArrayList(u8) = .empty; - defer stderr.deinit(args.allocator); + defer stderr.deinit(allocator); try child.spawn(); errdefer { - _ = child.kill() catch {}; + _ = child.kill(io) catch {}; } - try child.collectOutput(args.allocator, &stdout, &stderr, args.max_output_bytes); + try child.collectOutput(allocator, &stdout, &stderr, args.max_output_bytes); return .{ - .stdout = try stdout.toOwnedSlice(args.allocator), - .stderr = try stderr.toOwnedSlice(args.allocator), + .stdout = try stdout.toOwnedSlice(allocator), + .stderr = try stderr.toOwnedSlice(allocator), .term = try child.wait(), }; } diff --git a/test/src/convert-stack-trace.zig b/test/src/convert-stack-trace.zig index 8d04500fba..272edad18e 100644 --- a/test/src/convert-stack-trace.zig +++ b/test/src/convert-stack-trace.zig @@ -41,13 +41,13 @@ pub fn main() !void { var read_buf: [1024]u8 = undefined; var write_buf: [1024]u8 = undefined; - const in_file = try std.fs.cwd().openFile(args[1], .{}); - defer in_file.close(); + const in_file = try std.Io.Dir.cwd().openFile(io, args[1], .{}); + defer in_file.close(io); const out_file: std.Io.File = .stdout(); var in_fr = in_file.reader(io, &read_buf); - var out_fw = out_file.writer(&write_buf); + var out_fw = out_file.writer(io, &write_buf); const w = &out_fw.interface; diff --git a/test/standalone/child_process/main.zig b/test/standalone/child_process/main.zig index b20a7fed49..755beacb6d 100644 --- a/test/standalone/child_process/main.zig +++ b/test/standalone/child_process/main.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const Io = std.Io; pub fn main() !void { // make sure safety checks are enabled even in release modes @@ -20,7 +21,7 @@ pub fn main() !void { }; defer if (needs_free) gpa.free(child_path); - var threaded: std.Io.Threaded = .init(gpa); + var threaded: Io.Threaded = .init(gpa); defer threaded.deinit(); const io = threaded.io(); @@ -31,7 +32,7 @@ pub fn main() !void { try child.spawn(); const child_stdin = child.stdin.?; try child_stdin.writeAll("hello from stdin"); // verified in child - child_stdin.close(); + child_stdin.close(io); child.stdin = null; const hello_stdout = "hello from stdout"; @@ -39,17 +40,17 @@ pub fn main() !void { var stdout_reader = child.stdout.?.readerStreaming(io, &.{}); const n = try stdout_reader.interface.readSliceShort(&buf); if (!std.mem.eql(u8, buf[0..n], hello_stdout)) { - testError("child stdout: '{s}'; want '{s}'", .{ buf[0..n], hello_stdout }); + testError(io, "child stdout: '{s}'; want '{s}'", .{ buf[0..n], hello_stdout }); } switch (try child.wait()) { .Exited => |code| { const child_ok_code = 42; // set by child if no test errors if (code != child_ok_code) { - testError("child exit code: {d}; want {d}", .{ code, child_ok_code }); + testError(io, "child exit code: {d}; want {d}", .{ code, child_ok_code }); } }, - else => |term| testError("abnormal child exit: {}", .{term}), + else => |term| testError(io, "abnormal child exit: {}", .{term}), } if (parent_test_error) return error.ParentTestError; @@ -61,8 +62,8 @@ pub fn main() !void { var parent_test_error = false; -fn testError(comptime fmt: []const u8, args: anytype) void { - var stderr_writer = std.Io.File.stderr().writer(&.{}); +fn testError(io: Io, comptime fmt: []const u8, args: anytype) void { + var stderr_writer = Io.File.stderr().writer(io, &.{}); const stderr = &stderr_writer.interface; stderr.print("PARENT TEST ERROR: ", .{}) catch {}; stderr.print(fmt, args) catch {}; diff --git a/test/standalone/cmakedefine/check.zig b/test/standalone/cmakedefine/check.zig index 782e7f4dc3..d0751913d7 100644 --- a/test/standalone/cmakedefine/check.zig +++ b/test/standalone/cmakedefine/check.zig @@ -9,8 +9,11 @@ pub fn main() !void { const actual_path = args[1]; const expected_path = args[2]; - const actual = try std.fs.cwd().readFileAlloc(actual_path, arena, .limited(1024 * 1024)); - const expected = try std.fs.cwd().readFileAlloc(expected_path, arena, .limited(1024 * 1024)); + var threaded: std.Io.Threaded = .init_single_threaded; + const io = threaded.io(); + + const actual = try std.Io.Dir.cwd().readFileAlloc(io, actual_path, arena, .limited(1024 * 1024)); + const expected = try std.Io.Dir.cwd().readFileAlloc(io, expected_path, arena, .limited(1024 * 1024)); // The actual output starts with a comment which we should strip out before comparing. const comment_str = "/* This file was generated by ConfigHeader using the Zig Build System. */\n"; diff --git a/test/standalone/dirname/build.zig b/test/standalone/dirname/build.zig index 0da85e2923..b850680ba9 100644 --- a/test/standalone/dirname/build.zig +++ b/test/standalone/dirname/build.zig @@ -59,13 +59,15 @@ pub fn build(b: *std.Build) void { // Absolute path: const abs_path = setup_abspath: { + // TODO this is a bad pattern, don't do this + const io = b.graph.io; const temp_dir = b.makeTempPath(); - var dir = std.fs.cwd().openDir(temp_dir, .{}) catch @panic("failed to open temp dir"); - defer dir.close(); + var dir = std.Io.Dir.cwd().openDir(io, temp_dir, .{}) catch @panic("failed to open temp dir"); + defer dir.close(io); - var file = dir.createFile("foo.txt", .{}) catch @panic("failed to create file"); - file.close(); + var file = dir.createFile(io, "foo.txt", .{}) catch @panic("failed to create file"); + file.close(io); break :setup_abspath std.Build.LazyPath{ .cwd_relative = temp_dir }; }; diff --git a/test/standalone/dirname/exists_in.zig b/test/standalone/dirname/exists_in.zig index 7aec1f423d..7bddc4e613 100644 --- a/test/standalone/dirname/exists_in.zig +++ b/test/standalone/dirname/exists_in.zig @@ -34,8 +34,11 @@ fn run(allocator: std.mem.Allocator) !void { return error.BadUsage; }; - var dir = try std.fs.cwd().openDir(dir_path, .{}); - defer dir.close(); + var threaded: std.Io.Threaded = .init_single_threaded; + const io = threaded.io(); - _ = try dir.statFile(relpath); + var dir = try std.Io.Dir.cwd().openDir(io, dir_path, .{}); + defer dir.close(io); + + _ = try dir.statFile(io, relpath); } diff --git a/test/standalone/dirname/touch.zig b/test/standalone/dirname/touch.zig index 43fcabf91e..9255d27c72 100644 --- a/test/standalone/dirname/touch.zig +++ b/test/standalone/dirname/touch.zig @@ -26,14 +26,17 @@ fn run(allocator: std.mem.Allocator) !void { return error.BadUsage; }; - const dir_path = std.fs.path.dirname(path) orelse unreachable; - const basename = std.fs.path.basename(path); + const dir_path = std.Io.Dir.path.dirname(path) orelse unreachable; + const basename = std.Io.Dir.path.basename(path); - var dir = try std.fs.cwd().openDir(dir_path, .{}); - defer dir.close(); + var threaded: std.Io.Threaded = .init_single_threaded; + const io = threaded.io(); - _ = dir.statFile(basename) catch { - var file = try dir.createFile(basename, .{}); - file.close(); + var dir = try std.Io.Dir.cwd().openDir(io, dir_path, .{}); + defer dir.close(io); + + _ = dir.statFile(io, basename) catch { + var file = try dir.createFile(io, basename, .{}); + file.close(io); }; } diff --git a/test/standalone/entry_point/check_differ.zig b/test/standalone/entry_point/check_differ.zig index 63d1ec0294..bba45e5f8c 100644 --- a/test/standalone/entry_point/check_differ.zig +++ b/test/standalone/entry_point/check_differ.zig @@ -6,8 +6,11 @@ pub fn main() !void { const args = try std.process.argsAlloc(arena); if (args.len != 3) return error.BadUsage; // usage: 'check_differ ' - const contents_1 = try std.fs.cwd().readFileAlloc(args[1], arena, .limited(1024 * 1024 * 64)); // 64 MiB ought to be plenty - const contents_2 = try std.fs.cwd().readFileAlloc(args[2], arena, .limited(1024 * 1024 * 64)); // 64 MiB ought to be plenty + var threaded: std.Io.Threaded = .init_single_threaded; + const io = threaded.io(); + + const contents_1 = try std.Io.Dir.cwd().readFileAlloc(io, args[1], arena, .limited(1024 * 1024 * 64)); // 64 MiB ought to be plenty + const contents_2 = try std.Io.Dir.cwd().readFileAlloc(io, args[2], arena, .limited(1024 * 1024 * 64)); // 64 MiB ought to be plenty if (std.mem.eql(u8, contents_1, contents_2)) { return error.FilesMatch; diff --git a/test/standalone/glibc_compat/glibc_runtime_check.zig b/test/standalone/glibc_compat/glibc_runtime_check.zig index 82f4a54ee1..78b1f7efb3 100644 --- a/test/standalone/glibc_compat/glibc_runtime_check.zig +++ b/test/standalone/glibc_compat/glibc_runtime_check.zig @@ -28,10 +28,10 @@ extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: [*]const u8) c_int; // PR #17034 - fstat moved between libc_nonshared and libc fn checkStat() !void { - const cwdFd = std.fs.cwd().fd; + const cwd_fd = std.Io.Dir.cwd().handle; var buf: [256]u8 = @splat(0); - var result = fstatat(cwdFd, "a_file_that_definitely_does_not_exist", &buf, 0); + var result = fstatat(cwd_fd, "a_file_that_definitely_does_not_exist", &buf, 0); assert(result == -1); assert(std.posix.errno(result) == .NOENT); diff --git a/test/standalone/install_headers/check_exists.zig b/test/standalone/install_headers/check_exists.zig index 62706749aa..8a7104e4f2 100644 --- a/test/standalone/install_headers/check_exists.zig +++ b/test/standalone/install_headers/check_exists.zig @@ -11,8 +11,11 @@ pub fn main() !void { var arg_it = try std.process.argsWithAllocator(arena); _ = arg_it.next(); - const cwd = std.fs.cwd(); - const cwd_realpath = try cwd.realpathAlloc(arena, "."); + var threaded: std.Io.Threaded = .init_single_threaded; + const io = threaded.io(); + + const cwd = std.Io.Dir.cwd(); + const cwd_realpath = try cwd.realPathAlloc(io, arena, "."); while (arg_it.next()) |file_path| { if (file_path.len > 0 and file_path[0] == '!') { @@ -20,7 +23,7 @@ pub fn main() !void { "exclusive file check '{s}{c}{s}' failed", .{ cwd_realpath, std.fs.path.sep, file_path[1..] }, ); - if (std.fs.cwd().statFile(file_path[1..])) |_| { + if (cwd.statFile(io, file_path[1..])) |_| { return error.FileFound; } else |err| switch (err) { error.FileNotFound => {}, @@ -31,7 +34,7 @@ pub fn main() !void { "inclusive file check '{s}{c}{s}' failed", .{ cwd_realpath, std.fs.path.sep, file_path }, ); - _ = try std.fs.cwd().statFile(file_path); + _ = try cwd.statFile(io, file_path); } } } diff --git a/test/standalone/libfuzzer/main.zig b/test/standalone/libfuzzer/main.zig index b275b6d593..077af4ad3e 100644 --- a/test/standalone/libfuzzer/main.zig +++ b/test/standalone/libfuzzer/main.zig @@ -20,8 +20,8 @@ pub fn main() !void { const io = threaded.io(); const cache_dir_path = args.next() orelse @panic("expected cache directory path argument"); - var cache_dir = try std.fs.cwd().openDir(cache_dir_path, .{}); - defer cache_dir.close(); + var cache_dir = try std.Io.Dir.cwd().openDir(io, cache_dir_path, .{}); + defer cache_dir.close(io); abi.fuzzer_init(.fromSlice(cache_dir_path)); abi.fuzzer_init_test(testOne, .fromSlice("test")); @@ -30,8 +30,8 @@ pub fn main() !void { const pc_digest = abi.fuzzer_coverage().id; const coverage_file_path = "v/" ++ std.fmt.hex(pc_digest); - const coverage_file = try cache_dir.openFile(coverage_file_path, .{}); - defer coverage_file.close(); + const coverage_file = try cache_dir.openFile(io, coverage_file_path, .{}); + defer coverage_file.close(io); var read_buf: [@sizeOf(abi.SeenPcsHeader)]u8 = undefined; var r = coverage_file.reader(io, &read_buf); @@ -42,6 +42,6 @@ pub fn main() !void { const expected_len = @sizeOf(abi.SeenPcsHeader) + try std.math.divCeil(usize, pcs_header.pcs_len, @bitSizeOf(usize)) * @sizeOf(usize) + pcs_header.pcs_len * @sizeOf(usize); - if (try coverage_file.getEndPos() != expected_len) + if (try coverage_file.length(io) != expected_len) return error.WrongEnd; } diff --git a/test/standalone/posix/relpaths.zig b/test/standalone/posix/relpaths.zig index 40e2e09464..70e9fad687 100644 --- a/test/standalone/posix/relpaths.zig +++ b/test/standalone/posix/relpaths.zig @@ -1,9 +1,11 @@ // Test relative paths through POSIX APIS. These tests have to change the cwd, so // they shouldn't be Zig unit tests. -const std = @import("std"); const builtin = @import("builtin"); +const std = @import("std"); +const Io = std.Io; + pub fn main() !void { if (builtin.target.os.tag == .wasi) return; // Can link, but can't change into tmpDir @@ -11,6 +13,11 @@ pub fn main() !void { const a = Allocator.allocator(); defer std.debug.assert(Allocator.deinit() == .ok); + var threaded: std.Io.Threaded = .init_single_threaded; + const io = threaded.io(); + + // TODO this API isn't supposed to be used outside of unit testing. make it compilation error if used + // outside of unit testing. var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); @@ -18,7 +25,7 @@ pub fn main() !void { try tmp.dir.setAsCwd(); try test_symlink(a, tmp); - try test_link(tmp); + try test_link(io, tmp); } fn test_symlink(a: std.mem.Allocator, tmp: std.testing.TmpDir) !void { @@ -65,7 +72,7 @@ fn getLinkInfo(fd: std.posix.fd_t) !struct { std.posix.ino_t, std.posix.nlink_t return .{ st.ino, st.nlink }; } -fn test_link(tmp: std.testing.TmpDir) !void { +fn test_link(io: Io, tmp: std.testing.TmpDir) !void { switch (builtin.target.os.tag) { .linux, .illumos => {}, else => return, @@ -74,17 +81,17 @@ fn test_link(tmp: std.testing.TmpDir) !void { const target_name = "link-target"; const link_name = "newlink"; - try tmp.dir.writeFile(.{ .sub_path = target_name, .data = "example" }); + try tmp.dir.writeFile(io, .{ .sub_path = target_name, .data = "example" }); // Test 1: create the relative link from inside tmp try std.posix.link(target_name, link_name); // Verify - const efd = try tmp.dir.openFile(target_name, .{}); - defer efd.close(); + const efd = try tmp.dir.openFile(io, target_name, .{}); + defer efd.close(io); - const nfd = try tmp.dir.openFile(link_name, .{}); - defer nfd.close(); + const nfd = try tmp.dir.openFile(io, link_name, .{}); + defer nfd.close(io); { const eino, _ = try getLinkInfo(efd.handle); diff --git a/test/standalone/run_cwd/check_file_exists.zig b/test/standalone/run_cwd/check_file_exists.zig index 640fc99a7a..8830cc115a 100644 --- a/test/standalone/run_cwd/check_file_exists.zig +++ b/test/standalone/run_cwd/check_file_exists.zig @@ -8,7 +8,10 @@ pub fn main() !void { if (args.len != 2) return error.BadUsage; const path = args[1]; - std.fs.cwd().access(path, .{}) catch return error.AccessFailed; + var threaded: std.Io.Threaded = .init_single_threaded; + const io = threaded.io(); + + std.Io.Dir.cwd().access(io, path, .{}) catch return error.AccessFailed; } const std = @import("std"); diff --git a/test/standalone/run_output_caching/main.zig b/test/standalone/run_output_caching/main.zig index e4e6332f11..c21d8d0f6b 100644 --- a/test/standalone/run_output_caching/main.zig +++ b/test/standalone/run_output_caching/main.zig @@ -1,10 +1,12 @@ const std = @import("std"); pub fn main() !void { + var threaded: std.Io.Threaded = .init_single_threaded; + const io = threaded.io(); var args = try std.process.argsWithAllocator(std.heap.page_allocator); _ = args.skip(); const filename = args.next().?; - const file = try std.fs.cwd().createFile(filename, .{}); - defer file.close(); - try file.writeAll(filename); + const file = try std.Io.Dir.cwd().createFile(io, filename, .{}); + defer file.close(io); + try file.writeAll(io, filename); } diff --git a/test/standalone/run_output_paths/create_file.zig b/test/standalone/run_output_paths/create_file.zig index 260c36f10c..4830790c79 100644 --- a/test/standalone/run_output_paths/create_file.zig +++ b/test/standalone/run_output_paths/create_file.zig @@ -1,10 +1,12 @@ const std = @import("std"); pub fn main() !void { + var threaded: std.Io.Threaded = .init_single_threaded; + const io = threaded.io(); var args = try std.process.argsWithAllocator(std.heap.page_allocator); _ = args.skip(); const dir_name = args.next().?; - const dir = try std.fs.cwd().openDir(if (std.mem.startsWith(u8, dir_name, "--dir=")) + const dir = try std.Io.Dir.cwd().openDir(io, if (std.mem.startsWith(u8, dir_name, "--dir=")) dir_name["--dir=".len..] else dir_name, .{}); diff --git a/test/standalone/self_exe_symlink/create-symlink.zig b/test/standalone/self_exe_symlink/create-symlink.zig index dac5891ba8..6ccf6596aa 100644 --- a/test/standalone/self_exe_symlink/create-symlink.zig +++ b/test/standalone/self_exe_symlink/create-symlink.zig @@ -14,5 +14,9 @@ pub fn main() anyerror!void { // If `exe_path` is relative to our cwd, we need to convert it to be relative to the dirname of `symlink_path`. const exe_rel_path = try std.fs.path.relative(allocator, std.fs.path.dirname(symlink_path) orelse ".", exe_path); defer allocator.free(exe_rel_path); - try std.fs.cwd().symLink(exe_rel_path, symlink_path, .{}); + + var threaded: std.Io.Threaded = .init_single_threaded; + const io = threaded.io(); + + try std.Io.Dir.cwd().symLink(io, exe_rel_path, symlink_path, .{}); } diff --git a/test/standalone/self_exe_symlink/main.zig b/test/standalone/self_exe_symlink/main.zig index b74c4c7f95..4df5b02720 100644 --- a/test/standalone/self_exe_symlink/main.zig +++ b/test/standalone/self_exe_symlink/main.zig @@ -1,15 +1,19 @@ const std = @import("std"); pub fn main() !void { - var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; - defer std.debug.assert(gpa.deinit() == .ok); - const allocator = gpa.allocator(); + var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + defer if (debug_allocator.deinit() == .leak) @panic("found memory leaks"); + const gpa = debug_allocator.allocator(); - const self_path = try std.fs.selfExePathAlloc(allocator); - defer allocator.free(self_path); + var threaded: std.Io.Threaded = .init(gpa); + defer threaded.deinit(); + const io = threaded.io(); + + const self_path = try std.fs.selfExePathAlloc(gpa); + defer gpa.free(self_path); var self_exe = try std.fs.openSelfExe(.{}); - defer self_exe.close(); + defer self_exe.close(io); var buf: [std.fs.max_path_bytes]u8 = undefined; const self_exe_path = try std.os.getFdPath(self_exe.handle, &buf); diff --git a/test/standalone/simple/cat/main.zig b/test/standalone/simple/cat/main.zig index dea528f4f7..699fc8075d 100644 --- a/test/standalone/simple/cat/main.zig +++ b/test/standalone/simple/cat/main.zig @@ -18,7 +18,7 @@ pub fn main() !void { const exe = args[0]; var catted_anything = false; var stdout_buffer: [4096]u8 = undefined; - var stdout_writer = Io.File.stdout().writerStreaming(&stdout_buffer); + var stdout_writer = Io.File.stdout().writerStreaming(io, &stdout_buffer); const stdout = &stdout_writer.interface; var stdin_reader = Io.File.stdin().readerStreaming(io, &.{}); @@ -32,8 +32,8 @@ pub fn main() !void { } else if (mem.startsWith(u8, arg, "-")) { return usage(exe); } else { - const file = cwd.openFile(arg, .{}) catch |err| fatal("unable to open file: {t}\n", .{err}); - defer file.close(); + const file = cwd.openFile(io, arg, .{}) catch |err| fatal("unable to open file: {t}\n", .{err}); + defer file.close(io); catted_anything = true; var file_reader = file.reader(io, &.{}); diff --git a/test/standalone/windows_spawn/main.zig b/test/standalone/windows_spawn/main.zig index f388070097..1acae0aca2 100644 --- a/test/standalone/windows_spawn/main.zig +++ b/test/standalone/windows_spawn/main.zig @@ -1,15 +1,20 @@ const std = @import("std"); const Io = std.Io; +const Allocator = std.mem.Allocator; const windows = std.os.windows; const utf16Literal = std.unicode.utf8ToUtf16LeStringLiteral; pub fn main() anyerror!void { - var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; - defer if (gpa.deinit() == .leak) @panic("found memory leaks"); - const allocator = gpa.allocator(); + var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + defer if (debug_allocator.deinit() == .leak) @panic("found memory leaks"); + const gpa = debug_allocator.allocator(); - var it = try std.process.argsWithAllocator(allocator); + var threaded: std.Io.Threaded = .init(gpa); + defer threaded.deinit(); + const io = threaded.io(); + + var it = try std.process.argsWithAllocator(gpa); defer it.deinit(); _ = it.next() orelse unreachable; // skip binary name const hello_exe_cache_path = it.next() orelse unreachable; @@ -17,14 +22,14 @@ pub fn main() anyerror!void { var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); - const tmp_absolute_path = try tmp.dir.realpathAlloc(allocator, "."); - defer allocator.free(tmp_absolute_path); - const tmp_absolute_path_w = try std.unicode.utf8ToUtf16LeAllocZ(allocator, tmp_absolute_path); - defer allocator.free(tmp_absolute_path_w); - const cwd_absolute_path = try std.fs.cwd().realpathAlloc(allocator, "."); - defer allocator.free(cwd_absolute_path); - const tmp_relative_path = try std.fs.path.relative(allocator, cwd_absolute_path, tmp_absolute_path); - defer allocator.free(tmp_relative_path); + const tmp_absolute_path = try tmp.dir.realpathAlloc(gpa, "."); + defer gpa.free(tmp_absolute_path); + const tmp_absolute_path_w = try std.unicode.utf8ToUtf16LeAllocZ(gpa, tmp_absolute_path); + defer gpa.free(tmp_absolute_path_w); + const cwd_absolute_path = try Io.Dir.cwd().realpathAlloc(gpa, "."); + defer gpa.free(cwd_absolute_path); + const tmp_relative_path = try std.fs.path.relative(gpa, cwd_absolute_path, tmp_absolute_path); + defer gpa.free(tmp_relative_path); // Clear PATH std.debug.assert(windows.kernel32.SetEnvironmentVariableW( @@ -39,10 +44,10 @@ pub fn main() anyerror!void { ) == windows.TRUE); // No PATH, so it should fail to find anything not in the cwd - try testExecError(error.FileNotFound, allocator, "something_missing"); + try testExecError(error.FileNotFound, gpa, "something_missing"); // make sure we don't get error.BadPath traversing out of cwd with a relative path - try testExecError(error.FileNotFound, allocator, "..\\.\\.\\.\\\\..\\more_missing"); + try testExecError(error.FileNotFound, gpa, "..\\.\\.\\.\\\\..\\more_missing"); std.debug.assert(windows.kernel32.SetEnvironmentVariableW( utf16Literal("PATH"), @@ -50,14 +55,14 @@ pub fn main() anyerror!void { ) == windows.TRUE); // Move hello.exe into the tmp dir which is now added to the path - try std.fs.cwd().copyFile(hello_exe_cache_path, tmp.dir, "hello.exe", .{}); + try Io.Dir.cwd().copyFile(hello_exe_cache_path, tmp.dir, "hello.exe", .{}); // with extension should find the .exe (case insensitive) - try testExec(allocator, "HeLLo.exe", "hello from exe\n"); + try testExec(gpa, "HeLLo.exe", "hello from exe\n"); // without extension should find the .exe (case insensitive) - try testExec(allocator, "heLLo", "hello from exe\n"); + try testExec(gpa, "heLLo", "hello from exe\n"); // with invalid cwd - try std.testing.expectError(error.FileNotFound, testExecWithCwd(allocator, "hello.exe", "missing_dir", "")); + try std.testing.expectError(error.FileNotFound, testExecWithCwd(gpa, io, "hello.exe", "missing_dir", "")); // now add a .bat try tmp.dir.writeFile(.{ .sub_path = "hello.bat", .data = "@echo hello from bat" }); @@ -65,33 +70,33 @@ pub fn main() anyerror!void { try tmp.dir.writeFile(.{ .sub_path = "hello.cmd", .data = "@echo hello from cmd" }); // with extension should find the .bat (case insensitive) - try testExec(allocator, "heLLo.bat", "hello from bat\r\n"); + try testExec(gpa, "heLLo.bat", "hello from bat\r\n"); // with extension should find the .cmd (case insensitive) - try testExec(allocator, "heLLo.cmd", "hello from cmd\r\n"); + try testExec(gpa, "heLLo.cmd", "hello from cmd\r\n"); // without extension should find the .exe (since its first in PATHEXT) - try testExec(allocator, "heLLo", "hello from exe\n"); + try testExec(gpa, "heLLo", "hello from exe\n"); // now rename the exe to not have an extension try renameExe(tmp.dir, "hello.exe", "hello"); // with extension should now fail - try testExecError(error.FileNotFound, allocator, "hello.exe"); + try testExecError(error.FileNotFound, gpa, "hello.exe"); // without extension should succeed (case insensitive) - try testExec(allocator, "heLLo", "hello from exe\n"); + try testExec(gpa, "heLLo", "hello from exe\n"); try tmp.dir.makeDir("something"); try renameExe(tmp.dir, "hello", "something/hello.exe"); - const relative_path_no_ext = try std.fs.path.join(allocator, &.{ tmp_relative_path, "something/hello" }); - defer allocator.free(relative_path_no_ext); + const relative_path_no_ext = try std.fs.path.join(gpa, &.{ tmp_relative_path, "something/hello" }); + defer gpa.free(relative_path_no_ext); // Giving a full relative path to something/hello should work - try testExec(allocator, relative_path_no_ext, "hello from exe\n"); + try testExec(gpa, relative_path_no_ext, "hello from exe\n"); // But commands with path separators get excluded from PATH searching, so this will fail - try testExecError(error.FileNotFound, allocator, "something/hello"); + try testExecError(error.FileNotFound, gpa, "something/hello"); // Now that .BAT is the first PATHEXT that should be found, this should succeed - try testExec(allocator, "heLLo", "hello from bat\r\n"); + try testExec(gpa, "heLLo", "hello from bat\r\n"); // Add a hello.exe that is not a valid executable try tmp.dir.writeFile(.{ .sub_path = "hello.exe", .data = "invalid" }); @@ -100,18 +105,18 @@ pub fn main() anyerror!void { // case for .EXE extensions, where if they ever try to get executed but they are // invalid, that gets treated as a fatal error wherever they are found and InvalidExe // is returned immediately. - try testExecError(error.InvalidExe, allocator, "hello.exe"); + try testExecError(error.InvalidExe, gpa, "hello.exe"); // Same thing applies to the command with no extension--even though there is a // hello.bat that could be executed, it should stop after it tries executing // hello.exe and getting InvalidExe. - try testExecError(error.InvalidExe, allocator, "hello"); + try testExecError(error.InvalidExe, gpa, "hello"); // If we now rename hello.exe to have no extension, it will behave differently try renameExe(tmp.dir, "hello.exe", "hello"); // Now, trying to execute it without an extension should treat InvalidExe as recoverable // and skip over it and find hello.bat and execute that - try testExec(allocator, "hello", "hello from bat\r\n"); + try testExec(gpa, "hello", "hello from bat\r\n"); // If we rename the invalid exe to something else try renameExe(tmp.dir, "hello", "goodbye"); @@ -119,13 +124,13 @@ pub fn main() anyerror!void { // since that is what the original error will be after searching for 'goodbye' // in the cwd. It will try to execute 'goodbye' from the PATH but the InvalidExe error // should be ignored in this case. - try testExecError(error.FileNotFound, allocator, "goodbye"); + try testExecError(error.FileNotFound, gpa, "goodbye"); // Now let's set the tmp dir as the cwd and set the path only include the "something" sub dir try tmp.dir.setAsCwd(); defer tmp.parent_dir.setAsCwd() catch {}; - const something_subdir_abs_path = try std.mem.concatWithSentinel(allocator, u16, &.{ tmp_absolute_path_w, utf16Literal("\\something") }, 0); - defer allocator.free(something_subdir_abs_path); + const something_subdir_abs_path = try std.mem.concatWithSentinel(gpa, u16, &.{ tmp_absolute_path_w, utf16Literal("\\something") }, 0); + defer gpa.free(something_subdir_abs_path); std.debug.assert(windows.kernel32.SetEnvironmentVariableW( utf16Literal("PATH"), @@ -134,37 +139,37 @@ pub fn main() anyerror!void { // Now trying to execute goodbye should give error.InvalidExe since it's the original // error that we got when trying within the cwd - try testExecError(error.InvalidExe, allocator, "goodbye"); + try testExecError(error.InvalidExe, gpa, "goodbye"); // hello should still find the .bat - try testExec(allocator, "hello", "hello from bat\r\n"); + try testExec(gpa, "hello", "hello from bat\r\n"); // If we rename something/hello.exe to something/goodbye.exe try renameExe(tmp.dir, "something/hello.exe", "something/goodbye.exe"); // And try to execute goodbye, then the one in something should be found // since the one in cwd is an invalid executable - try testExec(allocator, "goodbye", "hello from exe\n"); + try testExec(gpa, "goodbye", "hello from exe\n"); // If we use an absolute path to execute the invalid goodbye - const goodbye_abs_path = try std.mem.join(allocator, "\\", &.{ tmp_absolute_path, "goodbye" }); - defer allocator.free(goodbye_abs_path); + const goodbye_abs_path = try std.mem.join(gpa, "\\", &.{ tmp_absolute_path, "goodbye" }); + defer gpa.free(goodbye_abs_path); // then the PATH should not be searched and we should get InvalidExe - try testExecError(error.InvalidExe, allocator, goodbye_abs_path); + try testExecError(error.InvalidExe, gpa, goodbye_abs_path); // If we try to exec but provide a cwd that is an absolute path, the PATH // should still be searched and the goodbye.exe in something should be found. - try testExecWithCwd(allocator, "goodbye", tmp_absolute_path, "hello from exe\n"); + try testExecWithCwd(gpa, "goodbye", tmp_absolute_path, "hello from exe\n"); // introduce some extra path separators into the path which is dealt with inside the spawn call. const denormed_something_subdir_size = std.mem.replacementSize(u16, something_subdir_abs_path, utf16Literal("\\"), utf16Literal("\\\\\\\\")); - const denormed_something_subdir_abs_path = try allocator.allocSentinel(u16, denormed_something_subdir_size, 0); - defer allocator.free(denormed_something_subdir_abs_path); + const denormed_something_subdir_abs_path = try gpa.allocSentinel(u16, denormed_something_subdir_size, 0); + defer gpa.free(denormed_something_subdir_abs_path); _ = std.mem.replace(u16, something_subdir_abs_path, utf16Literal("\\"), utf16Literal("\\\\\\\\"), denormed_something_subdir_abs_path); - const denormed_something_subdir_wtf8 = try std.unicode.wtf16LeToWtf8Alloc(allocator, denormed_something_subdir_abs_path); - defer allocator.free(denormed_something_subdir_wtf8); + const denormed_something_subdir_wtf8 = try std.unicode.wtf16LeToWtf8Alloc(gpa, denormed_something_subdir_abs_path); + defer gpa.free(denormed_something_subdir_wtf8); // clear the path to ensure that the match comes from the cwd std.debug.assert(windows.kernel32.SetEnvironmentVariableW( @@ -172,18 +177,18 @@ pub fn main() anyerror!void { null, ) == windows.TRUE); - try testExecWithCwd(allocator, "goodbye", denormed_something_subdir_wtf8, "hello from exe\n"); + try testExecWithCwd(gpa, "goodbye", denormed_something_subdir_wtf8, "hello from exe\n"); // normalization should also work if the non-normalized path is found in the PATH var. std.debug.assert(windows.kernel32.SetEnvironmentVariableW( utf16Literal("PATH"), denormed_something_subdir_abs_path, ) == windows.TRUE); - try testExec(allocator, "goodbye", "hello from exe\n"); + try testExec(gpa, "goodbye", "hello from exe\n"); // now make sure we can launch executables "outside" of the cwd var subdir_cwd = try tmp.dir.openDir(denormed_something_subdir_wtf8, .{}); - defer subdir_cwd.close(); + defer subdir_cwd.close(io); try renameExe(tmp.dir, "something/goodbye.exe", "hello.exe"); try subdir_cwd.setAsCwd(); @@ -195,25 +200,24 @@ pub fn main() anyerror!void { ) == windows.TRUE); // while we're at it make sure non-windows separators work fine - try testExec(allocator, "../hello", "hello from exe\n"); + try testExec(gpa, "../hello", "hello from exe\n"); } -fn testExecError(err: anyerror, allocator: std.mem.Allocator, command: []const u8) !void { - return std.testing.expectError(err, testExec(allocator, command, "")); +fn testExecError(err: anyerror, gpa: Allocator, command: []const u8) !void { + return std.testing.expectError(err, testExec(gpa, command, "")); } -fn testExec(allocator: std.mem.Allocator, command: []const u8, expected_stdout: []const u8) !void { - return testExecWithCwd(allocator, command, null, expected_stdout); +fn testExec(gpa: Allocator, command: []const u8, expected_stdout: []const u8) !void { + return testExecWithCwd(gpa, command, null, expected_stdout); } -fn testExecWithCwd(allocator: std.mem.Allocator, command: []const u8, cwd: ?[]const u8, expected_stdout: []const u8) !void { - const result = try std.process.Child.run(.{ - .allocator = allocator, +fn testExecWithCwd(gpa: Allocator, io: Io, command: []const u8, cwd: ?[]const u8, expected_stdout: []const u8) !void { + const result = try std.process.Child.run(gpa, io, .{ .argv = &[_][]const u8{command}, .cwd = cwd, }); - defer allocator.free(result.stdout); - defer allocator.free(result.stderr); + defer gpa.free(result.stdout); + defer gpa.free(result.stderr); try std.testing.expectEqualStrings("", result.stderr); try std.testing.expectEqualStrings(expected_stdout, result.stdout); diff --git a/test/tests.zig b/test/tests.zig index 2b0e8fb5d0..5ca170c83c 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -2024,6 +2024,7 @@ pub fn addLinkTests( pub fn addCliTests(b: *std.Build) *Step { const step = b.step("test-cli", "Test the command line interface"); const s = std.fs.path.sep_str; + const io = b.graph.io; { // Test `zig init`. @@ -2132,13 +2133,13 @@ pub fn addCliTests(b: *std.Build) *Step { const tmp_path = b.makeTempPath(); const unformatted_code = " // no reason for indent"; - var dir = std.fs.cwd().openDir(tmp_path, .{}) catch @panic("unhandled"); - defer dir.close(); + var dir = std.Io.Dir.cwd().openDir(io, tmp_path, .{}) catch @panic("unhandled"); + defer dir.close(io); dir.writeFile(.{ .sub_path = "fmt1.zig", .data = unformatted_code }) catch @panic("unhandled"); dir.writeFile(.{ .sub_path = "fmt2.zig", .data = unformatted_code }) catch @panic("unhandled"); dir.makeDir("subdir") catch @panic("unhandled"); - var subdir = dir.openDir("subdir", .{}) catch @panic("unhandled"); - defer subdir.close(); + var subdir = dir.openDir(io, "subdir", .{}) catch @panic("unhandled"); + defer subdir.close(io); subdir.writeFile(.{ .sub_path = "fmt3.zig", .data = unformatted_code }) catch @panic("unhandled"); // Test zig fmt affecting only the appropriate files. @@ -2634,7 +2635,7 @@ pub fn addCases( var cases = @import("src/Cases.zig").init(gpa, arena); var dir = try b.build_root.handle.openDir(io, "test/cases", .{ .iterate = true }); - defer dir.close(); + defer dir.close(io); cases.addFromDir(dir, b); try @import("cases.zig").addCases(&cases, build_options, b); @@ -2680,6 +2681,8 @@ pub fn addDebuggerTests(b: *std.Build, options: DebuggerContext.Options) ?*Step } pub fn addIncrementalTests(b: *std.Build, test_step: *Step) !void { + const io = b.graph.io; + const incr_check = b.addExecutable(.{ .name = "incr-check", .root_module = b.createModule(.{ @@ -2689,11 +2692,11 @@ pub fn addIncrementalTests(b: *std.Build, test_step: *Step) !void { }), }); - var dir = try b.build_root.handle.openDir("test/incremental", .{ .iterate = true }); - defer dir.close(); + var dir = try b.build_root.handle.openDir(io, "test/incremental", .{ .iterate = true }); + defer dir.close(io); var it = try dir.walk(b.graph.arena); - while (try it.next()) |entry| { + while (try it.next(io)) |entry| { if (entry.kind != .file) continue; const run = b.addRunArtifact(incr_check);