authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-08 18:24:52-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-23 22:15:08-08:00
log9e3bda5efffb12dd6491b4f7c92ccc89b9043c64
treeccb1af8488c729172ca9727ccbbf8dd04348b546
parenta91c6dc71d42ea59ec53ce4e0ae83e4970731313

tests: close() -> close(io)


21 files changed, 187 insertions(+), 139 deletions(-)

lib/std/Io/File/Writer.zig+4-2
......@@ -56,8 +56,9 @@ pub const WriteFileError = error{
5656
5757pub const SeekError = Io.File.SeekError;
5858
59pub fn init(file: File, buffer: []u8) Writer {
59pub fn init(file: File, io: Io, buffer: []u8) Writer {
6060 return .{
61 .io = io,
6162 .file = file,
6263 .interface = initInterface(buffer),
6364 .mode = .positional,
......@@ -67,8 +68,9 @@ pub fn init(file: File, buffer: []u8) Writer {
6768/// Positional is more threadsafe, since the global seek position is not
6869/// affected, but when such syscalls are not available, preemptively
6970/// initializing in streaming mode will skip a failed syscall.
70pub fn initStreaming(file: File, buffer: []u8) Writer {
71pub fn initStreaming(file: File, io: Io, buffer: []u8) Writer {
7172 return .{
73 .io = io,
7274 .file = file,
7375 .interface = initInterface(buffer),
7476 .mode = .streaming,
lib/std/process/Child.zig+8-9
......@@ -435,8 +435,7 @@ pub const RunError = posix.GetCwdError || posix.ReadError || SpawnError || posix
435435
436436/// Spawns a child process, waits for it, collecting stdout and stderr, and then returns.
437437/// If it succeeds, the caller owns result.stdout and result.stderr memory.
438pub fn run(args: struct {
439 allocator: mem.Allocator,
438pub fn run(allocator: Allocator, io: Io, args: struct {
440439 argv: []const []const u8,
441440 cwd: ?[]const u8 = null,
442441 cwd_dir: ?Io.Dir = null,
......@@ -447,7 +446,7 @@ pub fn run(args: struct {
447446 expand_arg0: Arg0Expand = .no_expand,
448447 progress_node: std.Progress.Node = std.Progress.Node.none,
449448}) RunError!RunResult {
450 var child = ChildProcess.init(args.argv, args.allocator);
449 var child = ChildProcess.init(args.argv, allocator);
451450 child.stdin_behavior = .Ignore;
452451 child.stdout_behavior = .Pipe;
453452 child.stderr_behavior = .Pipe;
......@@ -458,19 +457,19 @@ pub fn run(args: struct {
458457 child.progress_node = args.progress_node;
459458
460459 var stdout: ArrayList(u8) = .empty;
461 defer stdout.deinit(args.allocator);
460 defer stdout.deinit(allocator);
462461 var stderr: ArrayList(u8) = .empty;
463 defer stderr.deinit(args.allocator);
462 defer stderr.deinit(allocator);
464463
465464 try child.spawn();
466465 errdefer {
467 _ = child.kill() catch {};
466 _ = child.kill(io) catch {};
468467 }
469 try child.collectOutput(args.allocator, &stdout, &stderr, args.max_output_bytes);
468 try child.collectOutput(allocator, &stdout, &stderr, args.max_output_bytes);
470469
471470 return .{
472 .stdout = try stdout.toOwnedSlice(args.allocator),
473 .stderr = try stderr.toOwnedSlice(args.allocator),
471 .stdout = try stdout.toOwnedSlice(allocator),
472 .stderr = try stderr.toOwnedSlice(allocator),
474473 .term = try child.wait(),
475474 };
476475}
test/src/convert-stack-trace.zig+3-3
......@@ -41,13 +41,13 @@ pub fn main() !void {
4141 var read_buf: [1024]u8 = undefined;
4242 var write_buf: [1024]u8 = undefined;
4343
44 const in_file = try std.fs.cwd().openFile(args[1], .{});
45 defer in_file.close();
44 const in_file = try std.Io.Dir.cwd().openFile(io, args[1], .{});
45 defer in_file.close(io);
4646
4747 const out_file: std.Io.File = .stdout();
4848
4949 var in_fr = in_file.reader(io, &read_buf);
50 var out_fw = out_file.writer(&write_buf);
50 var out_fw = out_file.writer(io, &write_buf);
5151
5252 const w = &out_fw.interface;
5353
test/standalone/child_process/main.zig+8-7
......@@ -1,4 +1,5 @@
11const std = @import("std");
2const Io = std.Io;
23
34pub fn main() !void {
45 // make sure safety checks are enabled even in release modes
......@@ -20,7 +21,7 @@ pub fn main() !void {
2021 };
2122 defer if (needs_free) gpa.free(child_path);
2223
23 var threaded: std.Io.Threaded = .init(gpa);
24 var threaded: Io.Threaded = .init(gpa);
2425 defer threaded.deinit();
2526 const io = threaded.io();
2627
......@@ -31,7 +32,7 @@ pub fn main() !void {
3132 try child.spawn();
3233 const child_stdin = child.stdin.?;
3334 try child_stdin.writeAll("hello from stdin"); // verified in child
34 child_stdin.close();
35 child_stdin.close(io);
3536 child.stdin = null;
3637
3738 const hello_stdout = "hello from stdout";
......@@ -39,17 +40,17 @@ pub fn main() !void {
3940 var stdout_reader = child.stdout.?.readerStreaming(io, &.{});
4041 const n = try stdout_reader.interface.readSliceShort(&buf);
4142 if (!std.mem.eql(u8, buf[0..n], hello_stdout)) {
42 testError("child stdout: '{s}'; want '{s}'", .{ buf[0..n], hello_stdout });
43 testError(io, "child stdout: '{s}'; want '{s}'", .{ buf[0..n], hello_stdout });
4344 }
4445
4546 switch (try child.wait()) {
4647 .Exited => |code| {
4748 const child_ok_code = 42; // set by child if no test errors
4849 if (code != child_ok_code) {
49 testError("child exit code: {d}; want {d}", .{ code, child_ok_code });
50 testError(io, "child exit code: {d}; want {d}", .{ code, child_ok_code });
5051 }
5152 },
52 else => |term| testError("abnormal child exit: {}", .{term}),
53 else => |term| testError(io, "abnormal child exit: {}", .{term}),
5354 }
5455 if (parent_test_error) return error.ParentTestError;
5556
......@@ -61,8 +62,8 @@ pub fn main() !void {
6162
6263var parent_test_error = false;
6364
64fn testError(comptime fmt: []const u8, args: anytype) void {
65 var stderr_writer = std.Io.File.stderr().writer(&.{});
65fn testError(io: Io, comptime fmt: []const u8, args: anytype) void {
66 var stderr_writer = Io.File.stderr().writer(io, &.{});
6667 const stderr = &stderr_writer.interface;
6768 stderr.print("PARENT TEST ERROR: ", .{}) catch {};
6869 stderr.print(fmt, args) catch {};
test/standalone/cmakedefine/check.zig+5-2
......@@ -9,8 +9,11 @@ pub fn main() !void {
99 const actual_path = args[1];
1010 const expected_path = args[2];
1111
12 const actual = try std.fs.cwd().readFileAlloc(actual_path, arena, .limited(1024 * 1024));
13 const expected = try std.fs.cwd().readFileAlloc(expected_path, arena, .limited(1024 * 1024));
12 var threaded: std.Io.Threaded = .init_single_threaded;
13 const io = threaded.io();
14
15 const actual = try std.Io.Dir.cwd().readFileAlloc(io, actual_path, arena, .limited(1024 * 1024));
16 const expected = try std.Io.Dir.cwd().readFileAlloc(io, expected_path, arena, .limited(1024 * 1024));
1417
1518 // The actual output starts with a comment which we should strip out before comparing.
1619 const comment_str = "/* This file was generated by ConfigHeader using the Zig Build System. */\n";
test/standalone/dirname/build.zig+6-4
......@@ -59,13 +59,15 @@ pub fn build(b: *std.Build) void {
5959
6060 // Absolute path:
6161 const abs_path = setup_abspath: {
62 // TODO this is a bad pattern, don't do this
63 const io = b.graph.io;
6264 const temp_dir = b.makeTempPath();
6365
64 var dir = std.fs.cwd().openDir(temp_dir, .{}) catch @panic("failed to open temp dir");
65 defer dir.close();
66 var dir = std.Io.Dir.cwd().openDir(io, temp_dir, .{}) catch @panic("failed to open temp dir");
67 defer dir.close(io);
6668
67 var file = dir.createFile("foo.txt", .{}) catch @panic("failed to create file");
68 file.close();
69 var file = dir.createFile(io, "foo.txt", .{}) catch @panic("failed to create file");
70 file.close(io);
6971
7072 break :setup_abspath std.Build.LazyPath{ .cwd_relative = temp_dir };
7173 };
test/standalone/dirname/exists_in.zig+6-3
......@@ -34,8 +34,11 @@ fn run(allocator: std.mem.Allocator) !void {
3434 return error.BadUsage;
3535 };
3636
37 var dir = try std.fs.cwd().openDir(dir_path, .{});
38 defer dir.close();
37 var threaded: std.Io.Threaded = .init_single_threaded;
38 const io = threaded.io();
3939
40 _ = try dir.statFile(relpath);
40 var dir = try std.Io.Dir.cwd().openDir(io, dir_path, .{});
41 defer dir.close(io);
42
43 _ = try dir.statFile(io, relpath);
4144}
test/standalone/dirname/touch.zig+10-7
......@@ -26,14 +26,17 @@ fn run(allocator: std.mem.Allocator) !void {
2626 return error.BadUsage;
2727 };
2828
29 const dir_path = std.fs.path.dirname(path) orelse unreachable;
30 const basename = std.fs.path.basename(path);
29 const dir_path = std.Io.Dir.path.dirname(path) orelse unreachable;
30 const basename = std.Io.Dir.path.basename(path);
3131
32 var dir = try std.fs.cwd().openDir(dir_path, .{});
33 defer dir.close();
32 var threaded: std.Io.Threaded = .init_single_threaded;
33 const io = threaded.io();
3434
35 _ = dir.statFile(basename) catch {
36 var file = try dir.createFile(basename, .{});
37 file.close();
35 var dir = try std.Io.Dir.cwd().openDir(io, dir_path, .{});
36 defer dir.close(io);
37
38 _ = dir.statFile(io, basename) catch {
39 var file = try dir.createFile(io, basename, .{});
40 file.close(io);
3841 };
3942}
test/standalone/entry_point/check_differ.zig+5-2
......@@ -6,8 +6,11 @@ pub fn main() !void {
66 const args = try std.process.argsAlloc(arena);
77 if (args.len != 3) return error.BadUsage; // usage: 'check_differ <path a> <path b>'
88
9 const contents_1 = try std.fs.cwd().readFileAlloc(args[1], arena, .limited(1024 * 1024 * 64)); // 64 MiB ought to be plenty
10 const contents_2 = try std.fs.cwd().readFileAlloc(args[2], arena, .limited(1024 * 1024 * 64)); // 64 MiB ought to be plenty
9 var threaded: std.Io.Threaded = .init_single_threaded;
10 const io = threaded.io();
11
12 const contents_1 = try std.Io.Dir.cwd().readFileAlloc(io, args[1], arena, .limited(1024 * 1024 * 64)); // 64 MiB ought to be plenty
13 const contents_2 = try std.Io.Dir.cwd().readFileAlloc(io, args[2], arena, .limited(1024 * 1024 * 64)); // 64 MiB ought to be plenty
1114
1215 if (std.mem.eql(u8, contents_1, contents_2)) {
1316 return error.FilesMatch;
test/standalone/glibc_compat/glibc_runtime_check.zig+2-2
......@@ -28,10 +28,10 @@ extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: [*]const u8) c_int;
2828
2929// PR #17034 - fstat moved between libc_nonshared and libc
3030fn checkStat() !void {
31 const cwdFd = std.fs.cwd().fd;
31 const cwd_fd = std.Io.Dir.cwd().handle;
3232
3333 var buf: [256]u8 = @splat(0);
34 var result = fstatat(cwdFd, "a_file_that_definitely_does_not_exist", &buf, 0);
34 var result = fstatat(cwd_fd, "a_file_that_definitely_does_not_exist", &buf, 0);
3535 assert(result == -1);
3636 assert(std.posix.errno(result) == .NOENT);
3737
test/standalone/install_headers/check_exists.zig+7-4
......@@ -11,8 +11,11 @@ pub fn main() !void {
1111 var arg_it = try std.process.argsWithAllocator(arena);
1212 _ = arg_it.next();
1313
14 const cwd = std.fs.cwd();
15 const cwd_realpath = try cwd.realpathAlloc(arena, ".");
14 var threaded: std.Io.Threaded = .init_single_threaded;
15 const io = threaded.io();
16
17 const cwd = std.Io.Dir.cwd();
18 const cwd_realpath = try cwd.realPathAlloc(io, arena, ".");
1619
1720 while (arg_it.next()) |file_path| {
1821 if (file_path.len > 0 and file_path[0] == '!') {
......@@ -20,7 +23,7 @@ pub fn main() !void {
2023 "exclusive file check '{s}{c}{s}' failed",
2124 .{ cwd_realpath, std.fs.path.sep, file_path[1..] },
2225 );
23 if (std.fs.cwd().statFile(file_path[1..])) |_| {
26 if (cwd.statFile(io, file_path[1..])) |_| {
2427 return error.FileFound;
2528 } else |err| switch (err) {
2629 error.FileNotFound => {},
......@@ -31,7 +34,7 @@ pub fn main() !void {
3134 "inclusive file check '{s}{c}{s}' failed",
3235 .{ cwd_realpath, std.fs.path.sep, file_path },
3336 );
34 _ = try std.fs.cwd().statFile(file_path);
37 _ = try cwd.statFile(io, file_path);
3538 }
3639 }
3740}
test/standalone/libfuzzer/main.zig+5-5
......@@ -20,8 +20,8 @@ pub fn main() !void {
2020 const io = threaded.io();
2121
2222 const cache_dir_path = args.next() orelse @panic("expected cache directory path argument");
23 var cache_dir = try std.fs.cwd().openDir(cache_dir_path, .{});
24 defer cache_dir.close();
23 var cache_dir = try std.Io.Dir.cwd().openDir(io, cache_dir_path, .{});
24 defer cache_dir.close(io);
2525
2626 abi.fuzzer_init(.fromSlice(cache_dir_path));
2727 abi.fuzzer_init_test(testOne, .fromSlice("test"));
......@@ -30,8 +30,8 @@ pub fn main() !void {
3030
3131 const pc_digest = abi.fuzzer_coverage().id;
3232 const coverage_file_path = "v/" ++ std.fmt.hex(pc_digest);
33 const coverage_file = try cache_dir.openFile(coverage_file_path, .{});
34 defer coverage_file.close();
33 const coverage_file = try cache_dir.openFile(io, coverage_file_path, .{});
34 defer coverage_file.close(io);
3535
3636 var read_buf: [@sizeOf(abi.SeenPcsHeader)]u8 = undefined;
3737 var r = coverage_file.reader(io, &read_buf);
......@@ -42,6 +42,6 @@ pub fn main() !void {
4242 const expected_len = @sizeOf(abi.SeenPcsHeader) +
4343 try std.math.divCeil(usize, pcs_header.pcs_len, @bitSizeOf(usize)) * @sizeOf(usize) +
4444 pcs_header.pcs_len * @sizeOf(usize);
45 if (try coverage_file.getEndPos() != expected_len)
45 if (try coverage_file.length(io) != expected_len)
4646 return error.WrongEnd;
4747}
test/standalone/posix/relpaths.zig+15-8
......@@ -1,9 +1,11 @@
11// Test relative paths through POSIX APIS. These tests have to change the cwd, so
22// they shouldn't be Zig unit tests.
33
4const std = @import("std");
54const builtin = @import("builtin");
65
6const std = @import("std");
7const Io = std.Io;
8
79pub fn main() !void {
810 if (builtin.target.os.tag == .wasi) return; // Can link, but can't change into tmpDir
911
......@@ -11,6 +13,11 @@ pub fn main() !void {
1113 const a = Allocator.allocator();
1214 defer std.debug.assert(Allocator.deinit() == .ok);
1315
16 var threaded: std.Io.Threaded = .init_single_threaded;
17 const io = threaded.io();
18
19 // TODO this API isn't supposed to be used outside of unit testing. make it compilation error if used
20 // outside of unit testing.
1421 var tmp = std.testing.tmpDir(.{});
1522 defer tmp.cleanup();
1623
......@@ -18,7 +25,7 @@ pub fn main() !void {
1825 try tmp.dir.setAsCwd();
1926
2027 try test_symlink(a, tmp);
21 try test_link(tmp);
28 try test_link(io, tmp);
2229}
2330
2431fn 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
6572 return .{ st.ino, st.nlink };
6673}
6774
68fn test_link(tmp: std.testing.TmpDir) !void {
75fn test_link(io: Io, tmp: std.testing.TmpDir) !void {
6976 switch (builtin.target.os.tag) {
7077 .linux, .illumos => {},
7178 else => return,
......@@ -74,17 +81,17 @@ fn test_link(tmp: std.testing.TmpDir) !void {
7481 const target_name = "link-target";
7582 const link_name = "newlink";
7683
77 try tmp.dir.writeFile(.{ .sub_path = target_name, .data = "example" });
84 try tmp.dir.writeFile(io, .{ .sub_path = target_name, .data = "example" });
7885
7986 // Test 1: create the relative link from inside tmp
8087 try std.posix.link(target_name, link_name);
8188
8289 // Verify
83 const efd = try tmp.dir.openFile(target_name, .{});
84 defer efd.close();
90 const efd = try tmp.dir.openFile(io, target_name, .{});
91 defer efd.close(io);
8592
86 const nfd = try tmp.dir.openFile(link_name, .{});
87 defer nfd.close();
93 const nfd = try tmp.dir.openFile(io, link_name, .{});
94 defer nfd.close(io);
8895
8996 {
9097 const eino, _ = try getLinkInfo(efd.handle);
test/standalone/run_cwd/check_file_exists.zig+4-1
......@@ -8,7 +8,10 @@ pub fn main() !void {
88 if (args.len != 2) return error.BadUsage;
99 const path = args[1];
1010
11 std.fs.cwd().access(path, .{}) catch return error.AccessFailed;
11 var threaded: std.Io.Threaded = .init_single_threaded;
12 const io = threaded.io();
13
14 std.Io.Dir.cwd().access(io, path, .{}) catch return error.AccessFailed;
1215}
1316
1417const std = @import("std");
test/standalone/run_output_caching/main.zig+5-3
......@@ -1,10 +1,12 @@
11const std = @import("std");
22
33pub fn main() !void {
4 var threaded: std.Io.Threaded = .init_single_threaded;
5 const io = threaded.io();
46 var args = try std.process.argsWithAllocator(std.heap.page_allocator);
57 _ = args.skip();
68 const filename = args.next().?;
7 const file = try std.fs.cwd().createFile(filename, .{});
8 defer file.close();
9 try file.writeAll(filename);
9 const file = try std.Io.Dir.cwd().createFile(io, filename, .{});
10 defer file.close(io);
11 try file.writeAll(io, filename);
1012}
test/standalone/run_output_paths/create_file.zig+3-1
......@@ -1,10 +1,12 @@
11const std = @import("std");
22
33pub fn main() !void {
4 var threaded: std.Io.Threaded = .init_single_threaded;
5 const io = threaded.io();
46 var args = try std.process.argsWithAllocator(std.heap.page_allocator);
57 _ = args.skip();
68 const dir_name = args.next().?;
7 const dir = try std.fs.cwd().openDir(if (std.mem.startsWith(u8, dir_name, "--dir="))
9 const dir = try std.Io.Dir.cwd().openDir(io, if (std.mem.startsWith(u8, dir_name, "--dir="))
810 dir_name["--dir=".len..]
911 else
1012 dir_name, .{});
test/standalone/self_exe_symlink/create-symlink.zig+5-1
......@@ -14,5 +14,9 @@ pub fn main() anyerror!void {
1414 // If `exe_path` is relative to our cwd, we need to convert it to be relative to the dirname of `symlink_path`.
1515 const exe_rel_path = try std.fs.path.relative(allocator, std.fs.path.dirname(symlink_path) orelse ".", exe_path);
1616 defer allocator.free(exe_rel_path);
17 try std.fs.cwd().symLink(exe_rel_path, symlink_path, .{});
17
18 var threaded: std.Io.Threaded = .init_single_threaded;
19 const io = threaded.io();
20
21 try std.Io.Dir.cwd().symLink(io, exe_rel_path, symlink_path, .{});
1822}
test/standalone/self_exe_symlink/main.zig+10-6
......@@ -1,15 +1,19 @@
11const std = @import("std");
22
33pub fn main() !void {
4 var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
5 defer std.debug.assert(gpa.deinit() == .ok);
6 const allocator = gpa.allocator();
4 var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
5 defer if (debug_allocator.deinit() == .leak) @panic("found memory leaks");
6 const gpa = debug_allocator.allocator();
77
8 const self_path = try std.fs.selfExePathAlloc(allocator);
9 defer allocator.free(self_path);
8 var threaded: std.Io.Threaded = .init(gpa);
9 defer threaded.deinit();
10 const io = threaded.io();
11
12 const self_path = try std.fs.selfExePathAlloc(gpa);
13 defer gpa.free(self_path);
1014
1115 var self_exe = try std.fs.openSelfExe(.{});
12 defer self_exe.close();
16 defer self_exe.close(io);
1317 var buf: [std.fs.max_path_bytes]u8 = undefined;
1418 const self_exe_path = try std.os.getFdPath(self_exe.handle, &buf);
1519
test/standalone/simple/cat/main.zig+3-3
......@@ -18,7 +18,7 @@ pub fn main() !void {
1818 const exe = args[0];
1919 var catted_anything = false;
2020 var stdout_buffer: [4096]u8 = undefined;
21 var stdout_writer = Io.File.stdout().writerStreaming(&stdout_buffer);
21 var stdout_writer = Io.File.stdout().writerStreaming(io, &stdout_buffer);
2222 const stdout = &stdout_writer.interface;
2323 var stdin_reader = Io.File.stdin().readerStreaming(io, &.{});
2424
......@@ -32,8 +32,8 @@ pub fn main() !void {
3232 } else if (mem.startsWith(u8, arg, "-")) {
3333 return usage(exe);
3434 } else {
35 const file = cwd.openFile(arg, .{}) catch |err| fatal("unable to open file: {t}\n", .{err});
36 defer file.close();
35 const file = cwd.openFile(io, arg, .{}) catch |err| fatal("unable to open file: {t}\n", .{err});
36 defer file.close(io);
3737
3838 catted_anything = true;
3939 var file_reader = file.reader(io, &.{});
test/standalone/windows_spawn/main.zig+62-58
......@@ -1,15 +1,20 @@
11const std = @import("std");
22const Io = std.Io;
3const Allocator = std.mem.Allocator;
34
45const windows = std.os.windows;
56const utf16Literal = std.unicode.utf8ToUtf16LeStringLiteral;
67
78pub fn main() anyerror!void {
8 var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
9 defer if (gpa.deinit() == .leak) @panic("found memory leaks");
10 const allocator = gpa.allocator();
9 var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
10 defer if (debug_allocator.deinit() == .leak) @panic("found memory leaks");
11 const gpa = debug_allocator.allocator();
1112
12 var it = try std.process.argsWithAllocator(allocator);
13 var threaded: std.Io.Threaded = .init(gpa);
14 defer threaded.deinit();
15 const io = threaded.io();
16
17 var it = try std.process.argsWithAllocator(gpa);
1318 defer it.deinit();
1419 _ = it.next() orelse unreachable; // skip binary name
1520 const hello_exe_cache_path = it.next() orelse unreachable;
......@@ -17,14 +22,14 @@ pub fn main() anyerror!void {
1722 var tmp = std.testing.tmpDir(.{});
1823 defer tmp.cleanup();
1924
20 const tmp_absolute_path = try tmp.dir.realpathAlloc(allocator, ".");
21 defer allocator.free(tmp_absolute_path);
22 const tmp_absolute_path_w = try std.unicode.utf8ToUtf16LeAllocZ(allocator, tmp_absolute_path);
23 defer allocator.free(tmp_absolute_path_w);
24 const cwd_absolute_path = try std.fs.cwd().realpathAlloc(allocator, ".");
25 defer allocator.free(cwd_absolute_path);
26 const tmp_relative_path = try std.fs.path.relative(allocator, cwd_absolute_path, tmp_absolute_path);
27 defer allocator.free(tmp_relative_path);
25 const tmp_absolute_path = try tmp.dir.realpathAlloc(gpa, ".");
26 defer gpa.free(tmp_absolute_path);
27 const tmp_absolute_path_w = try std.unicode.utf8ToUtf16LeAllocZ(gpa, tmp_absolute_path);
28 defer gpa.free(tmp_absolute_path_w);
29 const cwd_absolute_path = try Io.Dir.cwd().realpathAlloc(gpa, ".");
30 defer gpa.free(cwd_absolute_path);
31 const tmp_relative_path = try std.fs.path.relative(gpa, cwd_absolute_path, tmp_absolute_path);
32 defer gpa.free(tmp_relative_path);
2833
2934 // Clear PATH
3035 std.debug.assert(windows.kernel32.SetEnvironmentVariableW(
......@@ -39,10 +44,10 @@ pub fn main() anyerror!void {
3944 ) == windows.TRUE);
4045
4146 // No PATH, so it should fail to find anything not in the cwd
42 try testExecError(error.FileNotFound, allocator, "something_missing");
47 try testExecError(error.FileNotFound, gpa, "something_missing");
4348
4449 // make sure we don't get error.BadPath traversing out of cwd with a relative path
45 try testExecError(error.FileNotFound, allocator, "..\\.\\.\\.\\\\..\\more_missing");
50 try testExecError(error.FileNotFound, gpa, "..\\.\\.\\.\\\\..\\more_missing");
4651
4752 std.debug.assert(windows.kernel32.SetEnvironmentVariableW(
4853 utf16Literal("PATH"),
......@@ -50,14 +55,14 @@ pub fn main() anyerror!void {
5055 ) == windows.TRUE);
5156
5257 // Move hello.exe into the tmp dir which is now added to the path
53 try std.fs.cwd().copyFile(hello_exe_cache_path, tmp.dir, "hello.exe", .{});
58 try Io.Dir.cwd().copyFile(hello_exe_cache_path, tmp.dir, "hello.exe", .{});
5459
5560 // with extension should find the .exe (case insensitive)
56 try testExec(allocator, "HeLLo.exe", "hello from exe\n");
61 try testExec(gpa, "HeLLo.exe", "hello from exe\n");
5762 // without extension should find the .exe (case insensitive)
58 try testExec(allocator, "heLLo", "hello from exe\n");
63 try testExec(gpa, "heLLo", "hello from exe\n");
5964 // with invalid cwd
60 try std.testing.expectError(error.FileNotFound, testExecWithCwd(allocator, "hello.exe", "missing_dir", ""));
65 try std.testing.expectError(error.FileNotFound, testExecWithCwd(gpa, io, "hello.exe", "missing_dir", ""));
6166
6267 // now add a .bat
6368 try tmp.dir.writeFile(.{ .sub_path = "hello.bat", .data = "@echo hello from bat" });
......@@ -65,33 +70,33 @@ pub fn main() anyerror!void {
6570 try tmp.dir.writeFile(.{ .sub_path = "hello.cmd", .data = "@echo hello from cmd" });
6671
6772 // with extension should find the .bat (case insensitive)
68 try testExec(allocator, "heLLo.bat", "hello from bat\r\n");
73 try testExec(gpa, "heLLo.bat", "hello from bat\r\n");
6974 // with extension should find the .cmd (case insensitive)
70 try testExec(allocator, "heLLo.cmd", "hello from cmd\r\n");
75 try testExec(gpa, "heLLo.cmd", "hello from cmd\r\n");
7176 // without extension should find the .exe (since its first in PATHEXT)
72 try testExec(allocator, "heLLo", "hello from exe\n");
77 try testExec(gpa, "heLLo", "hello from exe\n");
7378
7479 // now rename the exe to not have an extension
7580 try renameExe(tmp.dir, "hello.exe", "hello");
7681
7782 // with extension should now fail
78 try testExecError(error.FileNotFound, allocator, "hello.exe");
83 try testExecError(error.FileNotFound, gpa, "hello.exe");
7984 // without extension should succeed (case insensitive)
80 try testExec(allocator, "heLLo", "hello from exe\n");
85 try testExec(gpa, "heLLo", "hello from exe\n");
8186
8287 try tmp.dir.makeDir("something");
8388 try renameExe(tmp.dir, "hello", "something/hello.exe");
8489
85 const relative_path_no_ext = try std.fs.path.join(allocator, &.{ tmp_relative_path, "something/hello" });
86 defer allocator.free(relative_path_no_ext);
90 const relative_path_no_ext = try std.fs.path.join(gpa, &.{ tmp_relative_path, "something/hello" });
91 defer gpa.free(relative_path_no_ext);
8792
8893 // Giving a full relative path to something/hello should work
89 try testExec(allocator, relative_path_no_ext, "hello from exe\n");
94 try testExec(gpa, relative_path_no_ext, "hello from exe\n");
9095 // But commands with path separators get excluded from PATH searching, so this will fail
91 try testExecError(error.FileNotFound, allocator, "something/hello");
96 try testExecError(error.FileNotFound, gpa, "something/hello");
9297
9398 // Now that .BAT is the first PATHEXT that should be found, this should succeed
94 try testExec(allocator, "heLLo", "hello from bat\r\n");
99 try testExec(gpa, "heLLo", "hello from bat\r\n");
95100
96101 // Add a hello.exe that is not a valid executable
97102 try tmp.dir.writeFile(.{ .sub_path = "hello.exe", .data = "invalid" });
......@@ -100,18 +105,18 @@ pub fn main() anyerror!void {
100105 // case for .EXE extensions, where if they ever try to get executed but they are
101106 // invalid, that gets treated as a fatal error wherever they are found and InvalidExe
102107 // is returned immediately.
103 try testExecError(error.InvalidExe, allocator, "hello.exe");
108 try testExecError(error.InvalidExe, gpa, "hello.exe");
104109 // Same thing applies to the command with no extension--even though there is a
105110 // hello.bat that could be executed, it should stop after it tries executing
106111 // hello.exe and getting InvalidExe.
107 try testExecError(error.InvalidExe, allocator, "hello");
112 try testExecError(error.InvalidExe, gpa, "hello");
108113
109114 // If we now rename hello.exe to have no extension, it will behave differently
110115 try renameExe(tmp.dir, "hello.exe", "hello");
111116
112117 // Now, trying to execute it without an extension should treat InvalidExe as recoverable
113118 // and skip over it and find hello.bat and execute that
114 try testExec(allocator, "hello", "hello from bat\r\n");
119 try testExec(gpa, "hello", "hello from bat\r\n");
115120
116121 // If we rename the invalid exe to something else
117122 try renameExe(tmp.dir, "hello", "goodbye");
......@@ -119,13 +124,13 @@ pub fn main() anyerror!void {
119124 // since that is what the original error will be after searching for 'goodbye'
120125 // in the cwd. It will try to execute 'goodbye' from the PATH but the InvalidExe error
121126 // should be ignored in this case.
122 try testExecError(error.FileNotFound, allocator, "goodbye");
127 try testExecError(error.FileNotFound, gpa, "goodbye");
123128
124129 // Now let's set the tmp dir as the cwd and set the path only include the "something" sub dir
125130 try tmp.dir.setAsCwd();
126131 defer tmp.parent_dir.setAsCwd() catch {};
127 const something_subdir_abs_path = try std.mem.concatWithSentinel(allocator, u16, &.{ tmp_absolute_path_w, utf16Literal("\\something") }, 0);
128 defer allocator.free(something_subdir_abs_path);
132 const something_subdir_abs_path = try std.mem.concatWithSentinel(gpa, u16, &.{ tmp_absolute_path_w, utf16Literal("\\something") }, 0);
133 defer gpa.free(something_subdir_abs_path);
129134
130135 std.debug.assert(windows.kernel32.SetEnvironmentVariableW(
131136 utf16Literal("PATH"),
......@@ -134,37 +139,37 @@ pub fn main() anyerror!void {
134139
135140 // Now trying to execute goodbye should give error.InvalidExe since it's the original
136141 // error that we got when trying within the cwd
137 try testExecError(error.InvalidExe, allocator, "goodbye");
142 try testExecError(error.InvalidExe, gpa, "goodbye");
138143
139144 // hello should still find the .bat
140 try testExec(allocator, "hello", "hello from bat\r\n");
145 try testExec(gpa, "hello", "hello from bat\r\n");
141146
142147 // If we rename something/hello.exe to something/goodbye.exe
143148 try renameExe(tmp.dir, "something/hello.exe", "something/goodbye.exe");
144149 // And try to execute goodbye, then the one in something should be found
145150 // since the one in cwd is an invalid executable
146 try testExec(allocator, "goodbye", "hello from exe\n");
151 try testExec(gpa, "goodbye", "hello from exe\n");
147152
148153 // If we use an absolute path to execute the invalid goodbye
149 const goodbye_abs_path = try std.mem.join(allocator, "\\", &.{ tmp_absolute_path, "goodbye" });
150 defer allocator.free(goodbye_abs_path);
154 const goodbye_abs_path = try std.mem.join(gpa, "\\", &.{ tmp_absolute_path, "goodbye" });
155 defer gpa.free(goodbye_abs_path);
151156 // then the PATH should not be searched and we should get InvalidExe
152 try testExecError(error.InvalidExe, allocator, goodbye_abs_path);
157 try testExecError(error.InvalidExe, gpa, goodbye_abs_path);
153158
154159 // If we try to exec but provide a cwd that is an absolute path, the PATH
155160 // should still be searched and the goodbye.exe in something should be found.
156 try testExecWithCwd(allocator, "goodbye", tmp_absolute_path, "hello from exe\n");
161 try testExecWithCwd(gpa, "goodbye", tmp_absolute_path, "hello from exe\n");
157162
158163 // introduce some extra path separators into the path which is dealt with inside the spawn call.
159164 const denormed_something_subdir_size = std.mem.replacementSize(u16, something_subdir_abs_path, utf16Literal("\\"), utf16Literal("\\\\\\\\"));
160165
161 const denormed_something_subdir_abs_path = try allocator.allocSentinel(u16, denormed_something_subdir_size, 0);
162 defer allocator.free(denormed_something_subdir_abs_path);
166 const denormed_something_subdir_abs_path = try gpa.allocSentinel(u16, denormed_something_subdir_size, 0);
167 defer gpa.free(denormed_something_subdir_abs_path);
163168
164169 _ = std.mem.replace(u16, something_subdir_abs_path, utf16Literal("\\"), utf16Literal("\\\\\\\\"), denormed_something_subdir_abs_path);
165170
166 const denormed_something_subdir_wtf8 = try std.unicode.wtf16LeToWtf8Alloc(allocator, denormed_something_subdir_abs_path);
167 defer allocator.free(denormed_something_subdir_wtf8);
171 const denormed_something_subdir_wtf8 = try std.unicode.wtf16LeToWtf8Alloc(gpa, denormed_something_subdir_abs_path);
172 defer gpa.free(denormed_something_subdir_wtf8);
168173
169174 // clear the path to ensure that the match comes from the cwd
170175 std.debug.assert(windows.kernel32.SetEnvironmentVariableW(
......@@ -172,18 +177,18 @@ pub fn main() anyerror!void {
172177 null,
173178 ) == windows.TRUE);
174179
175 try testExecWithCwd(allocator, "goodbye", denormed_something_subdir_wtf8, "hello from exe\n");
180 try testExecWithCwd(gpa, "goodbye", denormed_something_subdir_wtf8, "hello from exe\n");
176181
177182 // normalization should also work if the non-normalized path is found in the PATH var.
178183 std.debug.assert(windows.kernel32.SetEnvironmentVariableW(
179184 utf16Literal("PATH"),
180185 denormed_something_subdir_abs_path,
181186 ) == windows.TRUE);
182 try testExec(allocator, "goodbye", "hello from exe\n");
187 try testExec(gpa, "goodbye", "hello from exe\n");
183188
184189 // now make sure we can launch executables "outside" of the cwd
185190 var subdir_cwd = try tmp.dir.openDir(denormed_something_subdir_wtf8, .{});
186 defer subdir_cwd.close();
191 defer subdir_cwd.close(io);
187192
188193 try renameExe(tmp.dir, "something/goodbye.exe", "hello.exe");
189194 try subdir_cwd.setAsCwd();
......@@ -195,25 +200,24 @@ pub fn main() anyerror!void {
195200 ) == windows.TRUE);
196201
197202 // while we're at it make sure non-windows separators work fine
198 try testExec(allocator, "../hello", "hello from exe\n");
203 try testExec(gpa, "../hello", "hello from exe\n");
199204}
200205
201fn testExecError(err: anyerror, allocator: std.mem.Allocator, command: []const u8) !void {
202 return std.testing.expectError(err, testExec(allocator, command, ""));
206fn testExecError(err: anyerror, gpa: Allocator, command: []const u8) !void {
207 return std.testing.expectError(err, testExec(gpa, command, ""));
203208}
204209
205fn testExec(allocator: std.mem.Allocator, command: []const u8, expected_stdout: []const u8) !void {
206 return testExecWithCwd(allocator, command, null, expected_stdout);
210fn testExec(gpa: Allocator, command: []const u8, expected_stdout: []const u8) !void {
211 return testExecWithCwd(gpa, command, null, expected_stdout);
207212}
208213
209fn testExecWithCwd(allocator: std.mem.Allocator, command: []const u8, cwd: ?[]const u8, expected_stdout: []const u8) !void {
210 const result = try std.process.Child.run(.{
211 .allocator = allocator,
214fn testExecWithCwd(gpa: Allocator, io: Io, command: []const u8, cwd: ?[]const u8, expected_stdout: []const u8) !void {
215 const result = try std.process.Child.run(gpa, io, .{
212216 .argv = &[_][]const u8{command},
213217 .cwd = cwd,
214218 });
215 defer allocator.free(result.stdout);
216 defer allocator.free(result.stderr);
219 defer gpa.free(result.stdout);
220 defer gpa.free(result.stderr);
217221
218222 try std.testing.expectEqualStrings("", result.stderr);
219223 try std.testing.expectEqualStrings(expected_stdout, result.stdout);
test/tests.zig+11-8
......@@ -2024,6 +2024,7 @@ pub fn addLinkTests(
20242024pub fn addCliTests(b: *std.Build) *Step {
20252025 const step = b.step("test-cli", "Test the command line interface");
20262026 const s = std.fs.path.sep_str;
2027 const io = b.graph.io;
20272028
20282029 {
20292030 // Test `zig init`.
......@@ -2132,13 +2133,13 @@ pub fn addCliTests(b: *std.Build) *Step {
21322133 const tmp_path = b.makeTempPath();
21332134 const unformatted_code = " // no reason for indent";
21342135
2135 var dir = std.fs.cwd().openDir(tmp_path, .{}) catch @panic("unhandled");
2136 defer dir.close();
2136 var dir = std.Io.Dir.cwd().openDir(io, tmp_path, .{}) catch @panic("unhandled");
2137 defer dir.close(io);
21372138 dir.writeFile(.{ .sub_path = "fmt1.zig", .data = unformatted_code }) catch @panic("unhandled");
21382139 dir.writeFile(.{ .sub_path = "fmt2.zig", .data = unformatted_code }) catch @panic("unhandled");
21392140 dir.makeDir("subdir") catch @panic("unhandled");
2140 var subdir = dir.openDir("subdir", .{}) catch @panic("unhandled");
2141 defer subdir.close();
2141 var subdir = dir.openDir(io, "subdir", .{}) catch @panic("unhandled");
2142 defer subdir.close(io);
21422143 subdir.writeFile(.{ .sub_path = "fmt3.zig", .data = unformatted_code }) catch @panic("unhandled");
21432144
21442145 // Test zig fmt affecting only the appropriate files.
......@@ -2634,7 +2635,7 @@ pub fn addCases(
26342635 var cases = @import("src/Cases.zig").init(gpa, arena);
26352636
26362637 var dir = try b.build_root.handle.openDir(io, "test/cases", .{ .iterate = true });
2637 defer dir.close();
2638 defer dir.close(io);
26382639
26392640 cases.addFromDir(dir, b);
26402641 try @import("cases.zig").addCases(&cases, build_options, b);
......@@ -2680,6 +2681,8 @@ pub fn addDebuggerTests(b: *std.Build, options: DebuggerContext.Options) ?*Step
26802681}
26812682
26822683pub fn addIncrementalTests(b: *std.Build, test_step: *Step) !void {
2684 const io = b.graph.io;
2685
26832686 const incr_check = b.addExecutable(.{
26842687 .name = "incr-check",
26852688 .root_module = b.createModule(.{
......@@ -2689,11 +2692,11 @@ pub fn addIncrementalTests(b: *std.Build, test_step: *Step) !void {
26892692 }),
26902693 });
26912694
2692 var dir = try b.build_root.handle.openDir("test/incremental", .{ .iterate = true });
2693 defer dir.close();
2695 var dir = try b.build_root.handle.openDir(io, "test/incremental", .{ .iterate = true });
2696 defer dir.close(io);
26942697
26952698 var it = try dir.walk(b.graph.arena);
2696 while (try it.next()) |entry| {
2699 while (try it.next(io)) |entry| {
26972700 if (entry.kind != .file) continue;
26982701
26992702 const run = b.addRunArtifact(incr_check);