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;
33
44const std = @import("../std.zig");
55const Io = std.Io;
6const fs = std.fs;
76const mem = std.mem;
87const wasi = std.os.wasi;
98const windows = std.os.windows;
......@@ -14,11 +13,11 @@ const SymLinkFlags = std.Io.Dir.SymLinkFlags;
1413
1514const testing = std.testing;
1615const expect = std.testing.expect;
17const expectError = std.testing.expectError;
1816const expectEqual = std.testing.expectEqual;
19const tmpDir = std.testing.tmpDir;
20const expectEqualStrings = std.testing.expectEqualStrings;
2117const expectEqualSlices = std.testing.expectEqualSlices;
18const expectEqualStrings = std.testing.expectEqualStrings;
19const expectError = std.testing.expectError;
20const tmpDir = std.testing.tmpDir;
2221
2322const PathType = enum {
2423 relative,
......@@ -33,7 +32,7 @@ const PathType = enum {
3332 };
3433 }
3534
36 pub const TransformError = Io.Dir.RealPathError || error{OutOfMemory};
35 pub const TransformError = Dir.RealPathError || error{OutOfMemory};
3736 pub const TransformFn = fn (allocator: mem.Allocator, dir: Dir, relative_path: [:0]const u8) TransformError![:0]const u8;
3837
3938 pub fn getTransformFn(comptime path_type: PathType) TransformFn {
......@@ -49,24 +48,24 @@ const PathType = enum {
4948 fn transform(allocator: mem.Allocator, dir: Dir, relative_path: [:0]const u8) TransformError![:0]const u8 {
5049 // The final path may not actually exist which would cause realpath to fail.
5150 // 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;
5352 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 });
5554 }
5655 }.transform,
5756 .unc => return struct {
5857 fn transform(allocator: mem.Allocator, dir: Dir, relative_path: [:0]const u8) TransformError![:0]const u8 {
5958 // Any drive absolute path (C:\foo) can be converted into a UNC path by
6059 // 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;
6261 const dir_path = try std.os.getFdPath(dir.handle, &fd_path_buf);
6362 const windows_path_type = windows.getWin32PathType(u8, dir_path);
6463 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 }),
6665 .drive_absolute => {
6766 // `C:\<...>` -> `\\127.0.0.1\C$\<...>`
6867 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 });
7069 path[prepended.len + 1] = '$';
7170 return path;
7271 },
......@@ -84,7 +83,7 @@ const TestContext = struct {
8483 path_sep: u8,
8584 arena: ArenaAllocator,
8685 tmp: testing.TmpDir,
87 dir: Io.Dir,
86 dir: Dir,
8887 transform_fn: *const PathType.TransformFn,
8988
9089 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 {
154153
155154fn testWithPathTypeIfSupported(comptime path_type: PathType, comptime path_sep: u8, test_func: anytype) !void {
156155 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
159158 var ctx = TestContext.init(path_type, path_sep, testing.allocator, path_type.getTransformFn());
160159 defer ctx.deinit();
......@@ -174,8 +173,8 @@ fn setupSymlink(io: Io, dir: Dir, target: []const u8, link: []const u8, flags: S
174173
175174// For use in test setup. If the symlink creation fails on Windows with
176175// AccessDenied, then make the test failure silent (it is not a Zig failure).
177fn setupSymlinkAbsolute(target: []const u8, link: []const u8, flags: SymLinkFlags) !void {
178 return fs.symLinkAbsolute(target, link, flags) catch |err| switch (err) {
176fn setupSymlinkAbsolute(io: Io, target: []const u8, link: []const u8, flags: SymLinkFlags) !void {
177 return Dir.symLinkAbsolute(io, target, link, flags) catch |err| switch (err) {
179178 error.AccessDenied => if (native_os == .windows) return error.SkipZigTest else return err,
180179 else => return err,
181180 };
......@@ -211,7 +210,7 @@ test "Dir.readLink" {
211210 }
212211
213212 // 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";
215214 const canonical_parent_file = try ctx.toCanonicalPathSep(parent_file);
216215 var subdir = try ctx.dir.makeOpenPath(io, "subdir", .{});
217216 defer subdir.close(io);
......@@ -251,7 +250,7 @@ test "Dir.readLink on non-symlinks" {
251250}
252251
253252fn 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;
255254 const actual = try dir.readLink(io, symlink_path, &buffer);
256255 try expectEqualStrings(target_path, actual);
257256}
......@@ -267,9 +266,9 @@ fn testReadLinkW(allocator: mem.Allocator, dir: Dir, target_path: []const u8, sy
267266 try expectEqualSlices(u16, target_path_w, actual);
268267}
269268
270fn testReadLinkAbsolute(target_path: []const u8, symlink_path: []const u8) !void {
271 var buffer: [fs.max_path_bytes]u8 = undefined;
272 const given = try fs.readLinkAbsolute(symlink_path, buffer[0..]);
269fn testReadLinkAbsolute(io: Io, target_path: []const u8, symlink_path: []const u8) !void {
270 var buffer: [Dir.max_path_bytes]u8 = undefined;
271 const given = try Dir.readLinkAbsolute(io, symlink_path, buffer[0..]);
273272 try expectEqualStrings(target_path, given);
274273}
275274
......@@ -309,7 +308,7 @@ test "openDir" {
309308 try ctx.dir.makeDir(io, subdir_path, .default_dir);
310309
311310 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 });
313312 var dir = try ctx.dir.openDir(io, dir_path, .{});
314313 defer dir.close(io);
315314 }
......@@ -321,13 +320,16 @@ test "accessAbsolute" {
321320 if (native_os == .wasi) return error.SkipZigTest;
322321 if (native_os == .openbsd) return error.SkipZigTest;
323322
323 const io = testing.io;
324 const gpa = testing.allocator;
325
324326 var tmp = tmpDir(.{});
325327 defer tmp.cleanup();
326328
327 const base_path = try tmp.dir.realpathAlloc(testing.allocator, ".");
328 defer testing.allocator.free(base_path);
329 const base_path = try tmp.dir.realPathAlloc(io, ".", gpa);
330 defer gpa.free(base_path);
329331
330 try fs.accessAbsolute(base_path, .{});
332 try Dir.accessAbsolute(io, base_path, .{});
331333}
332334
333335test "openDirAbsolute" {
......@@ -335,6 +337,7 @@ test "openDirAbsolute" {
335337 if (native_os == .openbsd) return error.SkipZigTest;
336338
337339 const io = testing.io;
340 const gpa = testing.allocator;
338341
339342 var tmp = tmpDir(.{});
340343 defer tmp.cleanup();
......@@ -342,8 +345,8 @@ test "openDirAbsolute" {
342345 const tmp_ino = (try tmp.dir.stat(io)).inode;
343346
344347 try tmp.dir.makeDir(io, "subdir", .default_dir);
345 const sub_path = try tmp.dir.realpathAlloc(testing.allocator, "subdir");
346 defer testing.allocator.free(sub_path);
348 const sub_path = try tmp.dir.realPathAlloc(io, "subdir", gpa);
349 defer gpa.free(sub_path);
347350
348351 // Can open sub_path
349352 var tmp_sub = try Dir.openDirAbsolute(io, sub_path, .{});
......@@ -353,7 +356,7 @@ test "openDirAbsolute" {
353356
354357 {
355358 // 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, ".." });
357360 defer testing.allocator.free(dir_path);
358361
359362 var dir = try Dir.openDirAbsolute(io, dir_path, .{});
......@@ -365,7 +368,7 @@ test "openDirAbsolute" {
365368
366369 {
367370 // 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, "." });
369372 defer testing.allocator.free(dir_path);
370373
371374 var dir = try Dir.openDirAbsolute(io, dir_path, .{});
......@@ -377,7 +380,7 @@ test "openDirAbsolute" {
377380
378381 {
379382 // 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, ".", "..", "." });
381384 defer testing.allocator.free(dir_path);
382385
383386 var dir = try Dir.openDirAbsolute(io, dir_path, .{});
......@@ -391,7 +394,7 @@ test "openDirAbsolute" {
391394test "openDir cwd parent '..'" {
392395 const io = testing.io;
393396
394 var dir = Io.Dir.cwd().openDir(io, "..", .{}) catch |err| {
397 var dir = Dir.cwd().openDir(io, "..", .{}) catch |err| {
395398 if (native_os == .wasi and err == error.PermissionDenied) {
396399 return; // This is okay. WASI disallows escaping from the fs sandbox
397400 }
......@@ -407,6 +410,7 @@ test "openDir non-cwd parent '..'" {
407410 }
408411
409412 const io = testing.io;
413 const gpa = testing.allocator;
410414
411415 var tmp = tmpDir(.{});
412416 defer tmp.cleanup();
......@@ -417,11 +421,11 @@ test "openDir non-cwd parent '..'" {
417421 var dir = try subdir.openDir(io, "..", .{});
418422 defer dir.close(io);
419423
420 const expected_path = try tmp.dir.realpathAlloc(testing.allocator, ".");
421 defer testing.allocator.free(expected_path);
424 const expected_path = try tmp.dir.realPathAlloc(io, ".", gpa);
425 defer gpa.free(expected_path);
422426
423 const actual_path = try dir.realpathAlloc(testing.allocator, ".");
424 defer testing.allocator.free(actual_path);
427 const actual_path = try dir.realPathAlloc(io, ".", gpa);
428 defer gpa.free(actual_path);
425429
426430 try expectEqualStrings(expected_path, actual_path);
427431}
......@@ -440,27 +444,27 @@ test "readLinkAbsolute" {
440444 try tmp.dir.makeDir(io, "subdir", .default_dir);
441445
442446 // Get base abs path
443 var arena = ArenaAllocator.init(testing.allocator);
444 defer arena.deinit();
445 const allocator = arena.allocator();
447 var arena_allocator = ArenaAllocator.init(testing.allocator);
448 defer arena_allocator.deinit();
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
449453 {
450 const target_path = try fs.path.join(allocator, &.{ base_path, "file.txt" });
451 const symlink_path = try fs.path.join(allocator, &.{ base_path, "symlink1" });
454 const target_path = try Dir.path.join(arena, &.{ base_path, "file.txt" });
455 const symlink_path = try Dir.path.join(arena, &.{ base_path, "symlink1" });
452456
453457 // Create symbolic link by path
454 try setupSymlinkAbsolute(target_path, symlink_path, .{});
455 try testReadLinkAbsolute(target_path, symlink_path);
458 try setupSymlinkAbsolute(io, target_path, symlink_path, .{});
459 try testReadLinkAbsolute(io, target_path, symlink_path);
456460 }
457461 {
458 const target_path = try fs.path.join(allocator, &.{ base_path, "subdir" });
459 const symlink_path = try fs.path.join(allocator, &.{ base_path, "symlink2" });
462 const target_path = try Dir.path.join(arena, &.{ base_path, "subdir" });
463 const symlink_path = try Dir.path.join(arena, &.{ base_path, "symlink2" });
460464
461465 // Create symbolic link to a directory by path
462 try setupSymlinkAbsolute(target_path, symlink_path, .{ .is_directory = true });
463 try testReadLinkAbsolute(target_path, symlink_path);
466 try setupSymlinkAbsolute(io, target_path, symlink_path, .{ .is_directory = true });
467 try testReadLinkAbsolute(io, target_path, symlink_path);
464468 }
465469}
466470
......@@ -492,8 +496,8 @@ test "Dir.Iterator" {
492496 }
493497
494498 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 }));
496 try testing.expect(contains(&entries, .{ .name = "some_dir", .kind = .directory }));
499 try expect(contains(&entries, .{ .name = "some_file", .kind = .file }));
500 try expect(contains(&entries, .{ .name = "some_dir", .kind = .directory }));
497501}
498502
499503test "Dir.Iterator many entries" {
......@@ -529,7 +533,7 @@ test "Dir.Iterator many entries" {
529533 i = 0;
530534 while (i < num) : (i += 1) {
531535 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 }));
533537 }
534538}
535539
......@@ -563,8 +567,8 @@ test "Dir.Iterator twice" {
563567 }
564568
565569 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 }));
567 try testing.expect(contains(&entries, .{ .name = "some_dir", .kind = .directory }));
570 try expect(contains(&entries, .{ .name = "some_file", .kind = .file }));
571 try expect(contains(&entries, .{ .name = "some_dir", .kind = .directory }));
568572 }
569573}
570574
......@@ -599,8 +603,8 @@ test "Dir.Iterator reset" {
599603 }
600604
601605 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 }));
603 try testing.expect(contains(&entries, .{ .name = "some_dir", .kind = .directory }));
606 try expect(contains(&entries, .{ .name = "some_file", .kind = .file }));
607 try expect(contains(&entries, .{ .name = "some_dir", .kind = .directory }));
604608
605609 iter.reset();
606610 }
......@@ -619,7 +623,7 @@ test "Dir.Iterator but dir is deleted during iteration" {
619623 var iterator = subdir.iterate();
620624
621625 // 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
624628 // Then, before iterating, delete the directory that we're iterating.
625629 // 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" {
629633
630634 // Now, when we try to iterate, the next call should return null immediately.
631635 const entry = try iterator.next();
632 try std.testing.expect(entry == null);
636 try std.expect(entry == null);
633637
634638 // On Linux, we can opt-in to receiving a more specific error by calling `nextLinux`
635639 if (native_os == .linux) {
......@@ -657,25 +661,25 @@ test "Dir.realpath smoke test" {
657661 const allocator = ctx.arena.allocator();
658662 const test_file_path = try ctx.transformPath("test_file");
659663 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
662666 // FileNotFound if the path doesn't exist
663 try expectError(error.FileNotFound, ctx.dir.realpathAlloc(allocator, test_file_path));
664 try expectError(error.FileNotFound, ctx.dir.realpath(test_file_path, &buf));
665 try expectError(error.FileNotFound, ctx.dir.realpathAlloc(allocator, test_dir_path));
666 try expectError(error.FileNotFound, ctx.dir.realpath(test_dir_path, &buf));
667 try expectError(error.FileNotFound, ctx.dir.realPathAlloc(io, test_file_path, allocator));
668 try expectError(error.FileNotFound, ctx.dir.realPath(io, test_file_path, &buf));
669 try expectError(error.FileNotFound, ctx.dir.realPathAlloc(io, test_dir_path, allocator));
670 try expectError(error.FileNotFound, ctx.dir.realPath(io, test_dir_path, &buf));
667671
668672 // Now create the file and dir
669673 try ctx.dir.writeFile(io, .{ .sub_path = test_file_path, .data = "" });
670674 try ctx.dir.makeDir(io, test_dir_path, .default_dir);
671675
672676 const base_path = try ctx.transformPath(".");
673 const base_realpath = try ctx.dir.realpathAlloc(allocator, base_path);
674 const expected_file_path = try fs.path.join(
677 const base_realpath = try ctx.dir.realPathAlloc(io, base_path, allocator);
678 const expected_file_path = try Dir.path.join(
675679 allocator,
676680 &.{ base_realpath, "test_file" },
677681 );
678 const expected_dir_path = try fs.path.join(
682 const expected_dir_path = try Dir.path.join(
679683 allocator,
680684 &.{ base_realpath, "test_dir" },
681685 );
......@@ -691,10 +695,10 @@ test "Dir.realpath smoke test" {
691695
692696 // Next, test alloc version
693697 {
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);
695699 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);
698702 try expectEqualStrings(expected_dir_path, dir_path);
699703 }
700704 }
......@@ -715,7 +719,7 @@ test "readFileAlloc" {
715719 try expectEqualStrings("", buf1);
716720
717721 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
720724 {
721725 // max_bytes > file_size
......@@ -779,7 +783,7 @@ test "statFile on dangling symlink" {
779783 fn impl(ctx: *TestContext) !void {
780784 const io = ctx.io;
781785 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
784788 try setupSymlink(io, ctx.dir, symlink_target, symlink_name, .{});
785789
......@@ -803,8 +807,8 @@ test "directory operations on files" {
803807 try expectError(error.NotDir, ctx.dir.deleteDir(io, test_file_name));
804808
805809 if (ctx.path_type == .absolute and comptime PathType.absolute.isSupported(builtin.os)) {
806 try expectError(error.PathAlreadyExists, fs.makeDirAbsolute(test_file_name));
807 try expectError(error.NotDir, fs.deleteDirAbsolute(test_file_name));
810 try expectError(error.PathAlreadyExists, Dir.makeDirAbsolute(io, test_file_name));
811 try expectError(error.NotDir, Dir.deleteDirAbsolute(io, test_file_name));
808812 }
809813
810814 // ensure the file still exists and is a file as a sanity check
......@@ -862,8 +866,8 @@ test "file operations on directories" {
862866 try expectError(error.IsDir, ctx.dir.openFile(io, test_dir_name, .{ .allow_directory = false, .mode = .read_only }));
863867
864868 if (ctx.path_type == .absolute and comptime PathType.absolute.isSupported(builtin.os)) {
865 try expectError(error.IsDir, fs.createFileAbsolute(test_dir_name, .{}));
866 try expectError(error.IsDir, fs.deleteFileAbsolute(test_dir_name));
869 try expectError(error.IsDir, Dir.createFileAbsolute(io, test_dir_name, .{}));
870 try expectError(error.IsDir, Dir.deleteFileAbsolute(io, test_dir_name));
867871 }
868872
869873 // ensure the directory still exists as a sanity check
......@@ -892,7 +896,7 @@ test "deleteDir" {
892896 fn impl(ctx: *TestContext) !void {
893897 const io = ctx.io;
894898 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
897901 // deleting a non-existent directory
898902 try expectError(error.FileNotFound, ctx.dir.deleteDir(io, test_dir_path));
......@@ -1097,11 +1101,12 @@ test "renameAbsolute" {
10971101 defer arena.deinit();
10981102 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(
1103 try fs.path.join(allocator, &.{ base_path, "missing_file_name" }),
1104 try fs.path.join(allocator, &.{ base_path, "something_else" }),
1106 try expectError(error.FileNotFound, Dir.renameAbsolute(
1107 io,
1108 try Dir.path.join(allocator, &.{ base_path, "missing_file_name" }),
1109 try Dir.path.join(allocator, &.{ base_path, "something_else" }),
11051110 ));
11061111
11071112 // Renaming files
......@@ -1109,9 +1114,10 @@ test "renameAbsolute" {
11091114 const renamed_test_file_name = "test_file_renamed";
11101115 var file = try tmp_dir.dir.createFile(io, test_file_name, .{ .read = true });
11111116 file.close(io);
1112 try fs.renameAbsolute(
1113 try fs.path.join(allocator, &.{ base_path, test_file_name }),
1114 try fs.path.join(allocator, &.{ base_path, renamed_test_file_name }),
1117 try Dir.renameAbsolute(
1118 io,
1119 try Dir.path.join(allocator, &.{ base_path, test_file_name }),
1120 try Dir.path.join(allocator, &.{ base_path, renamed_test_file_name }),
11151121 );
11161122
11171123 // ensure the file was renamed
......@@ -1125,9 +1131,10 @@ test "renameAbsolute" {
11251131 const test_dir_name = "test_dir";
11261132 const renamed_test_dir_name = "test_dir_renamed";
11271133 try tmp_dir.dir.makeDir(io, test_dir_name, .default_dir);
1128 try fs.renameAbsolute(
1129 try fs.path.join(allocator, &.{ base_path, test_dir_name }),
1130 try fs.path.join(allocator, &.{ base_path, renamed_test_dir_name }),
1134 try Dir.renameAbsolute(
1135 io,
1136 try Dir.path.join(allocator, &.{ base_path, test_dir_name }),
1137 try Dir.path.join(allocator, &.{ base_path, renamed_test_dir_name }),
11311138 );
11321139
11331140 // ensure the directory was renamed
......@@ -1149,7 +1156,7 @@ test "executablePath" {
11491156 if (native_os == .wasi) return error.SkipZigTest;
11501157
11511158 const io = testing.io;
1152 var buf: [fs.max_path_bytes]u8 = undefined;
1159 var buf: [Dir.max_path_bytes]u8 = undefined;
11531160 const buf_self_exe_path = try std.process.executablePath(io, &buf);
11541161 const alloc_self_exe_path = try std.process.executablePathAlloc(io, testing.allocator);
11551162 defer testing.allocator.free(alloc_self_exe_path);
......@@ -1206,13 +1213,13 @@ test "makePath, put some files in it, deleteTree" {
12061213 const allocator = ctx.arena.allocator();
12071214 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" }));
12101217 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" }),
12121219 .data = "nonsense",
12131220 });
12141221 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" }),
12161223 .data = "blah",
12171224 });
12181225
......@@ -1229,13 +1236,13 @@ test "makePath, put some files in it, deleteTreeMinStackSize" {
12291236 const allocator = ctx.arena.allocator();
12301237 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" }));
12331240 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" }),
12351242 .data = "nonsense",
12361243 });
12371244 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" }),
12391246 .data = "blah",
12401247 });
12411248
......@@ -1285,7 +1292,7 @@ test "makepath existing directories" {
12851292 defer tmpA.close(io);
12861293 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";
12891296 try tmp.dir.makePath(io, testPath);
12901297
12911298 try expectDir(io, tmp.dir, testPath);
......@@ -1298,11 +1305,11 @@ test "makepath through existing valid symlink" {
12981305 defer tmp.cleanup();
12991306
13001307 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");
13061313}
13071314
13081315test "makepath relative walks" {
......@@ -1311,7 +1318,7 @@ test "makepath relative walks" {
13111318 var tmp = tmpDir(.{});
13121319 defer tmp.cleanup();
13131320
1314 const relPath = try fs.path.join(testing.allocator, &.{
1321 const relPath = try Dir.path.join(testing.allocator, &.{
13151322 "first", "..", "second", "..", "third", "..", "first", "A", "..", "B", "..", "C",
13161323 });
13171324 defer testing.allocator.free(relPath);
......@@ -1323,14 +1330,14 @@ test "makepath relative walks" {
13231330 .windows => {
13241331 // On Windows, .. is resolved before passing the path to NtCreateFile,
13251332 // 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");
13271334 try expectError(error.FileNotFound, tmp.dir.access(io, "second", .{}));
13281335 try expectError(error.FileNotFound, tmp.dir.access(io, "third", .{}));
13291336 },
13301337 else => {
1331 try expectDir(io, tmp.dir, "first" ++ fs.path.sep_str ++ "A");
1332 try expectDir(io, tmp.dir, "first" ++ fs.path.sep_str ++ "B");
1333 try expectDir(io, tmp.dir, "first" ++ fs.path.sep_str ++ "C");
1338 try expectDir(io, tmp.dir, "first" ++ Dir.path.sep_str ++ "A");
1339 try expectDir(io, tmp.dir, "first" ++ Dir.path.sep_str ++ "B");
1340 try expectDir(io, tmp.dir, "first" ++ Dir.path.sep_str ++ "C");
13341341 try expectDir(io, tmp.dir, "second");
13351342 try expectDir(io, tmp.dir, "third");
13361343 },
......@@ -1344,13 +1351,13 @@ test "makepath ignores '.'" {
13441351 defer tmp.cleanup();
13451352
13461353 // Path to create, with "." elements:
1347 const dotPath = try fs.path.join(testing.allocator, &.{
1354 const dotPath = try Dir.path.join(testing.allocator, &.{
13481355 "first", ".", "second", ".", "third",
13491356 });
13501357 defer testing.allocator.free(dotPath);
13511358
13521359 // Path to expect to find:
1353 const expectedPath = try fs.path.join(testing.allocator, &.{
1360 const expectedPath = try Dir.path.join(testing.allocator, &.{
13541361 "first", "second", "third",
13551362 });
13561363 defer testing.allocator.free(expectedPath);
......@@ -1473,7 +1480,7 @@ test "access file" {
14731480 fn impl(ctx: *TestContext) !void {
14741481 const io = ctx.io;
14751482 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
14781485 try ctx.dir.makePath(io, dir_path);
14791486 try expectError(error.FileNotFound, ctx.dir.access(io, file_path, .{}));
......@@ -1546,7 +1553,7 @@ test "sendfile with buffered data" {
15461553 var src_file = try dir.createFile(io, "sendfile1.txt", .{ .read = true });
15471554 defer src_file.close(io);
15481555
1549 try src_file.writeAll("AAAABBBB");
1556 try src_file.writeStreamingAll(io, "AAAABBBB");
15501557
15511558 var dest_file = try dir.createFile(io, "sendfile2.txt", .{ .read = true });
15521559 defer dest_file.close(io);
......@@ -1691,7 +1698,7 @@ test "open file with exclusive lock twice, make sure second lock waits" {
16911698 errdefer file.close(io);
16921699
16931700 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 {
16951702 started.set();
16961703 const file1 = try dir.createFile(io, path, .{ .lock = .exclusive });
16971704
......@@ -1731,8 +1738,8 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {
17311738 var random_bytes: [12]u8 = undefined;
17321739 std.crypto.random.bytes(&random_bytes);
17331740
1734 var random_b64: [fs.base64_encoder.calcSize(random_bytes.len)]u8 = undefined;
1735 _ = fs.base64_encoder.encode(&random_b64, &random_bytes);
1741 var random_b64: [std.fs.base64_encoder.calcSize(random_bytes.len)]u8 = undefined;
1742 _ = std.fs.base64_encoder.encode(&random_b64, &random_bytes);
17361743
17371744 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)" {
17411748 const cwd = try std.process.getCwdAlloc(gpa);
17421749 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 });
17451752 defer gpa.free(filename);
17461753
1747 defer fs.deleteFileAbsolute(filename) catch {}; // createFileAbsolute can leave files on failures
1748 const file1 = try fs.createFileAbsolute(filename, .{
1754 defer Dir.deleteFileAbsolute(io, filename) catch {}; // createFileAbsolute can leave files on failures
1755 const file1 = try Dir.createFileAbsolute(io, filename, .{
17491756 .lock = .exclusive,
17501757 .lock_nonblocking = true,
17511758 });
17521759
1753 const file2 = fs.createFileAbsolute(filename, .{
1760 const file2 = Dir.createFileAbsolute(io, filename, .{
17541761 .lock = .exclusive,
17551762 .lock_nonblocking = true,
17561763 });
......@@ -1802,9 +1809,9 @@ test "walker" {
18021809 .{ "dir2", 1 },
18031810 .{ "dir3", 1 },
18041811 .{ "dir4", 1 },
1805 .{ "dir3" ++ fs.path.sep_str ++ "sub1", 2 },
1806 .{ "dir3" ++ fs.path.sep_str ++ "sub2", 2 },
1807 .{ "dir3" ++ fs.path.sep_str ++ "sub2" ++ fs.path.sep_str ++ "subsub1", 3 },
1812 .{ "dir3" ++ Dir.path.sep_str ++ "sub1", 2 },
1813 .{ "dir3" ++ Dir.path.sep_str ++ "sub2", 2 },
1814 .{ "dir3" ++ Dir.path.sep_str ++ "sub2" ++ Dir.path.sep_str ++ "subsub1", 3 },
18081815 });
18091816
18101817 const expected_basenames = std.StaticStringMap(void).initComptime(.{
......@@ -1826,11 +1833,11 @@ test "walker" {
18261833
18271834 var num_walked: usize = 0;
18281835 while (try walker.next()) |entry| {
1829 testing.expect(expected_basenames.has(entry.basename)) catch |err| {
1836 expect(expected_basenames.has(entry.basename)) catch |err| {
18301837 std.debug.print("found unexpected basename: {f}\n", .{std.ascii.hexEscape(entry.basename, .lower)});
18311838 return err;
18321839 };
1833 testing.expect(expected_paths.has(entry.path)) catch |err| {
1840 expect(expected_paths.has(entry.path)) catch |err| {
18341841 std.debug.print("found unexpected path: {f}\n", .{std.ascii.hexEscape(entry.path, .lower)});
18351842 return err;
18361843 };
......@@ -1863,11 +1870,11 @@ test "selective walker, skip entries that start with ." {
18631870
18641871 const expected_paths = std.StaticStringMap(usize).initComptime(.{
18651872 .{ "dir1", 1 },
1866 .{ "dir1" ++ fs.path.sep_str ++ "foo", 2 },
1873 .{ "dir1" ++ Dir.path.sep_str ++ "foo", 2 },
18671874 .{ "a", 1 },
1868 .{ "a" ++ fs.path.sep_str ++ "b", 2 },
1869 .{ "a" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c", 3 },
1870 .{ "a" ++ fs.path.sep_str ++ "baz", 2 },
1875 .{ "a" ++ Dir.path.sep_str ++ "b", 2 },
1876 .{ "a" ++ Dir.path.sep_str ++ "b" ++ Dir.path.sep_str ++ "c", 3 },
1877 .{ "a" ++ Dir.path.sep_str ++ "baz", 2 },
18711878 });
18721879
18731880 const expected_basenames = std.StaticStringMap(void).initComptime(.{
......@@ -1893,11 +1900,11 @@ test "selective walker, skip entries that start with ." {
18931900 try walker.enter(entry);
18941901 }
18951902
1896 testing.expect(expected_basenames.has(entry.basename)) catch |err| {
1903 expect(expected_basenames.has(entry.basename)) catch |err| {
18971904 std.debug.print("found unexpected basename: {f}\n", .{std.ascii.hexEscape(entry.basename, .lower)});
18981905 return err;
18991906 };
1900 testing.expect(expected_paths.has(entry.path)) catch |err| {
1907 expect(expected_paths.has(entry.path)) catch |err| {
19011908 std.debug.print("found unexpected path: {f}\n", .{std.ascii.hexEscape(entry.path, .lower)});
19021909 return err;
19031910 };
......@@ -1937,7 +1944,7 @@ test "walker without fully iterating" {
19371944 try expectEqual(@as(usize, 1), num_walked);
19381945}
19391946
1940test "'.' and '..' in Io.Dir functions" {
1947test "'.' and '..' in Dir functions" {
19411948 if (native_os == .windows and builtin.cpu.arch == .aarch64) {
19421949 // https://github.com/ziglang/zig/issues/17134
19431950 return error.SkipZigTest;
......@@ -1970,7 +1977,7 @@ test "'.' and '..' in Io.Dir functions" {
19701977 try ctx.dir.writeFile(io, .{ .sub_path = update_path, .data = "something" });
19711978 var dir = ctx.dir;
19721979 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
19751982 try ctx.dir.deleteDir(io, subdir_path);
19761983 }
......@@ -1990,28 +1997,28 @@ test "'.' and '..' in absolute functions" {
19901997 defer arena.deinit();
19911998 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" });
1996 try fs.makeDirAbsolute(subdir_path);
1997 try fs.accessAbsolute(subdir_path, .{});
2002 const subdir_path = try Dir.path.join(allocator, &.{ base_path, "./subdir" });
2003 try Dir.makeDirAbsolute(io, subdir_path);
2004 try Dir.accessAbsolute(io, subdir_path, .{});
19982005 var created_subdir = try Dir.openDirAbsolute(io, subdir_path, .{});
19992006 created_subdir.close(io);
20002007
2001 const created_file_path = try fs.path.join(allocator, &.{ subdir_path, "../file" });
2002 const created_file = try fs.createFileAbsolute(created_file_path, .{});
2008 const created_file_path = try Dir.path.join(allocator, &.{ subdir_path, "../file" });
2009 const created_file = try Dir.createFileAbsolute(io, created_file_path, .{});
20032010 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" });
2007 try fs.copyFileAbsolute(created_file_path, copied_file_path, .{});
2008 const renamed_file_path = try fs.path.join(allocator, &.{ subdir_path, "../rename" });
2009 try fs.renameAbsolute(copied_file_path, renamed_file_path);
2013 const copied_file_path = try Dir.path.join(allocator, &.{ subdir_path, "../copy" });
2014 try Dir.copyFileAbsolute(io, created_file_path, copied_file_path, .{});
2015 const renamed_file_path = try Dir.path.join(allocator, &.{ subdir_path, "../rename" });
2016 try Dir.renameAbsolute(io, copied_file_path, renamed_file_path);
20102017 const renamed_file = try Dir.openFileAbsolute(renamed_file_path, .{});
20112018 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);
20152022}
20162023
20172024test "chmod" {
......@@ -2114,8 +2121,8 @@ test "invalid UTF-8/WTF-8 paths" {
21142121 try expectError(expected_err, ctx.dir.statFile(invalid_path));
21152122
21162123 if (native_os != .wasi) {
2117 try expectError(expected_err, ctx.dir.realpath(invalid_path, &[_]u8{}));
2118 try expectError(expected_err, ctx.dir.realpathAlloc(testing.allocator, invalid_path));
2124 try expectError(expected_err, ctx.dir.realPath(io, invalid_path, &[_]u8{}));
2125 try expectError(expected_err, ctx.dir.realPathAlloc(io, invalid_path, testing.allocator));
21192126 }
21202127
21212128 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" {
21332140 var readlink_buf: [Dir.max_path_bytes]u8 = undefined;
21342141 try expectError(expected_err, Dir.readLinkAbsolute(invalid_path, &readlink_buf));
21352142 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));
21372144 }
21382145 }
21392146 }.impl);
......@@ -2303,7 +2310,7 @@ test "readlink on Windows" {
23032310}
23042311
23052312fn 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;
23072314 const given = try Dir.readLinkAbsolute(io, symlink_path, &buffer);
23082315 try expect(mem.eql(u8, target_path, given));
23092316}
......@@ -2326,7 +2333,7 @@ test "readlinkat" {
23262333 };
23272334
23282335 // read the link
2329 var buffer: [fs.max_path_bytes]u8 = undefined;
2336 var buffer: [Dir.max_path_bytes]u8 = undefined;
23302337 const read_link = try tmp.dir.readLink(io, "link", &buffer);
23312338 try expectEqualStrings("file.txt", read_link);
23322339}
......@@ -2387,3 +2394,239 @@ fn expectMode(io: Io, dir: Dir, file: []const u8, permissions: File.Permissions)
23872394 const st = try dir.statFile(io, file, .{ .follow_symlinks = false });
23882395 try expectEqual(mode, st.mode & 0b111_111_111);
23892396}
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 {
10341034 }
10351035}
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
11941037/// On Windows, `sub_dir_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
11951038/// On WASI, `sub_dir_path` should be encoded as valid UTF-8.
11961039/// 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 @@
11const builtin = @import("builtin");
22const native_os = builtin.target.os.tag;
3const AtomicRmwOp = std.builtin.AtomicRmwOp;
4const AtomicOrder = std.builtin.AtomicOrder;
35
46const std = @import("../std.zig");
57const Io = std.Io;
8const Dir = std.Io.Dir;
69const posix = std.posix;
10const mem = std.mem;
11const elf = std.elf;
12const linux = std.os.linux;
13const AT = std.posix.AT;
14
715const testing = std.testing;
816const expect = std.testing.expect;
917const expectEqual = std.testing.expectEqual;
18const expectEqualSlices = std.testing.expectEqualSlices;
19const expectEqualStrings = std.testing.expectEqualStrings;
1020const 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;
1821const 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
2423// https://github.com/ziglang/zig/issues/20288
2524test "WTF-8 to WTF-16 conversion buffer overflows" {
......@@ -43,165 +42,6 @@ test "check WASI CWD" {
4342 }
4443}
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
20545test "getrandom" {
20646 var buf_a: [50]u8 = undefined;
20747 var buf_b: [50]u8 = undefined;
......@@ -232,7 +72,7 @@ test "sigaltstack" {
23272 // Setting a stack size less than MINSIGSTKSZ returns ENOMEM
23373 st.flags = 0;
23474 st.size = 1;
235 try testing.expectError(error.SizeTooSmall, posix.sigaltstack(&st, null));
75 try expectError(error.SizeTooSmall, posix.sigaltstack(&st, null));
23676}
23777
23878// If the type is not available use void to avoid erroring out when `iter_fn` is
......@@ -304,7 +144,7 @@ test "pipe" {
304144 try expect((try posix.write(fds[1], "hello")) == 5);
305145 var buf: [16]u8 = undefined;
306146 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");
308148 posix.close(fds[1]);
309149 posix.close(fds[0]);
310150}
......@@ -315,6 +155,8 @@ test "argsAlloc" {
315155}
316156
317157test "memfd_create" {
158 const io = testing.io;
159
318160 // memfd_create is only supported by linux and freebsd.
319161 switch (native_os) {
320162 .linux => {},
......@@ -325,15 +167,14 @@ test "memfd_create" {
325167 else => return error.SkipZigTest,
326168 }
327169
328 const fd = try posix.memfd_create("test", 0);
329 defer posix.close(fd);
330 try expect((try posix.write(fd, "test")) == 4);
331 try posix.lseek_SET(fd, 0);
170 const file: Io.File = .{ .handle = try posix.memfd_create("test", 0) };
171 defer file.close(io);
172 try file.writePositionalAll(io, "test", 0);
332173
333174 var buf: [10]u8 = undefined;
334 const bytes_read = try posix.read(fd, &buf);
175 const bytes_read = try file.readPositionalAll(io, &buf, 0);
335176 try expect(bytes_read == 4);
336 try expect(mem.eql(u8, buf[0..4], "test"));
177 try expectEqualStrings("test", buf[0..4]);
337178}
338179
339180test "mmap" {
......@@ -357,14 +198,14 @@ test "mmap" {
357198 );
358199 defer posix.munmap(data);
359200
360 try testing.expectEqual(@as(usize, 1234), data.len);
201 try expectEqual(@as(usize, 1234), data.len);
361202
362203 // 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
365206 // Make sure the memory is writeable as requested
366207 @memset(data, 0x55);
367 try testing.expect(mem.eql(u8, data, &[_]u8{0x55} ** 1234));
208 try expect(mem.eql(u8, data, &[_]u8{0x55} ** 1234));
368209 }
369210
370211 const test_out_file = "os_tmp_test";
......@@ -403,7 +244,7 @@ test "mmap" {
403244
404245 var i: usize = 0;
405246 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));
407248 }
408249 }
409250
......@@ -428,7 +269,7 @@ test "mmap" {
428269
429270 var i: usize = alloc_size / 2 / @sizeOf(u32);
430271 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));
432273 }
433274 }
434275}
......@@ -498,7 +339,7 @@ test "fsync" {
498339 const file = try tmp.dir.createFile(io, test_out_file, .{});
499340 defer file.close(io);
500341
501 try posix.fsync(file.handle);
342 try file.sync(io);
502343 try posix.fdatasync(file.handle);
503344}
504345
......@@ -536,9 +377,9 @@ test "sigrtmin/max" {
536377 return error.SkipZigTest;
537378 }
538379
539 try std.testing.expect(posix.sigrtmin() >= 32);
540 try std.testing.expect(posix.sigrtmin() >= posix.system.sigrtmin());
541 try std.testing.expect(posix.sigrtmin() < posix.system.sigrtmax());
380 try expect(posix.sigrtmin() >= 32);
381 try expect(posix.sigrtmin() >= posix.system.sigrtmin());
382 try expect(posix.sigrtmin() < posix.system.sigrtmax());
542383}
543384
544385test "sigset empty/full" {
......@@ -622,18 +463,18 @@ test "dup & dup2" {
622463
623464 var duped = Io.File{ .handle = try posix.dup(file.handle) };
624465 defer duped.close(io);
625 try duped.writeAll("dup");
466 try duped.writeStreamingAll(io, "dup");
626467
627468 // Tests aren't run in parallel so using the next fd shouldn't be an issue.
628469 const new_fd = duped.handle + 1;
629470 try posix.dup2(file.handle, new_fd);
630471 var dup2ed = Io.File{ .handle = new_fd };
631472 defer dup2ed.close(io);
632 try dup2ed.writeAll("dup2");
473 try dup2ed.writeStreamingAll(io, "dup2");
633474 }
634475
635476 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));
637478}
638479
639480test "getpid" {
......@@ -651,147 +492,78 @@ test "getppid" {
651492 try expect(posix.getppid() >= 0);
652493}
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
670495test "rename smoke test" {
671496 if (native_os == .wasi) return error.SkipZigTest;
672497 if (native_os == .windows) return error.SkipZigTest;
673498 if (native_os == .openbsd) return error.SkipZigTest;
674499
500 const io = testing.io;
501 const gpa = testing.allocator;
502
675503 var tmp = tmpDir(.{});
676504 defer tmp.cleanup();
677505
678 const base_path = try tmp.dir.realpathAlloc(a, ".");
679 defer a.free(base_path);
506 const base_path = try tmp.dir.realPathAlloc(io, ".", gpa);
507 defer gpa.free(base_path);
680508
681509 const mode: posix.mode_t = if (native_os == .windows) 0 else 0o666;
682510
683511 {
684512 // Create some file using `open`.
685 const file_path = try fs.path.join(a, &.{ base_path, "some_file" });
686 defer a.free(file_path);
513 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_file" });
514 defer gpa.free(file_path);
687515 const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode);
688516 posix.close(fd);
689517
690518 // Rename the file
691 const new_file_path = try fs.path.join(a, &.{ base_path, "some_other_file" });
692 defer a.free(new_file_path);
693 try Io.Dir.renameAbsolute(file_path, new_file_path);
519 const new_file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_file" });
520 defer gpa.free(new_file_path);
521 try Io.Dir.renameAbsolute(io, file_path, new_file_path);
694522 }
695523
696524 {
697525 // Try opening renamed file
698 const file_path = try fs.path.join(a, &.{ base_path, "some_other_file" });
699 defer a.free(file_path);
526 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_file" });
527 defer gpa.free(file_path);
700528 const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR }, mode);
701529 posix.close(fd);
702530 }
703531
704532 {
705533 // Try opening original file - should fail with error.FileNotFound
706 const file_path = try fs.path.join(a, &.{ base_path, "some_file" });
707 defer a.free(file_path);
534 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_file" });
535 defer gpa.free(file_path);
708536 try expectError(error.FileNotFound, posix.open(file_path, .{ .ACCMODE = .RDWR }, mode));
709537 }
710538
711539 {
712540 // Create some directory
713 const file_path = try fs.path.join(a, &.{ base_path, "some_dir" });
714 defer a.free(file_path);
541 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_dir" });
542 defer gpa.free(file_path);
715543 try posix.mkdir(file_path, mode);
716544
717545 // Rename the directory
718 const new_file_path = try fs.path.join(a, &.{ base_path, "some_other_dir" });
719 defer a.free(new_file_path);
720 try Io.Dir.renameAbsolute(file_path, new_file_path);
546 const new_file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_dir" });
547 defer gpa.free(new_file_path);
548 try Io.Dir.renameAbsolute(io, file_path, new_file_path);
721549 }
722550
723551 {
724552 // Try opening renamed directory
725 const file_path = try fs.path.join(a, &.{ base_path, "some_other_dir" });
726 defer a.free(file_path);
553 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_dir" });
554 defer gpa.free(file_path);
727555 const fd = try posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode);
728556 posix.close(fd);
729557 }
730558
731559 {
732560 // Try opening original directory - should fail with error.FileNotFound
733 const file_path = try fs.path.join(a, &.{ base_path, "some_dir" });
734 defer a.free(file_path);
561 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_dir" });
562 defer gpa.free(file_path);
735563 try expectError(error.FileNotFound, posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode));
736564 }
737565}
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
795567test "timerfd" {
796568 if (native_os != .linux) return error.SkipZigTest;
797569
......@@ -809,89 +581,3 @@ test "timerfd" {
809581 const expect_disarmed_timer: linux.itimerspec = .{ .it_interval = .{ .sec = 0, .nsec = 0 }, .it_value = .{ .sec = 0, .nsec = 0 } };
810582 try expectEqual(expect_disarmed_timer, git);
811583}
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
63356335 var buffer: [64]u8 = undefined;
63366336 const stderr = try io.lockStderr(&buffer, null);
63376337 defer io.unlockStderr();
6338 const writer = &stderr.file_writer.interface;
63386339
63396340 var tree = try std.zig.Ast.parse(allocator, source, .zig);
63406341 defer tree.deinit(allocator);
63416342
63426343 for (tree.errors) |parse_error| {
63436344 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 tree.renderError(parse_error, stderr.writer);
6346 try stderr.writer.print("\n{s}\n", .{source[loc.line_start..loc.line_end]});
6345 try writer.print("(memory buffer):{d}:{d}: error: ", .{ loc.line + 1, loc.column + 1 });
6346 try tree.renderError(parse_error, writer);
6347 try writer.print("\n{s}\n", .{source[loc.line_start..loc.line_end]});
63476348 {
63486349 var i: usize = 0;
63496350 while (i < loc.column) : (i += 1) {
6350 try stderr.writer.writeAll(" ");
6351 try writer.writeAll(" ");
63516352 }
6352 try stderr.writer.writeAll("^");
6353 try writer.writeAll("^");
63536354 }
6354 try stderr.writer.writeAll("\n");
6355 try writer.writeAll("\n");
63556356 }
63566357 if (tree.errors.len != 0) {
63576358 return error.ParseError;