authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-18 13:50:39-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-23 22:15:10-08:00
logb4bfd501aedb4abe4257b7f54a225a9654c4e08e
treef1dbfa5d22e3278e06b22df967296e6b1753a085
parentf3723b42e1f05d14479d6f5507943cd1905e0fcf

std: move some tests from posix to fs


4 files changed, 443 insertions(+), 670 deletions(-)

lib/std/fs/test.zig+382-139
...@@ -3,7 +3,6 @@ const native_os = builtin.os.tag;...@@ -3,7 +3,6 @@ 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 fs = std.fs;
7const mem = std.mem;6const mem = std.mem;
8const wasi = std.os.wasi;7const wasi = std.os.wasi;
9const windows = std.os.windows;8const windows = std.os.windows;
...@@ -14,11 +13,11 @@ const SymLinkFlags = std.Io.Dir.SymLinkFlags;...@@ -14,11 +13,11 @@ const SymLinkFlags = std.Io.Dir.SymLinkFlags;
1413
15const testing = std.testing;14const testing = std.testing;
16const expect = std.testing.expect;15const expect = std.testing.expect;
17const expectError = std.testing.expectError;
18const expectEqual = std.testing.expectEqual;16const expectEqual = std.testing.expectEqual;
19const tmpDir = std.testing.tmpDir;
20const expectEqualStrings = std.testing.expectEqualStrings;
21const expectEqualSlices = std.testing.expectEqualSlices;17const expectEqualSlices = std.testing.expectEqualSlices;
18const expectEqualStrings = std.testing.expectEqualStrings;
19const expectError = std.testing.expectError;
20const tmpDir = std.testing.tmpDir;
2221
23const PathType = enum {22const PathType = enum {
24 relative,23 relative,
...@@ -33,7 +32,7 @@ const PathType = enum {...@@ -33,7 +32,7 @@ const PathType = enum {
33 };32 };
34 }33 }
3534
36 pub const TransformError = Io.Dir.RealPathError || error{OutOfMemory};35 pub const TransformError = Dir.RealPathError || error{OutOfMemory};
37 pub const TransformFn = fn (allocator: mem.Allocator, dir: Dir, relative_path: [:0]const u8) TransformError![:0]const u8;36 pub const TransformFn = fn (allocator: mem.Allocator, dir: Dir, relative_path: [:0]const u8) TransformError![:0]const u8;
3837
39 pub fn getTransformFn(comptime path_type: PathType) TransformFn {38 pub fn getTransformFn(comptime path_type: PathType) TransformFn {
...@@ -49,24 +48,24 @@ const PathType = enum {...@@ -49,24 +48,24 @@ const PathType = enum {
49 fn transform(allocator: mem.Allocator, dir: Dir, relative_path: [:0]const u8) TransformError![:0]const u8 {48 fn transform(allocator: mem.Allocator, dir: Dir, relative_path: [:0]const u8) TransformError![:0]const u8 {
50 // The final path may not actually exist which would cause realpath to fail.49 // The final path may not actually exist which would cause realpath to fail.
51 // So instead, we get the path of the dir and join it with the relative path.50 // So instead, we get the path of the dir and join it with the relative path.
52 var fd_path_buf: [fs.max_path_bytes]u8 = undefined;51 var fd_path_buf: [Dir.max_path_bytes]u8 = undefined;
53 const dir_path = try std.os.getFdPath(dir.handle, &fd_path_buf);52 const dir_path = try std.os.getFdPath(dir.handle, &fd_path_buf);
54 return fs.path.joinZ(allocator, &.{ dir_path, relative_path });53 return Dir.path.joinZ(allocator, &.{ dir_path, relative_path });
55 }54 }
56 }.transform,55 }.transform,
57 .unc => return struct {56 .unc => return struct {
58 fn transform(allocator: mem.Allocator, dir: Dir, relative_path: [:0]const u8) TransformError![:0]const u8 {57 fn transform(allocator: mem.Allocator, dir: Dir, relative_path: [:0]const u8) TransformError![:0]const u8 {
59 // Any drive absolute path (C:\foo) can be converted into a UNC path by58 // Any drive absolute path (C:\foo) can be converted into a UNC path by
60 // using '127.0.0.1' as the server name and '<drive letter>$' as the share name.59 // using '127.0.0.1' as the server name and '<drive letter>$' as the share name.
61 var fd_path_buf: [fs.max_path_bytes]u8 = undefined;60 var fd_path_buf: [Dir.max_path_bytes]u8 = undefined;
62 const dir_path = try std.os.getFdPath(dir.handle, &fd_path_buf);61 const dir_path = try std.os.getFdPath(dir.handle, &fd_path_buf);
63 const windows_path_type = windows.getWin32PathType(u8, dir_path);62 const windows_path_type = windows.getWin32PathType(u8, dir_path);
64 switch (windows_path_type) {63 switch (windows_path_type) {
65 .unc_absolute => return fs.path.joinZ(allocator, &.{ dir_path, relative_path }),64 .unc_absolute => return Dir.path.joinZ(allocator, &.{ dir_path, relative_path }),
66 .drive_absolute => {65 .drive_absolute => {
67 // `C:\<...>` -> `\\127.0.0.1\C$\<...>`66 // `C:\<...>` -> `\\127.0.0.1\C$\<...>`
68 const prepended = "\\\\127.0.0.1\\";67 const prepended = "\\\\127.0.0.1\\";
69 var path = try fs.path.joinZ(allocator, &.{ prepended, dir_path, relative_path });68 var path = try Dir.path.joinZ(allocator, &.{ prepended, dir_path, relative_path });
70 path[prepended.len + 1] = '$';69 path[prepended.len + 1] = '$';
71 return path;70 return path;
72 },71 },
...@@ -84,7 +83,7 @@ const TestContext = struct {...@@ -84,7 +83,7 @@ const TestContext = struct {
84 path_sep: u8,83 path_sep: u8,
85 arena: ArenaAllocator,84 arena: ArenaAllocator,
86 tmp: testing.TmpDir,85 tmp: testing.TmpDir,
87 dir: Io.Dir,86 dir: Dir,
88 transform_fn: *const PathType.TransformFn,87 transform_fn: *const PathType.TransformFn,
8988
90 pub fn init(path_type: PathType, path_sep: u8, allocator: mem.Allocator, transform_fn: *const PathType.TransformFn) TestContext {89 pub fn init(path_type: PathType, path_sep: u8, allocator: mem.Allocator, transform_fn: *const PathType.TransformFn) TestContext {
...@@ -154,7 +153,7 @@ fn testWithAllSupportedPathTypes(test_func: anytype) !void {...@@ -154,7 +153,7 @@ fn testWithAllSupportedPathTypes(test_func: anytype) !void {
154153
155fn testWithPathTypeIfSupported(comptime path_type: PathType, comptime path_sep: u8, test_func: anytype) !void {154fn testWithPathTypeIfSupported(comptime path_type: PathType, comptime path_sep: u8, test_func: anytype) !void {
156 if (!(comptime path_type.isSupported(builtin.os))) return;155 if (!(comptime path_type.isSupported(builtin.os))) return;
157 if (!(comptime fs.path.isSep(path_sep))) return;156 if (!(comptime Dir.path.isSep(path_sep))) return;
158157
159 var ctx = TestContext.init(path_type, path_sep, testing.allocator, path_type.getTransformFn());158 var ctx = TestContext.init(path_type, path_sep, testing.allocator, path_type.getTransformFn());
160 defer ctx.deinit();159 defer ctx.deinit();
...@@ -174,8 +173,8 @@ fn setupSymlink(io: Io, dir: Dir, target: []const u8, link: []const u8, flags: S...@@ -174,8 +173,8 @@ fn setupSymlink(io: Io, dir: Dir, target: []const u8, link: []const u8, flags: S
174173
175// For use in test setup. If the symlink creation fails on Windows with174// For use in test setup. If the symlink creation fails on Windows with
176// AccessDenied, then make the test failure silent (it is not a Zig failure).175// AccessDenied, then make the test failure silent (it is not a Zig failure).
177fn setupSymlinkAbsolute(target: []const u8, link: []const u8, flags: SymLinkFlags) !void {176fn setupSymlinkAbsolute(io: Io, target: []const u8, link: []const u8, flags: SymLinkFlags) !void {
178 return fs.symLinkAbsolute(target, link, flags) catch |err| switch (err) {177 return Dir.symLinkAbsolute(io, target, link, flags) catch |err| switch (err) {
179 error.AccessDenied => if (native_os == .windows) return error.SkipZigTest else return err,178 error.AccessDenied => if (native_os == .windows) return error.SkipZigTest else return err,
180 else => return err,179 else => return err,
181 };180 };
...@@ -211,7 +210,7 @@ test "Dir.readLink" {...@@ -211,7 +210,7 @@ test "Dir.readLink" {
211 }210 }
212211
213 // test 3: relative path symlink212 // test 3: relative path symlink
214 const parent_file = ".." ++ fs.path.sep_str ++ "target.txt";213 const parent_file = ".." ++ Dir.path.sep_str ++ "target.txt";
215 const canonical_parent_file = try ctx.toCanonicalPathSep(parent_file);214 const canonical_parent_file = try ctx.toCanonicalPathSep(parent_file);
216 var subdir = try ctx.dir.makeOpenPath(io, "subdir", .{});215 var subdir = try ctx.dir.makeOpenPath(io, "subdir", .{});
217 defer subdir.close(io);216 defer subdir.close(io);
...@@ -251,7 +250,7 @@ test "Dir.readLink on non-symlinks" {...@@ -251,7 +250,7 @@ test "Dir.readLink on non-symlinks" {
251}250}
252251
253fn testReadLink(io: Io, dir: Dir, target_path: []const u8, symlink_path: []const u8) !void {252fn testReadLink(io: Io, dir: Dir, target_path: []const u8, symlink_path: []const u8) !void {
254 var buffer: [fs.max_path_bytes]u8 = undefined;253 var buffer: [Dir.max_path_bytes]u8 = undefined;
255 const actual = try dir.readLink(io, symlink_path, &buffer);254 const actual = try dir.readLink(io, symlink_path, &buffer);
256 try expectEqualStrings(target_path, actual);255 try expectEqualStrings(target_path, actual);
257}256}
...@@ -267,9 +266,9 @@ fn testReadLinkW(allocator: mem.Allocator, dir: Dir, target_path: []const u8, sy...@@ -267,9 +266,9 @@ fn testReadLinkW(allocator: mem.Allocator, dir: Dir, target_path: []const u8, sy
267 try expectEqualSlices(u16, target_path_w, actual);266 try expectEqualSlices(u16, target_path_w, actual);
268}267}
269268
270fn testReadLinkAbsolute(target_path: []const u8, symlink_path: []const u8) !void {269fn testReadLinkAbsolute(io: Io, target_path: []const u8, symlink_path: []const u8) !void {
271 var buffer: [fs.max_path_bytes]u8 = undefined;270 var buffer: [Dir.max_path_bytes]u8 = undefined;
272 const given = try fs.readLinkAbsolute(symlink_path, buffer[0..]);271 const given = try Dir.readLinkAbsolute(io, symlink_path, buffer[0..]);
273 try expectEqualStrings(target_path, given);272 try expectEqualStrings(target_path, given);
274}273}
275274
...@@ -309,7 +308,7 @@ test "openDir" {...@@ -309,7 +308,7 @@ test "openDir" {
309 try ctx.dir.makeDir(io, subdir_path, .default_dir);308 try ctx.dir.makeDir(io, subdir_path, .default_dir);
310309
311 for ([_][]const u8{ "", ".", ".." }) |sub_path| {310 for ([_][]const u8{ "", ".", ".." }) |sub_path| {
312 const dir_path = try fs.path.join(allocator, &.{ subdir_path, sub_path });311 const dir_path = try Dir.path.join(allocator, &.{ subdir_path, sub_path });
313 var dir = try ctx.dir.openDir(io, dir_path, .{});312 var dir = try ctx.dir.openDir(io, dir_path, .{});
314 defer dir.close(io);313 defer dir.close(io);
315 }314 }
...@@ -321,13 +320,16 @@ test "accessAbsolute" {...@@ -321,13 +320,16 @@ test "accessAbsolute" {
321 if (native_os == .wasi) return error.SkipZigTest;320 if (native_os == .wasi) return error.SkipZigTest;
322 if (native_os == .openbsd) return error.SkipZigTest;321 if (native_os == .openbsd) return error.SkipZigTest;
323322
323 const io = testing.io;
324 const gpa = testing.allocator;
325
324 var tmp = tmpDir(.{});326 var tmp = tmpDir(.{});
325 defer tmp.cleanup();327 defer tmp.cleanup();
326328
327 const base_path = try tmp.dir.realpathAlloc(testing.allocator, ".");329 const base_path = try tmp.dir.realPathAlloc(io, ".", gpa);
328 defer testing.allocator.free(base_path);330 defer gpa.free(base_path);
329331
330 try fs.accessAbsolute(base_path, .{});332 try Dir.accessAbsolute(io, base_path, .{});
331}333}
332334
333test "openDirAbsolute" {335test "openDirAbsolute" {
...@@ -335,6 +337,7 @@ test "openDirAbsolute" {...@@ -335,6 +337,7 @@ test "openDirAbsolute" {
335 if (native_os == .openbsd) return error.SkipZigTest;337 if (native_os == .openbsd) return error.SkipZigTest;
336338
337 const io = testing.io;339 const io = testing.io;
340 const gpa = testing.allocator;
338341
339 var tmp = tmpDir(.{});342 var tmp = tmpDir(.{});
340 defer tmp.cleanup();343 defer tmp.cleanup();
...@@ -342,8 +345,8 @@ test "openDirAbsolute" {...@@ -342,8 +345,8 @@ test "openDirAbsolute" {
342 const tmp_ino = (try tmp.dir.stat(io)).inode;345 const tmp_ino = (try tmp.dir.stat(io)).inode;
343346
344 try tmp.dir.makeDir(io, "subdir", .default_dir);347 try tmp.dir.makeDir(io, "subdir", .default_dir);
345 const sub_path = try tmp.dir.realpathAlloc(testing.allocator, "subdir");348 const sub_path = try tmp.dir.realPathAlloc(io, "subdir", gpa);
346 defer testing.allocator.free(sub_path);349 defer gpa.free(sub_path);
347350
348 // Can open sub_path351 // Can open sub_path
349 var tmp_sub = try Dir.openDirAbsolute(io, sub_path, .{});352 var tmp_sub = try Dir.openDirAbsolute(io, sub_path, .{});
...@@ -353,7 +356,7 @@ test "openDirAbsolute" {...@@ -353,7 +356,7 @@ test "openDirAbsolute" {
353356
354 {357 {
355 // Can open sub_path + ".."358 // Can open sub_path + ".."
356 const dir_path = try fs.path.join(testing.allocator, &.{ sub_path, ".." });359 const dir_path = try Dir.path.join(testing.allocator, &.{ sub_path, ".." });
357 defer testing.allocator.free(dir_path);360 defer testing.allocator.free(dir_path);
358361
359 var dir = try Dir.openDirAbsolute(io, dir_path, .{});362 var dir = try Dir.openDirAbsolute(io, dir_path, .{});
...@@ -365,7 +368,7 @@ test "openDirAbsolute" {...@@ -365,7 +368,7 @@ test "openDirAbsolute" {
365368
366 {369 {
367 // Can open sub_path + "."370 // Can open sub_path + "."
368 const dir_path = try fs.path.join(testing.allocator, &.{ sub_path, "." });371 const dir_path = try Dir.path.join(testing.allocator, &.{ sub_path, "." });
369 defer testing.allocator.free(dir_path);372 defer testing.allocator.free(dir_path);
370373
371 var dir = try Dir.openDirAbsolute(io, dir_path, .{});374 var dir = try Dir.openDirAbsolute(io, dir_path, .{});
...@@ -377,7 +380,7 @@ test "openDirAbsolute" {...@@ -377,7 +380,7 @@ test "openDirAbsolute" {
377380
378 {381 {
379 // Can open subdir + "..", with some extra "."382 // Can open subdir + "..", with some extra "."
380 const dir_path = try fs.path.join(testing.allocator, &.{ sub_path, ".", "..", "." });383 const dir_path = try Dir.path.join(testing.allocator, &.{ sub_path, ".", "..", "." });
381 defer testing.allocator.free(dir_path);384 defer testing.allocator.free(dir_path);
382385
383 var dir = try Dir.openDirAbsolute(io, dir_path, .{});386 var dir = try Dir.openDirAbsolute(io, dir_path, .{});
...@@ -391,7 +394,7 @@ test "openDirAbsolute" {...@@ -391,7 +394,7 @@ test "openDirAbsolute" {
391test "openDir cwd parent '..'" {394test "openDir cwd parent '..'" {
392 const io = testing.io;395 const io = testing.io;
393396
394 var dir = Io.Dir.cwd().openDir(io, "..", .{}) catch |err| {397 var dir = Dir.cwd().openDir(io, "..", .{}) catch |err| {
395 if (native_os == .wasi and err == error.PermissionDenied) {398 if (native_os == .wasi and err == error.PermissionDenied) {
396 return; // This is okay. WASI disallows escaping from the fs sandbox399 return; // This is okay. WASI disallows escaping from the fs sandbox
397 }400 }
...@@ -407,6 +410,7 @@ test "openDir non-cwd parent '..'" {...@@ -407,6 +410,7 @@ test "openDir non-cwd parent '..'" {
407 }410 }
408411
409 const io = testing.io;412 const io = testing.io;
413 const gpa = testing.allocator;
410414
411 var tmp = tmpDir(.{});415 var tmp = tmpDir(.{});
412 defer tmp.cleanup();416 defer tmp.cleanup();
...@@ -417,11 +421,11 @@ test "openDir non-cwd parent '..'" {...@@ -417,11 +421,11 @@ test "openDir non-cwd parent '..'" {
417 var dir = try subdir.openDir(io, "..", .{});421 var dir = try subdir.openDir(io, "..", .{});
418 defer dir.close(io);422 defer dir.close(io);
419423
420 const expected_path = try tmp.dir.realpathAlloc(testing.allocator, ".");424 const expected_path = try tmp.dir.realPathAlloc(io, ".", gpa);
421 defer testing.allocator.free(expected_path);425 defer gpa.free(expected_path);
422426
423 const actual_path = try dir.realpathAlloc(testing.allocator, ".");427 const actual_path = try dir.realPathAlloc(io, ".", gpa);
424 defer testing.allocator.free(actual_path);428 defer gpa.free(actual_path);
425429
426 try expectEqualStrings(expected_path, actual_path);430 try expectEqualStrings(expected_path, actual_path);
427}431}
...@@ -440,27 +444,27 @@ test "readLinkAbsolute" {...@@ -440,27 +444,27 @@ test "readLinkAbsolute" {
440 try tmp.dir.makeDir(io, "subdir", .default_dir);444 try tmp.dir.makeDir(io, "subdir", .default_dir);
441445
442 // Get base abs path446 // Get base abs path
443 var arena = ArenaAllocator.init(testing.allocator);447 var arena_allocator = ArenaAllocator.init(testing.allocator);
444 defer arena.deinit();448 defer arena_allocator.deinit();
445 const allocator = arena.allocator();449 const arena = arena_allocator.allocator();
446450
447 const base_path = try tmp.dir.realpathAlloc(allocator, ".");451 const base_path = try tmp.dir.realPathAlloc(io, ".", arena);
448452
449 {453 {
450 const target_path = try fs.path.join(allocator, &.{ base_path, "file.txt" });454 const target_path = try Dir.path.join(arena, &.{ base_path, "file.txt" });
451 const symlink_path = try fs.path.join(allocator, &.{ base_path, "symlink1" });455 const symlink_path = try Dir.path.join(arena, &.{ base_path, "symlink1" });
452456
453 // Create symbolic link by path457 // Create symbolic link by path
454 try setupSymlinkAbsolute(target_path, symlink_path, .{});458 try setupSymlinkAbsolute(io, target_path, symlink_path, .{});
455 try testReadLinkAbsolute(target_path, symlink_path);459 try testReadLinkAbsolute(io, target_path, symlink_path);
456 }460 }
457 {461 {
458 const target_path = try fs.path.join(allocator, &.{ base_path, "subdir" });462 const target_path = try Dir.path.join(arena, &.{ base_path, "subdir" });
459 const symlink_path = try fs.path.join(allocator, &.{ base_path, "symlink2" });463 const symlink_path = try Dir.path.join(arena, &.{ base_path, "symlink2" });
460464
461 // Create symbolic link to a directory by path465 // Create symbolic link to a directory by path
462 try setupSymlinkAbsolute(target_path, symlink_path, .{ .is_directory = true });466 try setupSymlinkAbsolute(io, target_path, symlink_path, .{ .is_directory = true });
463 try testReadLinkAbsolute(target_path, symlink_path);467 try testReadLinkAbsolute(io, target_path, symlink_path);
464 }468 }
465}469}
466470
...@@ -492,8 +496,8 @@ test "Dir.Iterator" {...@@ -492,8 +496,8 @@ test "Dir.Iterator" {
492 }496 }
493497
494 try expectEqual(@as(usize, 2), entries.items.len); // note that the Iterator skips '.' and '..'498 try expectEqual(@as(usize, 2), entries.items.len); // note that the Iterator skips '.' and '..'
495 try testing.expect(contains(&entries, .{ .name = "some_file", .kind = .file }));499 try expect(contains(&entries, .{ .name = "some_file", .kind = .file }));
496 try testing.expect(contains(&entries, .{ .name = "some_dir", .kind = .directory }));500 try expect(contains(&entries, .{ .name = "some_dir", .kind = .directory }));
497}501}
498502
499test "Dir.Iterator many entries" {503test "Dir.Iterator many entries" {
...@@ -529,7 +533,7 @@ test "Dir.Iterator many entries" {...@@ -529,7 +533,7 @@ test "Dir.Iterator many entries" {
529 i = 0;533 i = 0;
530 while (i < num) : (i += 1) {534 while (i < num) : (i += 1) {
531 const name = try std.fmt.bufPrint(&buf, "{}", .{i});535 const name = try std.fmt.bufPrint(&buf, "{}", .{i});
532 try testing.expect(contains(&entries, .{ .name = name, .kind = .file }));536 try expect(contains(&entries, .{ .name = name, .kind = .file }));
533 }537 }
534}538}
535539
...@@ -563,8 +567,8 @@ test "Dir.Iterator twice" {...@@ -563,8 +567,8 @@ test "Dir.Iterator twice" {
563 }567 }
564568
565 try expectEqual(@as(usize, 2), entries.items.len); // note that the Iterator skips '.' and '..'569 try expectEqual(@as(usize, 2), entries.items.len); // note that the Iterator skips '.' and '..'
566 try testing.expect(contains(&entries, .{ .name = "some_file", .kind = .file }));570 try expect(contains(&entries, .{ .name = "some_file", .kind = .file }));
567 try testing.expect(contains(&entries, .{ .name = "some_dir", .kind = .directory }));571 try expect(contains(&entries, .{ .name = "some_dir", .kind = .directory }));
568 }572 }
569}573}
570574
...@@ -599,8 +603,8 @@ test "Dir.Iterator reset" {...@@ -599,8 +603,8 @@ test "Dir.Iterator reset" {
599 }603 }
600604
601 try expectEqual(@as(usize, 2), entries.items.len); // note that the Iterator skips '.' and '..'605 try expectEqual(@as(usize, 2), entries.items.len); // note that the Iterator skips '.' and '..'
602 try testing.expect(contains(&entries, .{ .name = "some_file", .kind = .file }));606 try expect(contains(&entries, .{ .name = "some_file", .kind = .file }));
603 try testing.expect(contains(&entries, .{ .name = "some_dir", .kind = .directory }));607 try expect(contains(&entries, .{ .name = "some_dir", .kind = .directory }));
604608
605 iter.reset();609 iter.reset();
606 }610 }
...@@ -619,7 +623,7 @@ test "Dir.Iterator but dir is deleted during iteration" {...@@ -619,7 +623,7 @@ test "Dir.Iterator but dir is deleted during iteration" {
619 var iterator = subdir.iterate();623 var iterator = subdir.iterate();
620624
621 // Create something to iterate over within the subdir625 // Create something to iterate over within the subdir
622 try tmp.dir.makePath(io, "subdir" ++ fs.path.sep_str ++ "b");626 try tmp.dir.makePath(io, "subdir" ++ Dir.path.sep_str ++ "b");
623627
624 // Then, before iterating, delete the directory that we're iterating.628 // Then, before iterating, delete the directory that we're iterating.
625 // This is a contrived reproduction, but this could happen outside of the program, in another thread, etc.629 // This is a contrived reproduction, but this could happen outside of the program, in another thread, etc.
...@@ -629,7 +633,7 @@ test "Dir.Iterator but dir is deleted during iteration" {...@@ -629,7 +633,7 @@ test "Dir.Iterator but dir is deleted during iteration" {
629633
630 // Now, when we try to iterate, the next call should return null immediately.634 // Now, when we try to iterate, the next call should return null immediately.
631 const entry = try iterator.next();635 const entry = try iterator.next();
632 try std.testing.expect(entry == null);636 try std.expect(entry == null);
633637
634 // On Linux, we can opt-in to receiving a more specific error by calling `nextLinux`638 // On Linux, we can opt-in to receiving a more specific error by calling `nextLinux`
635 if (native_os == .linux) {639 if (native_os == .linux) {
...@@ -657,25 +661,25 @@ test "Dir.realpath smoke test" {...@@ -657,25 +661,25 @@ test "Dir.realpath smoke test" {
657 const allocator = ctx.arena.allocator();661 const allocator = ctx.arena.allocator();
658 const test_file_path = try ctx.transformPath("test_file");662 const test_file_path = try ctx.transformPath("test_file");
659 const test_dir_path = try ctx.transformPath("test_dir");663 const test_dir_path = try ctx.transformPath("test_dir");
660 var buf: [fs.max_path_bytes]u8 = undefined;664 var buf: [Dir.max_path_bytes]u8 = undefined;
661665
662 // FileNotFound if the path doesn't exist666 // FileNotFound if the path doesn't exist
663 try expectError(error.FileNotFound, ctx.dir.realpathAlloc(allocator, test_file_path));667 try expectError(error.FileNotFound, ctx.dir.realPathAlloc(io, test_file_path, allocator));
664 try expectError(error.FileNotFound, ctx.dir.realpath(test_file_path, &buf));668 try expectError(error.FileNotFound, ctx.dir.realPath(io, test_file_path, &buf));
665 try expectError(error.FileNotFound, ctx.dir.realpathAlloc(allocator, test_dir_path));669 try expectError(error.FileNotFound, ctx.dir.realPathAlloc(io, test_dir_path, allocator));
666 try expectError(error.FileNotFound, ctx.dir.realpath(test_dir_path, &buf));670 try expectError(error.FileNotFound, ctx.dir.realPath(io, test_dir_path, &buf));
667671
668 // Now create the file and dir672 // Now create the file and dir
669 try ctx.dir.writeFile(io, .{ .sub_path = test_file_path, .data = "" });673 try ctx.dir.writeFile(io, .{ .sub_path = test_file_path, .data = "" });
670 try ctx.dir.makeDir(io, test_dir_path, .default_dir);674 try ctx.dir.makeDir(io, test_dir_path, .default_dir);
671675
672 const base_path = try ctx.transformPath(".");676 const base_path = try ctx.transformPath(".");
673 const base_realpath = try ctx.dir.realpathAlloc(allocator, base_path);677 const base_realpath = try ctx.dir.realPathAlloc(io, base_path, allocator);
674 const expected_file_path = try fs.path.join(678 const expected_file_path = try Dir.path.join(
675 allocator,679 allocator,
676 &.{ base_realpath, "test_file" },680 &.{ base_realpath, "test_file" },
677 );681 );
678 const expected_dir_path = try fs.path.join(682 const expected_dir_path = try Dir.path.join(
679 allocator,683 allocator,
680 &.{ base_realpath, "test_dir" },684 &.{ base_realpath, "test_dir" },
681 );685 );
...@@ -691,10 +695,10 @@ test "Dir.realpath smoke test" {...@@ -691,10 +695,10 @@ test "Dir.realpath smoke test" {
691695
692 // Next, test alloc version696 // Next, test alloc version
693 {697 {
694 const file_path = try ctx.dir.realpathAlloc(allocator, test_file_path);698 const file_path = try ctx.dir.realPathAlloc(io, test_file_path, allocator);
695 try expectEqualStrings(expected_file_path, file_path);699 try expectEqualStrings(expected_file_path, file_path);
696700
697 const dir_path = try ctx.dir.realpathAlloc(allocator, test_dir_path);701 const dir_path = try ctx.dir.realPathAlloc(io, test_dir_path, allocator);
698 try expectEqualStrings(expected_dir_path, dir_path);702 try expectEqualStrings(expected_dir_path, dir_path);
699 }703 }
700 }704 }
...@@ -715,7 +719,7 @@ test "readFileAlloc" {...@@ -715,7 +719,7 @@ test "readFileAlloc" {
715 try expectEqualStrings("", buf1);719 try expectEqualStrings("", buf1);
716720
717 const write_buf: []const u8 = "this is a test.\nthis is a test.\nthis is a test.\nthis is a test.\n";721 const write_buf: []const u8 = "this is a test.\nthis is a test.\nthis is a test.\nthis is a test.\n";
718 try file.writeAll(write_buf);722 try file.writeStreamingAll(io, write_buf);
719723
720 {724 {
721 // max_bytes > file_size725 // max_bytes > file_size
...@@ -779,7 +783,7 @@ test "statFile on dangling symlink" {...@@ -779,7 +783,7 @@ test "statFile on dangling symlink" {
779 fn impl(ctx: *TestContext) !void {783 fn impl(ctx: *TestContext) !void {
780 const io = ctx.io;784 const io = ctx.io;
781 const symlink_name = try ctx.transformPath("dangling-symlink");785 const symlink_name = try ctx.transformPath("dangling-symlink");
782 const symlink_target = "." ++ fs.path.sep_str ++ "doesnotexist";786 const symlink_target = "." ++ Dir.path.sep_str ++ "doesnotexist";
783787
784 try setupSymlink(io, ctx.dir, symlink_target, symlink_name, .{});788 try setupSymlink(io, ctx.dir, symlink_target, symlink_name, .{});
785789
...@@ -803,8 +807,8 @@ test "directory operations on files" {...@@ -803,8 +807,8 @@ test "directory operations on files" {
803 try expectError(error.NotDir, ctx.dir.deleteDir(io, test_file_name));807 try expectError(error.NotDir, ctx.dir.deleteDir(io, test_file_name));
804808
805 if (ctx.path_type == .absolute and comptime PathType.absolute.isSupported(builtin.os)) {809 if (ctx.path_type == .absolute and comptime PathType.absolute.isSupported(builtin.os)) {
806 try expectError(error.PathAlreadyExists, fs.makeDirAbsolute(test_file_name));810 try expectError(error.PathAlreadyExists, Dir.makeDirAbsolute(io, test_file_name));
807 try expectError(error.NotDir, fs.deleteDirAbsolute(test_file_name));811 try expectError(error.NotDir, Dir.deleteDirAbsolute(io, test_file_name));
808 }812 }
809813
810 // ensure the file still exists and is a file as a sanity check814 // ensure the file still exists and is a file as a sanity check
...@@ -862,8 +866,8 @@ test "file operations on directories" {...@@ -862,8 +866,8 @@ test "file operations on directories" {
862 try expectError(error.IsDir, ctx.dir.openFile(io, test_dir_name, .{ .allow_directory = false, .mode = .read_only }));866 try expectError(error.IsDir, ctx.dir.openFile(io, test_dir_name, .{ .allow_directory = false, .mode = .read_only }));
863867
864 if (ctx.path_type == .absolute and comptime PathType.absolute.isSupported(builtin.os)) {868 if (ctx.path_type == .absolute and comptime PathType.absolute.isSupported(builtin.os)) {
865 try expectError(error.IsDir, fs.createFileAbsolute(test_dir_name, .{}));869 try expectError(error.IsDir, Dir.createFileAbsolute(io, test_dir_name, .{}));
866 try expectError(error.IsDir, fs.deleteFileAbsolute(test_dir_name));870 try expectError(error.IsDir, Dir.deleteFileAbsolute(io, test_dir_name));
867 }871 }
868872
869 // ensure the directory still exists as a sanity check873 // ensure the directory still exists as a sanity check
...@@ -892,7 +896,7 @@ test "deleteDir" {...@@ -892,7 +896,7 @@ test "deleteDir" {
892 fn impl(ctx: *TestContext) !void {896 fn impl(ctx: *TestContext) !void {
893 const io = ctx.io;897 const io = ctx.io;
894 const test_dir_path = try ctx.transformPath("test_dir");898 const test_dir_path = try ctx.transformPath("test_dir");
895 const test_file_path = try ctx.transformPath("test_dir" ++ fs.path.sep_str ++ "test_file");899 const test_file_path = try ctx.transformPath("test_dir" ++ Dir.path.sep_str ++ "test_file");
896900
897 // deleting a non-existent directory901 // deleting a non-existent directory
898 try expectError(error.FileNotFound, ctx.dir.deleteDir(io, test_dir_path));902 try expectError(error.FileNotFound, ctx.dir.deleteDir(io, test_dir_path));
...@@ -1097,11 +1101,12 @@ test "renameAbsolute" {...@@ -1097,11 +1101,12 @@ test "renameAbsolute" {
1097 defer arena.deinit();1101 defer arena.deinit();
1098 const allocator = arena.allocator();1102 const allocator = arena.allocator();
10991103
1100 const base_path = try tmp_dir.dir.realpathAlloc(allocator, ".");1104 const base_path = try tmp_dir.dir.realPathAlloc(io, ".", allocator);
11011105
1102 try expectError(error.FileNotFound, fs.renameAbsolute(1106 try expectError(error.FileNotFound, Dir.renameAbsolute(
1103 try fs.path.join(allocator, &.{ base_path, "missing_file_name" }),1107 io,
1104 try fs.path.join(allocator, &.{ base_path, "something_else" }),1108 try Dir.path.join(allocator, &.{ base_path, "missing_file_name" }),
1109 try Dir.path.join(allocator, &.{ base_path, "something_else" }),
1105 ));1110 ));
11061111
1107 // Renaming files1112 // Renaming files
...@@ -1109,9 +1114,10 @@ test "renameAbsolute" {...@@ -1109,9 +1114,10 @@ test "renameAbsolute" {
1109 const renamed_test_file_name = "test_file_renamed";1114 const renamed_test_file_name = "test_file_renamed";
1110 var file = try tmp_dir.dir.createFile(io, test_file_name, .{ .read = true });1115 var file = try tmp_dir.dir.createFile(io, test_file_name, .{ .read = true });
1111 file.close(io);1116 file.close(io);
1112 try fs.renameAbsolute(1117 try Dir.renameAbsolute(
1113 try fs.path.join(allocator, &.{ base_path, test_file_name }),1118 io,
1114 try fs.path.join(allocator, &.{ base_path, renamed_test_file_name }),1119 try Dir.path.join(allocator, &.{ base_path, test_file_name }),
1120 try Dir.path.join(allocator, &.{ base_path, renamed_test_file_name }),
1115 );1121 );
11161122
1117 // ensure the file was renamed1123 // ensure the file was renamed
...@@ -1125,9 +1131,10 @@ test "renameAbsolute" {...@@ -1125,9 +1131,10 @@ test "renameAbsolute" {
1125 const test_dir_name = "test_dir";1131 const test_dir_name = "test_dir";
1126 const renamed_test_dir_name = "test_dir_renamed";1132 const renamed_test_dir_name = "test_dir_renamed";
1127 try tmp_dir.dir.makeDir(io, test_dir_name, .default_dir);1133 try tmp_dir.dir.makeDir(io, test_dir_name, .default_dir);
1128 try fs.renameAbsolute(1134 try Dir.renameAbsolute(
1129 try fs.path.join(allocator, &.{ base_path, test_dir_name }),1135 io,
1130 try fs.path.join(allocator, &.{ base_path, renamed_test_dir_name }),1136 try Dir.path.join(allocator, &.{ base_path, test_dir_name }),
1137 try Dir.path.join(allocator, &.{ base_path, renamed_test_dir_name }),
1131 );1138 );
11321139
1133 // ensure the directory was renamed1140 // ensure the directory was renamed
...@@ -1149,7 +1156,7 @@ test "executablePath" {...@@ -1149,7 +1156,7 @@ test "executablePath" {
1149 if (native_os == .wasi) return error.SkipZigTest;1156 if (native_os == .wasi) return error.SkipZigTest;
11501157
1151 const io = testing.io;1158 const io = testing.io;
1152 var buf: [fs.max_path_bytes]u8 = undefined;1159 var buf: [Dir.max_path_bytes]u8 = undefined;
1153 const buf_self_exe_path = try std.process.executablePath(io, &buf);1160 const buf_self_exe_path = try std.process.executablePath(io, &buf);
1154 const alloc_self_exe_path = try std.process.executablePathAlloc(io, testing.allocator);1161 const alloc_self_exe_path = try std.process.executablePathAlloc(io, testing.allocator);
1155 defer testing.allocator.free(alloc_self_exe_path);1162 defer testing.allocator.free(alloc_self_exe_path);
...@@ -1206,13 +1213,13 @@ test "makePath, put some files in it, deleteTree" {...@@ -1206,13 +1213,13 @@ test "makePath, put some files in it, deleteTree" {
1206 const allocator = ctx.arena.allocator();1213 const allocator = ctx.arena.allocator();
1207 const dir_path = try ctx.transformPath("os_test_tmp");1214 const dir_path = try ctx.transformPath("os_test_tmp");
12081215
1209 try ctx.dir.makePath(io, try fs.path.join(allocator, &.{ "os_test_tmp", "b", "c" }));1216 try ctx.dir.makePath(io, try Dir.path.join(allocator, &.{ "os_test_tmp", "b", "c" }));
1210 try ctx.dir.writeFile(io, .{1217 try ctx.dir.writeFile(io, .{
1211 .sub_path = try fs.path.join(allocator, &.{ "os_test_tmp", "b", "c", "file.txt" }),1218 .sub_path = try Dir.path.join(allocator, &.{ "os_test_tmp", "b", "c", "file.txt" }),
1212 .data = "nonsense",1219 .data = "nonsense",
1213 });1220 });
1214 try ctx.dir.writeFile(io, .{1221 try ctx.dir.writeFile(io, .{
1215 .sub_path = try fs.path.join(allocator, &.{ "os_test_tmp", "b", "file2.txt" }),1222 .sub_path = try Dir.path.join(allocator, &.{ "os_test_tmp", "b", "file2.txt" }),
1216 .data = "blah",1223 .data = "blah",
1217 });1224 });
12181225
...@@ -1229,13 +1236,13 @@ test "makePath, put some files in it, deleteTreeMinStackSize" {...@@ -1229,13 +1236,13 @@ test "makePath, put some files in it, deleteTreeMinStackSize" {
1229 const allocator = ctx.arena.allocator();1236 const allocator = ctx.arena.allocator();
1230 const dir_path = try ctx.transformPath("os_test_tmp");1237 const dir_path = try ctx.transformPath("os_test_tmp");
12311238
1232 try ctx.dir.makePath(io, try fs.path.join(allocator, &.{ "os_test_tmp", "b", "c" }));1239 try ctx.dir.makePath(io, try Dir.path.join(allocator, &.{ "os_test_tmp", "b", "c" }));
1233 try ctx.dir.writeFile(io, .{1240 try ctx.dir.writeFile(io, .{
1234 .sub_path = try fs.path.join(allocator, &.{ "os_test_tmp", "b", "c", "file.txt" }),1241 .sub_path = try Dir.path.join(allocator, &.{ "os_test_tmp", "b", "c", "file.txt" }),
1235 .data = "nonsense",1242 .data = "nonsense",
1236 });1243 });
1237 try ctx.dir.writeFile(io, .{1244 try ctx.dir.writeFile(io, .{
1238 .sub_path = try fs.path.join(allocator, &.{ "os_test_tmp", "b", "file2.txt" }),1245 .sub_path = try Dir.path.join(allocator, &.{ "os_test_tmp", "b", "file2.txt" }),
1239 .data = "blah",1246 .data = "blah",
1240 });1247 });
12411248
...@@ -1285,7 +1292,7 @@ test "makepath existing directories" {...@@ -1285,7 +1292,7 @@ test "makepath existing directories" {
1285 defer tmpA.close(io);1292 defer tmpA.close(io);
1286 try tmpA.makeDir(io, "B", .default_dir);1293 try tmpA.makeDir(io, "B", .default_dir);
12871294
1288 const testPath = "A" ++ fs.path.sep_str ++ "B" ++ fs.path.sep_str ++ "C";1295 const testPath = "A" ++ Dir.path.sep_str ++ "B" ++ Dir.path.sep_str ++ "C";
1289 try tmp.dir.makePath(io, testPath);1296 try tmp.dir.makePath(io, testPath);
12901297
1291 try expectDir(io, tmp.dir, testPath);1298 try expectDir(io, tmp.dir, testPath);
...@@ -1298,11 +1305,11 @@ test "makepath through existing valid symlink" {...@@ -1298,11 +1305,11 @@ test "makepath through existing valid symlink" {
1298 defer tmp.cleanup();1305 defer tmp.cleanup();
12991306
1300 try tmp.dir.makeDir(io, "realfolder", .default_dir);1307 try tmp.dir.makeDir(io, "realfolder", .default_dir);
1301 try setupSymlink(io, tmp.dir, "." ++ fs.path.sep_str ++ "realfolder", "working-symlink", .{});1308 try setupSymlink(io, tmp.dir, "." ++ Dir.path.sep_str ++ "realfolder", "working-symlink", .{});
13021309
1303 try tmp.dir.makePath(io, "working-symlink" ++ fs.path.sep_str ++ "in-realfolder");1310 try tmp.dir.makePath(io, "working-symlink" ++ Dir.path.sep_str ++ "in-realfolder");
13041311
1305 try expectDir(io, tmp.dir, "realfolder" ++ fs.path.sep_str ++ "in-realfolder");1312 try expectDir(io, tmp.dir, "realfolder" ++ Dir.path.sep_str ++ "in-realfolder");
1306}1313}
13071314
1308test "makepath relative walks" {1315test "makepath relative walks" {
...@@ -1311,7 +1318,7 @@ test "makepath relative walks" {...@@ -1311,7 +1318,7 @@ test "makepath relative walks" {
1311 var tmp = tmpDir(.{});1318 var tmp = tmpDir(.{});
1312 defer tmp.cleanup();1319 defer tmp.cleanup();
13131320
1314 const relPath = try fs.path.join(testing.allocator, &.{1321 const relPath = try Dir.path.join(testing.allocator, &.{
1315 "first", "..", "second", "..", "third", "..", "first", "A", "..", "B", "..", "C",1322 "first", "..", "second", "..", "third", "..", "first", "A", "..", "B", "..", "C",
1316 });1323 });
1317 defer testing.allocator.free(relPath);1324 defer testing.allocator.free(relPath);
...@@ -1323,14 +1330,14 @@ test "makepath relative walks" {...@@ -1323,14 +1330,14 @@ test "makepath relative walks" {
1323 .windows => {1330 .windows => {
1324 // On Windows, .. is resolved before passing the path to NtCreateFile,1331 // On Windows, .. is resolved before passing the path to NtCreateFile,
1325 // meaning everything except `first/C` drops out.1332 // meaning everything except `first/C` drops out.
1326 try expectDir(io, tmp.dir, "first" ++ fs.path.sep_str ++ "C");1333 try expectDir(io, tmp.dir, "first" ++ Dir.path.sep_str ++ "C");
1327 try expectError(error.FileNotFound, tmp.dir.access(io, "second", .{}));1334 try expectError(error.FileNotFound, tmp.dir.access(io, "second", .{}));
1328 try expectError(error.FileNotFound, tmp.dir.access(io, "third", .{}));1335 try expectError(error.FileNotFound, tmp.dir.access(io, "third", .{}));
1329 },1336 },
1330 else => {1337 else => {
1331 try expectDir(io, tmp.dir, "first" ++ fs.path.sep_str ++ "A");1338 try expectDir(io, tmp.dir, "first" ++ Dir.path.sep_str ++ "A");
1332 try expectDir(io, tmp.dir, "first" ++ fs.path.sep_str ++ "B");1339 try expectDir(io, tmp.dir, "first" ++ Dir.path.sep_str ++ "B");
1333 try expectDir(io, tmp.dir, "first" ++ fs.path.sep_str ++ "C");1340 try expectDir(io, tmp.dir, "first" ++ Dir.path.sep_str ++ "C");
1334 try expectDir(io, tmp.dir, "second");1341 try expectDir(io, tmp.dir, "second");
1335 try expectDir(io, tmp.dir, "third");1342 try expectDir(io, tmp.dir, "third");
1336 },1343 },
...@@ -1344,13 +1351,13 @@ test "makepath ignores '.'" {...@@ -1344,13 +1351,13 @@ test "makepath ignores '.'" {
1344 defer tmp.cleanup();1351 defer tmp.cleanup();
13451352
1346 // Path to create, with "." elements:1353 // Path to create, with "." elements:
1347 const dotPath = try fs.path.join(testing.allocator, &.{1354 const dotPath = try Dir.path.join(testing.allocator, &.{
1348 "first", ".", "second", ".", "third",1355 "first", ".", "second", ".", "third",
1349 });1356 });
1350 defer testing.allocator.free(dotPath);1357 defer testing.allocator.free(dotPath);
13511358
1352 // Path to expect to find:1359 // Path to expect to find:
1353 const expectedPath = try fs.path.join(testing.allocator, &.{1360 const expectedPath = try Dir.path.join(testing.allocator, &.{
1354 "first", "second", "third",1361 "first", "second", "third",
1355 });1362 });
1356 defer testing.allocator.free(expectedPath);1363 defer testing.allocator.free(expectedPath);
...@@ -1473,7 +1480,7 @@ test "access file" {...@@ -1473,7 +1480,7 @@ test "access file" {
1473 fn impl(ctx: *TestContext) !void {1480 fn impl(ctx: *TestContext) !void {
1474 const io = ctx.io;1481 const io = ctx.io;
1475 const dir_path = try ctx.transformPath("os_test_tmp");1482 const dir_path = try ctx.transformPath("os_test_tmp");
1476 const file_path = try ctx.transformPath("os_test_tmp" ++ fs.path.sep_str ++ "file.txt");1483 const file_path = try ctx.transformPath("os_test_tmp" ++ Dir.path.sep_str ++ "file.txt");
14771484
1478 try ctx.dir.makePath(io, dir_path);1485 try ctx.dir.makePath(io, dir_path);
1479 try expectError(error.FileNotFound, ctx.dir.access(io, file_path, .{}));1486 try expectError(error.FileNotFound, ctx.dir.access(io, file_path, .{}));
...@@ -1546,7 +1553,7 @@ test "sendfile with buffered data" {...@@ -1546,7 +1553,7 @@ test "sendfile with buffered data" {
1546 var src_file = try dir.createFile(io, "sendfile1.txt", .{ .read = true });1553 var src_file = try dir.createFile(io, "sendfile1.txt", .{ .read = true });
1547 defer src_file.close(io);1554 defer src_file.close(io);
15481555
1549 try src_file.writeAll("AAAABBBB");1556 try src_file.writeStreamingAll(io, "AAAABBBB");
15501557
1551 var dest_file = try dir.createFile(io, "sendfile2.txt", .{ .read = true });1558 var dest_file = try dir.createFile(io, "sendfile2.txt", .{ .read = true });
1552 defer dest_file.close(io);1559 defer dest_file.close(io);
...@@ -1691,7 +1698,7 @@ test "open file with exclusive lock twice, make sure second lock waits" {...@@ -1691,7 +1698,7 @@ test "open file with exclusive lock twice, make sure second lock waits" {
1691 errdefer file.close(io);1698 errdefer file.close(io);
16921699
1693 const S = struct {1700 const S = struct {
1694 fn checkFn(dir: *Io.Dir, path: []const u8, started: *std.Thread.ResetEvent, locked: *std.Thread.ResetEvent) !void {1701 fn checkFn(dir: *Dir, path: []const u8, started: *std.Thread.ResetEvent, locked: *std.Thread.ResetEvent) !void {
1695 started.set();1702 started.set();
1696 const file1 = try dir.createFile(io, path, .{ .lock = .exclusive });1703 const file1 = try dir.createFile(io, path, .{ .lock = .exclusive });
16971704
...@@ -1731,8 +1738,8 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {...@@ -1731,8 +1738,8 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {
1731 var random_bytes: [12]u8 = undefined;1738 var random_bytes: [12]u8 = undefined;
1732 std.crypto.random.bytes(&random_bytes);1739 std.crypto.random.bytes(&random_bytes);
17331740
1734 var random_b64: [fs.base64_encoder.calcSize(random_bytes.len)]u8 = undefined;1741 var random_b64: [std.fs.base64_encoder.calcSize(random_bytes.len)]u8 = undefined;
1735 _ = fs.base64_encoder.encode(&random_b64, &random_bytes);1742 _ = std.fs.base64_encoder.encode(&random_b64, &random_bytes);
17361743
1737 const sub_path = random_b64 ++ "-zig-test-absolute-paths.txt";1744 const sub_path = random_b64 ++ "-zig-test-absolute-paths.txt";
17381745
...@@ -1741,16 +1748,16 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {...@@ -1741,16 +1748,16 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {
1741 const cwd = try std.process.getCwdAlloc(gpa);1748 const cwd = try std.process.getCwdAlloc(gpa);
1742 defer gpa.free(cwd);1749 defer gpa.free(cwd);
17431750
1744 const filename = try fs.path.resolve(gpa, &.{ cwd, sub_path });1751 const filename = try Dir.path.resolve(gpa, &.{ cwd, sub_path });
1745 defer gpa.free(filename);1752 defer gpa.free(filename);
17461753
1747 defer fs.deleteFileAbsolute(filename) catch {}; // createFileAbsolute can leave files on failures1754 defer Dir.deleteFileAbsolute(io, filename) catch {}; // createFileAbsolute can leave files on failures
1748 const file1 = try fs.createFileAbsolute(filename, .{1755 const file1 = try Dir.createFileAbsolute(io, filename, .{
1749 .lock = .exclusive,1756 .lock = .exclusive,
1750 .lock_nonblocking = true,1757 .lock_nonblocking = true,
1751 });1758 });
17521759
1753 const file2 = fs.createFileAbsolute(filename, .{1760 const file2 = Dir.createFileAbsolute(io, filename, .{
1754 .lock = .exclusive,1761 .lock = .exclusive,
1755 .lock_nonblocking = true,1762 .lock_nonblocking = true,
1756 });1763 });
...@@ -1802,9 +1809,9 @@ test "walker" {...@@ -1802,9 +1809,9 @@ test "walker" {
1802 .{ "dir2", 1 },1809 .{ "dir2", 1 },
1803 .{ "dir3", 1 },1810 .{ "dir3", 1 },
1804 .{ "dir4", 1 },1811 .{ "dir4", 1 },
1805 .{ "dir3" ++ fs.path.sep_str ++ "sub1", 2 },1812 .{ "dir3" ++ Dir.path.sep_str ++ "sub1", 2 },
1806 .{ "dir3" ++ fs.path.sep_str ++ "sub2", 2 },1813 .{ "dir3" ++ Dir.path.sep_str ++ "sub2", 2 },
1807 .{ "dir3" ++ fs.path.sep_str ++ "sub2" ++ fs.path.sep_str ++ "subsub1", 3 },1814 .{ "dir3" ++ Dir.path.sep_str ++ "sub2" ++ Dir.path.sep_str ++ "subsub1", 3 },
1808 });1815 });
18091816
1810 const expected_basenames = std.StaticStringMap(void).initComptime(.{1817 const expected_basenames = std.StaticStringMap(void).initComptime(.{
...@@ -1826,11 +1833,11 @@ test "walker" {...@@ -1826,11 +1833,11 @@ test "walker" {
18261833
1827 var num_walked: usize = 0;1834 var num_walked: usize = 0;
1828 while (try walker.next()) |entry| {1835 while (try walker.next()) |entry| {
1829 testing.expect(expected_basenames.has(entry.basename)) catch |err| {1836 expect(expected_basenames.has(entry.basename)) catch |err| {
1830 std.debug.print("found unexpected basename: {f}\n", .{std.ascii.hexEscape(entry.basename, .lower)});1837 std.debug.print("found unexpected basename: {f}\n", .{std.ascii.hexEscape(entry.basename, .lower)});
1831 return err;1838 return err;
1832 };1839 };
1833 testing.expect(expected_paths.has(entry.path)) catch |err| {1840 expect(expected_paths.has(entry.path)) catch |err| {
1834 std.debug.print("found unexpected path: {f}\n", .{std.ascii.hexEscape(entry.path, .lower)});1841 std.debug.print("found unexpected path: {f}\n", .{std.ascii.hexEscape(entry.path, .lower)});
1835 return err;1842 return err;
1836 };1843 };
...@@ -1863,11 +1870,11 @@ test "selective walker, skip entries that start with ." {...@@ -1863,11 +1870,11 @@ test "selective walker, skip entries that start with ." {
18631870
1864 const expected_paths = std.StaticStringMap(usize).initComptime(.{1871 const expected_paths = std.StaticStringMap(usize).initComptime(.{
1865 .{ "dir1", 1 },1872 .{ "dir1", 1 },
1866 .{ "dir1" ++ fs.path.sep_str ++ "foo", 2 },1873 .{ "dir1" ++ Dir.path.sep_str ++ "foo", 2 },
1867 .{ "a", 1 },1874 .{ "a", 1 },
1868 .{ "a" ++ fs.path.sep_str ++ "b", 2 },1875 .{ "a" ++ Dir.path.sep_str ++ "b", 2 },
1869 .{ "a" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c", 3 },1876 .{ "a" ++ Dir.path.sep_str ++ "b" ++ Dir.path.sep_str ++ "c", 3 },
1870 .{ "a" ++ fs.path.sep_str ++ "baz", 2 },1877 .{ "a" ++ Dir.path.sep_str ++ "baz", 2 },
1871 });1878 });
18721879
1873 const expected_basenames = std.StaticStringMap(void).initComptime(.{1880 const expected_basenames = std.StaticStringMap(void).initComptime(.{
...@@ -1893,11 +1900,11 @@ test "selective walker, skip entries that start with ." {...@@ -1893,11 +1900,11 @@ test "selective walker, skip entries that start with ." {
1893 try walker.enter(entry);1900 try walker.enter(entry);
1894 }1901 }
18951902
1896 testing.expect(expected_basenames.has(entry.basename)) catch |err| {1903 expect(expected_basenames.has(entry.basename)) catch |err| {
1897 std.debug.print("found unexpected basename: {f}\n", .{std.ascii.hexEscape(entry.basename, .lower)});1904 std.debug.print("found unexpected basename: {f}\n", .{std.ascii.hexEscape(entry.basename, .lower)});
1898 return err;1905 return err;
1899 };1906 };
1900 testing.expect(expected_paths.has(entry.path)) catch |err| {1907 expect(expected_paths.has(entry.path)) catch |err| {
1901 std.debug.print("found unexpected path: {f}\n", .{std.ascii.hexEscape(entry.path, .lower)});1908 std.debug.print("found unexpected path: {f}\n", .{std.ascii.hexEscape(entry.path, .lower)});
1902 return err;1909 return err;
1903 };1910 };
...@@ -1937,7 +1944,7 @@ test "walker without fully iterating" {...@@ -1937,7 +1944,7 @@ test "walker without fully iterating" {
1937 try expectEqual(@as(usize, 1), num_walked);1944 try expectEqual(@as(usize, 1), num_walked);
1938}1945}
19391946
1940test "'.' and '..' in Io.Dir functions" {1947test "'.' and '..' in Dir functions" {
1941 if (native_os == .windows and builtin.cpu.arch == .aarch64) {1948 if (native_os == .windows and builtin.cpu.arch == .aarch64) {
1942 // https://github.com/ziglang/zig/issues/171341949 // https://github.com/ziglang/zig/issues/17134
1943 return error.SkipZigTest;1950 return error.SkipZigTest;
...@@ -1970,7 +1977,7 @@ test "'.' and '..' in Io.Dir functions" {...@@ -1970,7 +1977,7 @@ test "'.' and '..' in Io.Dir functions" {
1970 try ctx.dir.writeFile(io, .{ .sub_path = update_path, .data = "something" });1977 try ctx.dir.writeFile(io, .{ .sub_path = update_path, .data = "something" });
1971 var dir = ctx.dir;1978 var dir = ctx.dir;
1972 const prev_status = try dir.updateFile(io, file_path, dir, update_path, .{});1979 const prev_status = try dir.updateFile(io, file_path, dir, update_path, .{});
1973 try expectEqual(Io.Dir.PrevStatus.stale, prev_status);1980 try expectEqual(Dir.PrevStatus.stale, prev_status);
19741981
1975 try ctx.dir.deleteDir(io, subdir_path);1982 try ctx.dir.deleteDir(io, subdir_path);
1976 }1983 }
...@@ -1990,28 +1997,28 @@ test "'.' and '..' in absolute functions" {...@@ -1990,28 +1997,28 @@ test "'.' and '..' in absolute functions" {
1990 defer arena.deinit();1997 defer arena.deinit();
1991 const allocator = arena.allocator();1998 const allocator = arena.allocator();
19921999
1993 const base_path = try tmp.dir.realpathAlloc(allocator, ".");2000 const base_path = try tmp.dir.realPathAlloc(io, ".", allocator);
19942001
1995 const subdir_path = try fs.path.join(allocator, &.{ base_path, "./subdir" });2002 const subdir_path = try Dir.path.join(allocator, &.{ base_path, "./subdir" });
1996 try fs.makeDirAbsolute(subdir_path);2003 try Dir.makeDirAbsolute(io, subdir_path);
1997 try fs.accessAbsolute(subdir_path, .{});2004 try Dir.accessAbsolute(io, subdir_path, .{});
1998 var created_subdir = try Dir.openDirAbsolute(io, subdir_path, .{});2005 var created_subdir = try Dir.openDirAbsolute(io, subdir_path, .{});
1999 created_subdir.close(io);2006 created_subdir.close(io);
20002007
2001 const created_file_path = try fs.path.join(allocator, &.{ subdir_path, "../file" });2008 const created_file_path = try Dir.path.join(allocator, &.{ subdir_path, "../file" });
2002 const created_file = try fs.createFileAbsolute(created_file_path, .{});2009 const created_file = try Dir.createFileAbsolute(io, created_file_path, .{});
2003 created_file.close(io);2010 created_file.close(io);
2004 try fs.accessAbsolute(created_file_path, .{});2011 try Dir.accessAbsolute(io, created_file_path, .{});
20052012
2006 const copied_file_path = try fs.path.join(allocator, &.{ subdir_path, "../copy" });2013 const copied_file_path = try Dir.path.join(allocator, &.{ subdir_path, "../copy" });
2007 try fs.copyFileAbsolute(created_file_path, copied_file_path, .{});2014 try Dir.copyFileAbsolute(io, created_file_path, copied_file_path, .{});
2008 const renamed_file_path = try fs.path.join(allocator, &.{ subdir_path, "../rename" });2015 const renamed_file_path = try Dir.path.join(allocator, &.{ subdir_path, "../rename" });
2009 try fs.renameAbsolute(copied_file_path, renamed_file_path);2016 try Dir.renameAbsolute(io, copied_file_path, renamed_file_path);
2010 const renamed_file = try Dir.openFileAbsolute(renamed_file_path, .{});2017 const renamed_file = try Dir.openFileAbsolute(renamed_file_path, .{});
2011 renamed_file.close(io);2018 renamed_file.close(io);
2012 try fs.deleteFileAbsolute(renamed_file_path);2019 try Dir.deleteFileAbsolute(io, renamed_file_path);
20132020
2014 try fs.deleteDirAbsolute(subdir_path);2021 try Dir.deleteDirAbsolute(io, subdir_path);
2015}2022}
20162023
2017test "chmod" {2024test "chmod" {
...@@ -2114,8 +2121,8 @@ test "invalid UTF-8/WTF-8 paths" {...@@ -2114,8 +2121,8 @@ test "invalid UTF-8/WTF-8 paths" {
2114 try expectError(expected_err, ctx.dir.statFile(invalid_path));2121 try expectError(expected_err, ctx.dir.statFile(invalid_path));
21152122
2116 if (native_os != .wasi) {2123 if (native_os != .wasi) {
2117 try expectError(expected_err, ctx.dir.realpath(invalid_path, &[_]u8{}));2124 try expectError(expected_err, ctx.dir.realPath(io, invalid_path, &[_]u8{}));
2118 try expectError(expected_err, ctx.dir.realpathAlloc(testing.allocator, invalid_path));2125 try expectError(expected_err, ctx.dir.realPathAlloc(io, invalid_path, testing.allocator));
2119 }2126 }
21202127
2121 try expectError(expected_err, Dir.rename(ctx.dir, invalid_path, ctx.dir, invalid_path, io));2128 try expectError(expected_err, Dir.rename(ctx.dir, invalid_path, ctx.dir, invalid_path, io));
...@@ -2133,7 +2140,7 @@ test "invalid UTF-8/WTF-8 paths" {...@@ -2133,7 +2140,7 @@ test "invalid UTF-8/WTF-8 paths" {
2133 var readlink_buf: [Dir.max_path_bytes]u8 = undefined;2140 var readlink_buf: [Dir.max_path_bytes]u8 = undefined;
2134 try expectError(expected_err, Dir.readLinkAbsolute(invalid_path, &readlink_buf));2141 try expectError(expected_err, Dir.readLinkAbsolute(invalid_path, &readlink_buf));
2135 try expectError(expected_err, Dir.symLinkAbsolute(invalid_path, invalid_path, .{}));2142 try expectError(expected_err, Dir.symLinkAbsolute(invalid_path, invalid_path, .{}));
2136 try expectError(expected_err, Dir.realpathAlloc(testing.allocator, invalid_path));2143 try expectError(expected_err, Dir.realPathAlloc(io, invalid_path, testing.allocator));
2137 }2144 }
2138 }2145 }
2139 }.impl);2146 }.impl);
...@@ -2303,7 +2310,7 @@ test "readlink on Windows" {...@@ -2303,7 +2310,7 @@ test "readlink on Windows" {
2303}2310}
23042311
2305fn testReadLinkWindows(io: Io, target_path: []const u8, symlink_path: []const u8) !void {2312fn testReadLinkWindows(io: Io, target_path: []const u8, symlink_path: []const u8) !void {
2306 var buffer: [fs.max_path_bytes]u8 = undefined;2313 var buffer: [Dir.max_path_bytes]u8 = undefined;
2307 const given = try Dir.readLinkAbsolute(io, symlink_path, &buffer);2314 const given = try Dir.readLinkAbsolute(io, symlink_path, &buffer);
2308 try expect(mem.eql(u8, target_path, given));2315 try expect(mem.eql(u8, target_path, given));
2309}2316}
...@@ -2326,7 +2333,7 @@ test "readlinkat" {...@@ -2326,7 +2333,7 @@ test "readlinkat" {
2326 };2333 };
23272334
2328 // read the link2335 // read the link
2329 var buffer: [fs.max_path_bytes]u8 = undefined;2336 var buffer: [Dir.max_path_bytes]u8 = undefined;
2330 const read_link = try tmp.dir.readLink(io, "link", &buffer);2337 const read_link = try tmp.dir.readLink(io, "link", &buffer);
2331 try expectEqualStrings("file.txt", read_link);2338 try expectEqualStrings("file.txt", read_link);
2332}2339}
...@@ -2387,3 +2394,239 @@ fn expectMode(io: Io, dir: Dir, file: []const u8, permissions: File.Permissions)...@@ -2387,3 +2394,239 @@ fn expectMode(io: Io, dir: Dir, file: []const u8, permissions: File.Permissions)
2387 const st = try dir.statFile(io, file, .{ .follow_symlinks = false });2394 const st = try dir.statFile(io, file, .{ .follow_symlinks = false });
2388 try expectEqual(mode, st.mode & 0b111_111_111);2395 try expectEqual(mode, st.mode & 0b111_111_111);
2389}2396}
2397
2398test "isatty" {
2399 const io = testing.io;
2400
2401 var tmp = tmpDir(.{});
2402 defer tmp.cleanup();
2403
2404 var file = try tmp.dir.createFile(io, "foo", .{});
2405 defer file.close(io);
2406
2407 try expectEqual(false, try file.isTty(io));
2408}
2409
2410test "read positional empty buffer" {
2411 const io = testing.io;
2412
2413 var tmp = tmpDir(.{});
2414 defer tmp.cleanup();
2415
2416 var file = try tmp.dir.createFile(io, "pread_empty", .{ .read = true });
2417 defer file.close(io);
2418
2419 var buffer: [0]u8 = undefined;
2420 try expectEqual(0, try file.readPositional(io, &buffer, 0));
2421}
2422
2423test "write streaming empty buffer" {
2424 const io = testing.io;
2425
2426 var tmp = tmpDir(.{});
2427 defer tmp.cleanup();
2428
2429 var file = try tmp.dir.createFile(io, "write_empty", .{});
2430 defer file.close(io);
2431
2432 var buffer: [0]u8 = &.{};
2433 try expectEqual(0, try file.writeStreaming(io, &buffer));
2434}
2435
2436test "write positional empty buffer" {
2437 const io = testing.io;
2438
2439 var tmp = tmpDir(.{});
2440 defer tmp.cleanup();
2441
2442 var file = try tmp.dir.createFile(io, "pwrite_empty", .{});
2443 defer file.close(io);
2444
2445 var buffer: [0]u8 = &.{};
2446 try expectEqual(0, try file.writePositional(io, &buffer, 0));
2447}
2448
2449test "access smoke test" {
2450 if (native_os == .wasi) return error.SkipZigTest;
2451 if (native_os == .windows) return error.SkipZigTest;
2452 if (native_os == .openbsd) return error.SkipZigTest;
2453
2454 const io = testing.io;
2455 const gpa = testing.allocator;
2456
2457 var tmp = tmpDir(.{});
2458 defer tmp.cleanup();
2459
2460 const base_path = try tmp.dir.realPathAlloc(io, ".", gpa);
2461 defer gpa.free(base_path);
2462
2463 {
2464 // Create some file using `open`.
2465 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_file" });
2466 defer gpa.free(file_path);
2467 const file = Dir.cwd().createFile(io, file_path, .{ .read = true, .exclusive = true });
2468 file.close(io);
2469 }
2470
2471 {
2472 // Try to access() the file
2473 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_file" });
2474 defer gpa.free(file_path);
2475 if (native_os == .windows) {
2476 try Dir.cwd().access(io, file_path, .{});
2477 } else {
2478 try Dir.cwd().access(io, file_path, .{ .read = true, .write = true });
2479 }
2480 }
2481
2482 {
2483 // Try to access() a non-existent file - should fail with error.FileNotFound
2484 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_file" });
2485 defer gpa.free(file_path);
2486 try expectError(error.FileNotFound, Dir.cwd().access(io, file_path, .{}));
2487 }
2488
2489 {
2490 // Create some directory
2491 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_dir" });
2492 defer gpa.free(file_path);
2493 try Dir.makeDir(io, file_path, .default_file);
2494 }
2495
2496 {
2497 // Try to access() the directory
2498 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_dir" });
2499 defer gpa.free(file_path);
2500
2501 try Dir.access(io, file_path, .{});
2502 }
2503}
2504
2505test "write streaming a long vector" {
2506 const io = testing.io;
2507
2508 var tmp = tmpDir(.{});
2509 defer tmp.cleanup();
2510
2511 var file = try tmp.dir.createFile(io, "pwritev", .{});
2512 defer file.close(io);
2513
2514 var vecs: [2000][]const u8 = undefined;
2515 for (&vecs) |*v| v.* = "a";
2516
2517 const n = try file.writePositional(io, &vecs, 0);
2518 try expect(n <= vecs.len);
2519}
2520
2521test "open smoke test" {
2522 if (native_os == .wasi) return error.SkipZigTest;
2523 if (native_os == .windows) return error.SkipZigTest;
2524 if (native_os == .openbsd) return error.SkipZigTest;
2525
2526 // TODO verify file attributes using `fstat`
2527
2528 var tmp = tmpDir(.{});
2529 defer tmp.cleanup();
2530
2531 const io = testing.io;
2532
2533 {
2534 // Create some file using `open`.
2535 const file = try tmp.dir.createFile(io, "some_file", .{ .exclusive = true });
2536 file.close(io);
2537 }
2538
2539 // Try this again with the same flags. This op should fail with error.PathAlreadyExists.
2540 try expectError(
2541 error.PathAlreadyExists,
2542 tmp.dir.createFile(io, "some_file", .{ .exclusive = true }),
2543 );
2544
2545 {
2546 // Try opening without exclusive flag.
2547 const file = try tmp.dir.createFile(io, "some_file", .{});
2548 file.close(io);
2549 }
2550
2551 try expectError(error.NotDir, tmp.dir.openDir(io, "some_file", .{}));
2552 try tmp.dir.makeDir(io, "some_dir", .default_dir);
2553
2554 {
2555 const dir = try tmp.dir.openDir("some_dir", .{});
2556 dir.close(io);
2557 }
2558
2559 // Try opening as file which should fail.
2560 try expectError(error.IsDir, tmp.dir.openFile("some_dir", .{}));
2561}
2562
2563test "hard link with different directories" {
2564 const io = testing.io;
2565
2566 var tmp = tmpDir(.{});
2567 defer tmp.cleanup();
2568
2569 const target_name = "link-target";
2570 const link_name = "newlink";
2571
2572 const subdir = try tmp.dir.makeOpenPath(io, "subdir", .{});
2573
2574 defer tmp.dir.deleteFile(io, target_name) catch {};
2575 try tmp.dir.writeFile(io, .{ .sub_path = target_name, .data = "example" });
2576
2577 // Test 1: link from file in subdir back up to target in parent directory
2578 tmp.dir.hardLink(target_name, subdir, link_name, 0) catch |err| switch (err) {
2579 error.OperationUnsupported => return error.SkipZigTest,
2580 else => |e| return e,
2581 };
2582
2583 const efd = try tmp.dir.openFile(io, target_name, .{});
2584 defer efd.close(io);
2585
2586 const nfd = try subdir.openFile(io, link_name, .{});
2587 defer nfd.close(io);
2588
2589 {
2590 const e_stat = try efd.stat(io);
2591 const n_stat = try nfd.stat(io);
2592
2593 try expectEqual(e_stat.inode, n_stat.inode);
2594 try expectEqual(2, e_stat.nlink);
2595 try expectEqual(2, n_stat.nlink);
2596 }
2597
2598 // Test 2: remove link
2599 try subdir.deleteFile(io, link_name, .{});
2600 const e_stat = try efd.stat(io);
2601 try expectEqual(1, e_stat.nlink);
2602}
2603
2604test "stat smoke test" {
2605 if (native_os == .wasi and !builtin.link_libc) return error.SkipZigTest;
2606
2607 const io = testing.io;
2608
2609 var tmp = tmpDir(.{});
2610 defer tmp.cleanup();
2611
2612 // create dummy file
2613 const contents = "nonsense";
2614 try tmp.dir.writeFile(io, .{ .sub_path = "file.txt", .data = contents });
2615
2616 // fetch file's info on the opened fd directly
2617 const file = try tmp.dir.openFile(io, "file.txt", .{});
2618 const stat = try file.stat(io);
2619 defer file.close(io);
2620
2621 // now repeat but using directory handle instead
2622 const statat = try tmp.dir.statFile(io, "file.txt", .{ .follow_symlinks = false });
2623
2624 try expectEqual(stat.inode, statat.inode);
2625 try expectEqual(stat.nlink, statat.nlink);
2626 try expectEqual(stat.size, statat.size);
2627 try expectEqual(stat.permissions, statat.permissions);
2628 try expectEqual(stat.kind, statat.kind);
2629 try expectEqual(stat.atime, statat.atime);
2630 try expectEqual(stat.mtime, statat.mtime);
2631 try expectEqual(stat.ctime, statat.ctime);
2632}
lib/std/posix.zig-157
...@@ -1034,163 +1034,6 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {...@@ -1034,163 +1034,6 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {
1034 }1034 }
1035}1035}
10361036
1037pub const LinkError = UnexpectedError || error{
1038 AccessDenied,
1039 PermissionDenied,
1040 DiskQuota,
1041 PathAlreadyExists,
1042 FileSystem,
1043 SymLinkLoop,
1044 LinkQuotaExceeded,
1045 NameTooLong,
1046 FileNotFound,
1047 SystemResources,
1048 NoSpaceLeft,
1049 ReadOnlyFileSystem,
1050 NotSameFileSystem,
1051 BadPathName,
1052};
1053
1054/// On WASI, both paths should be encoded as valid UTF-8.
1055/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
1056pub fn linkZ(oldpath: [*:0]const u8, newpath: [*:0]const u8) LinkError!void {
1057 if (native_os == .wasi and !builtin.link_libc) {
1058 return link(mem.sliceTo(oldpath, 0), mem.sliceTo(newpath, 0));
1059 }
1060 switch (errno(system.link(oldpath, newpath))) {
1061 .SUCCESS => return,
1062 .ACCES => return error.AccessDenied,
1063 .DQUOT => return error.DiskQuota,
1064 .EXIST => return error.PathAlreadyExists,
1065 .FAULT => unreachable,
1066 .IO => return error.FileSystem,
1067 .LOOP => return error.SymLinkLoop,
1068 .MLINK => return error.LinkQuotaExceeded,
1069 .NAMETOOLONG => return error.NameTooLong,
1070 .NOENT => return error.FileNotFound,
1071 .NOMEM => return error.SystemResources,
1072 .NOSPC => return error.NoSpaceLeft,
1073 .PERM => return error.PermissionDenied,
1074 .ROFS => return error.ReadOnlyFileSystem,
1075 .XDEV => return error.NotSameFileSystem,
1076 .INVAL => unreachable,
1077 .ILSEQ => return error.BadPathName,
1078 else => |err| return unexpectedErrno(err),
1079 }
1080}
1081
1082/// On WASI, both paths should be encoded as valid UTF-8.
1083/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
1084pub fn link(oldpath: []const u8, newpath: []const u8) LinkError!void {
1085 if (native_os == .wasi and !builtin.link_libc) {
1086 return linkat(AT.FDCWD, oldpath, AT.FDCWD, newpath, 0) catch |err| switch (err) {
1087 error.NotDir => unreachable, // link() does not support directories
1088 else => |e| return e,
1089 };
1090 }
1091 const old = try toPosixPath(oldpath);
1092 const new = try toPosixPath(newpath);
1093 return try linkZ(&old, &new);
1094}
1095
1096pub const LinkatError = LinkError || error{NotDir};
1097
1098/// On WASI, both paths should be encoded as valid UTF-8.
1099/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
1100pub fn linkatZ(
1101 olddir: fd_t,
1102 oldpath: [*:0]const u8,
1103 newdir: fd_t,
1104 newpath: [*:0]const u8,
1105 flags: i32,
1106) LinkatError!void {
1107 if (native_os == .wasi and !builtin.link_libc) {
1108 return linkat(olddir, mem.sliceTo(oldpath, 0), newdir, mem.sliceTo(newpath, 0), flags);
1109 }
1110 switch (errno(system.linkat(olddir, oldpath, newdir, newpath, flags))) {
1111 .SUCCESS => return,
1112 .ACCES => return error.AccessDenied,
1113 .DQUOT => return error.DiskQuota,
1114 .EXIST => return error.PathAlreadyExists,
1115 .FAULT => unreachable,
1116 .IO => return error.FileSystem,
1117 .LOOP => return error.SymLinkLoop,
1118 .MLINK => return error.LinkQuotaExceeded,
1119 .NAMETOOLONG => return error.NameTooLong,
1120 .NOENT => return error.FileNotFound,
1121 .NOMEM => return error.SystemResources,
1122 .NOSPC => return error.NoSpaceLeft,
1123 .NOTDIR => return error.NotDir,
1124 .PERM => return error.PermissionDenied,
1125 .ROFS => return error.ReadOnlyFileSystem,
1126 .XDEV => return error.NotSameFileSystem,
1127 .INVAL => unreachable,
1128 .ILSEQ => return error.BadPathName,
1129 else => |err| return unexpectedErrno(err),
1130 }
1131}
1132
1133/// On WASI, both paths should be encoded as valid UTF-8.
1134/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
1135pub fn linkat(
1136 olddir: fd_t,
1137 oldpath: []const u8,
1138 newdir: fd_t,
1139 newpath: []const u8,
1140 flags: i32,
1141) LinkatError!void {
1142 if (native_os == .wasi and !builtin.link_libc) {
1143 const old: RelativePathWasi = .{ .dir_fd = olddir, .relative_path = oldpath };
1144 const new: RelativePathWasi = .{ .dir_fd = newdir, .relative_path = newpath };
1145 const old_flags: wasi.lookupflags_t = .{
1146 .SYMLINK_FOLLOW = (flags & AT.SYMLINK_FOLLOW) != 0,
1147 };
1148 switch (wasi.path_link(
1149 old.dir_fd,
1150 old_flags,
1151 old.relative_path.ptr,
1152 old.relative_path.len,
1153 new.dir_fd,
1154 new.relative_path.ptr,
1155 new.relative_path.len,
1156 )) {
1157 .SUCCESS => return,
1158 .ACCES => return error.AccessDenied,
1159 .DQUOT => return error.DiskQuota,
1160 .EXIST => return error.PathAlreadyExists,
1161 .FAULT => unreachable,
1162 .IO => return error.FileSystem,
1163 .LOOP => return error.SymLinkLoop,
1164 .MLINK => return error.LinkQuotaExceeded,
1165 .NAMETOOLONG => return error.NameTooLong,
1166 .NOENT => return error.FileNotFound,
1167 .NOMEM => return error.SystemResources,
1168 .NOSPC => return error.NoSpaceLeft,
1169 .NOTDIR => return error.NotDir,
1170 .PERM => return error.PermissionDenied,
1171 .ROFS => return error.ReadOnlyFileSystem,
1172 .XDEV => return error.NotSameFileSystem,
1173 .INVAL => unreachable,
1174 .ILSEQ => return error.BadPathName,
1175 else => |err| return unexpectedErrno(err),
1176 }
1177 }
1178 const old = try toPosixPath(oldpath);
1179 const new = try toPosixPath(newpath);
1180 return try linkatZ(olddir, &old, newdir, &new, flags);
1181}
1182
1183/// An fd-relative file path
1184///
1185/// This is currently only used for WASI-specific functionality, but the concept
1186/// is the same as the dirfd/pathname pairs in the `*at(...)` POSIX functions.
1187const RelativePathWasi = struct {
1188 /// Handle to directory
1189 dir_fd: fd_t,
1190 /// Path to resource within `dir_fd`.
1191 relative_path: []const u8,
1192};
1193
1194/// On Windows, `sub_dir_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).1037/// On Windows, `sub_dir_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
1195/// On WASI, `sub_dir_path` should be encoded as valid UTF-8.1038/// On WASI, `sub_dir_path` should be encoded as valid UTF-8.
1196/// On other platforms, `sub_dir_path` is an opaque sequence of bytes with no particular encoding.1039/// On other platforms, `sub_dir_path` is an opaque sequence of bytes with no particular encoding.
lib/std/posix/test.zig+54-368
...@@ -1,25 +1,24 @@...@@ -1,25 +1,24 @@
1const builtin = @import("builtin");1const builtin = @import("builtin");
2const native_os = builtin.target.os.tag;2const native_os = builtin.target.os.tag;
3const AtomicRmwOp = std.builtin.AtomicRmwOp;
4const AtomicOrder = std.builtin.AtomicOrder;
35
4const std = @import("../std.zig");6const std = @import("../std.zig");
5const Io = std.Io;7const Io = std.Io;
8const Dir = std.Io.Dir;
6const posix = std.posix;9const posix = std.posix;
10const mem = std.mem;
11const elf = std.elf;
12const linux = std.os.linux;
13const AT = std.posix.AT;
14
7const testing = std.testing;15const testing = std.testing;
8const expect = std.testing.expect;16const expect = std.testing.expect;
9const expectEqual = std.testing.expectEqual;17const expectEqual = std.testing.expectEqual;
18const expectEqualSlices = std.testing.expectEqualSlices;
19const expectEqualStrings = std.testing.expectEqualStrings;
10const expectError = std.testing.expectError;20const expectError = std.testing.expectError;
11const fs = std.fs;
12const mem = std.mem;
13const elf = std.elf;
14const linux = std.os.linux;
15const a = std.testing.allocator;
16const AtomicRmwOp = std.builtin.AtomicRmwOp;
17const AtomicOrder = std.builtin.AtomicOrder;
18const tmpDir = std.testing.tmpDir;21const tmpDir = std.testing.tmpDir;
19const AT = posix.AT;
20
21// NOTE: several additional tests are in test/standalone/posix/. Any tests that mutate
22// process-wide POSIX state (cwd, signals, etc) cannot be Zig unit tests and should be over there.
2322
24// https://github.com/ziglang/zig/issues/2028823// https://github.com/ziglang/zig/issues/20288
25test "WTF-8 to WTF-16 conversion buffer overflows" {24test "WTF-8 to WTF-16 conversion buffer overflows" {
...@@ -43,165 +42,6 @@ test "check WASI CWD" {...@@ -43,165 +42,6 @@ test "check WASI CWD" {
43 }42 }
44}43}
4544
46test "open smoke test" {
47 if (native_os == .wasi) return error.SkipZigTest;
48 if (native_os == .windows) return error.SkipZigTest;
49 if (native_os == .openbsd) return error.SkipZigTest;
50
51 // TODO verify file attributes using `fstat`
52
53 var tmp = tmpDir(.{});
54 defer tmp.cleanup();
55
56 const base_path = try tmp.dir.realpathAlloc(a, ".");
57 defer a.free(base_path);
58
59 const mode: posix.mode_t = if (native_os == .windows) 0 else 0o666;
60
61 {
62 // Create some file using `open`.
63 const file_path = try fs.path.join(a, &.{ base_path, "some_file" });
64 defer a.free(file_path);
65 const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode);
66 posix.close(fd);
67 }
68
69 {
70 // Try this again with the same flags. This op should fail with error.PathAlreadyExists.
71 const file_path = try fs.path.join(a, &.{ base_path, "some_file" });
72 defer a.free(file_path);
73 try expectError(error.PathAlreadyExists, posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode));
74 }
75
76 {
77 // Try opening without `EXCL` flag.
78 const file_path = try fs.path.join(a, &.{ base_path, "some_file" });
79 defer a.free(file_path);
80 const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true }, mode);
81 posix.close(fd);
82 }
83
84 {
85 // Try opening as a directory which should fail.
86 const file_path = try fs.path.join(a, &.{ base_path, "some_file" });
87 defer a.free(file_path);
88 try expectError(error.NotDir, posix.open(file_path, .{ .ACCMODE = .RDWR, .DIRECTORY = true }, mode));
89 }
90
91 {
92 // Create some directory
93 const file_path = try fs.path.join(a, &.{ base_path, "some_dir" });
94 defer a.free(file_path);
95 try posix.mkdir(file_path, mode);
96 }
97
98 {
99 // Open dir using `open`
100 const file_path = try fs.path.join(a, &.{ base_path, "some_dir" });
101 defer a.free(file_path);
102 const fd = try posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode);
103 posix.close(fd);
104 }
105
106 {
107 // Try opening as file which should fail.
108 const file_path = try fs.path.join(a, &.{ base_path, "some_dir" });
109 defer a.free(file_path);
110 try expectError(error.IsDir, posix.open(file_path, .{ .ACCMODE = .RDWR }, mode));
111 }
112}
113
114fn getLinkInfo(fd: posix.fd_t) !struct { posix.ino_t, posix.nlink_t } {
115 if (native_os == .linux) {
116 const stx = try linux.wrapped.statx(
117 fd,
118 "",
119 posix.AT.EMPTY_PATH,
120 .{ .INO = true, .NLINK = true },
121 );
122 std.debug.assert(stx.mask.INO);
123 std.debug.assert(stx.mask.NLINK);
124 return .{ stx.ino, stx.nlink };
125 }
126
127 const st = try posix.fstat(fd);
128 return .{ st.ino, st.nlink };
129}
130
131test "linkat with different directories" {
132 switch (native_os) {
133 .wasi, .linux, .illumos => {},
134 else => return error.SkipZigTest,
135 }
136
137 const io = testing.io;
138
139 var tmp = tmpDir(.{});
140 defer tmp.cleanup();
141
142 const target_name = "link-target";
143 const link_name = "newlink";
144
145 const subdir = try tmp.dir.makeOpenPath(io, "subdir", .{});
146
147 defer tmp.dir.deleteFile(io, target_name) catch {};
148 try tmp.dir.writeFile(io, .{ .sub_path = target_name, .data = "example" });
149
150 // Test 1: link from file in subdir back up to target in parent directory
151 try posix.linkat(tmp.dir.handle, target_name, subdir.handle, link_name, 0);
152
153 const efd = try tmp.dir.openFile(io, target_name, .{});
154 defer efd.close(io);
155
156 const nfd = try subdir.openFile(io, link_name, .{});
157 defer nfd.close(io);
158
159 {
160 const eino, _ = try getLinkInfo(efd.handle);
161 const nino, const nlink = try getLinkInfo(nfd.handle);
162 try testing.expectEqual(eino, nino);
163 try testing.expectEqual(@as(posix.nlink_t, 2), nlink);
164 }
165
166 // Test 2: remove link
167 try posix.unlinkat(subdir.handle, link_name, 0);
168 _, const elink = try getLinkInfo(efd.handle);
169 try testing.expectEqual(@as(posix.nlink_t, 1), elink);
170}
171
172test "fstatat" {
173 if (posix.Stat == void) return error.SkipZigTest;
174 if (native_os == .wasi and !builtin.link_libc) return error.SkipZigTest;
175
176 var tmp = tmpDir(.{});
177 defer tmp.cleanup();
178
179 // create dummy file
180 const contents = "nonsense";
181 try tmp.dir.writeFile(.{ .sub_path = "file.txt", .data = contents });
182
183 // fetch file's info on the opened fd directly
184 const file = try tmp.dir.openFile("file.txt", .{});
185 const stat = try posix.fstat(file.handle);
186 defer file.close();
187
188 // now repeat but using `fstatat` instead
189 const statat = try posix.fstatat(tmp.dir.fd, "file.txt", posix.AT.SYMLINK_NOFOLLOW);
190
191 try expectEqual(stat.dev, statat.dev);
192 try expectEqual(stat.ino, statat.ino);
193 try expectEqual(stat.nlink, statat.nlink);
194 try expectEqual(stat.mode, statat.mode);
195 try expectEqual(stat.uid, statat.uid);
196 try expectEqual(stat.gid, statat.gid);
197 try expectEqual(stat.rdev, statat.rdev);
198 try expectEqual(stat.size, statat.size);
199 try expectEqual(stat.blksize, statat.blksize);
200 // The stat.blocks/statat.blocks count is managed by the filesystem and may
201 // change if the file is stored in a journal or "inline".
202 // try expectEqual(stat.blocks, statat.blocks);
203}
204
205test "getrandom" {45test "getrandom" {
206 var buf_a: [50]u8 = undefined;46 var buf_a: [50]u8 = undefined;
207 var buf_b: [50]u8 = undefined;47 var buf_b: [50]u8 = undefined;
...@@ -232,7 +72,7 @@ test "sigaltstack" {...@@ -232,7 +72,7 @@ test "sigaltstack" {
232 // Setting a stack size less than MINSIGSTKSZ returns ENOMEM72 // Setting a stack size less than MINSIGSTKSZ returns ENOMEM
233 st.flags = 0;73 st.flags = 0;
234 st.size = 1;74 st.size = 1;
235 try testing.expectError(error.SizeTooSmall, posix.sigaltstack(&st, null));75 try expectError(error.SizeTooSmall, posix.sigaltstack(&st, null));
236}76}
23777
238// If the type is not available use void to avoid erroring out when `iter_fn` is78// If the type is not available use void to avoid erroring out when `iter_fn` is
...@@ -304,7 +144,7 @@ test "pipe" {...@@ -304,7 +144,7 @@ test "pipe" {
304 try expect((try posix.write(fds[1], "hello")) == 5);144 try expect((try posix.write(fds[1], "hello")) == 5);
305 var buf: [16]u8 = undefined;145 var buf: [16]u8 = undefined;
306 try expect((try posix.read(fds[0], buf[0..])) == 5);146 try expect((try posix.read(fds[0], buf[0..])) == 5);
307 try testing.expectEqualSlices(u8, buf[0..5], "hello");147 try expectEqualSlices(u8, buf[0..5], "hello");
308 posix.close(fds[1]);148 posix.close(fds[1]);
309 posix.close(fds[0]);149 posix.close(fds[0]);
310}150}
...@@ -315,6 +155,8 @@ test "argsAlloc" {...@@ -315,6 +155,8 @@ test "argsAlloc" {
315}155}
316156
317test "memfd_create" {157test "memfd_create" {
158 const io = testing.io;
159
318 // memfd_create is only supported by linux and freebsd.160 // memfd_create is only supported by linux and freebsd.
319 switch (native_os) {161 switch (native_os) {
320 .linux => {},162 .linux => {},
...@@ -325,15 +167,14 @@ test "memfd_create" {...@@ -325,15 +167,14 @@ test "memfd_create" {
325 else => return error.SkipZigTest,167 else => return error.SkipZigTest,
326 }168 }
327169
328 const fd = try posix.memfd_create("test", 0);170 const file: Io.File = .{ .handle = try posix.memfd_create("test", 0) };
329 defer posix.close(fd);171 defer file.close(io);
330 try expect((try posix.write(fd, "test")) == 4);172 try file.writePositionalAll(io, "test", 0);
331 try posix.lseek_SET(fd, 0);
332173
333 var buf: [10]u8 = undefined;174 var buf: [10]u8 = undefined;
334 const bytes_read = try posix.read(fd, &buf);175 const bytes_read = try file.readPositionalAll(io, &buf, 0);
335 try expect(bytes_read == 4);176 try expect(bytes_read == 4);
336 try expect(mem.eql(u8, buf[0..4], "test"));177 try expectEqualStrings("test", buf[0..4]);
337}178}
338179
339test "mmap" {180test "mmap" {
...@@ -357,14 +198,14 @@ test "mmap" {...@@ -357,14 +198,14 @@ test "mmap" {
357 );198 );
358 defer posix.munmap(data);199 defer posix.munmap(data);
359200
360 try testing.expectEqual(@as(usize, 1234), data.len);201 try expectEqual(@as(usize, 1234), data.len);
361202
362 // By definition the data returned by mmap is zero-filled203 // By definition the data returned by mmap is zero-filled
363 try testing.expect(mem.eql(u8, data, &[_]u8{0x00} ** 1234));204 try expect(mem.eql(u8, data, &[_]u8{0x00} ** 1234));
364205
365 // Make sure the memory is writeable as requested206 // Make sure the memory is writeable as requested
366 @memset(data, 0x55);207 @memset(data, 0x55);
367 try testing.expect(mem.eql(u8, data, &[_]u8{0x55} ** 1234));208 try expect(mem.eql(u8, data, &[_]u8{0x55} ** 1234));
368 }209 }
369210
370 const test_out_file = "os_tmp_test";211 const test_out_file = "os_tmp_test";
...@@ -403,7 +244,7 @@ test "mmap" {...@@ -403,7 +244,7 @@ test "mmap" {
403244
404 var i: usize = 0;245 var i: usize = 0;
405 while (i < alloc_size / @sizeOf(u32)) : (i += 1) {246 while (i < alloc_size / @sizeOf(u32)) : (i += 1) {
406 try testing.expectEqual(i, try stream.takeInt(u32, .little));247 try expectEqual(i, try stream.takeInt(u32, .little));
407 }248 }
408 }249 }
409250
...@@ -428,7 +269,7 @@ test "mmap" {...@@ -428,7 +269,7 @@ test "mmap" {
428269
429 var i: usize = alloc_size / 2 / @sizeOf(u32);270 var i: usize = alloc_size / 2 / @sizeOf(u32);
430 while (i < alloc_size / @sizeOf(u32)) : (i += 1) {271 while (i < alloc_size / @sizeOf(u32)) : (i += 1) {
431 try testing.expectEqual(i, try stream.takeInt(u32, .little));272 try expectEqual(i, try stream.takeInt(u32, .little));
432 }273 }
433 }274 }
434}275}
...@@ -498,7 +339,7 @@ test "fsync" {...@@ -498,7 +339,7 @@ test "fsync" {
498 const file = try tmp.dir.createFile(io, test_out_file, .{});339 const file = try tmp.dir.createFile(io, test_out_file, .{});
499 defer file.close(io);340 defer file.close(io);
500341
501 try posix.fsync(file.handle);342 try file.sync(io);
502 try posix.fdatasync(file.handle);343 try posix.fdatasync(file.handle);
503}344}
504345
...@@ -536,9 +377,9 @@ test "sigrtmin/max" {...@@ -536,9 +377,9 @@ test "sigrtmin/max" {
536 return error.SkipZigTest;377 return error.SkipZigTest;
537 }378 }
538379
539 try std.testing.expect(posix.sigrtmin() >= 32);380 try expect(posix.sigrtmin() >= 32);
540 try std.testing.expect(posix.sigrtmin() >= posix.system.sigrtmin());381 try expect(posix.sigrtmin() >= posix.system.sigrtmin());
541 try std.testing.expect(posix.sigrtmin() < posix.system.sigrtmax());382 try expect(posix.sigrtmin() < posix.system.sigrtmax());
542}383}
543384
544test "sigset empty/full" {385test "sigset empty/full" {
...@@ -622,18 +463,18 @@ test "dup & dup2" {...@@ -622,18 +463,18 @@ test "dup & dup2" {
622463
623 var duped = Io.File{ .handle = try posix.dup(file.handle) };464 var duped = Io.File{ .handle = try posix.dup(file.handle) };
624 defer duped.close(io);465 defer duped.close(io);
625 try duped.writeAll("dup");466 try duped.writeStreamingAll(io, "dup");
626467
627 // Tests aren't run in parallel so using the next fd shouldn't be an issue.468 // Tests aren't run in parallel so using the next fd shouldn't be an issue.
628 const new_fd = duped.handle + 1;469 const new_fd = duped.handle + 1;
629 try posix.dup2(file.handle, new_fd);470 try posix.dup2(file.handle, new_fd);
630 var dup2ed = Io.File{ .handle = new_fd };471 var dup2ed = Io.File{ .handle = new_fd };
631 defer dup2ed.close(io);472 defer dup2ed.close(io);
632 try dup2ed.writeAll("dup2");473 try dup2ed.writeStreamingAll(io, "dup2");
633 }474 }
634475
635 var buffer: [8]u8 = undefined;476 var buffer: [8]u8 = undefined;
636 try testing.expectEqualStrings("dupdup2", try tmp.dir.readFile("os_dup_test", &buffer));477 try expectEqualStrings("dupdup2", try tmp.dir.readFile(io, "os_dup_test", &buffer));
637}478}
638479
639test "getpid" {480test "getpid" {
...@@ -651,147 +492,78 @@ test "getppid" {...@@ -651,147 +492,78 @@ test "getppid" {
651 try expect(posix.getppid() >= 0);492 try expect(posix.getppid() >= 0);
652}493}
653494
654test "writev longer than IOV_MAX" {
655 if (native_os == .windows or native_os == .wasi) return error.SkipZigTest;
656
657 const io = testing.io;
658
659 var tmp = tmpDir(.{});
660 defer tmp.cleanup();
661
662 var file = try tmp.dir.createFile(io, "pwritev", .{});
663 defer file.close(io);
664
665 const iovecs = [_]posix.iovec_const{.{ .base = "a", .len = 1 }} ** (posix.IOV_MAX + 1);
666 const amt = try file.writev(&iovecs);
667 try testing.expectEqual(@as(usize, posix.IOV_MAX), amt);
668}
669
670test "rename smoke test" {495test "rename smoke test" {
671 if (native_os == .wasi) return error.SkipZigTest;496 if (native_os == .wasi) return error.SkipZigTest;
672 if (native_os == .windows) return error.SkipZigTest;497 if (native_os == .windows) return error.SkipZigTest;
673 if (native_os == .openbsd) return error.SkipZigTest;498 if (native_os == .openbsd) return error.SkipZigTest;
674499
500 const io = testing.io;
501 const gpa = testing.allocator;
502
675 var tmp = tmpDir(.{});503 var tmp = tmpDir(.{});
676 defer tmp.cleanup();504 defer tmp.cleanup();
677505
678 const base_path = try tmp.dir.realpathAlloc(a, ".");506 const base_path = try tmp.dir.realPathAlloc(io, ".", gpa);
679 defer a.free(base_path);507 defer gpa.free(base_path);
680508
681 const mode: posix.mode_t = if (native_os == .windows) 0 else 0o666;509 const mode: posix.mode_t = if (native_os == .windows) 0 else 0o666;
682510
683 {511 {
684 // Create some file using `open`.512 // Create some file using `open`.
685 const file_path = try fs.path.join(a, &.{ base_path, "some_file" });513 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_file" });
686 defer a.free(file_path);514 defer gpa.free(file_path);
687 const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode);515 const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode);
688 posix.close(fd);516 posix.close(fd);
689517
690 // Rename the file518 // Rename the file
691 const new_file_path = try fs.path.join(a, &.{ base_path, "some_other_file" });519 const new_file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_file" });
692 defer a.free(new_file_path);520 defer gpa.free(new_file_path);
693 try Io.Dir.renameAbsolute(file_path, new_file_path);521 try Io.Dir.renameAbsolute(io, file_path, new_file_path);
694 }522 }
695523
696 {524 {
697 // Try opening renamed file525 // Try opening renamed file
698 const file_path = try fs.path.join(a, &.{ base_path, "some_other_file" });526 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_file" });
699 defer a.free(file_path);527 defer gpa.free(file_path);
700 const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR }, mode);528 const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR }, mode);
701 posix.close(fd);529 posix.close(fd);
702 }530 }
703531
704 {532 {
705 // Try opening original file - should fail with error.FileNotFound533 // Try opening original file - should fail with error.FileNotFound
706 const file_path = try fs.path.join(a, &.{ base_path, "some_file" });534 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_file" });
707 defer a.free(file_path);535 defer gpa.free(file_path);
708 try expectError(error.FileNotFound, posix.open(file_path, .{ .ACCMODE = .RDWR }, mode));536 try expectError(error.FileNotFound, posix.open(file_path, .{ .ACCMODE = .RDWR }, mode));
709 }537 }
710538
711 {539 {
712 // Create some directory540 // Create some directory
713 const file_path = try fs.path.join(a, &.{ base_path, "some_dir" });541 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_dir" });
714 defer a.free(file_path);542 defer gpa.free(file_path);
715 try posix.mkdir(file_path, mode);543 try posix.mkdir(file_path, mode);
716544
717 // Rename the directory545 // Rename the directory
718 const new_file_path = try fs.path.join(a, &.{ base_path, "some_other_dir" });546 const new_file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_dir" });
719 defer a.free(new_file_path);547 defer gpa.free(new_file_path);
720 try Io.Dir.renameAbsolute(file_path, new_file_path);548 try Io.Dir.renameAbsolute(io, file_path, new_file_path);
721 }549 }
722550
723 {551 {
724 // Try opening renamed directory552 // Try opening renamed directory
725 const file_path = try fs.path.join(a, &.{ base_path, "some_other_dir" });553 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_dir" });
726 defer a.free(file_path);554 defer gpa.free(file_path);
727 const fd = try posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode);555 const fd = try posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode);
728 posix.close(fd);556 posix.close(fd);
729 }557 }
730558
731 {559 {
732 // Try opening original directory - should fail with error.FileNotFound560 // Try opening original directory - should fail with error.FileNotFound
733 const file_path = try fs.path.join(a, &.{ base_path, "some_dir" });561 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_dir" });
734 defer a.free(file_path);562 defer gpa.free(file_path);
735 try expectError(error.FileNotFound, posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode));563 try expectError(error.FileNotFound, posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode));
736 }564 }
737}565}
738566
739test "access smoke test" {
740 if (native_os == .wasi) return error.SkipZigTest;
741 if (native_os == .windows) return error.SkipZigTest;
742 if (native_os == .openbsd) return error.SkipZigTest;
743
744 const io = testing.io;
745
746 var tmp = tmpDir(.{});
747 defer tmp.cleanup();
748
749 const base_path = try tmp.dir.realpathAlloc(a, ".");
750 defer a.free(base_path);
751
752 const mode: posix.mode_t = if (native_os == .windows) 0 else 0o666;
753 {
754 // Create some file using `open`.
755 const file_path = try fs.path.join(a, &.{ base_path, "some_file" });
756 defer a.free(file_path);
757 const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode);
758 posix.close(fd);
759 }
760
761 {
762 // Try to access() the file
763 const file_path = try fs.path.join(a, &.{ base_path, "some_file" });
764 defer a.free(file_path);
765 if (native_os == .windows) {
766 try posix.access(io, file_path, posix.F_OK);
767 } else {
768 try posix.access(io, file_path, posix.F_OK | posix.W_OK | posix.R_OK);
769 }
770 }
771
772 {
773 // Try to access() a non-existent file - should fail with error.FileNotFound
774 const file_path = try fs.path.join(a, &.{ base_path, "some_other_file" });
775 defer a.free(file_path);
776 try expectError(error.FileNotFound, posix.access(io, file_path, posix.F_OK));
777 }
778
779 {
780 // Create some directory
781 const file_path = try fs.path.join(a, &.{ base_path, "some_dir" });
782 defer a.free(file_path);
783 try posix.mkdir(file_path, mode);
784 }
785
786 {
787 // Try to access() the directory
788 const file_path = try fs.path.join(a, &.{ base_path, "some_dir" });
789 defer a.free(file_path);
790
791 try posix.access(io, file_path, posix.F_OK);
792 }
793}
794
795test "timerfd" {567test "timerfd" {
796 if (native_os != .linux) return error.SkipZigTest;568 if (native_os != .linux) return error.SkipZigTest;
797569
...@@ -809,89 +581,3 @@ test "timerfd" {...@@ -809,89 +581,3 @@ test "timerfd" {
809 const expect_disarmed_timer: linux.itimerspec = .{ .it_interval = .{ .sec = 0, .nsec = 0 }, .it_value = .{ .sec = 0, .nsec = 0 } };581 const expect_disarmed_timer: linux.itimerspec = .{ .it_interval = .{ .sec = 0, .nsec = 0 }, .it_value = .{ .sec = 0, .nsec = 0 } };
810 try expectEqual(expect_disarmed_timer, git);582 try expectEqual(expect_disarmed_timer, git);
811}583}
812
813test "isatty" {
814 const io = testing.io;
815
816 var tmp = tmpDir(.{});
817 defer tmp.cleanup();
818
819 var file = try tmp.dir.createFile(io, "foo", .{});
820 defer file.close(io);
821
822 try expectEqual(posix.isatty(file.handle), false);
823}
824
825test "pread with empty buffer" {
826 const io = testing.io;
827
828 var tmp = tmpDir(.{});
829 defer tmp.cleanup();
830
831 var file = try tmp.dir.createFile(io, "pread_empty", .{ .read = true });
832 defer file.close(io);
833
834 const bytes = try a.alloc(u8, 0);
835 defer a.free(bytes);
836
837 const rc = try posix.pread(file.handle, bytes, 0);
838 try expectEqual(rc, 0);
839}
840
841test "write with empty buffer" {
842 const io = testing.io;
843
844 var tmp = tmpDir(.{});
845 defer tmp.cleanup();
846
847 var file = try tmp.dir.createFile(io, "write_empty", .{});
848 defer file.close(io);
849
850 const bytes = try a.alloc(u8, 0);
851 defer a.free(bytes);
852
853 const rc = try posix.write(file.handle, bytes);
854 try expectEqual(rc, 0);
855}
856
857test "pwrite with empty buffer" {
858 const io = testing.io;
859
860 var tmp = tmpDir(.{});
861 defer tmp.cleanup();
862
863 var file = try tmp.dir.createFile(io, "pwrite_empty", .{});
864 defer file.close(io);
865
866 const bytes = try a.alloc(u8, 0);
867 defer a.free(bytes);
868
869 const rc = try posix.pwrite(file.handle, bytes, 0);
870 try expectEqual(rc, 0);
871}
872
873const CommonOpenFlags = packed struct {
874 ACCMODE: posix.ACCMODE = .RDONLY,
875 CREAT: bool = false,
876 EXCL: bool = false,
877 LARGEFILE: bool = false,
878 DIRECTORY: bool = false,
879 CLOEXEC: bool = false,
880 NONBLOCK: bool = false,
881
882 pub fn lower(cof: CommonOpenFlags) posix.O {
883 var result: posix.O = if (native_os == .wasi) .{
884 .read = cof.ACCMODE != .WRONLY,
885 .write = cof.ACCMODE != .RDONLY,
886 } else .{
887 .ACCMODE = cof.ACCMODE,
888 };
889 result.CREAT = cof.CREAT;
890 result.EXCL = cof.EXCL;
891 result.DIRECTORY = cof.DIRECTORY;
892 result.NONBLOCK = cof.NONBLOCK;
893 if (@hasField(posix.O, "CLOEXEC")) result.CLOEXEC = cof.CLOEXEC;
894 if (@hasField(posix.O, "LARGEFILE")) result.LARGEFILE = cof.LARGEFILE;
895 return result;
896 }
897};
lib/std/zig/parser_test.zig+7-6
...@@ -6335,23 +6335,24 @@ fn testParse(io: Io, source: [:0]const u8, allocator: Allocator, anything_change...@@ -6335,23 +6335,24 @@ fn testParse(io: Io, source: [:0]const u8, allocator: Allocator, anything_change
6335 var buffer: [64]u8 = undefined;6335 var buffer: [64]u8 = undefined;
6336 const stderr = try io.lockStderr(&buffer, null);6336 const stderr = try io.lockStderr(&buffer, null);
6337 defer io.unlockStderr();6337 defer io.unlockStderr();
6338 const writer = &stderr.file_writer.interface;
63386339
6339 var tree = try std.zig.Ast.parse(allocator, source, .zig);6340 var tree = try std.zig.Ast.parse(allocator, source, .zig);
6340 defer tree.deinit(allocator);6341 defer tree.deinit(allocator);
63416342
6342 for (tree.errors) |parse_error| {6343 for (tree.errors) |parse_error| {
6343 const loc = tree.tokenLocation(0, parse_error.token);6344 const loc = tree.tokenLocation(0, parse_error.token);
6344 try stderr.writer.print("(memory buffer):{d}:{d}: error: ", .{ loc.line + 1, loc.column + 1 });6345 try writer.print("(memory buffer):{d}:{d}: error: ", .{ loc.line + 1, loc.column + 1 });
6345 try tree.renderError(parse_error, stderr.writer);6346 try tree.renderError(parse_error, writer);
6346 try stderr.writer.print("\n{s}\n", .{source[loc.line_start..loc.line_end]});6347 try writer.print("\n{s}\n", .{source[loc.line_start..loc.line_end]});
6347 {6348 {
6348 var i: usize = 0;6349 var i: usize = 0;
6349 while (i < loc.column) : (i += 1) {6350 while (i < loc.column) : (i += 1) {
6350 try stderr.writer.writeAll(" ");6351 try writer.writeAll(" ");
6351 }6352 }
6352 try stderr.writer.writeAll("^");6353 try writer.writeAll("^");
6353 }6354 }
6354 try stderr.writer.writeAll("\n");6355 try writer.writeAll("\n");
6355 }6356 }
6356 if (tree.errors.len != 0) {6357 if (tree.errors.len != 0) {
6357 return error.ParseError;6358 return error.ParseError;