authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2026-01-05 02:04:28-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-05 20:41:18+01:00
log52141fe85f1b7719cecb868520f40431fcfff2a1
tree69451db5e274bc1ba3cda27131e7f409270cc1a2
parent50422d5c37e85f08da7579cbffb4c0281a5cb2a8

standalone tests: Delete all ad hoc TmpDir instances, use the build system instead


8 files changed, 71 insertions(+), 296 deletions(-)

test/standalone/posix/build.zig+6
...@@ -4,11 +4,13 @@ const builtin = @import("builtin");...@@ -4,11 +4,13 @@ const builtin = @import("builtin");
4const Case = struct {4const Case = struct {
5 src_path: []const u8,5 src_path: []const u8,
6 set_env_vars: bool = false,6 set_env_vars: bool = false,
7 make_tmp_dir: bool = false,
7};8};
89
9const cases = [_]Case{10const cases = [_]Case{
10 .{11 .{
11 .src_path = "cwd.zig",12 .src_path = "cwd.zig",
13 .make_tmp_dir = true,
12 },14 },
13 .{15 .{
14 .src_path = "getenv.zig",16 .src_path = "getenv.zig",
...@@ -19,6 +21,7 @@ const cases = [_]Case{...@@ -19,6 +21,7 @@ const cases = [_]Case{
19 },21 },
20 .{22 .{
21 .src_path = "relpaths.zig",23 .src_path = "relpaths.zig",
24 .make_tmp_dir = true,
22 },25 },
23};26};
2427
...@@ -69,6 +72,9 @@ fn run_exe(b: *std.Build, optimize: std.builtin.OptimizeMode, case: *const Case,...@@ -69,6 +72,9 @@ fn run_exe(b: *std.Build, optimize: std.builtin.OptimizeMode, case: *const Case,
69 });72 });
7073
71 const run_cmd = b.addRunArtifact(exe);74 const run_cmd = b.addRunArtifact(exe);
75 if (case.make_tmp_dir) {
76 run_cmd.addDirectoryArg(b.tmpPath());
77 }
7278
73 if (case.set_env_vars) {79 if (case.set_env_vars) {
74 run_cmd.setEnvironmentVariable("ZIG_TEST_POSIX_1EQ", "test=variable");80 run_cmd.setEnvironmentVariable("ZIG_TEST_POSIX_1EQ", "test=variable");
test/standalone/posix/cwd.zig+13-47
...@@ -13,10 +13,15 @@ pub fn main(init: std.process.Init) !void {...@@ -13,10 +13,15 @@ pub fn main(init: std.process.Init) !void {
13 .windows => return, // POSIX is not implemented by Windows13 .windows => return, // POSIX is not implemented by Windows
14 else => {},14 else => {},
15 }15 }
16 const args = try init.minimal.args.toSlice(init.arena.allocator());
17 const tmp_dir_path = args[1];
18
19 var tmp_dir = try Io.Dir.cwd().openDir(init.io, tmp_dir_path, .{});
20 defer tmp_dir.close(init.io);
1621
17 try test_chdir_self();22 try test_chdir_self();
18 try test_chdir_absolute();23 try test_chdir_absolute();
19 try test_chdir_relative(init.gpa, init.io);24 try test_chdir_relative(init.gpa, init.io, tmp_dir);
20}25}
2126
22// get current working directory and expect it to match given path27// get current working directory and expect it to match given path
...@@ -47,23 +52,22 @@ fn test_chdir_absolute() !void {...@@ -47,23 +52,22 @@ fn test_chdir_absolute() !void {
47 try expect_cwd(parent);52 try expect_cwd(parent);
48}53}
4954
50fn test_chdir_relative(gpa: Allocator, io: Io) !void {55fn test_chdir_relative(gpa: Allocator, io: Io, tmp_dir: Io.Dir) !void {
51 var tmp = tmpDir(io, .{});56 const subdir_path = "subdir";
52 defer tmp.cleanup(io);57 try tmp_dir.createDir(io, "subdir", .default_dir);
5358
54 // Use the tmpDir parent_dir as the "base" for the test. Then cd into the child59 // Use the tmp dir as the "base" for the test. Then cd into the child
55 try std.process.setCurrentDir(io, tmp.parent_dir);60 try std.process.setCurrentDir(io, tmp_dir);
5661
57 // Capture base working directory path, to build expected full path62 // Capture base working directory path, to build expected full path
58 var base_cwd_buf: [path_max]u8 = undefined;63 var base_cwd_buf: [path_max]u8 = undefined;
59 const base_cwd = try std.posix.getcwd(base_cwd_buf[0..]);64 const base_cwd = try std.posix.getcwd(base_cwd_buf[0..]);
6065
61 const relative_dir_name = &tmp.sub_path;66 const expected_path = try std.fs.path.resolve(gpa, &.{ base_cwd, subdir_path });
62 const expected_path = try std.fs.path.resolve(gpa, &.{ base_cwd, relative_dir_name });
63 defer gpa.free(expected_path);67 defer gpa.free(expected_path);
6468
65 // change current working directory to new test directory69 // change current working directory to new test directory
66 try std.Io.Threaded.chdir(relative_dir_name);70 try std.Io.Threaded.chdir(subdir_path);
6771
68 var new_cwd_buf: [path_max]u8 = undefined;72 var new_cwd_buf: [path_max]u8 = undefined;
69 const new_cwd = try std.posix.getcwd(new_cwd_buf[0..]);73 const new_cwd = try std.posix.getcwd(new_cwd_buf[0..]);
...@@ -74,41 +78,3 @@ fn test_chdir_relative(gpa: Allocator, io: Io) !void {...@@ -74,41 +78,3 @@ fn test_chdir_relative(gpa: Allocator, io: Io) !void {
7478
75 try std.testing.expectEqualStrings(expected_path, resolved_cwd);79 try std.testing.expectEqualStrings(expected_path, resolved_cwd);
76}80}
77
78pub fn tmpDir(io: Io, opts: Io.Dir.OpenOptions) TmpDir {
79 var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;
80 std.crypto.random.bytes(&random_bytes);
81 var sub_path: [TmpDir.sub_path_len]u8 = undefined;
82 _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);
83
84 const cwd = Io.Dir.cwd();
85 var cache_dir = cwd.createDirPathOpen(io, ".zig-cache", .{}) catch
86 @panic("unable to make tmp dir for testing: unable to make and open .zig-cache dir");
87 defer cache_dir.close(io);
88 const parent_dir = cache_dir.createDirPathOpen(io, "tmp", .{}) catch
89 @panic("unable to make tmp dir for testing: unable to make and open .zig-cache/tmp dir");
90 const dir = parent_dir.createDirPathOpen(io, &sub_path, .{ .open_options = opts }) catch
91 @panic("unable to make tmp dir for testing: unable to make and open the tmp dir");
92
93 return .{
94 .dir = dir,
95 .parent_dir = parent_dir,
96 .sub_path = sub_path,
97 };
98}
99
100pub const TmpDir = struct {
101 dir: Io.Dir,
102 parent_dir: Io.Dir,
103 sub_path: [sub_path_len]u8,
104
105 const random_bytes_count = 12;
106 const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count);
107
108 pub fn cleanup(self: *TmpDir, io: Io) void {
109 self.dir.close(io);
110 self.parent_dir.deleteTree(io, &self.sub_path) catch {};
111 self.parent_dir.close(io);
112 self.* = undefined;
113 }
114};
test/standalone/posix/relpaths.zig+12-47
...@@ -11,16 +11,19 @@ pub fn main(init: std.process.Init) !void {...@@ -11,16 +11,19 @@ pub fn main(init: std.process.Init) !void {
1111
12 const io = init.io;12 const io = init.io;
1313
14 var tmp = tmpDir(io, .{});14 const args = try init.minimal.args.toSlice(init.arena.allocator());
15 defer tmp.cleanup(io);15 const tmp_dir_path = args[1];
16
17 var tmp_dir = try Io.Dir.cwd().openDir(io, tmp_dir_path, .{});
18 defer tmp_dir.close(io);
1619
17 // Want to test relative paths, so cd into the tmpdir for these tests20 // Want to test relative paths, so cd into the tmpdir for these tests
18 try std.process.setCurrentDir(io, tmp.dir);21 try std.process.setCurrentDir(io, tmp_dir);
1922
20 try test_link(io, tmp);23 try test_link(io, tmp_dir);
21}24}
2225
23fn test_link(io: Io, tmp: TmpDir) !void {26fn test_link(io: Io, tmp_dir: Io.Dir) !void {
24 switch (builtin.target.os.tag) {27 switch (builtin.target.os.tag) {
25 .linux, .illumos => {},28 .linux, .illumos => {},
26 else => return,29 else => return,
...@@ -29,16 +32,16 @@ fn test_link(io: Io, tmp: TmpDir) !void {...@@ -29,16 +32,16 @@ fn test_link(io: Io, tmp: TmpDir) !void {
29 const target_name = "link-target";32 const target_name = "link-target";
30 const link_name = "newlink";33 const link_name = "newlink";
3134
32 try tmp.dir.writeFile(io, .{ .sub_path = target_name, .data = "example" });35 try tmp_dir.writeFile(io, .{ .sub_path = target_name, .data = "example" });
3336
34 // Test 1: create the relative link from inside tmp37 // Test 1: create the relative link from inside tmp_dir
35 try Io.Dir.hardLink(.cwd(), target_name, .cwd(), link_name, io, .{});38 try Io.Dir.hardLink(.cwd(), target_name, .cwd(), link_name, io, .{});
3639
37 // Verify40 // Verify
38 const efd = try tmp.dir.openFile(io, target_name, .{});41 const efd = try tmp_dir.openFile(io, target_name, .{});
39 defer efd.close(io);42 defer efd.close(io);
4043
41 const nfd = try tmp.dir.openFile(io, link_name, .{});44 const nfd = try tmp_dir.openFile(io, link_name, .{});
42 defer nfd.close(io);45 defer nfd.close(io);
4346
44 {47 {
...@@ -55,41 +58,3 @@ fn test_link(io: Io, tmp: TmpDir) !void {...@@ -55,41 +58,3 @@ fn test_link(io: Io, tmp: TmpDir) !void {
55 try std.testing.expectEqual(1, e_stat.nlink);58 try std.testing.expectEqual(1, e_stat.nlink);
56 }59 }
57}60}
58
59pub fn tmpDir(io: Io, opts: Io.Dir.OpenOptions) TmpDir {
60 var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;
61 std.crypto.random.bytes(&random_bytes);
62 var sub_path: [TmpDir.sub_path_len]u8 = undefined;
63 _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);
64
65 const cwd = Io.Dir.cwd();
66 var cache_dir = cwd.createDirPathOpen(io, ".zig-cache", .{}) catch
67 @panic("unable to make tmp dir for testing: unable to make and open .zig-cache dir");
68 defer cache_dir.close(io);
69 const parent_dir = cache_dir.createDirPathOpen(io, "tmp", .{}) catch
70 @panic("unable to make tmp dir for testing: unable to make and open .zig-cache/tmp dir");
71 const dir = parent_dir.createDirPathOpen(io, &sub_path, .{ .open_options = opts }) catch
72 @panic("unable to make tmp dir for testing: unable to make and open the tmp dir");
73
74 return .{
75 .dir = dir,
76 .parent_dir = parent_dir,
77 .sub_path = sub_path,
78 };
79}
80
81pub const TmpDir = struct {
82 dir: Io.Dir,
83 parent_dir: Io.Dir,
84 sub_path: [sub_path_len]u8,
85
86 const random_bytes_count = 12;
87 const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count);
88
89 pub fn cleanup(self: *TmpDir, io: Io) void {
90 self.dir.close(io);
91 self.parent_dir.deleteTree(io, &self.sub_path) catch {};
92 self.parent_dir.close(io);
93 self.* = undefined;
94 }
95};
test/standalone/windows_bat_args/build.zig+18-2
...@@ -19,6 +19,22 @@ pub fn build(b: *std.Build) !void {...@@ -19,6 +19,22 @@ pub fn build(b: *std.Build) !void {
19 }),19 }),
20 });20 });
2121
22 const bat_files = b.addWriteFiles();
23 {
24 const echo_args_basename = "echo-args.exe";
25 const preamble = std.fmt.allocPrint(b.allocator,
26 \\@echo off
27 \\"{s}"
28 , .{echo_args_basename}) catch @panic("OOM");
29 // Trailing newline intentionally omitted above so we can add args.
30
31 _ = bat_files.add("args1.bat", std.mem.concat(b.allocator, u8, &.{ preamble, " %*" }) catch @panic("OOM"));
32 _ = bat_files.add("args2.bat", std.mem.concat(b.allocator, u8, &.{ preamble, " %1 %2 %3 %4 %5 %6 %7 %8 %9" }) catch @panic("OOM"));
33 _ = bat_files.add("args3.bat", std.mem.concat(b.allocator, u8, &.{ preamble, " \"%~1\" \"%~2\" \"%~3\" \"%~4\" \"%~5\" \"%~6\" \"%~7\" \"%~8\" \"%~9\"" }) catch @panic("OOM"));
34
35 _ = bat_files.addCopyFile(echo_args.getEmittedBin(), echo_args_basename);
36 }
37
22 const test_exe = b.addExecutable(.{38 const test_exe = b.addExecutable(.{
23 .name = "test",39 .name = "test",
24 .root_module = b.createModule(.{40 .root_module = b.createModule(.{
...@@ -29,7 +45,7 @@ pub fn build(b: *std.Build) !void {...@@ -29,7 +45,7 @@ pub fn build(b: *std.Build) !void {
29 });45 });
3046
31 const run = b.addRunArtifact(test_exe);47 const run = b.addRunArtifact(test_exe);
32 run.addArtifactArg(echo_args);48 run.setCwd(bat_files.getDirectory());
33 run.expectExitCode(0);49 run.expectExitCode(0);
34 run.skip_foreign_checks = true;50 run.skip_foreign_checks = true;
3551
...@@ -55,7 +71,7 @@ pub fn build(b: *std.Build) !void {...@@ -55,7 +71,7 @@ pub fn build(b: *std.Build) !void {
55 const fuzz_seed_arg = std.fmt.allocPrint(b.allocator, "{}", .{fuzz_seed}) catch @panic("oom");71 const fuzz_seed_arg = std.fmt.allocPrint(b.allocator, "{}", .{fuzz_seed}) catch @panic("oom");
5672
57 const fuzz_run = b.addRunArtifact(fuzz);73 const fuzz_run = b.addRunArtifact(fuzz);
58 fuzz_run.addArtifactArg(echo_args);74 fuzz_run.setCwd(bat_files.getDirectory());
59 fuzz_run.addArgs(&.{ fuzz_iterations_arg, fuzz_seed_arg });75 fuzz_run.addArgs(&.{ fuzz_iterations_arg, fuzz_seed_arg });
60 fuzz_run.expectExitCode(0);76 fuzz_run.expectExitCode(0);
61 fuzz_run.skip_foreign_checks = true;77 fuzz_run.skip_foreign_checks = true;
test/standalone/windows_bat_args/fuzz.zig-70
...@@ -11,7 +11,6 @@ pub fn main(init: std.process.Init) !void {...@@ -11,7 +11,6 @@ pub fn main(init: std.process.Init) !void {
11 var it = try init.minimal.args.iterateAllocator(gpa);11 var it = try init.minimal.args.iterateAllocator(gpa);
12 defer it.deinit();12 defer it.deinit();
13 _ = it.next() orelse unreachable; // skip binary name13 _ = it.next() orelse unreachable; // skip binary name
14 const child_exe_path_orig = it.next() orelse unreachable;
1514
16 const iterations: u64 = iterations: {15 const iterations: u64 = iterations: {
17 const arg = it.next() orelse "0";16 const arg = it.next() orelse "0";
...@@ -37,37 +36,6 @@ pub fn main(init: std.process.Init) !void {...@@ -37,37 +36,6 @@ pub fn main(init: std.process.Init) !void {
37 std.debug.print("rand seed: {}\n", .{seed});36 std.debug.print("rand seed: {}\n", .{seed});
38 }37 }
3938
40 var tmp = tmpDir(io, .{});
41 defer tmp.cleanup(io);
42
43 try std.process.setCurrentDir(io, tmp.dir);
44 defer std.process.setCurrentDir(io, tmp.parent_dir) catch {};
45
46 // `child_exe_path_orig` might be relative; make it relative to our new cwd.
47 const child_exe_path = try std.fs.path.resolve(gpa, &.{ "..\\..\\..", child_exe_path_orig });
48 defer gpa.free(child_exe_path);
49
50 var buf: std.ArrayList(u8) = .empty;
51 defer buf.deinit(gpa);
52 try buf.print(gpa,
53 \\@echo off
54 \\"{s}"
55 , .{child_exe_path});
56 // Trailing newline intentionally omitted above so we can add args.
57 const preamble_len = buf.items.len;
58
59 try buf.appendSlice(gpa, " %*");
60 try tmp.dir.writeFile(io, .{ .sub_path = "args1.bat", .data = buf.items });
61 buf.shrinkRetainingCapacity(preamble_len);
62
63 try buf.appendSlice(gpa, " %1 %2 %3 %4 %5 %6 %7 %8 %9");
64 try tmp.dir.writeFile(io, .{ .sub_path = "args2.bat", .data = buf.items });
65 buf.shrinkRetainingCapacity(preamble_len);
66
67 try buf.appendSlice(gpa, " \"%~1\" \"%~2\" \"%~3\" \"%~4\" \"%~5\" \"%~6\" \"%~7\" \"%~8\" \"%~9\"");
68 try tmp.dir.writeFile(io, .{ .sub_path = "args3.bat", .data = buf.items });
69 buf.shrinkRetainingCapacity(preamble_len);
70
71 var i: u64 = 0;39 var i: u64 = 0;
72 while (iterations == 0 or i < iterations) {40 while (iterations == 0 or i < iterations) {
73 const rand_arg = try randomArg(gpa, rand);41 const rand_arg = try randomArg(gpa, rand);
...@@ -163,41 +131,3 @@ fn randomArg(gpa: Allocator, rand: std.Random) ![]const u8 {...@@ -163,41 +131,3 @@ fn randomArg(gpa: Allocator, rand: std.Random) ![]const u8 {
163131
164 return buf.toOwnedSlice(gpa);132 return buf.toOwnedSlice(gpa);
165}133}
166
167pub fn tmpDir(io: Io, opts: Io.Dir.OpenOptions) TmpDir {
168 var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;
169 std.crypto.random.bytes(&random_bytes);
170 var sub_path: [TmpDir.sub_path_len]u8 = undefined;
171 _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);
172
173 const cwd = Io.Dir.cwd();
174 var cache_dir = cwd.createDirPathOpen(io, ".zig-cache", .{}) catch
175 @panic("unable to make tmp dir for testing: unable to make and open .zig-cache dir");
176 defer cache_dir.close(io);
177 const parent_dir = cache_dir.createDirPathOpen(io, "tmp", .{}) catch
178 @panic("unable to make tmp dir for testing: unable to make and open .zig-cache/tmp dir");
179 const dir = parent_dir.createDirPathOpen(io, &sub_path, .{ .open_options = opts }) catch
180 @panic("unable to make tmp dir for testing: unable to make and open the tmp dir");
181
182 return .{
183 .dir = dir,
184 .parent_dir = parent_dir,
185 .sub_path = sub_path,
186 };
187}
188
189pub const TmpDir = struct {
190 dir: Io.Dir,
191 parent_dir: Io.Dir,
192 sub_path: [sub_path_len]u8,
193
194 const random_bytes_count = 12;
195 const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count);
196
197 pub fn cleanup(self: *TmpDir, io: Io) void {
198 self.dir.close(io);
199 self.parent_dir.deleteTree(io, &self.sub_path) catch {};
200 self.parent_dir.close(io);
201 self.* = undefined;
202 }
203};
test/standalone/windows_bat_args/test.zig+1-75
...@@ -6,42 +6,6 @@ pub fn main(init: std.process.Init) !void {...@@ -6,42 +6,6 @@ pub fn main(init: std.process.Init) !void {
6 const gpa = init.gpa;6 const gpa = init.gpa;
7 const io = init.io;7 const io = init.io;
88
9 var it = try init.minimal.args.iterateAllocator(gpa);
10 defer it.deinit();
11 _ = it.next() orelse unreachable; // skip binary name
12 const child_exe_path_orig = it.next() orelse unreachable;
13
14 var tmp = tmpDir(io, .{});
15 defer tmp.cleanup(io);
16
17 try std.process.setCurrentDir(io, tmp.dir);
18 defer std.process.setCurrentDir(io, tmp.parent_dir) catch {};
19
20 // `child_exe_path_orig` might be relative; make it relative to our new cwd.
21 const child_exe_path = try std.fs.path.resolve(gpa, &.{ "..\\..\\..", child_exe_path_orig });
22 defer gpa.free(child_exe_path);
23
24 var buf: std.ArrayList(u8) = .empty;
25 defer buf.deinit(gpa);
26 try buf.print(gpa,
27 \\@echo off
28 \\"{s}"
29 , .{child_exe_path});
30 // Trailing newline intentionally omitted above so we can add args.
31 const preamble_len = buf.items.len;
32
33 try buf.appendSlice(gpa, " %*");
34 try tmp.dir.writeFile(io, .{ .sub_path = "args1.bat", .data = buf.items });
35 buf.shrinkRetainingCapacity(preamble_len);
36
37 try buf.appendSlice(gpa, " %1 %2 %3 %4 %5 %6 %7 %8 %9");
38 try tmp.dir.writeFile(io, .{ .sub_path = "args2.bat", .data = buf.items });
39 buf.shrinkRetainingCapacity(preamble_len);
40
41 try buf.appendSlice(gpa, " \"%~1\" \"%~2\" \"%~3\" \"%~4\" \"%~5\" \"%~6\" \"%~7\" \"%~8\" \"%~9\"");
42 try tmp.dir.writeFile(io, .{ .sub_path = "args3.bat", .data = buf.items });
43 buf.shrinkRetainingCapacity(preamble_len);
44
45 // Test cases are from https://github.com/rust-lang/rust/blob/master/tests/ui/std/windows-bat-args.rs9 // Test cases are from https://github.com/rust-lang/rust/blob/master/tests/ui/std/windows-bat-args.rs
46 try testExecError(error.InvalidBatchScriptArg, gpa, io, &.{"\x00"});10 try testExecError(error.InvalidBatchScriptArg, gpa, io, &.{"\x00"});
47 try testExecError(error.InvalidBatchScriptArg, gpa, io, &.{"\n"});11 try testExecError(error.InvalidBatchScriptArg, gpa, io, &.{"\n"});
...@@ -119,7 +83,7 @@ pub fn main(init: std.process.Init) !void {...@@ -119,7 +83,7 @@ pub fn main(init: std.process.Init) !void {
119 try testExec(gpa, io, &.{"%FOO%"}, &env);83 try testExec(gpa, io, &.{"%FOO%"}, &env);
12084
121 // Ensure that none of the `>file.txt`s have caused file.txt to be created85 // Ensure that none of the `>file.txt`s have caused file.txt to be created
122 try std.testing.expectError(error.FileNotFound, tmp.dir.access(io, "file.txt", .{}));86 try std.testing.expectError(error.FileNotFound, Io.Dir.cwd().access(io, "file.txt", .{}));
123}87}
12488
125fn testExecError(err: anyerror, gpa: Allocator, io: Io, args: []const []const u8) !void {89fn testExecError(err: anyerror, gpa: Allocator, io: Io, args: []const []const u8) !void {
...@@ -160,41 +124,3 @@ fn testExecBat(gpa: Allocator, io: Io, bat: []const u8, args: []const []const u8...@@ -160,41 +124,3 @@ fn testExecBat(gpa: Allocator, io: Io, bat: []const u8, args: []const []const u8
160 i += 1;124 i += 1;
161 }125 }
162}126}
163
164pub fn tmpDir(io: Io, opts: Io.Dir.OpenOptions) TmpDir {
165 var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;
166 std.crypto.random.bytes(&random_bytes);
167 var sub_path: [TmpDir.sub_path_len]u8 = undefined;
168 _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);
169
170 const cwd = Io.Dir.cwd();
171 var cache_dir = cwd.createDirPathOpen(io, ".zig-cache", .{}) catch
172 @panic("unable to make tmp dir for testing: unable to make and open .zig-cache dir");
173 defer cache_dir.close(io);
174 const parent_dir = cache_dir.createDirPathOpen(io, "tmp", .{}) catch
175 @panic("unable to make tmp dir for testing: unable to make and open .zig-cache/tmp dir");
176 const dir = parent_dir.createDirPathOpen(io, &sub_path, .{ .open_options = opts }) catch
177 @panic("unable to make tmp dir for testing: unable to make and open the tmp dir");
178
179 return .{
180 .dir = dir,
181 .parent_dir = parent_dir,
182 .sub_path = sub_path,
183 };
184}
185
186pub const TmpDir = struct {
187 dir: Io.Dir,
188 parent_dir: Io.Dir,
189 sub_path: [sub_path_len]u8,
190
191 const random_bytes_count = 12;
192 const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count);
193
194 pub fn cleanup(self: *TmpDir, io: Io) void {
195 self.dir.close(io);
196 self.parent_dir.deleteTree(io, &self.sub_path) catch {};
197 self.parent_dir.close(io);
198 self.* = undefined;
199 }
200};
test/standalone/windows_spawn/build.zig+1
...@@ -30,6 +30,7 @@ pub fn build(b: *std.Build) void {...@@ -30,6 +30,7 @@ pub fn build(b: *std.Build) void {
3030
31 const run = b.addRunArtifact(main);31 const run = b.addRunArtifact(main);
32 run.addArtifactArg(hello);32 run.addArtifactArg(hello);
33 run.addDirectoryArg(b.tmpPath());
33 run.expectExitCode(0);34 run.expectExitCode(0);
34 run.skip_foreign_checks = true;35 run.skip_foreign_checks = true;
3536
test/standalone/windows_spawn/main.zig+20-55
...@@ -9,16 +9,19 @@ pub fn main(init: std.process.Init) !void {...@@ -9,16 +9,19 @@ pub fn main(init: std.process.Init) !void {
9 const gpa = init.gpa;9 const gpa = init.gpa;
10 const io = init.io;10 const io = init.io;
11 const process_cwd_path = try std.process.getCwdAlloc(init.arena.allocator());11 const process_cwd_path = try std.process.getCwdAlloc(init.arena.allocator());
12 var initial_process_cwd = try Io.Dir.cwd().openDir(io, ".", .{});
13 defer initial_process_cwd.close(io);
1214
13 var it = try init.minimal.args.iterateAllocator(gpa);15 var it = try init.minimal.args.iterateAllocator(gpa);
14 defer it.deinit();16 defer it.deinit();
15 _ = it.next() orelse unreachable; // skip binary name17 _ = it.next() orelse unreachable; // skip binary name
16 const hello_exe_cache_path = it.next() orelse unreachable;18 const hello_exe_cache_path = it.next() orelse unreachable;
19 const tmp_dir_path = it.next() orelse unreachable;
1720
18 var tmp = tmpDir(io, .{});21 var tmp_dir = try Io.Dir.cwd().openDir(io, tmp_dir_path, .{});
19 defer tmp.cleanup(io);22 defer tmp_dir.close(io);
2023
21 const tmp_absolute_path = try tmp.dir.realPathFileAlloc(io, ".", gpa);24 const tmp_absolute_path = try tmp_dir.realPathFileAlloc(io, ".", gpa);
22 defer gpa.free(tmp_absolute_path);25 defer gpa.free(tmp_absolute_path);
23 const tmp_absolute_path_w = try std.unicode.utf8ToUtf16LeAllocZ(gpa, tmp_absolute_path);26 const tmp_absolute_path_w = try std.unicode.utf8ToUtf16LeAllocZ(gpa, tmp_absolute_path);
24 defer gpa.free(tmp_absolute_path_w);27 defer gpa.free(tmp_absolute_path_w);
...@@ -51,7 +54,7 @@ pub fn main(init: std.process.Init) !void {...@@ -51,7 +54,7 @@ pub fn main(init: std.process.Init) !void {
51 ) == windows.TRUE);54 ) == windows.TRUE);
5255
53 // Move hello.exe into the tmp dir which is now added to the path56 // Move hello.exe into the tmp dir which is now added to the path
54 try Io.Dir.cwd().copyFile(hello_exe_cache_path, tmp.dir, "hello.exe", io, .{});57 try Io.Dir.cwd().copyFile(hello_exe_cache_path, tmp_dir, "hello.exe", io, .{});
5558
56 // with extension should find the .exe (case insensitive)59 // with extension should find the .exe (case insensitive)
57 try testExec(gpa, io, "HeLLo.exe", "hello from exe\n");60 try testExec(gpa, io, "HeLLo.exe", "hello from exe\n");
...@@ -61,9 +64,9 @@ pub fn main(init: std.process.Init) !void {...@@ -61,9 +64,9 @@ pub fn main(init: std.process.Init) !void {
61 try std.testing.expectError(error.FileNotFound, testExecWithCwd(gpa, io, "hello.exe", "missing_dir", ""));64 try std.testing.expectError(error.FileNotFound, testExecWithCwd(gpa, io, "hello.exe", "missing_dir", ""));
6265
63 // now add a .bat66 // now add a .bat
64 try tmp.dir.writeFile(io, .{ .sub_path = "hello.bat", .data = "@echo hello from bat" });67 try tmp_dir.writeFile(io, .{ .sub_path = "hello.bat", .data = "@echo hello from bat" });
65 // and a .cmd68 // and a .cmd
66 try tmp.dir.writeFile(io, .{ .sub_path = "hello.cmd", .data = "@echo hello from cmd" });69 try tmp_dir.writeFile(io, .{ .sub_path = "hello.cmd", .data = "@echo hello from cmd" });
6770
68 // with extension should find the .bat (case insensitive)71 // with extension should find the .bat (case insensitive)
69 try testExec(gpa, io, "heLLo.bat", "hello from bat\r\n");72 try testExec(gpa, io, "heLLo.bat", "hello from bat\r\n");
...@@ -73,15 +76,15 @@ pub fn main(init: std.process.Init) !void {...@@ -73,15 +76,15 @@ pub fn main(init: std.process.Init) !void {
73 try testExec(gpa, io, "heLLo", "hello from exe\n");76 try testExec(gpa, io, "heLLo", "hello from exe\n");
7477
75 // now rename the exe to not have an extension78 // now rename the exe to not have an extension
76 try renameExe(tmp.dir, io, "hello.exe", "hello");79 try renameExe(tmp_dir, io, "hello.exe", "hello");
7780
78 // with extension should now fail81 // with extension should now fail
79 try testExecError(error.FileNotFound, gpa, io, "hello.exe");82 try testExecError(error.FileNotFound, gpa, io, "hello.exe");
80 // without extension should succeed (case insensitive)83 // without extension should succeed (case insensitive)
81 try testExec(gpa, io, "heLLo", "hello from exe\n");84 try testExec(gpa, io, "heLLo", "hello from exe\n");
8285
83 try tmp.dir.createDir(io, "something", .default_dir);86 try tmp_dir.createDir(io, "something", .default_dir);
84 try renameExe(tmp.dir, io, "hello", "something/hello.exe");87 try renameExe(tmp_dir, io, "hello", "something/hello.exe");
8588
86 const relative_path_no_ext = try std.fs.path.join(gpa, &.{ tmp_relative_path, "something/hello" });89 const relative_path_no_ext = try std.fs.path.join(gpa, &.{ tmp_relative_path, "something/hello" });
87 defer gpa.free(relative_path_no_ext);90 defer gpa.free(relative_path_no_ext);
...@@ -95,7 +98,7 @@ pub fn main(init: std.process.Init) !void {...@@ -95,7 +98,7 @@ pub fn main(init: std.process.Init) !void {
95 try testExec(gpa, io, "heLLo", "hello from bat\r\n");98 try testExec(gpa, io, "heLLo", "hello from bat\r\n");
9699
97 // Add a hello.exe that is not a valid executable100 // Add a hello.exe that is not a valid executable
98 try tmp.dir.writeFile(io, .{ .sub_path = "hello.exe", .data = "invalid" });101 try tmp_dir.writeFile(io, .{ .sub_path = "hello.exe", .data = "invalid" });
99102
100 // Trying to execute it with extension will give InvalidExe. This is a special103 // Trying to execute it with extension will give InvalidExe. This is a special
101 // case for .EXE extensions, where if they ever try to get executed but they are104 // case for .EXE extensions, where if they ever try to get executed but they are
...@@ -108,14 +111,14 @@ pub fn main(init: std.process.Init) !void {...@@ -108,14 +111,14 @@ pub fn main(init: std.process.Init) !void {
108 try testExecError(error.InvalidExe, gpa, io, "hello");111 try testExecError(error.InvalidExe, gpa, io, "hello");
109112
110 // If we now rename hello.exe to have no extension, it will behave differently113 // If we now rename hello.exe to have no extension, it will behave differently
111 try renameExe(tmp.dir, io, "hello.exe", "hello");114 try renameExe(tmp_dir, io, "hello.exe", "hello");
112115
113 // Now, trying to execute it without an extension should treat InvalidExe as recoverable116 // Now, trying to execute it without an extension should treat InvalidExe as recoverable
114 // and skip over it and find hello.bat and execute that117 // and skip over it and find hello.bat and execute that
115 try testExec(gpa, io, "hello", "hello from bat\r\n");118 try testExec(gpa, io, "hello", "hello from bat\r\n");
116119
117 // If we rename the invalid exe to something else120 // If we rename the invalid exe to something else
118 try renameExe(tmp.dir, io, "hello", "goodbye");121 try renameExe(tmp_dir, io, "hello", "goodbye");
119 // Then we should now get FileNotFound when trying to execute 'goodbye',122 // Then we should now get FileNotFound when trying to execute 'goodbye',
120 // since that is what the original error will be after searching for 'goodbye'123 // since that is what the original error will be after searching for 'goodbye'
121 // in the cwd. It will try to execute 'goodbye' from the PATH but the InvalidExe error124 // in the cwd. It will try to execute 'goodbye' from the PATH but the InvalidExe error
...@@ -123,8 +126,8 @@ pub fn main(init: std.process.Init) !void {...@@ -123,8 +126,8 @@ pub fn main(init: std.process.Init) !void {
123 try testExecError(error.FileNotFound, gpa, io, "goodbye");126 try testExecError(error.FileNotFound, gpa, io, "goodbye");
124127
125 // Now let's set the tmp dir as the cwd and set the path only include the "something" sub dir128 // Now let's set the tmp dir as the cwd and set the path only include the "something" sub dir
126 try std.process.setCurrentDir(io, tmp.dir);129 try std.process.setCurrentDir(io, tmp_dir);
127 defer std.process.setCurrentDir(io, tmp.parent_dir) catch {};130 defer std.process.setCurrentDir(io, initial_process_cwd) catch {};
128 const something_subdir_abs_path = try std.mem.concatWithSentinel(gpa, u16, &.{ tmp_absolute_path_w, utf16Literal("\\something") }, 0);131 const something_subdir_abs_path = try std.mem.concatWithSentinel(gpa, u16, &.{ tmp_absolute_path_w, utf16Literal("\\something") }, 0);
129 defer gpa.free(something_subdir_abs_path);132 defer gpa.free(something_subdir_abs_path);
130133
...@@ -141,7 +144,7 @@ pub fn main(init: std.process.Init) !void {...@@ -141,7 +144,7 @@ pub fn main(init: std.process.Init) !void {
141 try testExec(gpa, io, "hello", "hello from bat\r\n");144 try testExec(gpa, io, "hello", "hello from bat\r\n");
142145
143 // If we rename something/hello.exe to something/goodbye.exe146 // If we rename something/hello.exe to something/goodbye.exe
144 try renameExe(tmp.dir, io, "something/hello.exe", "something/goodbye.exe");147 try renameExe(tmp_dir, io, "something/hello.exe", "something/goodbye.exe");
145 // And try to execute goodbye, then the one in something should be found148 // And try to execute goodbye, then the one in something should be found
146 // since the one in cwd is an invalid executable149 // since the one in cwd is an invalid executable
147 try testExec(gpa, io, "goodbye", "hello from exe\n");150 try testExec(gpa, io, "goodbye", "hello from exe\n");
...@@ -183,10 +186,10 @@ pub fn main(init: std.process.Init) !void {...@@ -183,10 +186,10 @@ pub fn main(init: std.process.Init) !void {
183 try testExec(gpa, io, "goodbye", "hello from exe\n");186 try testExec(gpa, io, "goodbye", "hello from exe\n");
184187
185 // now make sure we can launch executables "outside" of the cwd188 // now make sure we can launch executables "outside" of the cwd
186 var subdir_cwd = try tmp.dir.openDir(io, denormed_something_subdir_wtf8, .{});189 var subdir_cwd = try tmp_dir.openDir(io, denormed_something_subdir_wtf8, .{});
187 defer subdir_cwd.close(io);190 defer subdir_cwd.close(io);
188191
189 try renameExe(tmp.dir, io, "something/goodbye.exe", "hello.exe");192 try renameExe(tmp_dir, io, "something/goodbye.exe", "hello.exe");
190 try std.process.setCurrentDir(io, subdir_cwd);193 try std.process.setCurrentDir(io, subdir_cwd);
191194
192 // clear the PATH again195 // clear the PATH again
...@@ -232,41 +235,3 @@ fn renameExe(dir: Io.Dir, io: Io, old_sub_path: []const u8, new_sub_path: []cons...@@ -232,41 +235,3 @@ fn renameExe(dir: Io.Dir, io: Io, old_sub_path: []const u8, new_sub_path: []cons
232 else => |e| return e,235 else => |e| return e,
233 };236 };
234}237}
235
236pub fn tmpDir(io: Io, opts: Io.Dir.OpenOptions) TmpDir {
237 var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;
238 std.crypto.random.bytes(&random_bytes);
239 var sub_path: [TmpDir.sub_path_len]u8 = undefined;
240 _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);
241
242 const cwd = Io.Dir.cwd();
243 var cache_dir = cwd.createDirPathOpen(io, ".zig-cache", .{}) catch
244 @panic("unable to make tmp dir for testing: unable to make and open .zig-cache dir");
245 defer cache_dir.close(io);
246 const parent_dir = cache_dir.createDirPathOpen(io, "tmp", .{}) catch
247 @panic("unable to make tmp dir for testing: unable to make and open .zig-cache/tmp dir");
248 const dir = parent_dir.createDirPathOpen(io, &sub_path, .{ .open_options = opts }) catch
249 @panic("unable to make tmp dir for testing: unable to make and open the tmp dir");
250
251 return .{
252 .dir = dir,
253 .parent_dir = parent_dir,
254 .sub_path = sub_path,
255 };
256}
257
258pub const TmpDir = struct {
259 dir: Io.Dir,
260 parent_dir: Io.Dir,
261 sub_path: [sub_path_len]u8,
262
263 const random_bytes_count = 12;
264 const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count);
265
266 pub fn cleanup(self: *TmpDir, io: Io) void {
267 self.dir.close(io);
268 self.parent_dir.deleteTree(io, &self.sub_path) catch {};
269 self.parent_dir.close(io);
270 self.* = undefined;
271 }
272};