aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--test/standalone/env_vars/main.zig2
-rw-r--r--test/standalone/windows_argv/build.zig2
-rw-r--r--test/standalone/windows_bat_args/fuzz.zig43
-rw-r--r--test/standalone/windows_bat_args/test.zig134
-rw-r--r--test/standalone/windows_spawn/main.zig118
5 files changed, 208 insertions, 91 deletions
diff --git a/test/standalone/env_vars/main.zig b/test/standalone/env_vars/main.zig
index 12b911404a..b85105642e 100644
--- a/test/standalone/env_vars/main.zig
+++ b/test/standalone/env_vars/main.zig
@@ -3,6 +3,8 @@ const builtin = @import("builtin");
// Note: the environment variables under test are set by the build.zig
pub fn main() !void {
+ @setEvalBranchQuota(10000);
+
var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
defer _ = gpa.deinit();
const allocator = gpa.allocator();
diff --git a/test/standalone/windows_argv/build.zig b/test/standalone/windows_argv/build.zig
index df988d2371..afe6dd80e5 100644
--- a/test/standalone/windows_argv/build.zig
+++ b/test/standalone/windows_argv/build.zig
@@ -67,7 +67,7 @@ pub fn build(b: *std.Build) !void {
// Only target the MSVC ABI if MSVC/Windows SDK is available
const has_msvc = has_msvc: {
- const sdk = std.zig.WindowsSdk.find(b.allocator, builtin.cpu.arch) catch |err| switch (err) {
+ const sdk = std.zig.WindowsSdk.find(b.allocator, b.graph.io, builtin.cpu.arch) catch |err| switch (err) {
error.OutOfMemory => @panic("oom"),
else => break :has_msvc false,
};
diff --git a/test/standalone/windows_bat_args/fuzz.zig b/test/standalone/windows_bat_args/fuzz.zig
index b68639dda4..28749259f7 100644
--- a/test/standalone/windows_bat_args/fuzz.zig
+++ b/test/standalone/windows_bat_args/fuzz.zig
@@ -10,6 +10,7 @@ pub fn main() anyerror!void {
const gpa = debug_alloc_inst.allocator();
var threaded: Io.Threaded = .init(gpa, .{});
+ defer threaded.deinit();
const io = threaded.io();
var it = try std.process.argsWithAllocator(gpa);
@@ -41,8 +42,8 @@ pub fn main() anyerror!void {
std.debug.print("rand seed: {}\n", .{seed});
}
- var tmp = std.testing.tmpDir(.{});
- defer tmp.cleanup();
+ var tmp = tmpDir(io, .{});
+ defer tmp.cleanup(io);
try std.process.setCurrentDir(io, tmp.dir);
defer std.process.setCurrentDir(io, tmp.parent_dir) catch {};
@@ -167,3 +168,41 @@ fn randomArg(gpa: Allocator, rand: std.Random) ![]const u8 {
return buf.toOwnedSlice(gpa);
}
+
+pub fn tmpDir(io: Io, opts: Io.Dir.OpenOptions) TmpDir {
+ var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;
+ std.crypto.random.bytes(&random_bytes);
+ var sub_path: [TmpDir.sub_path_len]u8 = undefined;
+ _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);
+
+ const cwd = Io.Dir.cwd();
+ var cache_dir = cwd.createDirPathOpen(io, ".zig-cache", .{}) catch
+ @panic("unable to make tmp dir for testing: unable to make and open .zig-cache dir");
+ defer cache_dir.close(io);
+ const parent_dir = cache_dir.createDirPathOpen(io, "tmp", .{}) catch
+ @panic("unable to make tmp dir for testing: unable to make and open .zig-cache/tmp dir");
+ const dir = parent_dir.createDirPathOpen(io, &sub_path, .{ .open_options = opts }) catch
+ @panic("unable to make tmp dir for testing: unable to make and open the tmp dir");
+
+ return .{
+ .dir = dir,
+ .parent_dir = parent_dir,
+ .sub_path = sub_path,
+ };
+}
+
+pub const TmpDir = struct {
+ dir: Io.Dir,
+ parent_dir: Io.Dir,
+ sub_path: [sub_path_len]u8,
+
+ const random_bytes_count = 12;
+ const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count);
+
+ pub fn cleanup(self: *TmpDir, io: Io) void {
+ self.dir.close(io);
+ self.parent_dir.deleteTree(io, &self.sub_path) catch {};
+ self.parent_dir.close(io);
+ self.* = undefined;
+ }
+};
diff --git a/test/standalone/windows_bat_args/test.zig b/test/standalone/windows_bat_args/test.zig
index 73f0ee9453..e0d1abe806 100644
--- a/test/standalone/windows_bat_args/test.zig
+++ b/test/standalone/windows_bat_args/test.zig
@@ -15,8 +15,8 @@ pub fn main() anyerror!void {
_ = it.next() orelse unreachable; // skip binary name
const child_exe_path_orig = it.next() orelse unreachable;
- var tmp = std.testing.tmpDir(.{});
- defer tmp.cleanup();
+ var tmp = tmpDir(io, .{});
+ defer tmp.cleanup(io);
try std.process.setCurrentDir(io, tmp.dir);
defer std.process.setCurrentDir(io, tmp.parent_dir) catch {};
@@ -47,41 +47,41 @@ pub fn main() anyerror!void {
buf.shrinkRetainingCapacity(preamble_len);
// Test cases are from https://github.com/rust-lang/rust/blob/master/tests/ui/std/windows-bat-args.rs
- try testExecError(error.InvalidBatchScriptArg, gpa, &.{"\x00"});
- try testExecError(error.InvalidBatchScriptArg, gpa, &.{"\n"});
- try testExecError(error.InvalidBatchScriptArg, gpa, &.{"\r"});
- try testExec(gpa, &.{ "a", "b" }, null);
- try testExec(gpa, &.{ "c is for cat", "d is for dog" }, null);
- try testExec(gpa, &.{ "\"", " \"" }, null);
- try testExec(gpa, &.{ "\\", "\\" }, null);
- try testExec(gpa, &.{">file.txt"}, null);
- try testExec(gpa, &.{"whoami.exe"}, null);
- try testExec(gpa, &.{"&a.exe"}, null);
- try testExec(gpa, &.{"&echo hello "}, null);
- try testExec(gpa, &.{ "&echo hello", "&whoami", ">file.txt" }, null);
- try testExec(gpa, &.{"!TMP!"}, null);
- try testExec(gpa, &.{"key=value"}, null);
- try testExec(gpa, &.{"\"key=value\""}, null);
- try testExec(gpa, &.{"key = value"}, null);
- try testExec(gpa, &.{"key=[\"value\"]"}, null);
- try testExec(gpa, &.{ "", "a=b" }, null);
- try testExec(gpa, &.{"key=\"foo bar\""}, null);
- try testExec(gpa, &.{"key=[\"my_value]"}, null);
- try testExec(gpa, &.{"key=[\"my_value\",\"other-value\"]"}, null);
- try testExec(gpa, &.{"key\\=value"}, null);
- try testExec(gpa, &.{"key=\"&whoami\""}, null);
- try testExec(gpa, &.{"key=\"value\"=5"}, null);
- try testExec(gpa, &.{"key=[\">file.txt\"]"}, null);
- try testExec(gpa, &.{"%hello"}, null);
- try testExec(gpa, &.{"%PATH%"}, null);
- try testExec(gpa, &.{"%%cd:~,%"}, null);
- try testExec(gpa, &.{"%PATH%PATH%"}, null);
- try testExec(gpa, &.{"\">file.txt"}, null);
- try testExec(gpa, &.{"abc\"&echo hello"}, null);
- try testExec(gpa, &.{"123\">file.txt"}, null);
- try testExec(gpa, &.{"\"&echo hello&whoami.exe"}, null);
- try testExec(gpa, &.{ "\"hello^\"world\"", "hello &echo oh no >file.txt" }, null);
- try testExec(gpa, &.{"&whoami.exe"}, null);
+ try testExecError(error.InvalidBatchScriptArg, gpa, io, &.{"\x00"});
+ try testExecError(error.InvalidBatchScriptArg, gpa, io, &.{"\n"});
+ try testExecError(error.InvalidBatchScriptArg, gpa, io, &.{"\r"});
+ try testExec(gpa, io, &.{ "a", "b" }, null);
+ try testExec(gpa, io, &.{ "c is for cat", "d is for dog" }, null);
+ try testExec(gpa, io, &.{ "\"", " \"" }, null);
+ try testExec(gpa, io, &.{ "\\", "\\" }, null);
+ try testExec(gpa, io, &.{">file.txt"}, null);
+ try testExec(gpa, io, &.{"whoami.exe"}, null);
+ try testExec(gpa, io, &.{"&a.exe"}, null);
+ try testExec(gpa, io, &.{"&echo hello "}, null);
+ try testExec(gpa, io, &.{ "&echo hello", "&whoami", ">file.txt" }, null);
+ try testExec(gpa, io, &.{"!TMP!"}, null);
+ try testExec(gpa, io, &.{"key=value"}, null);
+ try testExec(gpa, io, &.{"\"key=value\""}, null);
+ try testExec(gpa, io, &.{"key = value"}, null);
+ try testExec(gpa, io, &.{"key=[\"value\"]"}, null);
+ try testExec(gpa, io, &.{ "", "a=b" }, null);
+ try testExec(gpa, io, &.{"key=\"foo bar\""}, null);
+ try testExec(gpa, io, &.{"key=[\"my_value]"}, null);
+ try testExec(gpa, io, &.{"key=[\"my_value\",\"other-value\"]"}, null);
+ try testExec(gpa, io, &.{"key\\=value"}, null);
+ try testExec(gpa, io, &.{"key=\"&whoami\""}, null);
+ try testExec(gpa, io, &.{"key=\"value\"=5"}, null);
+ try testExec(gpa, io, &.{"key=[\">file.txt\"]"}, null);
+ try testExec(gpa, io, &.{"%hello"}, null);
+ try testExec(gpa, io, &.{"%PATH%"}, null);
+ try testExec(gpa, io, &.{"%%cd:~,%"}, null);
+ try testExec(gpa, io, &.{"%PATH%PATH%"}, null);
+ try testExec(gpa, io, &.{"\">file.txt"}, null);
+ try testExec(gpa, io, &.{"abc\"&echo hello"}, null);
+ try testExec(gpa, io, &.{"123\">file.txt"}, null);
+ try testExec(gpa, io, &.{"\"&echo hello&whoami.exe"}, null);
+ try testExec(gpa, io, &.{ "\"hello^\"world\"", "hello &echo oh no >file.txt" }, null);
+ try testExec(gpa, io, &.{"&whoami.exe"}, null);
// Ensure that trailing space and . characters can't lead to unexpected bat/cmd script execution.
// In many Windows APIs (including CreateProcess), trailing space and . characters are stripped
@@ -99,14 +99,14 @@ pub fn main() anyerror!void {
// > "args1.bat .. "
// '"args1.bat .. "' is not recognized as an internal or external command,
// operable program or batch file.
- try std.testing.expectError(error.FileNotFound, testExecBat(gpa, "args1.bat .. ", &.{"abc"}, null));
+ try std.testing.expectError(error.FileNotFound, testExecBat(gpa, io, "args1.bat .. ", &.{"abc"}, null));
const absolute_with_trailing = blk: {
- const absolute_path = try Io.Dir.realPathFileAbsoluteAlloc(io, "args1.bat", gpa);
+ const absolute_path = try Io.Dir.cwd().realPathFileAlloc(io, "args1.bat", gpa);
defer gpa.free(absolute_path);
break :blk try std.mem.concat(gpa, u8, &.{ absolute_path, " .. " });
};
defer gpa.free(absolute_with_trailing);
- try std.testing.expectError(error.FileNotFound, testExecBat(gpa, absolute_with_trailing, &.{"abc"}, null));
+ try std.testing.expectError(error.FileNotFound, testExecBat(gpa, io, absolute_with_trailing, &.{"abc"}, null));
var env = env: {
var env = try std.process.getEnvMap(gpa);
@@ -120,20 +120,20 @@ pub fn main() anyerror!void {
break :env env;
};
defer env.deinit();
- try testExec(gpa, &.{"%FOO%"}, &env);
+ try testExec(gpa, io, &.{"%FOO%"}, &env);
// Ensure that none of the `>file.txt`s have caused file.txt to be created
- try std.testing.expectError(error.FileNotFound, tmp.dir.access("file.txt", .{}));
+ try std.testing.expectError(error.FileNotFound, tmp.dir.access(io, "file.txt", .{}));
}
-fn testExecError(err: anyerror, gpa: Allocator, args: []const []const u8) !void {
- return std.testing.expectError(err, testExec(gpa, args, null));
+fn testExecError(err: anyerror, gpa: Allocator, io: Io, args: []const []const u8) !void {
+ return std.testing.expectError(err, testExec(gpa, io, args, null));
}
-fn testExec(gpa: Allocator, args: []const []const u8, env: ?*std.process.EnvMap) !void {
- try testExecBat(gpa, "args1.bat", args, env);
- try testExecBat(gpa, "args2.bat", args, env);
- try testExecBat(gpa, "args3.bat", args, env);
+fn testExec(gpa: Allocator, io: Io, args: []const []const u8, env: ?*std.process.EnvMap) !void {
+ try testExecBat(gpa, io, "args1.bat", args, env);
+ try testExecBat(gpa, io, "args2.bat", args, env);
+ try testExecBat(gpa, io, "args3.bat", args, env);
}
fn testExecBat(gpa: Allocator, io: Io, bat: []const u8, args: []const []const u8, env: ?*std.process.EnvMap) !void {
@@ -164,3 +164,41 @@ fn testExecBat(gpa: Allocator, io: Io, bat: []const u8, args: []const []const u8
i += 1;
}
}
+
+pub fn tmpDir(io: Io, opts: Io.Dir.OpenOptions) TmpDir {
+ var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;
+ std.crypto.random.bytes(&random_bytes);
+ var sub_path: [TmpDir.sub_path_len]u8 = undefined;
+ _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);
+
+ const cwd = Io.Dir.cwd();
+ var cache_dir = cwd.createDirPathOpen(io, ".zig-cache", .{}) catch
+ @panic("unable to make tmp dir for testing: unable to make and open .zig-cache dir");
+ defer cache_dir.close(io);
+ const parent_dir = cache_dir.createDirPathOpen(io, "tmp", .{}) catch
+ @panic("unable to make tmp dir for testing: unable to make and open .zig-cache/tmp dir");
+ const dir = parent_dir.createDirPathOpen(io, &sub_path, .{ .open_options = opts }) catch
+ @panic("unable to make tmp dir for testing: unable to make and open the tmp dir");
+
+ return .{
+ .dir = dir,
+ .parent_dir = parent_dir,
+ .sub_path = sub_path,
+ };
+}
+
+pub const TmpDir = struct {
+ dir: Io.Dir,
+ parent_dir: Io.Dir,
+ sub_path: [sub_path_len]u8,
+
+ const random_bytes_count = 12;
+ const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count);
+
+ pub fn cleanup(self: *TmpDir, io: Io) void {
+ self.dir.close(io);
+ self.parent_dir.deleteTree(io, &self.sub_path) catch {};
+ self.parent_dir.close(io);
+ self.* = undefined;
+ }
+};
diff --git a/test/standalone/windows_spawn/main.zig b/test/standalone/windows_spawn/main.zig
index f5df4dee25..c9522bf4de 100644
--- a/test/standalone/windows_spawn/main.zig
+++ b/test/standalone/windows_spawn/main.zig
@@ -19,8 +19,8 @@ pub fn main() anyerror!void {
_ = it.next() orelse unreachable; // skip binary name
const hello_exe_cache_path = it.next() orelse unreachable;
- var tmp = std.testing.tmpDir(.{});
- defer tmp.cleanup();
+ var tmp = tmpDir(io, .{});
+ defer tmp.cleanup(io);
const tmp_absolute_path = try tmp.dir.realPathFileAlloc(io, ".", gpa);
defer gpa.free(tmp_absolute_path);
@@ -44,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, gpa, "something_missing");
+ try testExecError(error.FileNotFound, gpa, io, "something_missing");
// make sure we don't get error.BadPath traversing out of cwd with a relative path
- try testExecError(error.FileNotFound, gpa, "..\\.\\.\\.\\\\..\\more_missing");
+ try testExecError(error.FileNotFound, gpa, io, "..\\.\\.\\.\\\\..\\more_missing");
std.debug.assert(windows.kernel32.SetEnvironmentVariableW(
utf16Literal("PATH"),
@@ -55,12 +55,12 @@ pub fn main() anyerror!void {
) == windows.TRUE);
// Move hello.exe into the tmp dir which is now added to the path
- try Io.Dir.cwd().copyFile(hello_exe_cache_path, tmp.dir, "hello.exe", .{});
+ try Io.Dir.cwd().copyFile(hello_exe_cache_path, tmp.dir, "hello.exe", io, .{});
// with extension should find the .exe (case insensitive)
- try testExec(gpa, "HeLLo.exe", "hello from exe\n");
+ try testExec(gpa, io, "HeLLo.exe", "hello from exe\n");
// without extension should find the .exe (case insensitive)
- try testExec(gpa, "heLLo", "hello from exe\n");
+ try testExec(gpa, io, "heLLo", "hello from exe\n");
// with invalid cwd
try std.testing.expectError(error.FileNotFound, testExecWithCwd(gpa, io, "hello.exe", "missing_dir", ""));
@@ -70,33 +70,33 @@ pub fn main() anyerror!void {
try tmp.dir.writeFile(io, .{ .sub_path = "hello.cmd", .data = "@echo hello from cmd" });
// with extension should find the .bat (case insensitive)
- try testExec(gpa, "heLLo.bat", "hello from bat\r\n");
+ try testExec(gpa, io, "heLLo.bat", "hello from bat\r\n");
// with extension should find the .cmd (case insensitive)
- try testExec(gpa, "heLLo.cmd", "hello from cmd\r\n");
+ try testExec(gpa, io, "heLLo.cmd", "hello from cmd\r\n");
// without extension should find the .exe (since its first in PATHEXT)
- try testExec(gpa, "heLLo", "hello from exe\n");
+ try testExec(gpa, io, "heLLo", "hello from exe\n");
// now rename the exe to not have an extension
- try renameExe(tmp.dir, "hello.exe", "hello");
+ try renameExe(tmp.dir, io, "hello.exe", "hello");
// with extension should now fail
- try testExecError(error.FileNotFound, gpa, "hello.exe");
+ try testExecError(error.FileNotFound, gpa, io, "hello.exe");
// without extension should succeed (case insensitive)
- try testExec(gpa, "heLLo", "hello from exe\n");
+ try testExec(gpa, io, "heLLo", "hello from exe\n");
try tmp.dir.createDir(io, "something", .default_dir);
- try renameExe(tmp.dir, "hello", "something/hello.exe");
+ try renameExe(tmp.dir, io, "hello", "something/hello.exe");
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(gpa, relative_path_no_ext, "hello from exe\n");
+ try testExec(gpa, io, 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, gpa, "something/hello");
+ try testExecError(error.FileNotFound, gpa, io, "something/hello");
// Now that .BAT is the first PATHEXT that should be found, this should succeed
- try testExec(gpa, "heLLo", "hello from bat\r\n");
+ try testExec(gpa, io, "heLLo", "hello from bat\r\n");
// Add a hello.exe that is not a valid executable
try tmp.dir.writeFile(io, .{ .sub_path = "hello.exe", .data = "invalid" });
@@ -105,26 +105,26 @@ 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, gpa, "hello.exe");
+ try testExecError(error.InvalidExe, gpa, io, "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, gpa, "hello");
+ try testExecError(error.InvalidExe, gpa, io, "hello");
// If we now rename hello.exe to have no extension, it will behave differently
- try renameExe(tmp.dir, "hello.exe", "hello");
+ try renameExe(tmp.dir, io, "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(gpa, "hello", "hello from bat\r\n");
+ try testExec(gpa, io, "hello", "hello from bat\r\n");
// If we rename the invalid exe to something else
- try renameExe(tmp.dir, "hello", "goodbye");
+ try renameExe(tmp.dir, io, "hello", "goodbye");
// Then we should now get FileNotFound when trying to execute 'goodbye',
// 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, gpa, "goodbye");
+ try testExecError(error.FileNotFound, gpa, io, "goodbye");
// Now let's set the tmp dir as the cwd and set the path only include the "something" sub dir
try std.process.setCurrentDir(io, tmp.dir);
@@ -139,26 +139,26 @@ 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, gpa, "goodbye");
+ try testExecError(error.InvalidExe, gpa, io, "goodbye");
// hello should still find the .bat
- try testExec(gpa, "hello", "hello from bat\r\n");
+ try testExec(gpa, io, "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");
+ try renameExe(tmp.dir, io, "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(gpa, "goodbye", "hello from exe\n");
+ try testExec(gpa, io, "goodbye", "hello from exe\n");
// If we use an absolute path to execute the invalid goodbye
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, gpa, goodbye_abs_path);
+ try testExecError(error.InvalidExe, gpa, io, 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(gpa, "goodbye", tmp_absolute_path, "hello from exe\n");
+ try testExecWithCwd(gpa, io, "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("\\\\\\\\"));
@@ -177,20 +177,20 @@ pub fn main() anyerror!void {
null,
) == windows.TRUE);
- try testExecWithCwd(gpa, "goodbye", denormed_something_subdir_wtf8, "hello from exe\n");
+ try testExecWithCwd(gpa, io, "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(gpa, "goodbye", "hello from exe\n");
+ try testExec(gpa, io, "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, .{});
+ var subdir_cwd = try tmp.dir.openDir(io, denormed_something_subdir_wtf8, .{});
defer subdir_cwd.close(io);
- try renameExe(tmp.dir, "something/goodbye.exe", "hello.exe");
+ try renameExe(tmp.dir, io, "something/goodbye.exe", "hello.exe");
try std.process.setCurrentDir(io, subdir_cwd);
// clear the PATH again
@@ -200,15 +200,15 @@ pub fn main() anyerror!void {
) == windows.TRUE);
// while we're at it make sure non-windows separators work fine
- try testExec(gpa, "../hello", "hello from exe\n");
+ try testExec(gpa, io, "../hello", "hello from exe\n");
}
-fn testExecError(err: anyerror, gpa: Allocator, command: []const u8) !void {
- return std.testing.expectError(err, testExec(gpa, command, ""));
+fn testExecError(err: anyerror, gpa: Allocator, io: Io, command: []const u8) !void {
+ return std.testing.expectError(err, testExec(gpa, io, command, ""));
}
-fn testExec(gpa: Allocator, command: []const u8, expected_stdout: []const u8) !void {
- return testExecWithCwd(gpa, command, null, expected_stdout);
+fn testExec(gpa: Allocator, io: Io, command: []const u8, expected_stdout: []const u8) !void {
+ return testExecWithCwd(gpa, io, command, null, expected_stdout);
}
fn testExecWithCwd(gpa: Allocator, io: Io, command: []const u8, cwd: ?[]const u8, expected_stdout: []const u8) !void {
@@ -223,9 +223,9 @@ fn testExecWithCwd(gpa: Allocator, io: Io, command: []const u8, cwd: ?[]const u8
try std.testing.expectEqualStrings(expected_stdout, result.stdout);
}
-fn renameExe(dir: Io.Dir, old_sub_path: []const u8, new_sub_path: []const u8) !void {
+fn renameExe(dir: Io.Dir, io: Io, old_sub_path: []const u8, new_sub_path: []const u8) !void {
var attempt: u5 = 0;
- while (true) break dir.rename(old_sub_path, new_sub_path) catch |err| switch (err) {
+ while (true) break dir.rename(old_sub_path, dir, new_sub_path, io) catch |err| switch (err) {
error.AccessDenied => {
if (attempt == 13) return error.AccessDenied;
// give the kernel a chance to finish closing the executable handle
@@ -236,3 +236,41 @@ fn renameExe(dir: Io.Dir, old_sub_path: []const u8, new_sub_path: []const u8) !v
else => |e| return e,
};
}
+
+pub fn tmpDir(io: Io, opts: Io.Dir.OpenOptions) TmpDir {
+ var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;
+ std.crypto.random.bytes(&random_bytes);
+ var sub_path: [TmpDir.sub_path_len]u8 = undefined;
+ _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);
+
+ const cwd = Io.Dir.cwd();
+ var cache_dir = cwd.createDirPathOpen(io, ".zig-cache", .{}) catch
+ @panic("unable to make tmp dir for testing: unable to make and open .zig-cache dir");
+ defer cache_dir.close(io);
+ const parent_dir = cache_dir.createDirPathOpen(io, "tmp", .{}) catch
+ @panic("unable to make tmp dir for testing: unable to make and open .zig-cache/tmp dir");
+ const dir = parent_dir.createDirPathOpen(io, &sub_path, .{ .open_options = opts }) catch
+ @panic("unable to make tmp dir for testing: unable to make and open the tmp dir");
+
+ return .{
+ .dir = dir,
+ .parent_dir = parent_dir,
+ .sub_path = sub_path,
+ };
+}
+
+pub const TmpDir = struct {
+ dir: Io.Dir,
+ parent_dir: Io.Dir,
+ sub_path: [sub_path_len]u8,
+
+ const random_bytes_count = 12;
+ const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count);
+
+ pub fn cleanup(self: *TmpDir, io: Io) void {
+ self.dir.close(io);
+ self.parent_dir.deleteTree(io, &self.sub_path) catch {};
+ self.parent_dir.close(io);
+ self.* = undefined;
+ }
+};