diff --git a/lib/std/debug.zig b/lib/std/debug.zig index a5f8e5323ff0..0bc658385ab0 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1015,12 +1015,12 @@ test printLineFromFileAnyOs { var test_dir = std.testing.tmpDir(.{}); defer test_dir.cleanup(); - // Relies on testing.tmpDir internals which is not ideal, but SourceLocation requires paths. + // Relies on testing.tmpDir internals which is not ideal, but SourceLocation requires paths relative to cwd. const test_dir_path = try join(allocator, &.{ ".zig-cache", "tmp", test_dir.sub_path[0..] }); defer allocator.free(test_dir_path); - // Cases { + // no newlines in file const path = try join(allocator, &.{ test_dir_path, "one_line.zig" }); defer allocator.free(path); try test_dir.dir.writeFile(.{ .sub_path = "one_line.zig", .data = "no new lines in this file, but one is printed anyway" }); @@ -1032,6 +1032,7 @@ test printLineFromFileAnyOs { output.clearRetainingCapacity(); } { + // print 1 & 3 of 3-line file const path = try join(allocator, &.{ test_dir_path, "three_lines.zig" }); defer allocator.free(path); try test_dir.dir.writeFile(.{ @@ -1052,6 +1053,7 @@ test printLineFromFileAnyOs { output.clearRetainingCapacity(); } { + // mem.page_size boundary crossing line const file = try test_dir.dir.createFile("line_overlaps_page_boundary.zig", .{}); defer file.close(); const path = try join(allocator, &.{ test_dir_path, "line_overlaps_page_boundary.zig" }); @@ -1068,6 +1070,7 @@ test printLineFromFileAnyOs { output.clearRetainingCapacity(); } { + // ends on mem.page_size boundary const file = try test_dir.dir.createFile("file_ends_on_page_boundary.zig", .{}); defer file.close(); const path = try join(allocator, &.{ test_dir_path, "file_ends_on_page_boundary.zig" }); @@ -1081,6 +1084,7 @@ test printLineFromFileAnyOs { output.clearRetainingCapacity(); } { + // multi-mem.page_size "line" const file = try test_dir.dir.createFile("very_long_first_line_spanning_multiple_pages.zig", .{}); defer file.close(); const path = try join(allocator, &.{ test_dir_path, "very_long_first_line_spanning_multiple_pages.zig" }); @@ -1106,6 +1110,7 @@ test printLineFromFileAnyOs { output.clearRetainingCapacity(); } { + // mem.page_size pages of newlines const file = try test_dir.dir.createFile("file_of_newlines.zig", .{}); defer file.close(); const path = try join(allocator, &.{ test_dir_path, "file_of_newlines.zig" }); diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 0c4d58c1c0e8..2a7fa1c6e5df 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -329,14 +329,8 @@ test "accessAbsolute" { var tmp = tmpDir(.{}); defer tmp.cleanup(); - var arena = ArenaAllocator.init(testing.allocator); - defer arena.deinit(); - const allocator = arena.allocator(); - - const base_path = blk: { - const relative_path = try fs.path.join(allocator, &.{ ".zig-cache", "tmp", tmp.sub_path[0..] }); - break :blk try fs.realpathAlloc(allocator, relative_path); - }; + const base_path = try tmp.dir.realpathAlloc(testing.allocator, "."); + defer testing.allocator.free(base_path); try fs.accessAbsolute(base_path, .{}); } @@ -347,32 +341,62 @@ test "openDirAbsolute" { var tmp = tmpDir(.{}); defer tmp.cleanup(); + const tmp_ino = (try tmp.dir.stat()).inode; + try tmp.dir.makeDir("subdir"); - var arena = ArenaAllocator.init(testing.allocator); - defer arena.deinit(); - const allocator = arena.allocator(); + const sub_path = try tmp.dir.realpathAlloc(testing.allocator, "subdir"); + defer testing.allocator.free(sub_path); - const base_path = blk: { - const relative_path = try fs.path.join(allocator, &.{ ".zig-cache", "tmp", tmp.sub_path[0..], "subdir" }); - break :blk try fs.realpathAlloc(allocator, relative_path); - }; + // Can open subdir + var tmp_sub = try fs.openDirAbsolute(sub_path, .{}); + defer tmp_sub.close(); + + const sub_ino = (try tmp_sub.stat()).inode; + + { + // Can open parent + const dir_path = try fs.path.join(testing.allocator, &.{ sub_path, ".." }); + defer testing.allocator.free(dir_path); + + var dir = try fs.openDirAbsolute(dir_path, .{}); + defer dir.close(); + + const ino = (try dir.stat()).inode; + try testing.expectEqual(tmp_ino, ino); + } { - var dir = try fs.openDirAbsolute(base_path, .{}); + // Can open subdir + "." + const dir_path = try fs.path.join(testing.allocator, &.{ sub_path, "." }); + defer testing.allocator.free(dir_path); + + var dir = try fs.openDirAbsolute(dir_path, .{}); defer dir.close(); + + const ino = (try dir.stat()).inode; + try testing.expectEqual(sub_ino, ino); } - for ([_][]const u8{ ".", ".." }) |sub_path| { - const dir_path = try fs.path.join(allocator, &.{ base_path, sub_path }); + { + // Can open subdir + "..", with extra "." + const dir_path = try fs.path.join(testing.allocator, &.{ sub_path, ".", "..", "." }); + defer testing.allocator.free(dir_path); + var dir = try fs.openDirAbsolute(dir_path, .{}); defer dir.close(); + + const ino = (try dir.stat()).inode; + try testing.expectEqual(tmp_ino, ino); } } test "openDir cwd parent '..'" { - if (native_os == .wasi) return error.SkipZigTest; - - var dir = try fs.cwd().openDir("..", .{}); + var dir = fs.cwd().openDir("..", .{}) catch |err| { + if (native_os == .wasi and err == error.AccessDenied) { + return; // This is okay. WASI disallows escaping from the fs sandbox + } + return err; + }; defer dir.close(); } @@ -388,7 +412,15 @@ test "openDir non-cwd parent '..'" { var subdir = try tmp.dir.makeOpenPath("subdir", .{}); defer subdir.close(); - var dir = try subdir.openDir("..", .{}); + var dir = subdir.openDir("..", .{}) catch |err| { + if (native_os == .wasi and err == error.AccessDenied) { + // This is odd (we're asking for the parent of a subdirectory, + // which should be safely inside the sandbox), but this is the + // current wasmtime behavior (with and without libc). + return error.SkipZigTest; + } + return err; + }; defer dir.close(); if (supports_absolute_paths) { @@ -421,10 +453,7 @@ test "readLinkAbsolute" { defer arena.deinit(); const allocator = arena.allocator(); - const base_path = blk: { - const relative_path = try fs.path.join(allocator, &.{ ".zig-cache", "tmp", tmp.sub_path[0..] }); - break :blk try fs.realpathAlloc(allocator, relative_path); - }; + const base_path = try tmp.dir.realpathAlloc(allocator, "."); { const target_path = try fs.path.join(allocator, &.{ base_path, "file.txt" }); @@ -770,17 +799,24 @@ test "file operations on directories" { try testing.expectError(error.IsDir, ctx.dir.createFile(test_dir_name, .{})); try testing.expectError(error.IsDir, ctx.dir.deleteFile(test_dir_name)); switch (native_os) { - // no error when reading a directory. - .dragonfly, .netbsd => {}, - // Currently, WASI will return error.Unexpected (via ENOTCAPABLE) when attempting fd_read on a directory handle. - // TODO: Re-enable on WASI once https://github.com/bytecodealliance/wasmtime/issues/1935 is resolved. - .wasi => {}, + .dragonfly, .netbsd => { + // no error when reading a directory. See https://github.com/ziglang/zig/issues/5732 + const buf = try ctx.dir.readFileAlloc(testing.allocator, test_dir_name, std.math.maxInt(usize)); + testing.allocator.free(buf); + }, + .wasi => { + // WASI return EBADF, which gets mapped to NotOpenForReading. + // See https://github.com/bytecodealliance/wasmtime/issues/1935 + try testing.expectError(error.NotOpenForReading, ctx.dir.readFileAlloc(testing.allocator, test_dir_name, std.math.maxInt(usize))); + }, else => { try testing.expectError(error.IsDir, ctx.dir.readFileAlloc(testing.allocator, test_dir_name, std.math.maxInt(usize))); }, } + // Note: The `.mode = .read_write` is necessary to ensure the error occurs on all platforms. // TODO: Add a read-only test as well, see https://github.com/ziglang/zig/issues/5732 + // Beware, wasmtime v23.0.2 (at least) does not error here try testing.expectError(error.IsDir, ctx.dir.openFile(test_dir_name, .{ .mode = .read_write })); if (ctx.path_type == .absolute and supports_absolute_paths) { @@ -1004,10 +1040,7 @@ test "renameAbsolute" { defer arena.deinit(); const allocator = arena.allocator(); - const base_path = blk: { - const relative_path = try fs.path.join(allocator, &.{ ".zig-cache", "tmp", tmp_dir.sub_path[0..] }); - break :blk try fs.realpathAlloc(allocator, relative_path); - }; + const base_path = try tmp_dir.dir.realpathAlloc(allocator, "."); try testing.expectError(error.FileNotFound, fs.renameAbsolute( try fs.path.join(allocator, &.{ base_path, "missing_file_name" }), @@ -1397,7 +1430,6 @@ test "sendfile" { defer tmp.cleanup(); try tmp.dir.makePath("os_test_tmp"); - defer tmp.dir.deleteTree("os_test_tmp") catch {}; var dir = try tmp.dir.openDir("os_test_tmp", .{}); defer dir.close(); @@ -1462,7 +1494,6 @@ test "copyRangeAll" { defer tmp.cleanup(); try tmp.dir.makePath("os_test_tmp"); - defer tmp.dir.deleteTree("os_test_tmp") catch {}; var dir = try tmp.dir.openDir("os_test_tmp", .{}); defer dir.close(); @@ -1781,10 +1812,7 @@ test "'.' and '..' in absolute functions" { defer arena.deinit(); const allocator = arena.allocator(); - const base_path = blk: { - const relative_path = try fs.path.join(allocator, &.{ ".zig-cache", "tmp", tmp.sub_path[0..] }); - break :blk try fs.realpathAlloc(allocator, relative_path); - }; + const base_path = try tmp.dir.realpathAlloc(allocator, "."); const subdir_path = try fs.path.join(allocator, &.{ base_path, "./subdir" }); try fs.makeDirAbsolute(subdir_path); diff --git a/lib/std/io/test.zig b/lib/std/io/test.zig index 5ac4bb65d2b5..0c2e480e47be 100644 --- a/lib/std/io/test.zig +++ b/lib/std/io/test.zig @@ -108,10 +108,7 @@ test "File seek ops" { const tmp_file_name = "temp_test_file.txt"; var file = try tmp.dir.createFile(tmp_file_name, .{}); - defer { - file.close(); - tmp.dir.deleteFile(tmp_file_name) catch {}; - } + defer file.close(); try file.writeAll(&([_]u8{0x55} ** 8192)); @@ -135,10 +132,7 @@ test "setEndPos" { const tmp_file_name = "temp_test_file.txt"; var file = try tmp.dir.createFile(tmp_file_name, .{}); - defer { - file.close(); - tmp.dir.deleteFile(tmp_file_name) catch {}; - } + defer file.close(); // Verify that the file size changes and the file offset is not moved try std.testing.expect((try file.getEndPos()) == 0); @@ -161,10 +155,8 @@ test "updateTimes" { const tmp_file_name = "just_a_temporary_file.txt"; var file = try tmp.dir.createFile(tmp_file_name, .{ .read = true }); - defer { - file.close(); - tmp.dir.deleteFile(tmp_file_name) catch {}; - } + defer file.close(); + const stat_old = try file.stat(); // Set atime and mtime to 5s before try file.updateTimes( diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig index bf73cea84e67..11282ba4dfcb 100644 --- a/lib/std/posix/test.zig +++ b/lib/std/posix/test.zig @@ -8,7 +8,6 @@ const io = std.io; const fs = std.fs; const mem = std.mem; const elf = std.elf; -const File = std.fs.File; const Thread = std.Thread; const linux = std.os.linux; @@ -19,8 +18,6 @@ const AtomicRmwOp = std.builtin.AtomicRmwOp; const AtomicOrder = std.builtin.AtomicOrder; const native_os = builtin.target.os.tag; const tmpDir = std.testing.tmpDir; -const Dir = std.fs.Dir; -const ArenaAllocator = std.heap.ArenaAllocator; // Filter to skip tests on platforms that don't support chdir. const supports_chdir = (native_os != .wasi); @@ -28,13 +25,6 @@ const supports_chdir = (native_os != .wasi); // Filter to skip tests on platforms that don't support absolute paths const supports_absolute_paths = (native_os != .wasi); -// Tests that create/delete files in the current working directory are not safe to run -// in CI. So this is off by default. But handy when testing. Do not submit true. -// Test should probably be fixed to use unique names to avoid races. -// -// https://github.com/ziglang/zig/issues/14968 -const enable_sketchy_cwd_tests = true; - test "check WASI CWD" { if (native_os == .wasi) { if (std.options.wasiCwd() != 3) { @@ -57,9 +47,25 @@ test "WTF-8 to WTF-16 conversion buffer overflows" { try expectError(error.NameTooLong, posix.chdirZ(input_wtf8)); } -test "chdir smoke test" { +// For tests that create entries in the current working directory, use +// this to ensure a unique name. Otherwise parallel test invocations +// (e.g., running on multiple architectures on CI) might collide in the +// working directory. Tests should prefer tmpDir() over this. +fn unique_fname(allocator: std.mem.Allocator, basename: []const u8) ![]u8 { + const byte_count = 12; + const char_count = comptime std.fs.base64_encoder.calcSize(byte_count); + + var random_bytes: [byte_count]u8 = undefined; + std.crypto.random.bytes(&random_bytes); + + var prefix: [char_count]u8 = undefined; + _ = std.fs.base64_encoder.encode(&prefix, &random_bytes); + + return try std.fmt.allocPrint(allocator, "{s}-{s}", .{ prefix, basename }); +} + +test "chdir absolute parent" { if (!supports_chdir) return error.SkipZigTest; - if (!enable_sketchy_cwd_tests) return error.SkipZigTest; // Get current working directory path var old_cwd_buf: [fs.max_path_bytes]u8 = undefined; @@ -74,47 +80,45 @@ test "chdir smoke test" { } // Next, change current working directory to one level above - if (native_os != .wasi) { // WASI does not support navigating outside of Preopens - const parent = fs.path.dirname(old_cwd) orelse unreachable; // old_cwd should be absolute - try posix.chdir(parent); + const parent = fs.path.dirname(old_cwd) orelse unreachable; // old_cwd should be absolute + try posix.chdir(parent); + defer posix.chdir(old_cwd) catch unreachable; - // Restore cwd because process may have other tests that do not tolerate chdir. - defer posix.chdir(old_cwd) catch unreachable; + var new_cwd_buf: [fs.max_path_bytes]u8 = undefined; + const new_cwd = try posix.getcwd(new_cwd_buf[0..]); + try expect(mem.eql(u8, parent, new_cwd)); +} - var new_cwd_buf: [fs.max_path_bytes]u8 = undefined; - const new_cwd = try posix.getcwd(new_cwd_buf[0..]); - try expect(mem.eql(u8, parent, new_cwd)); - } +test "chdir relative subdir" { + if (!supports_chdir) return error.SkipZigTest; - // Next, change current working directory to a temp directory one level below - { - // Create a tmp directory - var tmp_dir_buf: [fs.max_path_bytes]u8 = undefined; - const tmp_dir_path = path: { - var allocator = std.heap.FixedBufferAllocator.init(&tmp_dir_buf); - break :path try fs.path.resolve(allocator.allocator(), &[_][]const u8{ old_cwd, "zig-test-tmp" }); - }; - var tmp_dir = try fs.cwd().makeOpenPath("zig-test-tmp", .{}); + // Get current working directory path + var old_cwd_buf: [fs.max_path_bytes]u8 = undefined; + const old_cwd = try posix.getcwd(old_cwd_buf[0..]); - // Change current working directory to tmp directory - try posix.chdir("zig-test-tmp"); + const dir_name = try unique_fname(a, "chdir-target"); + defer a.free(dir_name); - var new_cwd_buf: [fs.max_path_bytes]u8 = undefined; - const new_cwd = try posix.getcwd(new_cwd_buf[0..]); + // Create a new (sub)directory + const expected_tmp_dir_path = try fs.path.resolve(a, &.{ old_cwd, dir_name }); + defer a.free(expected_tmp_dir_path); - // On Windows, fs.path.resolve returns an uppercase drive letter, but the drive letter returned by getcwd may be lowercase - var resolved_cwd_buf: [fs.max_path_bytes]u8 = undefined; - const resolved_cwd = path: { - var allocator = std.heap.FixedBufferAllocator.init(&resolved_cwd_buf); - break :path try fs.path.resolve(allocator.allocator(), &[_][]const u8{new_cwd}); - }; - try expect(mem.eql(u8, tmp_dir_path, resolved_cwd)); + var tmp_dir = try fs.cwd().makeOpenPath(dir_name, .{}); + defer tmp_dir.close(); + defer fs.cwd().deleteDir(dir_name) catch {}; - // Restore cwd because process may have other tests that do not tolerate chdir. - tmp_dir.close(); - posix.chdir(old_cwd) catch unreachable; - try fs.cwd().deleteDir("zig-test-tmp"); - } + // Change current working directory to new directory + try posix.chdir(dir_name); + defer posix.chdir(old_cwd) catch unreachable; + + var new_cwd_buf: [fs.max_path_bytes]u8 = undefined; + const new_cwd = try posix.getcwd(new_cwd_buf[0..]); + + // On Windows, fs.path.resolve returns an uppercase drive letter, but the drive letter returned by getcwd may be lowercase + const resolved_cwd = try fs.path.resolve(a, &.{new_cwd}); + defer a.free(resolved_cwd); + + try expect(mem.eql(u8, expected_tmp_dir_path, resolved_cwd)); } const default_mode = switch (posix.mode_t) { @@ -131,50 +135,62 @@ test "open smoke test" { var tmp = tmpDir(.{}); defer tmp.cleanup(); - // Get base abs path - var arena = ArenaAllocator.init(testing.allocator); - defer arena.deinit(); - const allocator = arena.allocator(); + const base_path = try tmp.dir.realpathAlloc(a, "."); + defer a.free(base_path); - const base_path = blk: { - const relative_path = try fs.path.join(allocator, &[_][]const u8{ ".zig-cache", "tmp", tmp.sub_path[0..] }); - break :blk try fs.realpathAlloc(allocator, relative_path); - }; - - var file_path: []u8 = undefined; - var fd: posix.fd_t = undefined; const mode: posix.mode_t = default_mode; - // Create some file using `open`. - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode); - posix.close(fd); + { + // Create some file using `open`. + const file_path = try fs.path.join(a, &.{ base_path, "some_file" }); + defer a.free(file_path); + const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode); + posix.close(fd); + } - // Try this again with the same flags. This op should fail with error.PathAlreadyExists. - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - try expectError(error.PathAlreadyExists, posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode)); + { + // Try this again with the same flags. This op should fail with error.PathAlreadyExists. + const file_path = try fs.path.join(a, &.{ base_path, "some_file" }); + defer a.free(file_path); + try expectError(error.PathAlreadyExists, posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode)); + } - // Try opening without `EXCL` flag. - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true }, mode); - posix.close(fd); + { + // Try opening without `EXCL` flag. + const file_path = try fs.path.join(a, &.{ base_path, "some_file" }); + defer a.free(file_path); + const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true }, mode); + posix.close(fd); + } - // Try opening as a directory which should fail. - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - try expectError(error.NotDir, posix.open(file_path, .{ .ACCMODE = .RDWR, .DIRECTORY = true }, mode)); + { + // Try opening as a directory which should fail. + const file_path = try fs.path.join(a, &.{ base_path, "some_file" }); + defer a.free(file_path); + try expectError(error.NotDir, posix.open(file_path, .{ .ACCMODE = .RDWR, .DIRECTORY = true }, mode)); + } - // Create some directory - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" }); - try posix.mkdir(file_path, mode); + { + // Create some directory + const file_path = try fs.path.join(a, &.{ base_path, "some_dir" }); + defer a.free(file_path); + try posix.mkdir(file_path, mode); + } - // Open dir using `open` - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" }); - fd = try posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode); - posix.close(fd); + { + // Open dir using `open` + const file_path = try fs.path.join(a, &.{ base_path, "some_dir" }); + defer a.free(file_path); + const fd = try posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode); + posix.close(fd); + } - // Try opening as file which should fail. - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" }); - try expectError(error.IsDir, posix.open(file_path, .{ .ACCMODE = .RDWR }, mode)); + { + // Try opening as file which should fail. + const file_path = try fs.path.join(a, &.{ base_path, "some_dir" }); + defer a.free(file_path); + try expectError(error.IsDir, posix.open(file_path, .{ .ACCMODE = .RDWR }, mode)); + } } test "openat smoke test" { @@ -227,46 +243,45 @@ test "openat smoke test" { posix.close(fd); // Try opening as file which should fail. + // Note on wasmtime v23.0.2 (at least) this returns a file descriptor (not the IsDir error). try expectError(error.IsDir, posix.openat(tmp.dir.fd, "some_dir", CommonOpenFlags.lower(.{ .ACCMODE = .RDWR, }), mode)); } test "symlink with relative paths" { - if (!enable_sketchy_cwd_tests) return error.SkipZigTest; + const targetName = try unique_fname(a, "symlink-target"); + defer a.free(targetName); + + const symlinkName = try unique_fname(a, "symlinked"); + defer a.free(symlinkName); const cwd = fs.cwd(); - cwd.deleteFile("file.txt") catch {}; - cwd.deleteFile("symlinked") catch {}; - // First, try relative paths in cwd - try cwd.writeFile(.{ .sub_path = "file.txt", .data = "nonsense" }); + defer cwd.deleteFile(targetName) catch {}; + defer cwd.deleteFile(symlinkName) catch {}; + + // Create the target file + try cwd.writeFile(.{ .sub_path = targetName, .data = "nonsense" }); if (native_os == .windows) { - std.os.windows.CreateSymbolicLink( - cwd.fd, - &[_]u16{ 's', 'y', 'm', 'l', 'i', 'n', 'k', 'e', 'd' }, - &[_:0]u16{ 'f', 'i', 'l', 'e', '.', 't', 'x', 't' }, - false, - ) catch |err| switch (err) { + const wtargetName = try std.unicode.wtf8ToWtf16LeAllocZ(a, targetName); + const wsymlinkName = try std.unicode.wtf8ToWtf16LeAllocZ(a, symlinkName); + defer a.free(wtargetName); + defer a.free(wsymlinkName); + + std.os.windows.CreateSymbolicLink(cwd.fd, wsymlinkName, wtargetName, false) catch |err| switch (err) { // Symlink requires admin privileges on windows, so this test can legitimately fail. - error.AccessDenied => { - try cwd.deleteFile("file.txt"); - try cwd.deleteFile("symlinked"); - return error.SkipZigTest; - }, + error.AccessDenied => return error.SkipZigTest, else => return err, }; } else { - try posix.symlink("file.txt", "symlinked"); + try posix.symlink(targetName, symlinkName); } var buffer: [fs.max_path_bytes]u8 = undefined; - const given = try posix.readlink("symlinked", buffer[0..]); - try expect(mem.eql(u8, "file.txt", given)); - - try cwd.deleteFile("file.txt"); - try cwd.deleteFile("symlinked"); + const given = try posix.readlink(symlinkName, buffer[0..]); + try expect(mem.eql(u8, targetName, given)); } test "readlink on Windows" { @@ -288,20 +303,25 @@ test "link with relative paths" { .wasi, .linux, .solaris, .illumos => {}, else => return error.SkipZigTest, } - if (!enable_sketchy_cwd_tests) return error.SkipZigTest; + + const targetName = try unique_fname(a, "link-target"); + defer a.free(targetName); + + const linkName = try unique_fname(a, "newlink"); + defer a.free(linkName); var cwd = fs.cwd(); - cwd.deleteFile("example.txt") catch {}; - cwd.deleteFile("new.txt") catch {}; + defer cwd.deleteFile(targetName) catch {}; + defer cwd.deleteFile(linkName) catch {}; - try cwd.writeFile(.{ .sub_path = "example.txt", .data = "example" }); - try posix.link("example.txt", "new.txt"); + try cwd.writeFile(.{ .sub_path = targetName, .data = "example" }); + try posix.link(targetName, linkName); - const efd = try cwd.openFile("example.txt", .{}); + const efd = try cwd.openFile(targetName, .{}); defer efd.close(); - const nfd = try cwd.openFile("new.txt", .{}); + const nfd = try cwd.openFile(linkName, .{}); defer nfd.close(); { @@ -312,14 +332,12 @@ test "link with relative paths" { try testing.expectEqual(@as(@TypeOf(nstat.nlink), 2), nstat.nlink); } - try posix.unlink("new.txt"); + try posix.unlink(linkName); { const estat = try posix.fstat(efd.handle); try testing.expectEqual(@as(@TypeOf(estat.nlink), 1), estat.nlink); } - - try cwd.deleteFile("example.txt"); } test "linkat with different directories" { @@ -327,21 +345,25 @@ test "linkat with different directories" { .wasi, .linux, .solaris, .illumos => {}, else => return error.SkipZigTest, } - if (!enable_sketchy_cwd_tests) return error.SkipZigTest; + + const targetName = try unique_fname(a, "link-target"); + defer a.free(targetName); + + const linkName = try unique_fname(a, "newlink"); + defer a.free(linkName); var cwd = fs.cwd(); var tmp = tmpDir(.{}); - cwd.deleteFile("example.txt") catch {}; - tmp.dir.deleteFile("new.txt") catch {}; + defer cwd.deleteFile(targetName) catch {}; - try cwd.writeFile(.{ .sub_path = "example.txt", .data = "example" }); - try posix.linkat(cwd.fd, "example.txt", tmp.dir.fd, "new.txt", 0); + try cwd.writeFile(.{ .sub_path = targetName, .data = "example" }); + try posix.linkat(cwd.fd, targetName, tmp.dir.fd, linkName, 0); - const efd = try cwd.openFile("example.txt", .{}); + const efd = try cwd.openFile(targetName, .{}); defer efd.close(); - const nfd = try tmp.dir.openFile("new.txt", .{}); + const nfd = try tmp.dir.openFile(linkName, .{}); { defer nfd.close(); @@ -352,14 +374,12 @@ test "linkat with different directories" { try testing.expectEqual(@as(@TypeOf(nstat.nlink), 2), nstat.nlink); } - try posix.unlinkat(tmp.dir.fd, "new.txt", 0); + try posix.unlinkat(tmp.dir.fd, linkName, 0); { const estat = try posix.fstat(efd.handle); try testing.expectEqual(@as(@TypeOf(estat.nlink), 1), estat.nlink); } - - try cwd.deleteFile("example.txt"); } test "fstatat" { @@ -698,8 +718,6 @@ test "mmap" { try testing.expectEqual(i, try stream.readInt(u32, .little)); } } - - try tmp.dir.deleteFile(test_out_file); } test "getenv" { @@ -725,10 +743,7 @@ test "fcntl" { const test_out_file = "os_tmp_test"; const file = try tmp.dir.createFile(test_out_file, .{}); - defer { - file.close(); - tmp.dir.deleteFile(test_out_file) catch {}; - } + defer file.close(); // Note: The test assumes createFile opens the file with CLOEXEC { @@ -764,10 +779,7 @@ test "sync" { const test_out_file = "os_tmp_test"; const file = try tmp.dir.createFile(test_out_file, .{}); - defer { - file.close(); - tmp.dir.deleteFile(test_out_file) catch {}; - } + defer file.close(); posix.sync(); try posix.syncfs(file.handle); @@ -784,10 +796,7 @@ test "fsync" { const test_out_file = "os_tmp_test"; const file = try tmp.dir.createFile(test_out_file, .{}); - defer { - file.close(); - tmp.dir.deleteFile(test_out_file) catch {}; - } + defer file.close(); try posix.fsync(file.handle); try posix.fdatasync(file.handle); @@ -1034,54 +1043,65 @@ test "rename smoke test" { var tmp = tmpDir(.{}); defer tmp.cleanup(); - // Get base abs path - var arena = ArenaAllocator.init(testing.allocator); - defer arena.deinit(); - const allocator = arena.allocator(); - - const base_path = blk: { - const relative_path = try fs.path.join(allocator, &[_][]const u8{ ".zig-cache", "tmp", tmp.sub_path[0..] }); - break :blk try fs.realpathAlloc(allocator, relative_path); - }; + const base_path = try tmp.dir.realpathAlloc(a, "."); + defer a.free(base_path); - var file_path: []u8 = undefined; - var fd: posix.fd_t = undefined; const mode: posix.mode_t = default_mode; - // Create some file using `open`. - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode); - posix.close(fd); - - // Rename the file - var new_file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_file" }); - try posix.rename(file_path, new_file_path); - - // Try opening renamed file - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_file" }); - fd = try posix.open(file_path, .{ .ACCMODE = .RDWR }, mode); - posix.close(fd); + { + // Create some file using `open`. + const file_path = try fs.path.join(a, &.{ base_path, "some_file" }); + defer a.free(file_path); + const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode); + posix.close(fd); + + // Rename the file + const new_file_path = try fs.path.join(a, &.{ base_path, "some_other_file" }); + defer a.free(new_file_path); + try posix.rename(file_path, new_file_path); + } - // Try opening original file - should fail with error.FileNotFound - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - try expectError(error.FileNotFound, posix.open(file_path, .{ .ACCMODE = .RDWR }, mode)); + { + // Try opening renamed file + const file_path = try fs.path.join(a, &.{ base_path, "some_other_file" }); + defer a.free(file_path); + const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR }, mode); + posix.close(fd); + } - // Create some directory - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" }); - try posix.mkdir(file_path, mode); + { + // Try opening original file - should fail with error.FileNotFound + const file_path = try fs.path.join(a, &.{ base_path, "some_file" }); + defer a.free(file_path); + try expectError(error.FileNotFound, posix.open(file_path, .{ .ACCMODE = .RDWR }, mode)); + } - // Rename the directory - new_file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_dir" }); - try posix.rename(file_path, new_file_path); + { + // Create some directory + const file_path = try fs.path.join(a, &.{ base_path, "some_dir" }); + defer a.free(file_path); + try posix.mkdir(file_path, mode); + + // Rename the directory + const new_file_path = try fs.path.join(a, &.{ base_path, "some_other_dir" }); + defer a.free(new_file_path); + try posix.rename(file_path, new_file_path); + } - // Try opening renamed directory - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_dir" }); - fd = try posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode); - posix.close(fd); + { + // Try opening renamed directory + const file_path = try fs.path.join(a, &.{ base_path, "some_other_dir" }); + defer a.free(file_path); + const fd = try posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode); + posix.close(fd); + } - // Try opening original directory - should fail with error.FileNotFound - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" }); - try expectError(error.FileNotFound, posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode)); + { + // Try opening original directory - should fail with error.FileNotFound + const file_path = try fs.path.join(a, &.{ base_path, "some_dir" }); + defer a.free(file_path); + try expectError(error.FileNotFound, posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode)); + } } test "access smoke test" { @@ -1091,44 +1111,51 @@ test "access smoke test" { var tmp = tmpDir(.{}); defer tmp.cleanup(); - // Get base abs path - var arena = ArenaAllocator.init(testing.allocator); - defer arena.deinit(); - const allocator = arena.allocator(); + const base_path = try tmp.dir.realpathAlloc(a, "."); + defer a.free(base_path); - const base_path = blk: { - const relative_path = try fs.path.join(allocator, &[_][]const u8{ ".zig-cache", "tmp", tmp.sub_path[0..] }); - break :blk try fs.realpathAlloc(allocator, relative_path); - }; - - var file_path: []u8 = undefined; - var fd: posix.fd_t = undefined; const mode: posix.mode_t = default_mode; - // Create some file using `open`. - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode); - posix.close(fd); + { + // Create some file using `open`. + const file_path = try fs.path.join(a, &.{ base_path, "some_file" }); + defer a.free(file_path); + const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode); + posix.close(fd); + } - // Try to access() the file - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - if (native_os == .windows) { - try posix.access(file_path, posix.F_OK); - } else { - try posix.access(file_path, posix.F_OK | posix.W_OK | posix.R_OK); + { + // Try to access() the file + const file_path = try fs.path.join(a, &.{ base_path, "some_file" }); + defer a.free(file_path); + if (native_os == .windows) { + try posix.access(file_path, posix.F_OK); + } else { + try posix.access(file_path, posix.F_OK | posix.W_OK | posix.R_OK); + } } - // Try to access() a non-existent file - should fail with error.FileNotFound - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_file" }); - try expectError(error.FileNotFound, posix.access(file_path, posix.F_OK)); + { + // Try to access() a non-existent file - should fail with error.FileNotFound + const file_path = try fs.path.join(a, &.{ base_path, "some_other_file" }); + defer a.free(file_path); + try expectError(error.FileNotFound, posix.access(file_path, posix.F_OK)); + } - // Create some directory - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" }); - try posix.mkdir(file_path, mode); + { + // Create some directory + const file_path = try fs.path.join(a, &.{ base_path, "some_dir" }); + defer a.free(file_path); + try posix.mkdir(file_path, mode); + } + + { + // Try to access() the directory + const file_path = try fs.path.join(a, &.{ base_path, "some_dir" }); + defer a.free(file_path); - // Try to access() the directory - file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" }); - try posix.access(file_path, posix.F_OK); + try posix.access(file_path, posix.F_OK); + } } test "timerfd" { @@ -1160,103 +1187,59 @@ test "isatty" { } test "read with empty buffer" { - if (!supports_absolute_paths) return error.SkipZigTest; // Or fix test to use relative paths - var tmp = tmpDir(.{}); defer tmp.cleanup(); - var arena = ArenaAllocator.init(testing.allocator); - defer arena.deinit(); - const allocator = arena.allocator(); - - // Get base abs path - const base_path = blk: { - const relative_path = try fs.path.join(allocator, &[_][]const u8{ ".zig-cache", "tmp", tmp.sub_path[0..] }); - break :blk try fs.realpathAlloc(allocator, relative_path); - }; - - const file_path: []u8 = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - var file = try fs.cwd().createFile(file_path, .{ .read = true }); + var file = try tmp.dir.createFile("read_empty", .{ .read = true }); defer file.close(); - const bytes = try allocator.alloc(u8, 0); + const bytes = try a.alloc(u8, 0); + defer a.free(bytes); - _ = try posix.read(file.handle, bytes); + const rc = try posix.read(file.handle, bytes); + try expectEqual(rc, 0); } test "pread with empty buffer" { - if (!supports_absolute_paths) return error.SkipZigTest; // Or fix test to use relative paths - var tmp = tmpDir(.{}); defer tmp.cleanup(); - var arena = ArenaAllocator.init(testing.allocator); - defer arena.deinit(); - const allocator = arena.allocator(); - - // Get base abs path - const base_path = blk: { - const relative_path = try fs.path.join(allocator, &[_][]const u8{ ".zig-cache", "tmp", tmp.sub_path[0..] }); - break :blk try fs.realpathAlloc(allocator, relative_path); - }; - - const file_path: []u8 = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - var file = try fs.cwd().createFile(file_path, .{ .read = true }); + var file = try tmp.dir.createFile("pread_empty", .{ .read = true }); defer file.close(); - const bytes = try allocator.alloc(u8, 0); + const bytes = try a.alloc(u8, 0); + defer a.free(bytes); - _ = try posix.pread(file.handle, bytes, 0); + const rc = try posix.pread(file.handle, bytes, 0); + try expectEqual(rc, 0); } test "write with empty buffer" { - if (!supports_absolute_paths) return error.SkipZigTest; // Or fix test to use relative paths - var tmp = tmpDir(.{}); defer tmp.cleanup(); - var arena = ArenaAllocator.init(testing.allocator); - defer arena.deinit(); - const allocator = arena.allocator(); - - // Get base abs path - const base_path = blk: { - const relative_path = try fs.path.join(allocator, &[_][]const u8{ ".zig-cache", "tmp", tmp.sub_path[0..] }); - break :blk try fs.realpathAlloc(allocator, relative_path); - }; - - const file_path: []u8 = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - var file = try fs.cwd().createFile(file_path, .{}); + var file = try tmp.dir.createFile("write_empty", .{}); defer file.close(); - const bytes = try allocator.alloc(u8, 0); + const bytes = try a.alloc(u8, 0); + defer a.free(bytes); - _ = try posix.write(file.handle, bytes); + const rc = try posix.write(file.handle, bytes); + try expectEqual(rc, 0); } test "pwrite with empty buffer" { - if (!supports_absolute_paths) return error.SkipZigTest; // Or fix test to use relative paths - var tmp = tmpDir(.{}); defer tmp.cleanup(); - var arena = ArenaAllocator.init(testing.allocator); - defer arena.deinit(); - const allocator = arena.allocator(); - - // Get base abs path - const base_path = blk: { - const relative_path = try fs.path.join(allocator, &[_][]const u8{ ".zig-cache", "tmp", tmp.sub_path[0..] }); - break :blk try fs.realpathAlloc(allocator, relative_path); - }; - - const file_path: []u8 = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - var file = try fs.cwd().createFile(file_path, .{}); + var file = try tmp.dir.createFile("pwrite_empty", .{}); defer file.close(); - const bytes = try allocator.alloc(u8, 0); + const bytes = try a.alloc(u8, 0); + defer a.free(bytes); - _ = try posix.pwrite(file.handle, bytes, 0); + const rc = try posix.pwrite(file.handle, bytes, 0); + try expectEqual(rc, 0); } fn expectMode(dir: posix.fd_t, file: []const u8, mode: posix.mode_t) !void {