authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-11 22:35:55-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-23 22:15:09-08:00
loge1cf753db72425fd944f6fe9f2a991fb1de3f942
tree4b9c33aa2cd4e0da5ad7e5fec9f0de45c6747507
parent68621afd2e203d82b6f53bf4ede951827fa98db8

std: update fchmodat tests


5 files changed, 243 insertions(+), 313 deletions(-)

lib/std/Build.zig+1-1
...@@ -1698,7 +1698,7 @@ pub fn addCheckFile(...@@ -1698,7 +1698,7 @@ pub fn addCheckFile(
1698 return Step.CheckFile.create(b, file_source, options);1698 return Step.CheckFile.create(b, file_source, options);
1699}1699}
17001700
1701pub fn truncateFile(b: *Build, dest_path: []const u8) (Io.Dir.MakeError || Io.Dir.StatPathError)!void {1701pub fn truncateFile(b: *Build, dest_path: []const u8) (Io.Dir.MakeError || Io.Dir.StatFileError)!void {
1702 const io = b.graph.io;1702 const io = b.graph.io;
1703 if (b.verbose) log.info("truncate {s}", .{dest_path});1703 if (b.verbose) log.info("truncate {s}", .{dest_path});
1704 const cwd = Io.Dir.cwd();1704 const cwd = Io.Dir.cwd();
lib/std/Io/Dir.zig+1-1
...@@ -1710,7 +1710,7 @@ pub const SetFilePermissionsOptions = struct {...@@ -1710,7 +1710,7 @@ pub const SetFilePermissionsOptions = struct {
1710 follow_symlinks: bool = true,1710 follow_symlinks: bool = true,
1711};1711};
17121712
1713/// Also known as "chmodat".1713/// Also known as "fchmodat".
1714pub fn setFilePermissions(1714pub fn setFilePermissions(
1715 dir: Dir,1715 dir: Dir,
1716 io: Io,1716 io: Io,
lib/std/Io/Kqueue.zig+2-2
...@@ -873,7 +873,7 @@ pub fn io(k: *Kqueue) Io {...@@ -873,7 +873,7 @@ pub fn io(k: *Kqueue) Io {
873 .dirMakePath = dirMakePath,873 .dirMakePath = dirMakePath,
874 .dirMakeOpenPath = dirMakeOpenPath,874 .dirMakeOpenPath = dirMakeOpenPath,
875 .dirStat = dirStat,875 .dirStat = dirStat,
876 .dirStatPath = dirStatPath,876 .dirStatFile = dirStatFile,
877877
878 .fileStat = fileStat,878 .fileStat = fileStat,
879 .dirAccess = dirAccess,879 .dirAccess = dirAccess,
...@@ -1144,7 +1144,7 @@ fn dirStat(userdata: ?*anyopaque, dir: Dir) Dir.StatError!Dir.Stat {...@@ -1144,7 +1144,7 @@ fn dirStat(userdata: ?*anyopaque, dir: Dir) Dir.StatError!Dir.Stat {
1144 _ = dir;1144 _ = dir;
1145 @panic("TODO");1145 @panic("TODO");
1146}1146}
1147fn dirStatPath(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Dir.StatPathOptions) Dir.StatPathError!File.Stat {1147fn dirStatFile(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Dir.StatPathOptions) Dir.StatFileError!File.Stat {
1148 const k: *Kqueue = @ptrCast(@alignCast(userdata));1148 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1149 _ = k;1149 _ = k;
1150 _ = dir;1150 _ = dir;
lib/std/fs/test.zig+239-248
...@@ -3,19 +3,23 @@ const native_os = builtin.os.tag;...@@ -3,19 +3,23 @@ const native_os = builtin.os.tag;
33
4const std = @import("../std.zig");4const std = @import("../std.zig");
5const Io = std.Io;5const Io = std.Io;
6const testing = std.testing;
7const expect = std.testing.expect;
8const fs = std.fs;6const fs = std.fs;
9const mem = std.mem;7const mem = std.mem;
10const wasi = std.os.wasi;8const wasi = std.os.wasi;
11const windows = std.os.windows;9const windows = std.os.windows;
12const posix = std.posix;
13const ArenaAllocator = std.heap.ArenaAllocator;10const ArenaAllocator = std.heap.ArenaAllocator;
14const Dir = std.Io.Dir;11const Dir = std.Io.Dir;
15const File = std.Io.File;12const File = std.Io.File;
16const tmpDir = std.testing.tmpDir;
17const SymLinkFlags = std.Io.Dir.SymLinkFlags;13const SymLinkFlags = std.Io.Dir.SymLinkFlags;
1814
15const testing = std.testing;
16const expect = std.testing.expect;
17const expectError = std.testing.expectError;
18const expectEqual = std.testing.expectEqual;
19const tmpDir = std.testing.tmpDir;
20const expectEqualStrings = std.testing.expectEqualStrings;
21const expectEqualSlices = std.testing.expectEqualSlices;
22
19const PathType = enum {23const PathType = enum {
20 relative,24 relative,
21 absolute,25 absolute,
...@@ -29,7 +33,7 @@ const PathType = enum {...@@ -29,7 +33,7 @@ const PathType = enum {
29 };33 };
30 }34 }
3135
32 pub const TransformError = posix.RealPathError || error{OutOfMemory};36 pub const TransformError = Io.Dir.RealPathError || error{OutOfMemory};
33 pub const TransformFn = fn (allocator: mem.Allocator, dir: Dir, relative_path: [:0]const u8) TransformError![:0]const u8;37 pub const TransformFn = fn (allocator: mem.Allocator, dir: Dir, relative_path: [:0]const u8) TransformError![:0]const u8;
3438
35 pub fn getTransformFn(comptime path_type: PathType) TransformFn {39 pub fn getTransformFn(comptime path_type: PathType) TransformFn {
...@@ -249,7 +253,7 @@ test "Dir.readLink on non-symlinks" {...@@ -249,7 +253,7 @@ test "Dir.readLink on non-symlinks" {
249fn testReadLink(io: Io, dir: Dir, target_path: []const u8, symlink_path: []const u8) !void {253fn testReadLink(io: Io, dir: Dir, target_path: []const u8, symlink_path: []const u8) !void {
250 var buffer: [fs.max_path_bytes]u8 = undefined;254 var buffer: [fs.max_path_bytes]u8 = undefined;
251 const actual = try dir.readLink(io, symlink_path, &buffer);255 const actual = try dir.readLink(io, symlink_path, &buffer);
252 try testing.expectEqualStrings(target_path, actual);256 try expectEqualStrings(target_path, actual);
253}257}
254258
255fn testReadLinkW(allocator: mem.Allocator, dir: Dir, target_path: []const u8, symlink_path: []const u8) !void {259fn testReadLinkW(allocator: mem.Allocator, dir: Dir, target_path: []const u8, symlink_path: []const u8) !void {
...@@ -260,13 +264,13 @@ fn testReadLinkW(allocator: mem.Allocator, dir: Dir, target_path: []const u8, sy...@@ -260,13 +264,13 @@ fn testReadLinkW(allocator: mem.Allocator, dir: Dir, target_path: []const u8, sy
260 const wtf16_buffer = try allocator.alloc(u16, target_path_w.len);264 const wtf16_buffer = try allocator.alloc(u16, target_path_w.len);
261 defer allocator.free(wtf16_buffer);265 defer allocator.free(wtf16_buffer);
262 const actual = try dir.readLinkW(symlink_path_w.span(), wtf16_buffer);266 const actual = try dir.readLinkW(symlink_path_w.span(), wtf16_buffer);
263 try testing.expectEqualSlices(u16, target_path_w, actual);267 try expectEqualSlices(u16, target_path_w, actual);
264}268}
265269
266fn testReadLinkAbsolute(target_path: []const u8, symlink_path: []const u8) !void {270fn testReadLinkAbsolute(target_path: []const u8, symlink_path: []const u8) !void {
267 var buffer: [fs.max_path_bytes]u8 = undefined;271 var buffer: [fs.max_path_bytes]u8 = undefined;
268 const given = try fs.readLinkAbsolute(symlink_path, buffer[0..]);272 const given = try fs.readLinkAbsolute(symlink_path, buffer[0..]);
269 try testing.expectEqualStrings(target_path, given);273 try expectEqualStrings(target_path, given);
270}274}
271275
272test "File.stat on a File that is a symlink returns Kind.sym_link" {276test "File.stat on a File that is a symlink returns Kind.sym_link" {
...@@ -286,72 +290,11 @@ test "File.stat on a File that is a symlink returns Kind.sym_link" {...@@ -286,72 +290,11 @@ test "File.stat on a File that is a symlink returns Kind.sym_link" {
286290
287 try setupSymlink(io, ctx.dir, dir_target_path, "symlink", .{ .is_directory = true });291 try setupSymlink(io, ctx.dir, dir_target_path, "symlink", .{ .is_directory = true });
288292
289 var symlink: Dir = switch (builtin.target.os.tag) {293 var symlink: Dir = try ctx.dir.openDir("symlink", .{ .follow_symlinks = false });
290 .windows => windows_symlink: {
291 const sub_path_w = try windows.cStrToPrefixedFileW(ctx.dir.handle, "symlink");
292
293 var result: Dir = .{
294 .handle = undefined,
295 };
296
297 const path_len_bytes = @as(u16, @intCast(sub_path_w.span().len * 2));
298 var nt_name = windows.UNICODE_STRING{
299 .Length = path_len_bytes,
300 .MaximumLength = path_len_bytes,
301 .Buffer = @constCast(&sub_path_w.data),
302 };
303 var attr: windows.OBJECT_ATTRIBUTES = .{
304 .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
305 .RootDirectory = if (fs.path.isAbsoluteWindowsW(sub_path_w.span())) null else ctx.dir.handle,
306 .Attributes = 0,
307 .ObjectName = &nt_name,
308 .SecurityDescriptor = null,
309 .SecurityQualityOfService = null,
310 };
311 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
312 const rc = windows.ntdll.NtCreateFile(
313 &result.handle,
314 windows.STANDARD_RIGHTS_READ | windows.FILE_READ_ATTRIBUTES | windows.FILE_READ_EA | windows.SYNCHRONIZE | windows.FILE_TRAVERSE,
315 &attr,
316 &io_status_block,
317 null,
318 .{ .NORMAL = true },
319 .VALID_FLAGS,
320 .OPEN,
321 .{
322 .DIRECTORY_FILE = true,
323 .IO = .SYNCHRONOUS_NONALERT,
324 .OPEN_FOR_BACKUP_INTENT = true,
325 .OPEN_REPARSE_POINT = true, // the important thing here
326 },
327 null,
328 0,
329 );
330
331 switch (rc) {
332 .SUCCESS => break :windows_symlink .{ .fd = result.handle },
333 else => return windows.unexpectedStatus(rc),
334 }
335 },
336 .linux => linux_symlink: {
337 const sub_path_c = try posix.toPosixPath("symlink");
338 // the O_NOFOLLOW | O_PATH combination can obtain a fd to a symlink
339 // note that if O_DIRECTORY is set, then this will error with ENOTDIR
340 const flags: posix.O = .{
341 .NOFOLLOW = true,
342 .PATH = true,
343 .ACCMODE = .RDONLY,
344 .CLOEXEC = true,
345 };
346 const fd = try posix.openatZ(ctx.dir.handle, &sub_path_c, flags, 0);
347 break :linux_symlink .{ .handle = fd };
348 },
349 else => unreachable,
350 };
351 defer symlink.close(io);294 defer symlink.close(io);
352295
353 const stat = try symlink.stat(io);296 const stat = try symlink.stat(io);
354 try testing.expectEqual(File.Kind.sym_link, stat.kind);297 try expectEqual(File.Kind.sym_link, stat.kind);
355 }298 }
356 }.impl);299 }.impl);
357}300}
...@@ -417,7 +360,7 @@ test "openDirAbsolute" {...@@ -417,7 +360,7 @@ test "openDirAbsolute" {
417 defer dir.close(io);360 defer dir.close(io);
418361
419 const ino = (try dir.stat(io)).inode;362 const ino = (try dir.stat(io)).inode;
420 try testing.expectEqual(tmp_ino, ino);363 try expectEqual(tmp_ino, ino);
421 }364 }
422365
423 {366 {
...@@ -429,7 +372,7 @@ test "openDirAbsolute" {...@@ -429,7 +372,7 @@ test "openDirAbsolute" {
429 defer dir.close(io);372 defer dir.close(io);
430373
431 const ino = (try dir.stat(io)).inode;374 const ino = (try dir.stat(io)).inode;
432 try testing.expectEqual(sub_ino, ino);375 try expectEqual(sub_ino, ino);
433 }376 }
434377
435 {378 {
...@@ -441,7 +384,7 @@ test "openDirAbsolute" {...@@ -441,7 +384,7 @@ test "openDirAbsolute" {
441 defer dir.close(io);384 defer dir.close(io);
442385
443 const ino = (try dir.stat(io)).inode;386 const ino = (try dir.stat(io)).inode;
444 try testing.expectEqual(tmp_ino, ino);387 try expectEqual(tmp_ino, ino);
445 }388 }
446}389}
447390
...@@ -480,7 +423,7 @@ test "openDir non-cwd parent '..'" {...@@ -480,7 +423,7 @@ test "openDir non-cwd parent '..'" {
480 const actual_path = try dir.realpathAlloc(testing.allocator, ".");423 const actual_path = try dir.realpathAlloc(testing.allocator, ".");
481 defer testing.allocator.free(actual_path);424 defer testing.allocator.free(actual_path);
482425
483 try testing.expectEqualStrings(expected_path, actual_path);426 try expectEqualStrings(expected_path, actual_path);
484}427}
485428
486test "readLinkAbsolute" {429test "readLinkAbsolute" {
...@@ -548,7 +491,7 @@ test "Dir.Iterator" {...@@ -548,7 +491,7 @@ test "Dir.Iterator" {
548 try entries.append(Dir.Entry{ .name = name, .kind = entry.kind });491 try entries.append(Dir.Entry{ .name = name, .kind = entry.kind });
549 }492 }
550493
551 try testing.expectEqual(@as(usize, 2), entries.items.len); // note that the Iterator skips '.' and '..'494 try expectEqual(@as(usize, 2), entries.items.len); // note that the Iterator skips '.' and '..'
552 try testing.expect(contains(&entries, .{ .name = "some_file", .kind = .file }));495 try testing.expect(contains(&entries, .{ .name = "some_file", .kind = .file }));
553 try testing.expect(contains(&entries, .{ .name = "some_dir", .kind = .directory }));496 try testing.expect(contains(&entries, .{ .name = "some_dir", .kind = .directory }));
554}497}
...@@ -619,7 +562,7 @@ test "Dir.Iterator twice" {...@@ -619,7 +562,7 @@ test "Dir.Iterator twice" {
619 try entries.append(Dir.Entry{ .name = name, .kind = entry.kind });562 try entries.append(Dir.Entry{ .name = name, .kind = entry.kind });
620 }563 }
621564
622 try testing.expectEqual(@as(usize, 2), entries.items.len); // note that the Iterator skips '.' and '..'565 try expectEqual(@as(usize, 2), entries.items.len); // note that the Iterator skips '.' and '..'
623 try testing.expect(contains(&entries, .{ .name = "some_file", .kind = .file }));566 try testing.expect(contains(&entries, .{ .name = "some_file", .kind = .file }));
624 try testing.expect(contains(&entries, .{ .name = "some_dir", .kind = .directory }));567 try testing.expect(contains(&entries, .{ .name = "some_dir", .kind = .directory }));
625 }568 }
...@@ -655,7 +598,7 @@ test "Dir.Iterator reset" {...@@ -655,7 +598,7 @@ test "Dir.Iterator reset" {
655 try entries.append(.{ .name = name, .kind = entry.kind });598 try entries.append(.{ .name = name, .kind = entry.kind });
656 }599 }
657600
658 try testing.expectEqual(@as(usize, 2), entries.items.len); // note that the Iterator skips '.' and '..'601 try expectEqual(@as(usize, 2), entries.items.len); // note that the Iterator skips '.' and '..'
659 try testing.expect(contains(&entries, .{ .name = "some_file", .kind = .file }));602 try testing.expect(contains(&entries, .{ .name = "some_file", .kind = .file }));
660 try testing.expect(contains(&entries, .{ .name = "some_dir", .kind = .directory }));603 try testing.expect(contains(&entries, .{ .name = "some_dir", .kind = .directory }));
661604
...@@ -690,7 +633,7 @@ test "Dir.Iterator but dir is deleted during iteration" {...@@ -690,7 +633,7 @@ test "Dir.Iterator but dir is deleted during iteration" {
690633
691 // On Linux, we can opt-in to receiving a more specific error by calling `nextLinux`634 // On Linux, we can opt-in to receiving a more specific error by calling `nextLinux`
692 if (native_os == .linux) {635 if (native_os == .linux) {
693 try std.testing.expectError(error.DirNotFound, iterator.nextLinux());636 try expectError(error.DirNotFound, iterator.nextLinux());
694 }637 }
695}638}
696639
...@@ -717,10 +660,10 @@ test "Dir.realpath smoke test" {...@@ -717,10 +660,10 @@ test "Dir.realpath smoke test" {
717 var buf: [fs.max_path_bytes]u8 = undefined;660 var buf: [fs.max_path_bytes]u8 = undefined;
718661
719 // FileNotFound if the path doesn't exist662 // FileNotFound if the path doesn't exist
720 try testing.expectError(error.FileNotFound, ctx.dir.realpathAlloc(allocator, test_file_path));663 try expectError(error.FileNotFound, ctx.dir.realpathAlloc(allocator, test_file_path));
721 try testing.expectError(error.FileNotFound, ctx.dir.realpath(test_file_path, &buf));664 try expectError(error.FileNotFound, ctx.dir.realpath(test_file_path, &buf));
722 try testing.expectError(error.FileNotFound, ctx.dir.realpathAlloc(allocator, test_dir_path));665 try expectError(error.FileNotFound, ctx.dir.realpathAlloc(allocator, test_dir_path));
723 try testing.expectError(error.FileNotFound, ctx.dir.realpath(test_dir_path, &buf));666 try expectError(error.FileNotFound, ctx.dir.realpath(test_dir_path, &buf));
724667
725 // Now create the file and dir668 // Now create the file and dir
726 try ctx.dir.writeFile(io, .{ .sub_path = test_file_path, .data = "" });669 try ctx.dir.writeFile(io, .{ .sub_path = test_file_path, .data = "" });
...@@ -740,19 +683,19 @@ test "Dir.realpath smoke test" {...@@ -740,19 +683,19 @@ test "Dir.realpath smoke test" {
740 // First, test non-alloc version683 // First, test non-alloc version
741 {684 {
742 const file_path = try ctx.dir.realpath(test_file_path, &buf);685 const file_path = try ctx.dir.realpath(test_file_path, &buf);
743 try testing.expectEqualStrings(expected_file_path, file_path);686 try expectEqualStrings(expected_file_path, file_path);
744687
745 const dir_path = try ctx.dir.realpath(test_dir_path, &buf);688 const dir_path = try ctx.dir.realpath(test_dir_path, &buf);
746 try testing.expectEqualStrings(expected_dir_path, dir_path);689 try expectEqualStrings(expected_dir_path, dir_path);
747 }690 }
748691
749 // Next, test alloc version692 // Next, test alloc version
750 {693 {
751 const file_path = try ctx.dir.realpathAlloc(allocator, test_file_path);694 const file_path = try ctx.dir.realpathAlloc(allocator, test_file_path);
752 try testing.expectEqualStrings(expected_file_path, file_path);695 try expectEqualStrings(expected_file_path, file_path);
753696
754 const dir_path = try ctx.dir.realpathAlloc(allocator, test_dir_path);697 const dir_path = try ctx.dir.realpathAlloc(allocator, test_dir_path);
755 try testing.expectEqualStrings(expected_dir_path, dir_path);698 try expectEqualStrings(expected_dir_path, dir_path);
756 }699 }
757 }700 }
758 }.impl);701 }.impl);
...@@ -769,7 +712,7 @@ test "readFileAlloc" {...@@ -769,7 +712,7 @@ test "readFileAlloc" {
769712
770 const buf1 = try tmp_dir.dir.readFileAlloc(io, "test_file", testing.allocator, .limited(1024));713 const buf1 = try tmp_dir.dir.readFileAlloc(io, "test_file", testing.allocator, .limited(1024));
771 defer testing.allocator.free(buf1);714 defer testing.allocator.free(buf1);
772 try testing.expectEqualStrings("", buf1);715 try expectEqualStrings("", buf1);
773716
774 const write_buf: []const u8 = "this is a test.\nthis is a test.\nthis is a test.\nthis is a test.\n";717 const write_buf: []const u8 = "this is a test.\nthis is a test.\nthis is a test.\nthis is a test.\n";
775 try file.writeAll(write_buf);718 try file.writeAll(write_buf);
...@@ -778,12 +721,12 @@ test "readFileAlloc" {...@@ -778,12 +721,12 @@ test "readFileAlloc" {
778 // max_bytes > file_size721 // max_bytes > file_size
779 const buf2 = try tmp_dir.dir.readFileAlloc(io, "test_file", testing.allocator, .limited(1024));722 const buf2 = try tmp_dir.dir.readFileAlloc(io, "test_file", testing.allocator, .limited(1024));
780 defer testing.allocator.free(buf2);723 defer testing.allocator.free(buf2);
781 try testing.expectEqualStrings(write_buf, buf2);724 try expectEqualStrings(write_buf, buf2);
782 }725 }
783726
784 {727 {
785 // max_bytes == file_size728 // max_bytes == file_size
786 try testing.expectError(729 try expectError(
787 error.StreamTooLong,730 error.StreamTooLong,
788 tmp_dir.dir.readFileAlloc(io, "test_file", testing.allocator, .limited(write_buf.len)),731 tmp_dir.dir.readFileAlloc(io, "test_file", testing.allocator, .limited(write_buf.len)),
789 );732 );
...@@ -793,11 +736,11 @@ test "readFileAlloc" {...@@ -793,11 +736,11 @@ test "readFileAlloc" {
793 // max_bytes == file_size + 1736 // max_bytes == file_size + 1
794 const buf2 = try tmp_dir.dir.readFileAlloc(io, "test_file", testing.allocator, .limited(write_buf.len + 1));737 const buf2 = try tmp_dir.dir.readFileAlloc(io, "test_file", testing.allocator, .limited(write_buf.len + 1));
795 defer testing.allocator.free(buf2);738 defer testing.allocator.free(buf2);
796 try testing.expectEqualStrings(write_buf, buf2);739 try expectEqualStrings(write_buf, buf2);
797 }740 }
798741
799 // max_bytes < file_size742 // max_bytes < file_size
800 try testing.expectError(743 try expectError(
801 error.StreamTooLong,744 error.StreamTooLong,
802 tmp_dir.dir.readFileAlloc(io, "test_file", testing.allocator, .limited(write_buf.len - 1)),745 tmp_dir.dir.readFileAlloc(io, "test_file", testing.allocator, .limited(write_buf.len - 1)),
803 );746 );
...@@ -809,12 +752,12 @@ test "Dir.statFile" {...@@ -809,12 +752,12 @@ test "Dir.statFile" {
809 const io = ctx.io;752 const io = ctx.io;
810 const test_file_name = try ctx.transformPath("test_file");753 const test_file_name = try ctx.transformPath("test_file");
811754
812 try testing.expectError(error.FileNotFound, ctx.dir.statFile(io, test_file_name, .{}));755 try expectError(error.FileNotFound, ctx.dir.statFile(io, test_file_name, .{}));
813756
814 try ctx.dir.writeFile(io, .{ .sub_path = test_file_name, .data = "" });757 try ctx.dir.writeFile(io, .{ .sub_path = test_file_name, .data = "" });
815758
816 const stat = try ctx.dir.statFile(io, test_file_name, .{});759 const stat = try ctx.dir.statFile(io, test_file_name, .{});
817 try testing.expectEqual(File.Kind.file, stat.kind);760 try expectEqual(File.Kind.file, stat.kind);
818 }761 }
819 }.impl);762 }.impl);
820}763}
...@@ -828,7 +771,7 @@ test "statFile on dangling symlink" {...@@ -828,7 +771,7 @@ test "statFile on dangling symlink" {
828771
829 try setupSymlink(io, ctx.dir, symlink_target, symlink_name, .{});772 try setupSymlink(io, ctx.dir, symlink_target, symlink_name, .{});
830773
831 try std.testing.expectError(error.FileNotFound, ctx.dir.statFile(io, symlink_name, .{}));774 try expectError(error.FileNotFound, ctx.dir.statFile(io, symlink_name, .{}));
832 }775 }
833 }.impl);776 }.impl);
834}777}
...@@ -843,19 +786,19 @@ test "directory operations on files" {...@@ -843,19 +786,19 @@ test "directory operations on files" {
843 var file = try ctx.dir.createFile(io, test_file_name, .{ .read = true });786 var file = try ctx.dir.createFile(io, test_file_name, .{ .read = true });
844 file.close(io);787 file.close(io);
845788
846 try testing.expectError(error.PathAlreadyExists, ctx.dir.makeDir(io, test_file_name, .default_dir));789 try expectError(error.PathAlreadyExists, ctx.dir.makeDir(io, test_file_name, .default_dir));
847 try testing.expectError(error.NotDir, ctx.dir.openDir(io, test_file_name, .{}));790 try expectError(error.NotDir, ctx.dir.openDir(io, test_file_name, .{}));
848 try testing.expectError(error.NotDir, ctx.dir.deleteDir(io, test_file_name));791 try expectError(error.NotDir, ctx.dir.deleteDir(io, test_file_name));
849792
850 if (ctx.path_type == .absolute and comptime PathType.absolute.isSupported(builtin.os)) {793 if (ctx.path_type == .absolute and comptime PathType.absolute.isSupported(builtin.os)) {
851 try testing.expectError(error.PathAlreadyExists, fs.makeDirAbsolute(test_file_name));794 try expectError(error.PathAlreadyExists, fs.makeDirAbsolute(test_file_name));
852 try testing.expectError(error.NotDir, fs.deleteDirAbsolute(test_file_name));795 try expectError(error.NotDir, fs.deleteDirAbsolute(test_file_name));
853 }796 }
854797
855 // ensure the file still exists and is a file as a sanity check798 // ensure the file still exists and is a file as a sanity check
856 file = try ctx.dir.openFile(io, test_file_name, .{});799 file = try ctx.dir.openFile(io, test_file_name, .{});
857 const stat = try file.stat(io);800 const stat = try file.stat(io);
858 try testing.expectEqual(File.Kind.file, stat.kind);801 try expectEqual(File.Kind.file, stat.kind);
859 file.close(io);802 file.close(io);
860 }803 }
861 }.impl);804 }.impl);
...@@ -873,8 +816,8 @@ test "file operations on directories" {...@@ -873,8 +816,8 @@ test "file operations on directories" {
873816
874 try ctx.dir.makeDir(io, test_dir_name, .default_dir);817 try ctx.dir.makeDir(io, test_dir_name, .default_dir);
875818
876 try testing.expectError(error.IsDir, ctx.dir.createFile(io, test_dir_name, .{}));819 try expectError(error.IsDir, ctx.dir.createFile(io, test_dir_name, .{}));
877 try testing.expectError(error.IsDir, ctx.dir.deleteFile(io, test_dir_name));820 try expectError(error.IsDir, ctx.dir.deleteFile(io, test_dir_name));
878 switch (native_os) {821 switch (native_os) {
879 .dragonfly, .netbsd => {822 .dragonfly, .netbsd => {
880 // no error when reading a directory. See https://github.com/ziglang/zig/issues/5732823 // no error when reading a directory. See https://github.com/ziglang/zig/issues/5732
...@@ -884,10 +827,10 @@ test "file operations on directories" {...@@ -884,10 +827,10 @@ test "file operations on directories" {
884 .wasi => {827 .wasi => {
885 // WASI return EBADF, which gets mapped to NotOpenForReading.828 // WASI return EBADF, which gets mapped to NotOpenForReading.
886 // See https://github.com/bytecodealliance/wasmtime/issues/1935829 // See https://github.com/bytecodealliance/wasmtime/issues/1935
887 try testing.expectError(error.NotOpenForReading, ctx.dir.readFileAlloc(io, test_dir_name, testing.allocator, .unlimited));830 try expectError(error.NotOpenForReading, ctx.dir.readFileAlloc(io, test_dir_name, testing.allocator, .unlimited));
888 },831 },
889 else => {832 else => {
890 try testing.expectError(error.IsDir, ctx.dir.readFileAlloc(io, test_dir_name, testing.allocator, .unlimited));833 try expectError(error.IsDir, ctx.dir.readFileAlloc(io, test_dir_name, testing.allocator, .unlimited));
891 },834 },
892 }835 }
893836
...@@ -898,12 +841,12 @@ test "file operations on directories" {...@@ -898,12 +841,12 @@ test "file operations on directories" {
898 } else {841 } else {
899 // Note: The `.mode = .read_write` is necessary to ensure the error occurs on all platforms.842 // Note: The `.mode = .read_write` is necessary to ensure the error occurs on all platforms.
900 // TODO: Add a read-only test as well, see https://github.com/ziglang/zig/issues/5732843 // TODO: Add a read-only test as well, see https://github.com/ziglang/zig/issues/5732
901 try testing.expectError(error.IsDir, ctx.dir.openFile(io, test_dir_name, .{ .mode = .read_write }));844 try expectError(error.IsDir, ctx.dir.openFile(io, test_dir_name, .{ .mode = .read_write }));
902 }845 }
903846
904 if (ctx.path_type == .absolute and comptime PathType.absolute.isSupported(builtin.os)) {847 if (ctx.path_type == .absolute and comptime PathType.absolute.isSupported(builtin.os)) {
905 try testing.expectError(error.IsDir, fs.createFileAbsolute(test_dir_name, .{}));848 try expectError(error.IsDir, fs.createFileAbsolute(test_dir_name, .{}));
906 try testing.expectError(error.IsDir, fs.deleteFileAbsolute(test_dir_name));849 try expectError(error.IsDir, fs.deleteFileAbsolute(test_dir_name));
907 }850 }
908851
909 // ensure the directory still exists as a sanity check852 // ensure the directory still exists as a sanity check
...@@ -935,12 +878,12 @@ test "deleteDir" {...@@ -935,12 +878,12 @@ test "deleteDir" {
935 const test_file_path = try ctx.transformPath("test_dir" ++ fs.path.sep_str ++ "test_file");878 const test_file_path = try ctx.transformPath("test_dir" ++ fs.path.sep_str ++ "test_file");
936879
937 // deleting a non-existent directory880 // deleting a non-existent directory
938 try testing.expectError(error.FileNotFound, ctx.dir.deleteDir(io, test_dir_path));881 try expectError(error.FileNotFound, ctx.dir.deleteDir(io, test_dir_path));
939882
940 // deleting a non-empty directory883 // deleting a non-empty directory
941 try ctx.dir.makeDir(io, test_dir_path, .default_dir);884 try ctx.dir.makeDir(io, test_dir_path, .default_dir);
942 try ctx.dir.writeFile(io, .{ .sub_path = test_file_path, .data = "" });885 try ctx.dir.writeFile(io, .{ .sub_path = test_file_path, .data = "" });
943 try testing.expectError(error.DirNotEmpty, ctx.dir.deleteDir(io, test_dir_path));886 try expectError(error.DirNotEmpty, ctx.dir.deleteDir(io, test_dir_path));
944887
945 // deleting an empty directory888 // deleting an empty directory
946 try ctx.dir.deleteFile(io, test_file_path);889 try ctx.dir.deleteFile(io, test_file_path);
...@@ -962,7 +905,7 @@ test "Dir.rename files" {...@@ -962,7 +905,7 @@ test "Dir.rename files" {
962 const missing_file_path = try ctx.transformPath("missing_file_name");905 const missing_file_path = try ctx.transformPath("missing_file_name");
963 const something_else_path = try ctx.transformPath("something_else");906 const something_else_path = try ctx.transformPath("something_else");
964907
965 try testing.expectError(error.FileNotFound, ctx.dir.rename(missing_file_path, ctx.dir, something_else_path, io));908 try expectError(error.FileNotFound, ctx.dir.rename(missing_file_path, ctx.dir, something_else_path, io));
966909
967 // Renaming files910 // Renaming files
968 const test_file_name = try ctx.transformPath("test_file");911 const test_file_name = try ctx.transformPath("test_file");
...@@ -972,7 +915,7 @@ test "Dir.rename files" {...@@ -972,7 +915,7 @@ test "Dir.rename files" {
972 try ctx.dir.rename(test_file_name, ctx.dir, renamed_test_file_name, io);915 try ctx.dir.rename(test_file_name, ctx.dir, renamed_test_file_name, io);
973916
974 // Ensure the file was renamed917 // Ensure the file was renamed
975 try testing.expectError(error.FileNotFound, ctx.dir.openFile(io, test_file_name, .{}));918 try expectError(error.FileNotFound, ctx.dir.openFile(io, test_file_name, .{}));
976 file = try ctx.dir.openFile(io, renamed_test_file_name, .{});919 file = try ctx.dir.openFile(io, renamed_test_file_name, .{});
977 file.close(io);920 file.close(io);
978921
...@@ -985,7 +928,7 @@ test "Dir.rename files" {...@@ -985,7 +928,7 @@ test "Dir.rename files" {
985 existing_file.close(io);928 existing_file.close(io);
986 try ctx.dir.rename(renamed_test_file_name, ctx.dir, existing_file_path, io);929 try ctx.dir.rename(renamed_test_file_name, ctx.dir, existing_file_path, io);
987930
988 try testing.expectError(error.FileNotFound, ctx.dir.openFile(io, renamed_test_file_name, .{}));931 try expectError(error.FileNotFound, ctx.dir.openFile(io, renamed_test_file_name, .{}));
989 file = try ctx.dir.openFile(io, existing_file_path, .{});932 file = try ctx.dir.openFile(io, existing_file_path, .{});
990 file.close(io);933 file.close(io);
991 }934 }
...@@ -1011,7 +954,7 @@ test "Dir.rename directories" {...@@ -1011,7 +954,7 @@ test "Dir.rename directories" {
1011 try ctx.dir.rename(test_dir_path, ctx.dir, test_dir_renamed_path, io);954 try ctx.dir.rename(test_dir_path, ctx.dir, test_dir_renamed_path, io);
1012955
1013 // Ensure the directory was renamed956 // Ensure the directory was renamed
1014 try testing.expectError(error.FileNotFound, ctx.dir.openDir(io, test_dir_path, .{}));957 try expectError(error.FileNotFound, ctx.dir.openDir(io, test_dir_path, .{}));
1015 var dir = try ctx.dir.openDir(io, test_dir_renamed_path, .{});958 var dir = try ctx.dir.openDir(io, test_dir_renamed_path, .{});
1016959
1017 // Put a file in the directory960 // Put a file in the directory
...@@ -1023,7 +966,7 @@ test "Dir.rename directories" {...@@ -1023,7 +966,7 @@ test "Dir.rename directories" {
1023 try ctx.dir.rename(test_dir_renamed_path, ctx.dir, test_dir_renamed_again_path, io);966 try ctx.dir.rename(test_dir_renamed_path, ctx.dir, test_dir_renamed_again_path, io);
1024967
1025 // Ensure the directory was renamed and the file still exists in it968 // Ensure the directory was renamed and the file still exists in it
1026 try testing.expectError(error.FileNotFound, ctx.dir.openDir(io, test_dir_renamed_path, .{}));969 try expectError(error.FileNotFound, ctx.dir.openDir(io, test_dir_renamed_path, .{}));
1027 dir = try ctx.dir.openDir(io, test_dir_renamed_again_path, .{});970 dir = try ctx.dir.openDir(io, test_dir_renamed_again_path, .{});
1028 file = try dir.openFile(io, "test_file", .{});971 file = try dir.openFile(io, "test_file", .{});
1029 file.close(io);972 file.close(io);
...@@ -1048,7 +991,7 @@ test "Dir.rename directory onto empty dir" {...@@ -1048,7 +991,7 @@ test "Dir.rename directory onto empty dir" {
1048 try ctx.dir.rename(test_dir_path, ctx.dir, target_dir_path, io);991 try ctx.dir.rename(test_dir_path, ctx.dir, target_dir_path, io);
1049992
1050 // Ensure the directory was renamed993 // Ensure the directory was renamed
1051 try testing.expectError(error.FileNotFound, ctx.dir.openDir(io, test_dir_path, .{}));994 try expectError(error.FileNotFound, ctx.dir.openDir(io, test_dir_path, .{}));
1052 var dir = try ctx.dir.openDir(io, target_dir_path, .{});995 var dir = try ctx.dir.openDir(io, target_dir_path, .{});
1053 dir.close(io);996 dir.close(io);
1054 }997 }
...@@ -1073,7 +1016,7 @@ test "Dir.rename directory onto non-empty dir" {...@@ -1073,7 +1016,7 @@ test "Dir.rename directory onto non-empty dir" {
1073 target_dir.close(io);1016 target_dir.close(io);
10741017
1075 // Rename should fail with PathAlreadyExists if target_dir is non-empty1018 // Rename should fail with PathAlreadyExists if target_dir is non-empty
1076 try testing.expectError(error.PathAlreadyExists, ctx.dir.rename(test_dir_path, ctx.dir, target_dir_path, io));1019 try expectError(error.PathAlreadyExists, ctx.dir.rename(test_dir_path, ctx.dir, target_dir_path, io));
10771020
1078 // Ensure the directory was not renamed1021 // Ensure the directory was not renamed
1079 var dir = try ctx.dir.openDir(io, test_dir_path, .{});1022 var dir = try ctx.dir.openDir(io, test_dir_path, .{});
...@@ -1095,8 +1038,8 @@ test "Dir.rename file <-> dir" {...@@ -1095,8 +1038,8 @@ test "Dir.rename file <-> dir" {
1095 var file = try ctx.dir.createFile(io, test_file_path, .{ .read = true });1038 var file = try ctx.dir.createFile(io, test_file_path, .{ .read = true });
1096 file.close(io);1039 file.close(io);
1097 try ctx.dir.makeDir(io, test_dir_path, .default_dir);1040 try ctx.dir.makeDir(io, test_dir_path, .default_dir);
1098 try testing.expectError(error.IsDir, ctx.dir.rename(test_file_path, ctx.dir, test_dir_path, io));1041 try expectError(error.IsDir, ctx.dir.rename(test_file_path, ctx.dir, test_dir_path, io));
1099 try testing.expectError(error.NotDir, ctx.dir.rename(test_dir_path, ctx.dir, test_file_path, io));1042 try expectError(error.NotDir, ctx.dir.rename(test_dir_path, ctx.dir, test_file_path, io));
1100 }1043 }
1101 }.impl);1044 }.impl);
1102}1045}
...@@ -1118,7 +1061,7 @@ test "rename" {...@@ -1118,7 +1061,7 @@ test "rename" {
1118 try Dir.rename(tmp_dir1.dir, test_file_name, tmp_dir2.dir, renamed_test_file_name, io);1061 try Dir.rename(tmp_dir1.dir, test_file_name, tmp_dir2.dir, renamed_test_file_name, io);
11191062
1120 // ensure the file was renamed1063 // ensure the file was renamed
1121 try testing.expectError(error.FileNotFound, tmp_dir1.dir.openFile(io, test_file_name, .{}));1064 try expectError(error.FileNotFound, tmp_dir1.dir.openFile(io, test_file_name, .{}));
1122 file = try tmp_dir2.dir.openFile(io, renamed_test_file_name, .{});1065 file = try tmp_dir2.dir.openFile(io, renamed_test_file_name, .{});
1123 file.close(io);1066 file.close(io);
1124}1067}
...@@ -1139,7 +1082,7 @@ test "renameAbsolute" {...@@ -1139,7 +1082,7 @@ test "renameAbsolute" {
11391082
1140 const base_path = try tmp_dir.dir.realpathAlloc(allocator, ".");1083 const base_path = try tmp_dir.dir.realpathAlloc(allocator, ".");
11411084
1142 try testing.expectError(error.FileNotFound, fs.renameAbsolute(1085 try expectError(error.FileNotFound, fs.renameAbsolute(
1143 try fs.path.join(allocator, &.{ base_path, "missing_file_name" }),1086 try fs.path.join(allocator, &.{ base_path, "missing_file_name" }),
1144 try fs.path.join(allocator, &.{ base_path, "something_else" }),1087 try fs.path.join(allocator, &.{ base_path, "something_else" }),
1145 ));1088 ));
...@@ -1155,10 +1098,10 @@ test "renameAbsolute" {...@@ -1155,10 +1098,10 @@ test "renameAbsolute" {
1155 );1098 );
11561099
1157 // ensure the file was renamed1100 // ensure the file was renamed
1158 try testing.expectError(error.FileNotFound, tmp_dir.dir.openFile(io, test_file_name, .{}));1101 try expectError(error.FileNotFound, tmp_dir.dir.openFile(io, test_file_name, .{}));
1159 file = try tmp_dir.dir.openFile(io, renamed_test_file_name, .{});1102 file = try tmp_dir.dir.openFile(io, renamed_test_file_name, .{});
1160 const stat = try file.stat(io);1103 const stat = try file.stat(io);
1161 try testing.expectEqual(File.Kind.file, stat.kind);1104 try expectEqual(File.Kind.file, stat.kind);
1162 file.close(io);1105 file.close(io);
11631106
1164 // Renaming directories1107 // Renaming directories
...@@ -1171,7 +1114,7 @@ test "renameAbsolute" {...@@ -1171,7 +1114,7 @@ test "renameAbsolute" {
1171 );1114 );
11721115
1173 // ensure the directory was renamed1116 // ensure the directory was renamed
1174 try testing.expectError(error.FileNotFound, tmp_dir.dir.openDir(io, test_dir_name, .{}));1117 try expectError(error.FileNotFound, tmp_dir.dir.openDir(io, test_dir_name, .{}));
1175 var dir = try tmp_dir.dir.openDir(io, renamed_test_dir_name, .{});1118 var dir = try tmp_dir.dir.openDir(io, renamed_test_dir_name, .{});
1176 dir.close(io);1119 dir.close(io);
1177}1120}
...@@ -1193,7 +1136,7 @@ test "executablePath" {...@@ -1193,7 +1136,7 @@ test "executablePath" {
1193 const buf_self_exe_path = try std.process.executablePath(io, &buf);1136 const buf_self_exe_path = try std.process.executablePath(io, &buf);
1194 const alloc_self_exe_path = try std.process.executablePathAlloc(io, testing.allocator);1137 const alloc_self_exe_path = try std.process.executablePathAlloc(io, testing.allocator);
1195 defer testing.allocator.free(alloc_self_exe_path);1138 defer testing.allocator.free(alloc_self_exe_path);
1196 try testing.expectEqualSlices(u8, buf_self_exe_path, alloc_self_exe_path);1139 try expectEqualSlices(u8, buf_self_exe_path, alloc_self_exe_path);
1197}1140}
11981141
1199test "deleteTree does not follow symlinks" {1142test "deleteTree does not follow symlinks" {
...@@ -1212,7 +1155,7 @@ test "deleteTree does not follow symlinks" {...@@ -1212,7 +1155,7 @@ test "deleteTree does not follow symlinks" {
12121155
1213 try tmp.dir.deleteTree(io, "a");1156 try tmp.dir.deleteTree(io, "a");
12141157
1215 try testing.expectError(error.FileNotFound, tmp.dir.access(io, "a", .{}));1158 try expectError(error.FileNotFound, tmp.dir.access(io, "a", .{}));
1216 try tmp.dir.access(io, "b", .{});1159 try tmp.dir.access(io, "b", .{});
1217}1160}
12181161
...@@ -1227,7 +1170,7 @@ test "deleteTree on a symlink" {...@@ -1227,7 +1170,7 @@ test "deleteTree on a symlink" {
1227 try setupSymlink(io, tmp.dir, "file", "filelink", .{});1170 try setupSymlink(io, tmp.dir, "file", "filelink", .{});
12281171
1229 try tmp.dir.deleteTree(io, "filelink");1172 try tmp.dir.deleteTree(io, "filelink");
1230 try testing.expectError(error.FileNotFound, tmp.dir.access(io, "filelink", .{}));1173 try expectError(error.FileNotFound, tmp.dir.access(io, "filelink", .{}));
1231 try tmp.dir.access(io, "file", .{});1174 try tmp.dir.access(io, "file", .{});
12321175
1233 // Symlink to a directory1176 // Symlink to a directory
...@@ -1235,7 +1178,7 @@ test "deleteTree on a symlink" {...@@ -1235,7 +1178,7 @@ test "deleteTree on a symlink" {
1235 try setupSymlink(io, tmp.dir, "dir", "dirlink", .{ .is_directory = true });1178 try setupSymlink(io, tmp.dir, "dir", "dirlink", .{ .is_directory = true });
12361179
1237 try tmp.dir.deleteTree(io, "dirlink");1180 try tmp.dir.deleteTree(io, "dirlink");
1238 try testing.expectError(error.FileNotFound, tmp.dir.access(io, "dirlink", .{}));1181 try expectError(error.FileNotFound, tmp.dir.access(io, "dirlink", .{}));
1239 try tmp.dir.access(io, "dir", .{});1182 try tmp.dir.access(io, "dir", .{});
1240}1183}
12411184
...@@ -1257,7 +1200,7 @@ test "makePath, put some files in it, deleteTree" {...@@ -1257,7 +1200,7 @@ test "makePath, put some files in it, deleteTree" {
1257 });1200 });
12581201
1259 try ctx.dir.deleteTree(io, dir_path);1202 try ctx.dir.deleteTree(io, dir_path);
1260 try testing.expectError(error.FileNotFound, ctx.dir.openDir(io, dir_path, .{}));1203 try expectError(error.FileNotFound, ctx.dir.openDir(io, dir_path, .{}));
1261 }1204 }
1262 }.impl);1205 }.impl);
1263}1206}
...@@ -1280,7 +1223,7 @@ test "makePath, put some files in it, deleteTreeMinStackSize" {...@@ -1280,7 +1223,7 @@ test "makePath, put some files in it, deleteTreeMinStackSize" {
1280 });1223 });
12811224
1282 try ctx.dir.deleteTreeMinStackSize(dir_path);1225 try ctx.dir.deleteTreeMinStackSize(dir_path);
1283 try testing.expectError(error.FileNotFound, ctx.dir.openDir(io, dir_path, .{}));1226 try expectError(error.FileNotFound, ctx.dir.openDir(io, dir_path, .{}));
1284 }1227 }
1285 }.impl);1228 }.impl);
1286}1229}
...@@ -1294,7 +1237,7 @@ test "makePath in a directory that no longer exists" {...@@ -1294,7 +1237,7 @@ test "makePath in a directory that no longer exists" {
1294 defer tmp.cleanup();1237 defer tmp.cleanup();
1295 try tmp.parent_dir.deleteTree(io, &tmp.sub_path);1238 try tmp.parent_dir.deleteTree(io, &tmp.sub_path);
12961239
1297 try testing.expectError(error.FileNotFound, tmp.dir.makePath(io, "sub-path"));1240 try expectError(error.FileNotFound, tmp.dir.makePath(io, "sub-path"));
1298}1241}
12991242
1300test "makePath but sub_path contains pre-existing file" {1243test "makePath but sub_path contains pre-existing file" {
...@@ -1306,7 +1249,7 @@ test "makePath but sub_path contains pre-existing file" {...@@ -1306,7 +1249,7 @@ test "makePath but sub_path contains pre-existing file" {
1306 try tmp.dir.makeDir(io, "foo", .default_dir);1249 try tmp.dir.makeDir(io, "foo", .default_dir);
1307 try tmp.dir.writeFile(io, .{ .sub_path = "foo/bar", .data = "" });1250 try tmp.dir.writeFile(io, .{ .sub_path = "foo/bar", .data = "" });
13081251
1309 try testing.expectError(error.NotDir, tmp.dir.makePath(io, "foo/bar/baz"));1252 try expectError(error.NotDir, tmp.dir.makePath(io, "foo/bar/baz"));
1310}1253}
13111254
1312fn expectDir(io: Io, dir: Dir, path: []const u8) !void {1255fn expectDir(io: Io, dir: Dir, path: []const u8) !void {
...@@ -1364,8 +1307,8 @@ test "makepath relative walks" {...@@ -1364,8 +1307,8 @@ test "makepath relative walks" {
1364 // On Windows, .. is resolved before passing the path to NtCreateFile,1307 // On Windows, .. is resolved before passing the path to NtCreateFile,
1365 // meaning everything except `first/C` drops out.1308 // meaning everything except `first/C` drops out.
1366 try expectDir(io, tmp.dir, "first" ++ fs.path.sep_str ++ "C");1309 try expectDir(io, tmp.dir, "first" ++ fs.path.sep_str ++ "C");
1367 try testing.expectError(error.FileNotFound, tmp.dir.access(io, "second", .{}));1310 try expectError(error.FileNotFound, tmp.dir.access(io, "second", .{}));
1368 try testing.expectError(error.FileNotFound, tmp.dir.access(io, "third", .{}));1311 try expectError(error.FileNotFound, tmp.dir.access(io, "third", .{}));
1369 },1312 },
1370 else => {1313 else => {
1371 try expectDir(io, tmp.dir, "first" ++ fs.path.sep_str ++ "A");1314 try expectDir(io, tmp.dir, "first" ++ fs.path.sep_str ++ "A");
...@@ -1413,10 +1356,10 @@ fn testFilenameLimits(io: Io, iterable_dir: Dir, maxed_filename: []const u8) !vo...@@ -1413,10 +1356,10 @@ fn testFilenameLimits(io: Io, iterable_dir: Dir, maxed_filename: []const u8) !vo
14131356
1414 var count: usize = 0;1357 var count: usize = 0;
1415 while (try walker.next()) |entry| {1358 while (try walker.next()) |entry| {
1416 try testing.expectEqualStrings(maxed_filename, entry.basename);1359 try expectEqualStrings(maxed_filename, entry.basename);
1417 count += 1;1360 count += 1;
1418 }1361 }
1419 try testing.expectEqual(@as(usize, 2), count);1362 try expectEqual(@as(usize, 2), count);
1420 }1363 }
14211364
1422 // ensure that we can delete the tree1365 // ensure that we can delete the tree
...@@ -1467,14 +1410,14 @@ test "writev, readv" {...@@ -1467,14 +1410,14 @@ test "writev, readv" {
14671410
1468 try writer.interface.writeVecAll(&write_vecs);1411 try writer.interface.writeVecAll(&write_vecs);
1469 try writer.interface.flush();1412 try writer.interface.flush();
1470 try testing.expectEqual(@as(u64, line1.len + line2.len), try src_file.length(io));1413 try expectEqual(@as(u64, line1.len + line2.len), try src_file.length(io));
14711414
1472 var reader = writer.moveToReader(io);1415 var reader = writer.moveToReader(io);
1473 try reader.seekTo(0);1416 try reader.seekTo(0);
1474 try reader.interface.readVecAll(&read_vecs);1417 try reader.interface.readVecAll(&read_vecs);
1475 try testing.expectEqualStrings(&buf1, "line2\n");1418 try expectEqualStrings(&buf1, "line2\n");
1476 try testing.expectEqualStrings(&buf2, "line1\n");1419 try expectEqualStrings(&buf2, "line1\n");
1477 try testing.expectError(error.EndOfStream, reader.interface.readSliceAll(&buf1));1420 try expectError(error.EndOfStream, reader.interface.readSliceAll(&buf1));
1478}1421}
14791422
1480test "pwritev, preadv" {1423test "pwritev, preadv" {
...@@ -1498,14 +1441,14 @@ test "pwritev, preadv" {...@@ -1498,14 +1441,14 @@ test "pwritev, preadv" {
1498 try writer.seekTo(16);1441 try writer.seekTo(16);
1499 try writer.interface.writeVecAll(&lines);1442 try writer.interface.writeVecAll(&lines);
1500 try writer.interface.flush();1443 try writer.interface.flush();
1501 try testing.expectEqual(@as(u64, 16 + line1.len + line2.len), try src_file.length(io));1444 try expectEqual(@as(u64, 16 + line1.len + line2.len), try src_file.length(io));
15021445
1503 var reader = writer.moveToReader(io);1446 var reader = writer.moveToReader(io);
1504 try reader.seekTo(16);1447 try reader.seekTo(16);
1505 try reader.interface.readVecAll(&read_vecs);1448 try reader.interface.readVecAll(&read_vecs);
1506 try testing.expectEqualStrings(&buf1, "line2\n");1449 try expectEqualStrings(&buf1, "line2\n");
1507 try testing.expectEqualStrings(&buf2, "line1\n");1450 try expectEqualStrings(&buf2, "line1\n");
1508 try testing.expectError(error.EndOfStream, reader.interface.readSliceAll(&buf1));1451 try expectError(error.EndOfStream, reader.interface.readSliceAll(&buf1));
1509}1452}
15101453
1511test "setEndPos" {1454test "setEndPos" {
...@@ -1529,34 +1472,34 @@ test "setEndPos" {...@@ -1529,34 +1472,34 @@ test "setEndPos" {
15291472
1530 {1473 {
1531 try f.setEndPos(initial_size);1474 try f.setEndPos(initial_size);
1532 try testing.expectEqual(initial_size, try f.length(io));1475 try expectEqual(initial_size, try f.length(io));
1533 try reader.seekTo(0);1476 try reader.seekTo(0);
1534 try testing.expectEqual(initial_size, try reader.interface.readSliceShort(&buffer));1477 try expectEqual(initial_size, try reader.interface.readSliceShort(&buffer));
1535 try testing.expectEqualStrings("ninebytes", buffer[0..@intCast(initial_size)]);1478 try expectEqualStrings("ninebytes", buffer[0..@intCast(initial_size)]);
1536 }1479 }
15371480
1538 {1481 {
1539 const larger = initial_size + 4;1482 const larger = initial_size + 4;
1540 try f.setEndPos(larger);1483 try f.setEndPos(larger);
1541 try testing.expectEqual(larger, try f.length(io));1484 try expectEqual(larger, try f.length(io));
1542 try reader.seekTo(0);1485 try reader.seekTo(0);
1543 try testing.expectEqual(larger, try reader.interface.readSliceShort(&buffer));1486 try expectEqual(larger, try reader.interface.readSliceShort(&buffer));
1544 try testing.expectEqualStrings("ninebytes\x00\x00\x00\x00", buffer[0..@intCast(larger)]);1487 try expectEqualStrings("ninebytes\x00\x00\x00\x00", buffer[0..@intCast(larger)]);
1545 }1488 }
15461489
1547 {1490 {
1548 const smaller = initial_size - 5;1491 const smaller = initial_size - 5;
1549 try f.setEndPos(smaller);1492 try f.setEndPos(smaller);
1550 try testing.expectEqual(smaller, try f.length(io));1493 try expectEqual(smaller, try f.length(io));
1551 try reader.seekTo(0);1494 try reader.seekTo(0);
1552 try testing.expectEqual(smaller, try reader.interface.readSliceShort(&buffer));1495 try expectEqual(smaller, try reader.interface.readSliceShort(&buffer));
1553 try testing.expectEqualStrings("nine", buffer[0..@intCast(smaller)]);1496 try expectEqualStrings("nine", buffer[0..@intCast(smaller)]);
1554 }1497 }
15551498
1556 try f.setEndPos(0);1499 try f.setEndPos(0);
1557 try testing.expectEqual(0, try f.length(io));1500 try expectEqual(0, try f.length(io));
1558 try reader.seekTo(0);1501 try reader.seekTo(0);
1559 try testing.expectEqual(0, try reader.interface.readSliceShort(&buffer));1502 try expectEqual(0, try reader.interface.readSliceShort(&buffer));
1560}1503}
15611504
1562test "access file" {1505test "access file" {
...@@ -1567,7 +1510,7 @@ test "access file" {...@@ -1567,7 +1510,7 @@ test "access file" {
1567 const file_path = try ctx.transformPath("os_test_tmp" ++ fs.path.sep_str ++ "file.txt");1510 const file_path = try ctx.transformPath("os_test_tmp" ++ fs.path.sep_str ++ "file.txt");
15681511
1569 try ctx.dir.makePath(io, dir_path);1512 try ctx.dir.makePath(io, dir_path);
1570 try testing.expectError(error.FileNotFound, ctx.dir.access(io, file_path, .{}));1513 try expectError(error.FileNotFound, ctx.dir.access(io, file_path, .{}));
15711514
1572 try ctx.dir.writeFile(io, .{ .sub_path = file_path, .data = "" });1515 try ctx.dir.writeFile(io, .{ .sub_path = file_path, .data = "" });
1573 try ctx.dir.access(io, file_path, .{});1516 try ctx.dir.access(io, file_path, .{});
...@@ -1614,13 +1557,13 @@ test "sendfile" {...@@ -1614,13 +1557,13 @@ test "sendfile" {
1614 var file_writer = dest_file.writer(io, &fallback_buffer);1557 var file_writer = dest_file.writer(io, &fallback_buffer);
1615 try file_writer.interface.writeVecAll(&headers);1558 try file_writer.interface.writeVecAll(&headers);
1616 try file_reader.seekTo(1);1559 try file_reader.seekTo(1);
1617 try testing.expectEqual(10, try file_writer.interface.sendFileAll(&file_reader, .limited(10)));1560 try expectEqual(10, try file_writer.interface.sendFileAll(&file_reader, .limited(10)));
1618 try file_writer.interface.writeVecAll(&trailers);1561 try file_writer.interface.writeVecAll(&trailers);
1619 try file_writer.interface.flush();1562 try file_writer.interface.flush();
1620 var fr = file_writer.moveToReader(io);1563 var fr = file_writer.moveToReader(io);
1621 try fr.seekTo(0);1564 try fr.seekTo(0);
1622 const amt = try fr.interface.readSliceShort(&written_buf);1565 const amt = try fr.interface.readSliceShort(&written_buf);
1623 try testing.expectEqualStrings("header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n", written_buf[0..amt]);1566 try expectEqualStrings("header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n", written_buf[0..amt]);
1624}1567}
16251568
1626test "sendfile with buffered data" {1569test "sendfile with buffered data" {
...@@ -1651,15 +1594,15 @@ test "sendfile with buffered data" {...@@ -1651,15 +1594,15 @@ test "sendfile with buffered data" {
1651 var fallback_buffer: [32]u8 = undefined;1594 var fallback_buffer: [32]u8 = undefined;
1652 var file_writer = dest_file.writer(io, &fallback_buffer);1595 var file_writer = dest_file.writer(io, &fallback_buffer);
16531596
1654 try std.testing.expectEqual(4, try file_writer.interface.sendFileAll(&file_reader, .limited(4)));1597 try expectEqual(4, try file_writer.interface.sendFileAll(&file_reader, .limited(4)));
16551598
1656 var written_buf: [8]u8 = undefined;1599 var written_buf: [8]u8 = undefined;
1657 var fr = file_writer.moveToReader(io);1600 var fr = file_writer.moveToReader(io);
1658 try fr.seekTo(0);1601 try fr.seekTo(0);
1659 const amt = try fr.interface.readSliceShort(&written_buf);1602 const amt = try fr.interface.readSliceShort(&written_buf);
16601603
1661 try std.testing.expectEqual(4, amt);1604 try expectEqual(4, amt);
1662 try std.testing.expectEqualSlices(u8, "AAAA", written_buf[0..amt]);1605 try expectEqualSlices(u8, "AAAA", written_buf[0..amt]);
1663}1606}
16641607
1665test "copyFile" {1608test "copyFile" {
...@@ -1690,7 +1633,7 @@ fn expectFileContents(io: Io, dir: Dir, file_path: []const u8, data: []const u8)...@@ -1690,7 +1633,7 @@ fn expectFileContents(io: Io, dir: Dir, file_path: []const u8, data: []const u8)
1690 const contents = try dir.readFileAlloc(io, file_path, testing.allocator, .limited(1000));1633 const contents = try dir.readFileAlloc(io, file_path, testing.allocator, .limited(1000));
1691 defer testing.allocator.free(contents);1634 defer testing.allocator.free(contents);
16921635
1693 try testing.expectEqualSlices(u8, data, contents);1636 try expectEqualSlices(u8, data, contents);
1694}1637}
16951638
1696test "AtomicFile" {1639test "AtomicFile" {
...@@ -1712,7 +1655,7 @@ test "AtomicFile" {...@@ -1712,7 +1655,7 @@ test "AtomicFile" {
1712 try af.finish();1655 try af.finish();
1713 }1656 }
1714 const content = try ctx.dir.readFileAlloc(io, test_out_file, allocator, .limited(9999));1657 const content = try ctx.dir.readFileAlloc(io, test_out_file, allocator, .limited(9999));
1715 try testing.expectEqualStrings(test_content, content);1658 try expectEqualStrings(test_content, content);
17161659
1717 try ctx.dir.deleteFile(io, test_out_file);1660 try ctx.dir.deleteFile(io, test_out_file);
1718 }1661 }
...@@ -1731,7 +1674,7 @@ test "open file with exclusive nonblocking lock twice" {...@@ -1731,7 +1674,7 @@ test "open file with exclusive nonblocking lock twice" {
1731 defer file1.close(io);1674 defer file1.close(io);
17321675
1733 const file2 = ctx.dir.createFile(io, filename, .{ .lock = .exclusive, .lock_nonblocking = true });1676 const file2 = ctx.dir.createFile(io, filename, .{ .lock = .exclusive, .lock_nonblocking = true });
1734 try testing.expectError(error.WouldBlock, file2);1677 try expectError(error.WouldBlock, file2);
1735 }1678 }
1736 }.impl);1679 }.impl);
1737}1680}
...@@ -1748,7 +1691,7 @@ test "open file with shared and exclusive nonblocking lock" {...@@ -1748,7 +1691,7 @@ test "open file with shared and exclusive nonblocking lock" {
1748 defer file1.close(io);1691 defer file1.close(io);
17491692
1750 const file2 = ctx.dir.createFile(io, filename, .{ .lock = .exclusive, .lock_nonblocking = true });1693 const file2 = ctx.dir.createFile(io, filename, .{ .lock = .exclusive, .lock_nonblocking = true });
1751 try testing.expectError(error.WouldBlock, file2);1694 try expectError(error.WouldBlock, file2);
1752 }1695 }
1753 }.impl);1696 }.impl);
1754}1697}
...@@ -1765,7 +1708,7 @@ test "open file with exclusive and shared nonblocking lock" {...@@ -1765,7 +1708,7 @@ test "open file with exclusive and shared nonblocking lock" {
1765 defer file1.close(io);1708 defer file1.close(io);
17661709
1767 const file2 = ctx.dir.createFile(io, filename, .{ .lock = .shared, .lock_nonblocking = true });1710 const file2 = ctx.dir.createFile(io, filename, .{ .lock = .shared, .lock_nonblocking = true });
1768 try testing.expectError(error.WouldBlock, file2);1711 try expectError(error.WouldBlock, file2);
1769 }1712 }
1770 }.impl);1713 }.impl);
1771}1714}
...@@ -1805,7 +1748,7 @@ test "open file with exclusive lock twice, make sure second lock waits" {...@@ -1805,7 +1748,7 @@ test "open file with exclusive lock twice, make sure second lock waits" {
1805 // Wait for the spawned thread to start trying to acquire the exclusive file lock.1748 // Wait for the spawned thread to start trying to acquire the exclusive file lock.
1806 // Then wait a bit to make sure that can't acquire it since we currently hold the file lock.1749 // Then wait a bit to make sure that can't acquire it since we currently hold the file lock.
1807 started.wait();1750 started.wait();
1808 try testing.expectError(error.Timeout, locked.timedWait(10 * std.time.ns_per_ms));1751 try expectError(error.Timeout, locked.timedWait(10 * std.time.ns_per_ms));
18091752
1810 // Release the file lock which should unlock the thread to lock it and set the locked event.1753 // Release the file lock which should unlock the thread to lock it and set the locked event.
1811 file.close(io);1754 file.close(io);
...@@ -1846,7 +1789,7 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {...@@ -1846,7 +1789,7 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {
1846 .lock_nonblocking = true,1789 .lock_nonblocking = true,
1847 });1790 });
1848 file1.close(io);1791 file1.close(io);
1849 try testing.expectError(error.WouldBlock, file2);1792 try expectError(error.WouldBlock, file2);
1850}1793}
18511794
1852test "read from locked file" {1795test "read from locked file" {
...@@ -1871,9 +1814,9 @@ test "read from locked file" {...@@ -1871,9 +1814,9 @@ test "read from locked file" {
1871 defer f2.close(io);1814 defer f2.close(io);
1872 var buffer: [1]u8 = undefined;1815 var buffer: [1]u8 = undefined;
1873 if (builtin.os.tag == .windows) {1816 if (builtin.os.tag == .windows) {
1874 try std.testing.expectError(error.LockViolation, f2.read(&buffer));1817 try expectError(error.LockViolation, f2.read(&buffer));
1875 } else {1818 } else {
1876 try std.testing.expectEqual(0, f2.read(&buffer));1819 try expectEqual(0, f2.read(&buffer));
1877 }1820 }
1878 }1821 }
1879 }1822 }
...@@ -1925,7 +1868,7 @@ test "walker" {...@@ -1925,7 +1868,7 @@ test "walker" {
1925 std.debug.print("found unexpected path: {f}\n", .{std.ascii.hexEscape(entry.path, .lower)});1868 std.debug.print("found unexpected path: {f}\n", .{std.ascii.hexEscape(entry.path, .lower)});
1926 return err;1869 return err;
1927 };1870 };
1928 testing.expectEqual(expected_paths.get(entry.path).?, entry.depth()) catch |err| {1871 expectEqual(expected_paths.get(entry.path).?, entry.depth()) catch |err| {
1929 std.debug.print("path reported unexpected depth: {f}\n", .{std.ascii.hexEscape(entry.path, .lower)});1872 std.debug.print("path reported unexpected depth: {f}\n", .{std.ascii.hexEscape(entry.path, .lower)});
1930 return err;1873 return err;
1931 };1874 };
...@@ -1934,7 +1877,7 @@ test "walker" {...@@ -1934,7 +1877,7 @@ test "walker" {
1934 defer entry_dir.close(io);1877 defer entry_dir.close(io);
1935 num_walked += 1;1878 num_walked += 1;
1936 }1879 }
1937 try testing.expectEqual(expected_paths.kvs.len, num_walked);1880 try expectEqual(expected_paths.kvs.len, num_walked);
1938}1881}
19391882
1940test "selective walker, skip entries that start with ." {1883test "selective walker, skip entries that start with ." {
...@@ -1992,7 +1935,7 @@ test "selective walker, skip entries that start with ." {...@@ -1992,7 +1935,7 @@ test "selective walker, skip entries that start with ." {
1992 std.debug.print("found unexpected path: {f}\n", .{std.ascii.hexEscape(entry.path, .lower)});1935 std.debug.print("found unexpected path: {f}\n", .{std.ascii.hexEscape(entry.path, .lower)});
1993 return err;1936 return err;
1994 };1937 };
1995 testing.expectEqual(expected_paths.get(entry.path).?, entry.depth()) catch |err| {1938 expectEqual(expected_paths.get(entry.path).?, entry.depth()) catch |err| {
1996 std.debug.print("path reported unexpected depth: {f}\n", .{std.ascii.hexEscape(entry.path, .lower)});1939 std.debug.print("path reported unexpected depth: {f}\n", .{std.ascii.hexEscape(entry.path, .lower)});
1997 return err;1940 return err;
1998 };1941 };
...@@ -2002,7 +1945,7 @@ test "selective walker, skip entries that start with ." {...@@ -2002,7 +1945,7 @@ test "selective walker, skip entries that start with ." {
2002 defer entry_dir.close(io);1945 defer entry_dir.close(io);
2003 num_walked += 1;1946 num_walked += 1;
2004 }1947 }
2005 try testing.expectEqual(expected_paths.kvs.len, num_walked);1948 try expectEqual(expected_paths.kvs.len, num_walked);
2006}1949}
20071950
2008test "walker without fully iterating" {1951test "walker without fully iterating" {
...@@ -2025,7 +1968,7 @@ test "walker without fully iterating" {...@@ -2025,7 +1968,7 @@ test "walker without fully iterating" {
2025 num_walked += 1;1968 num_walked += 1;
2026 break;1969 break;
2027 }1970 }
2028 try testing.expectEqual(@as(usize, 1), num_walked);1971 try expectEqual(@as(usize, 1), num_walked);
2029}1972}
20301973
2031test "'.' and '..' in Io.Dir functions" {1974test "'.' and '..' in Io.Dir functions" {
...@@ -2061,7 +2004,7 @@ test "'.' and '..' in Io.Dir functions" {...@@ -2061,7 +2004,7 @@ test "'.' and '..' in Io.Dir functions" {
2061 try ctx.dir.writeFile(io, .{ .sub_path = update_path, .data = "something" });2004 try ctx.dir.writeFile(io, .{ .sub_path = update_path, .data = "something" });
2062 var dir = ctx.dir;2005 var dir = ctx.dir;
2063 const prev_status = try dir.updateFile(io, file_path, dir, update_path, .{});2006 const prev_status = try dir.updateFile(io, file_path, dir, update_path, .{});
2064 try testing.expectEqual(Io.Dir.PrevStatus.stale, prev_status);2007 try expectEqual(Io.Dir.PrevStatus.stale, prev_status);
20652008
2066 try ctx.dir.deleteDir(io, subdir_path);2009 try ctx.dir.deleteDir(io, subdir_path);
2067 }2010 }
...@@ -2106,8 +2049,7 @@ test "'.' and '..' in absolute functions" {...@@ -2106,8 +2049,7 @@ test "'.' and '..' in absolute functions" {
2106}2049}
21072050
2108test "chmod" {2051test "chmod" {
2109 if (native_os == .windows or native_os == .wasi)2052 if (native_os == .windows or native_os == .wasi) return;
2110 return error.SkipZigTest;
21112053
2112 const io = testing.io;2054 const io = testing.io;
21132055
...@@ -2116,17 +2058,17 @@ test "chmod" {...@@ -2116,17 +2058,17 @@ test "chmod" {
21162058
2117 const file = try tmp.dir.createFile(io, "test_file", .{ .permissions = .fromMode(0o600) });2059 const file = try tmp.dir.createFile(io, "test_file", .{ .permissions = .fromMode(0o600) });
2118 defer file.close(io);2060 defer file.close(io);
2119 try testing.expectEqual(@as(posix.mode_t, 0o600), (try file.stat(io)).permissions.toMode() & 0o7777);2061 try expectEqual(0o600, (try file.stat(io)).permissions.toMode() & 0o7777);
21202062
2121 try file.setPermissions(io, .fromMode(0o644));2063 try file.setPermissions(io, .fromMode(0o644));
2122 try testing.expectEqual(@as(posix.mode_t, 0o644), (try file.stat(io)).permissions.toMode() & 0o7777);2064 try expectEqual(0o644, (try file.stat(io)).permissions.toMode() & 0o7777);
21232065
2124 try tmp.dir.makeDir(io, "test_dir", .default_dir);2066 try tmp.dir.makeDir(io, "test_dir", .default_dir);
2125 var dir = try tmp.dir.openDir(io, "test_dir", .{ .iterate = true });2067 var dir = try tmp.dir.openDir(io, "test_dir", .{ .iterate = true });
2126 defer dir.close(io);2068 defer dir.close(io);
21272069
2128 try dir.setPermissions(io, .fromMode(0o700));2070 try dir.setPermissions(io, .fromMode(0o700));
2129 try testing.expectEqual(@as(posix.mode_t, 0o700), (try dir.stat(io)).permissions.toMode() & 0o7777);2071 try expectEqual(0o700, (try dir.stat(io)).permissions.toMode() & 0o7777);
2130}2072}
21312073
2132test "chown" {2074test "chown" {
...@@ -2162,70 +2104,70 @@ test "invalid UTF-8/WTF-8 paths" {...@@ -2162,70 +2104,70 @@ test "invalid UTF-8/WTF-8 paths" {
2162 // This is both invalid UTF-8 and WTF-8, since \xFF is an invalid start byte2104 // This is both invalid UTF-8 and WTF-8, since \xFF is an invalid start byte
2163 const invalid_path = try ctx.transformPath("\xFF");2105 const invalid_path = try ctx.transformPath("\xFF");
21642106
2165 try testing.expectError(expected_err, ctx.dir.openFile(invalid_path, .{}));2107 try expectError(expected_err, ctx.dir.openFile(invalid_path, .{}));
21662108
2167 try testing.expectError(expected_err, ctx.dir.createFile(invalid_path, .{}));2109 try expectError(expected_err, ctx.dir.createFile(invalid_path, .{}));
21682110
2169 try testing.expectError(expected_err, ctx.dir.makeDir(invalid_path, .default_dir));2111 try expectError(expected_err, ctx.dir.makeDir(invalid_path, .default_dir));
21702112
2171 try testing.expectError(expected_err, ctx.dir.makePath(invalid_path));2113 try expectError(expected_err, ctx.dir.makePath(invalid_path));
2172 try testing.expectError(expected_err, ctx.dir.makeOpenPath(invalid_path, .{}));2114 try expectError(expected_err, ctx.dir.makeOpenPath(invalid_path, .{}));
21732115
2174 try testing.expectError(expected_err, ctx.dir.openDir(invalid_path, .{}));2116 try expectError(expected_err, ctx.dir.openDir(invalid_path, .{}));
21752117
2176 try testing.expectError(expected_err, ctx.dir.deleteFile(invalid_path));2118 try expectError(expected_err, ctx.dir.deleteFile(invalid_path));
21772119
2178 try testing.expectError(expected_err, ctx.dir.deleteDir(io, invalid_path));2120 try expectError(expected_err, ctx.dir.deleteDir(io, invalid_path));
21792121
2180 try testing.expectError(expected_err, ctx.dir.rename(invalid_path, ctx.dir, invalid_path, io));2122 try expectError(expected_err, ctx.dir.rename(invalid_path, ctx.dir, invalid_path, io));
21812123
2182 try testing.expectError(expected_err, ctx.dir.symLink(io, invalid_path, invalid_path, .{}));2124 try expectError(expected_err, ctx.dir.symLink(io, invalid_path, invalid_path, .{}));
2183 if (native_os == .wasi) {2125 if (native_os == .wasi) {
2184 try testing.expectError(expected_err, ctx.dir.symLinkWasi(invalid_path, invalid_path, .{}));2126 try expectError(expected_err, ctx.dir.symLinkWasi(invalid_path, invalid_path, .{}));
2185 }2127 }
21862128
2187 try testing.expectError(expected_err, ctx.dir.readLink(io, invalid_path, &[_]u8{}));2129 try expectError(expected_err, ctx.dir.readLink(io, invalid_path, &[_]u8{}));
2188 if (native_os == .wasi) {2130 if (native_os == .wasi) {
2189 try testing.expectError(expected_err, ctx.dir.readLinkWasi(invalid_path, &[_]u8{}));2131 try expectError(expected_err, ctx.dir.readLinkWasi(invalid_path, &[_]u8{}));
2190 }2132 }
21912133
2192 try testing.expectError(expected_err, ctx.dir.readFile(invalid_path, &[_]u8{}));2134 try expectError(expected_err, ctx.dir.readFile(invalid_path, &[_]u8{}));
2193 try testing.expectError(expected_err, ctx.dir.readFileAlloc(invalid_path, testing.allocator, .limited(0)));2135 try expectError(expected_err, ctx.dir.readFileAlloc(invalid_path, testing.allocator, .limited(0)));
21942136
2195 try testing.expectError(expected_err, ctx.dir.deleteTree(io, invalid_path));2137 try expectError(expected_err, ctx.dir.deleteTree(io, invalid_path));
2196 try testing.expectError(expected_err, ctx.dir.deleteTreeMinStackSize(invalid_path));2138 try expectError(expected_err, ctx.dir.deleteTreeMinStackSize(invalid_path));
21972139
2198 try testing.expectError(expected_err, ctx.dir.writeFile(io, .{ .sub_path = invalid_path, .data = "" }));2140 try expectError(expected_err, ctx.dir.writeFile(io, .{ .sub_path = invalid_path, .data = "" }));
21992141
2200 try testing.expectError(expected_err, ctx.dir.access(invalid_path, .{}));2142 try expectError(expected_err, ctx.dir.access(invalid_path, .{}));
22012143
2202 var dir = ctx.dir;2144 var dir = ctx.dir;
2203 try testing.expectError(expected_err, dir.updateFile(io, invalid_path, dir, invalid_path, .{}));2145 try expectError(expected_err, dir.updateFile(io, invalid_path, dir, invalid_path, .{}));
2204 try testing.expectError(expected_err, ctx.dir.copyFile(invalid_path, ctx.dir, invalid_path, .{}));2146 try expectError(expected_err, ctx.dir.copyFile(invalid_path, ctx.dir, invalid_path, .{}));
22052147
2206 try testing.expectError(expected_err, ctx.dir.statFile(invalid_path));2148 try expectError(expected_err, ctx.dir.statFile(invalid_path));
22072149
2208 if (native_os != .wasi) {2150 if (native_os != .wasi) {
2209 try testing.expectError(expected_err, ctx.dir.realpath(invalid_path, &[_]u8{}));2151 try expectError(expected_err, ctx.dir.realpath(invalid_path, &[_]u8{}));
2210 try testing.expectError(expected_err, ctx.dir.realpathAlloc(testing.allocator, invalid_path));2152 try expectError(expected_err, ctx.dir.realpathAlloc(testing.allocator, invalid_path));
2211 }2153 }
22122154
2213 try testing.expectError(expected_err, Dir.rename(ctx.dir, invalid_path, ctx.dir, invalid_path, io));2155 try expectError(expected_err, Dir.rename(ctx.dir, invalid_path, ctx.dir, invalid_path, io));
22142156
2215 if (native_os != .wasi and ctx.path_type != .relative) {2157 if (native_os != .wasi and ctx.path_type != .relative) {
2216 try testing.expectError(expected_err, Dir.copyFileAbsolute(invalid_path, invalid_path, .{}));2158 try expectError(expected_err, Dir.copyFileAbsolute(invalid_path, invalid_path, .{}));
2217 try testing.expectError(expected_err, Dir.makeDirAbsolute(invalid_path));2159 try expectError(expected_err, Dir.makeDirAbsolute(invalid_path));
2218 try testing.expectError(expected_err, Dir.deleteDirAbsolute(invalid_path));2160 try expectError(expected_err, Dir.deleteDirAbsolute(invalid_path));
2219 try testing.expectError(expected_err, Dir.renameAbsolute(invalid_path, invalid_path));2161 try expectError(expected_err, Dir.renameAbsolute(invalid_path, invalid_path));
2220 try testing.expectError(expected_err, Dir.openDirAbsolute(invalid_path, .{}));2162 try expectError(expected_err, Dir.openDirAbsolute(invalid_path, .{}));
2221 try testing.expectError(expected_err, Dir.openFileAbsolute(invalid_path, .{}));2163 try expectError(expected_err, Dir.openFileAbsolute(invalid_path, .{}));
2222 try testing.expectError(expected_err, Dir.accessAbsolute(invalid_path, .{}));2164 try expectError(expected_err, Dir.accessAbsolute(invalid_path, .{}));
2223 try testing.expectError(expected_err, Dir.createFileAbsolute(invalid_path, .{}));2165 try expectError(expected_err, Dir.createFileAbsolute(invalid_path, .{}));
2224 try testing.expectError(expected_err, Dir.deleteFileAbsolute(invalid_path));2166 try expectError(expected_err, Dir.deleteFileAbsolute(invalid_path));
2225 var readlink_buf: [Dir.max_path_bytes]u8 = undefined;2167 var readlink_buf: [Dir.max_path_bytes]u8 = undefined;
2226 try testing.expectError(expected_err, Dir.readLinkAbsolute(invalid_path, &readlink_buf));2168 try expectError(expected_err, Dir.readLinkAbsolute(invalid_path, &readlink_buf));
2227 try testing.expectError(expected_err, Dir.symLinkAbsolute(invalid_path, invalid_path, .{}));2169 try expectError(expected_err, Dir.symLinkAbsolute(invalid_path, invalid_path, .{}));
2228 try testing.expectError(expected_err, Dir.realpathAlloc(testing.allocator, invalid_path));2170 try expectError(expected_err, Dir.realpathAlloc(testing.allocator, invalid_path));
2229 }2171 }
2230 }2172 }
2231 }.impl);2173 }.impl);
...@@ -2259,8 +2201,8 @@ test "read file non vectored" {...@@ -2259,8 +2201,8 @@ test "read file non vectored" {
2259 else => |e| return e,2201 else => |e| return e,
2260 };2202 };
2261 }2203 }
2262 try testing.expectEqualStrings(contents, w.buffered());2204 try expectEqualStrings(contents, w.buffered());
2263 try testing.expectEqual(contents.len, i);2205 try expectEqual(contents.len, i);
2264}2206}
22652207
2266test "seek keeping partial buffer" {2208test "seek keeping partial buffer" {
...@@ -2282,7 +2224,7 @@ test "seek keeping partial buffer" {...@@ -2282,7 +2224,7 @@ test "seek keeping partial buffer" {
2282 var read_buffer: [3]u8 = undefined;2224 var read_buffer: [3]u8 = undefined;
2283 var file_reader: Io.File.Reader = .init(file, io, &read_buffer);2225 var file_reader: Io.File.Reader = .init(file, io, &read_buffer);
22842226
2285 try testing.expectEqual(0, file_reader.logicalPos());2227 try expectEqual(0, file_reader.logicalPos());
22862228
2287 var buf: [4]u8 = undefined;2229 var buf: [4]u8 = undefined;
2288 try file_reader.interface.readSliceAll(&buf);2230 try file_reader.interface.readSliceAll(&buf);
...@@ -2292,18 +2234,18 @@ test "seek keeping partial buffer" {...@@ -2292,18 +2234,18 @@ test "seek keeping partial buffer" {
2292 return;2234 return;
2293 }2235 }
22942236
2295 try testing.expectEqual(4, file_reader.logicalPos());2237 try expectEqual(4, file_reader.logicalPos());
2296 try testing.expectEqual(7, file_reader.pos);2238 try expectEqual(7, file_reader.pos);
2297 try file_reader.seekTo(6);2239 try file_reader.seekTo(6);
2298 try testing.expectEqual(6, file_reader.logicalPos());2240 try expectEqual(6, file_reader.logicalPos());
2299 try testing.expectEqual(7, file_reader.pos);2241 try expectEqual(7, file_reader.pos);
23002242
2301 try testing.expectEqualStrings("0123", &buf);2243 try expectEqualStrings("0123", &buf);
23022244
2303 const n = try file_reader.interface.readSliceShort(&buf);2245 const n = try file_reader.interface.readSliceShort(&buf);
2304 try testing.expectEqual(4, n);2246 try expectEqual(4, n);
23052247
2306 try testing.expectEqualStrings("6789", &buf);2248 try expectEqualStrings("6789", &buf);
2307}2249}
23082250
2309test "seekBy" {2251test "seekBy" {
...@@ -2320,8 +2262,8 @@ test "seekBy" {...@@ -2320,8 +2262,8 @@ test "seekBy" {
23202262
2321 var buffer: [20]u8 = undefined;2263 var buffer: [20]u8 = undefined;
2322 const n = try reader.interface.readSliceShort(&buffer);2264 const n = try reader.interface.readSliceShort(&buffer);
2323 try testing.expectEqual(15, n);2265 try expectEqual(15, n);
2324 try testing.expectEqualStrings("t's test seekBy", buffer[0..15]);2266 try expectEqualStrings("t's test seekBy", buffer[0..15]);
2325}2267}
23262268
2327test "seekTo flushes buffered data" {2269test "seekTo flushes buffered data" {
...@@ -2348,7 +2290,7 @@ test "seekTo flushes buffered data" {...@@ -2348,7 +2290,7 @@ test "seekTo flushes buffered data" {
23482290
2349 var buf: [4]u8 = undefined;2291 var buf: [4]u8 = undefined;
2350 try file_reader.interface.readSliceAll(&buf);2292 try file_reader.interface.readSliceAll(&buf);
2351 try std.testing.expectEqualStrings(contents, &buf);2293 try expectEqualStrings(contents, &buf);
2352}2294}
23532295
2354test "File.Writer sendfile with buffered contents" {2296test "File.Writer sendfile with buffered contents" {
...@@ -2372,7 +2314,7 @@ test "File.Writer sendfile with buffered contents" {...@@ -2372,7 +2314,7 @@ test "File.Writer sendfile with buffered contents" {
2372 var out_buf: [1]u8 = undefined;2314 var out_buf: [1]u8 = undefined;
2373 var out_w = out.writerStreaming(&out_buf);2315 var out_w = out.writerStreaming(&out_buf);
2374 try out_w.interface.writeByte('a');2316 try out_w.interface.writeByte('a');
2375 try testing.expectEqual(3, try out_w.interface.sendFileAll(&in_r, .unlimited));2317 try expectEqual(3, try out_w.interface.sendFileAll(&in_r, .unlimited));
2376 try out_w.interface.flush();2318 try out_w.interface.flush();
2377 }2319 }
23782320
...@@ -2380,8 +2322,8 @@ test "File.Writer sendfile with buffered contents" {...@@ -2380,8 +2322,8 @@ test "File.Writer sendfile with buffered contents" {
2380 defer check.close(io);2322 defer check.close(io);
2381 var check_buf: [4]u8 = undefined;2323 var check_buf: [4]u8 = undefined;
2382 var check_r = check.reader(io, &check_buf);2324 var check_r = check.reader(io, &check_buf);
2383 try testing.expectEqualStrings("abcd", try check_r.interface.take(4));2325 try expectEqualStrings("abcd", try check_r.interface.take(4));
2384 try testing.expectError(error.EndOfStream, check_r.interface.takeByte());2326 try expectError(error.EndOfStream, check_r.interface.takeByte());
2385}2327}
23862328
2387test "readlink on Windows" {2329test "readlink on Windows" {
...@@ -2410,23 +2352,72 @@ test "readlinkat" {...@@ -2410,23 +2352,72 @@ test "readlinkat" {
2410 try tmp.dir.writeFile(io, .{ .sub_path = "file.txt", .data = "nonsense" });2352 try tmp.dir.writeFile(io, .{ .sub_path = "file.txt", .data = "nonsense" });
24112353
2412 // create a symbolic link2354 // create a symbolic link
2413 if (native_os == .windows) {2355 tmp.dir.symLink("file.txt", "link", .{}) catch |err| switch (err) {
2414 std.os.windows.CreateSymbolicLink(2356 error.AccessDenied => {
2415 tmp.dir.handle,
2416 &[_]u16{ 'l', 'i', 'n', 'k' },
2417 &[_:0]u16{ 'f', 'i', 'l', 'e', '.', 't', 'x', 't' },
2418 false,
2419 ) catch |err| switch (err) {
2420 // Symlink requires admin privileges on windows, so this test can legitimately fail.2357 // Symlink requires admin privileges on windows, so this test can legitimately fail.
2421 error.AccessDenied => return error.SkipZigTest,2358 if (native_os == .windows) return error.SkipZigTest;
2422 else => return err,2359 },
2423 };2360 };
2424 } else {
2425 try posix.symlinkat("file.txt", tmp.dir.handle, "link");
2426 }
24272361
2428 // read the link2362 // read the link
2429 var buffer: [fs.max_path_bytes]u8 = undefined;2363 var buffer: [fs.max_path_bytes]u8 = undefined;
2430 const read_link = try tmp.dir.readLink(io, "link", &buffer);2364 const read_link = try tmp.dir.readLink(io, "link", &buffer);
2431 try expect(mem.eql(u8, "file.txt", read_link));2365 try expectEqualStrings("file.txt", read_link);
2366}
2367
2368test "fchmodat smoke test" {
2369 if (!Io.File.Permissions.has_executable_bit) return error.SkipZigTest;
2370
2371 if (builtin.cpu.arch.isMIPS64() and (builtin.abi == .gnuabin32 or builtin.abi == .muslabin32)) {
2372 // https://github.com/ziglang/zig/issues/23808
2373 return error.SkipZigTest;
2374 }
2375
2376 const io = testing.io;
2377
2378 var tmp = tmpDir(.{});
2379 defer tmp.cleanup();
2380
2381 try expectError(error.FileNotFound, tmp.dir.setPermissions(io, "regfile", 0o666, .{}));
2382 const file = try tmp.dir.createFile(io, "regfile", .{
2383 .exclusive = true,
2384 .permissions = .fromMode(0o644),
2385 });
2386 file.close(io);
2387
2388 if ((builtin.cpu.arch == .riscv32 or builtin.cpu.arch.isLoongArch()) and
2389 builtin.os.tag == .linux and !builtin.link_libc)
2390 {
2391 return error.SkipZigTest; // No `fstatat()`.
2392 }
2393
2394 try tmp.dir.symLink(io, "regfile", "symlink", .{});
2395 const sym_mode = blk: {
2396 const st = try tmp.dir.statFile(io, "symlink", .{ .follow_symlinks = false });
2397 break :blk st.permissions.toMode() & 0b111_111_111;
2398 };
2399
2400 try tmp.dir.setFilePermissions(io, "regfile", .fromMode(0o640), .{});
2401 try expectMode(io, tmp.dir, "regfile", .fromMode(0o640));
2402 try tmp.dir.setFilePermissions(io, "regfile", .fromMode(0o600), .{ .follow_symlinks = false });
2403 try expectMode(io, tmp.dir, "regfile", .fromMode(0o600));
2404
2405 try tmp.dir.setFilePermissions(io, "symlink", .fromMode(0o640), .{});
2406 try expectMode(io, tmp.dir, "regfile", .fromMode(0o640));
2407 try expectMode(io, tmp.dir, "symlink", .fromMode(sym_mode));
2408
2409 var test_link = true;
2410 tmp.dir.setFilePermissions(io, "symlink", .fromMode(0o600), .{ .follow_symlinks = false }) catch |err| switch (err) {
2411 error.OperationNotSupported => test_link = false,
2412 else => |e| return e,
2413 };
2414 if (test_link)
2415 try expectMode(tmp.dir.handle, "symlink", 0o600);
2416 try expectMode(tmp.dir.handle, "regfile", 0o640);
2417}
2418
2419fn expectMode(io: Io, dir: Dir, file: []const u8, permissions: File.Permissions) !void {
2420 const mode = permissions.toMode();
2421 const st = try dir.statFile(io, file, .{ .follow_symlinks = false });
2422 try expectEqual(mode, st.mode & 0b111_111_111);
2432}2423}
lib/std/posix/test.zig-61
...@@ -870,67 +870,6 @@ test "pwrite with empty buffer" {...@@ -870,67 +870,6 @@ test "pwrite with empty buffer" {
870 try expectEqual(rc, 0);870 try expectEqual(rc, 0);
871}871}
872872
873fn getFileMode(dir: posix.fd_t, path: []const u8) !posix.mode_t {
874 const path_z = try posix.toPosixPath(path);
875 const mode: posix.mode_t = if (native_os == .linux) blk: {
876 const stx = try linux.wrapped.statx(
877 dir,
878 &path_z,
879 posix.AT.SYMLINK_NOFOLLOW,
880 .{ .MODE = true },
881 );
882 std.debug.assert(stx.mask.MODE);
883 break :blk stx.mode;
884 } else blk: {
885 const st = try posix.fstatatZ(dir, &path_z, posix.AT.SYMLINK_NOFOLLOW);
886 break :blk st.mode;
887 };
888
889 return mode & 0b111_111_111;
890}
891
892fn expectMode(dir: posix.fd_t, file: []const u8, mode: posix.mode_t) !void {
893 const actual = try getFileMode(dir, file);
894 try expectEqual(mode, actual & 0b111_111_111);
895}
896
897test "fchmodat smoke test" {
898 if (!Io.File.Permissions.has_executable_bit) return error.SkipZigTest;
899
900 var tmp = tmpDir(.{});
901 defer tmp.cleanup();
902
903 try expectError(error.FileNotFound, posix.fchmodat(tmp.dir.handle, "regfile", 0o666, 0));
904 const fd = try posix.openat(
905 tmp.dir.handle,
906 "regfile",
907 .{ .ACCMODE = .WRONLY, .CREAT = true, .EXCL = true, .TRUNC = true },
908 0o644,
909 );
910 posix.close(fd);
911
912 try posix.symlinkat("regfile", tmp.dir.handle, "symlink");
913 const sym_mode = try getFileMode(tmp.dir.handle, "symlink");
914
915 try posix.fchmodat(tmp.dir.handle, "regfile", 0o640, 0);
916 try expectMode(tmp.dir.handle, "regfile", 0o640);
917 try posix.fchmodat(tmp.dir.handle, "regfile", 0o600, posix.AT.SYMLINK_NOFOLLOW);
918 try expectMode(tmp.dir.handle, "regfile", 0o600);
919
920 try posix.fchmodat(tmp.dir.handle, "symlink", 0o640, 0);
921 try expectMode(tmp.dir.handle, "regfile", 0o640);
922 try expectMode(tmp.dir.handle, "symlink", sym_mode);
923
924 var test_link = true;
925 posix.fchmodat(tmp.dir.handle, "symlink", 0o600, posix.AT.SYMLINK_NOFOLLOW) catch |err| switch (err) {
926 error.OperationNotSupported => test_link = false,
927 else => |e| return e,
928 };
929 if (test_link)
930 try expectMode(tmp.dir.handle, "symlink", 0o600);
931 try expectMode(tmp.dir.handle, "regfile", 0o640);
932}
933
934const CommonOpenFlags = packed struct {873const CommonOpenFlags = packed struct {
935 ACCMODE: posix.ACCMODE = .RDONLY,874 ACCMODE: posix.ACCMODE = .RDONLY,
936 CREAT: bool = false,875 CREAT: bool = false,