| author | |
| committer | |
| log | 262f4c7b3a850594a75ec154db2ba8d5f9f517ab |
| tree | ca0cb887465d5a332e288427e1851710fdcb570f |
| parent | 2b67f56c35d0a61be43f8ca23535096ae3ca4948 |
5 files changed, 88 insertions(+), 54 deletions(-)
lib/std/fs.zig+29-34| ... | @@ -956,14 +956,11 @@ pub const IterableDir = struct { | ... | @@ -956,14 +956,11 @@ pub const IterableDir = struct { |
| 956 | 956 | ||
| 957 | pub const Dir = struct { | 957 | pub const Dir = struct { |
| 958 | fd: os.fd_t, | 958 | fd: os.fd_t, |
| 959 | iterable: @TypeOf(iterable_safety) = iterable_safety, | ||
| 960 | 959 | ||
| 961 | const iterable_safety = if (builtin.mode == .Debug) false else {}; | 960 | pub const iterate = @compileError("only 'IterableDir' can be iterated; 'IterableDir' can be obtained with 'openIterableDir'"); |
| 962 | 961 | pub const walk = @compileError("only 'IterableDir' can be walked; 'IterableDir' can be obtained with 'openIterableDir'"); | |
| 963 | pub const iterate = @compileError("only 'IterableDir' can be iterated; 'IterableDir' can be obtained with 'openIterableDir' or by opening with 'iterate = true' and using 'intoIterable'"); | 962 | pub const chmod = @compileError("only 'IterableDir' can have its mode changed; 'IterableDir' can be obtained with 'openIterableDir'"); |
| 964 | pub const walk = @compileError("only 'IterableDir' can be walked; 'IterableDir' can be obtained with 'openIterableDir' or by opening with 'iterate = true' and using 'intoIterable'"); | 963 | pub const chown = @compileError("only 'IterableDir' can have its owner changed; 'IterableDir' can be obtained with 'openIterableDir'"); |
| 965 | pub const chmod = @compileError("only 'IterableDir' can have its mode changed; 'IterableDir' can be obtained with 'openIterableDir' or by opening with 'iterate = true' and using 'intoIterable'"); | ||
| 966 | pub const chown = @compileError("only 'IterableDir' can have its owner changed; 'IterableDir' can be obtained with 'openIterableDir' or by opening with 'iterate = true' and using 'intoIterable'"); | ||
| 967 | 964 | ||
| 968 | pub const OpenError = error{ | 965 | pub const OpenError = error{ |
| 969 | FileNotFound, | 966 | FileNotFound, |
| ... | @@ -1381,6 +1378,15 @@ pub const Dir = struct { | ... | @@ -1381,6 +1378,15 @@ pub const Dir = struct { |
| 1381 | return self.openDir(sub_path, open_dir_options); | 1378 | return self.openDir(sub_path, open_dir_options); |
| 1382 | } | 1379 | } |
| 1383 | 1380 | ||
| 1381 | /// This function performs `makePath`, followed by `openIterableDir`. | ||
| 1382 | /// If supported by the OS, this operation is atomic. It is not atomic on | ||
| 1383 | /// all operating systems. | ||
| 1384 | pub fn makeOpenPathIterable(self: Dir, sub_path: []const u8, open_dir_options: OpenDirOptions) !IterableDir { | ||
| 1385 | // TODO improve this implementation on Windows; we can avoid 1 call to NtClose | ||
| 1386 | try self.makePath(sub_path); | ||
| 1387 | return self.openIterableDir(sub_path, open_dir_options); | ||
| 1388 | } | ||
| 1389 | |||
| 1384 | /// This function returns the canonicalized absolute pathname of | 1390 | /// This function returns the canonicalized absolute pathname of |
| 1385 | /// `pathname` relative to this `Dir`. If `pathname` is absolute, ignores this | 1391 | /// `pathname` relative to this `Dir`. If `pathname` is absolute, ignores this |
| 1386 | /// `Dir` handle and returns the canonicalized absolute pathname of `pathname` | 1392 | /// `Dir` handle and returns the canonicalized absolute pathname of `pathname` |
| ... | @@ -1530,10 +1536,6 @@ pub const Dir = struct { | ... | @@ -1530,10 +1536,6 @@ pub const Dir = struct { |
| 1530 | /// such operations are Illegal Behavior. | 1536 | /// such operations are Illegal Behavior. |
| 1531 | access_sub_paths: bool = true, | 1537 | access_sub_paths: bool = true, |
| 1532 | 1538 | ||
| 1533 | /// `true` means the opened directory can be scanned for the files and sub-directories | ||
| 1534 | /// of the result. It means the `iterate` function can be called. | ||
| 1535 | iterate: bool = false, | ||
| 1536 | |||
| 1537 | /// `true` means it won't dereference the symlinks. | 1539 | /// `true` means it won't dereference the symlinks. |
| 1538 | no_follow: bool = false, | 1540 | no_follow: bool = false, |
| 1539 | }; | 1541 | }; |
| ... | @@ -1545,12 +1547,12 @@ pub const Dir = struct { | ... | @@ -1545,12 +1547,12 @@ pub const Dir = struct { |
| 1545 | pub fn openDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!Dir { | 1547 | pub fn openDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!Dir { |
| 1546 | if (builtin.os.tag == .windows) { | 1548 | if (builtin.os.tag == .windows) { |
| 1547 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); | 1549 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); |
| 1548 | return self.openDirW(sub_path_w.span().ptr, args); | 1550 | return self.openDirW(sub_path_w.span().ptr, args, false); |
| 1549 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { | 1551 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| 1550 | return self.openDirWasi(sub_path, args); | 1552 | return self.openDirWasi(sub_path, args); |
| 1551 | } else { | 1553 | } else { |
| 1552 | const sub_path_c = try os.toPosixPath(sub_path); | 1554 | const sub_path_c = try os.toPosixPath(sub_path); |
| 1553 | return self.openDirZ(&sub_path_c, args); | 1555 | return self.openDirZ(&sub_path_c, args, false); |
| 1554 | } | 1556 | } |
| 1555 | } | 1557 | } |
| 1556 | 1558 | ||
| ... | @@ -1559,19 +1561,15 @@ pub const Dir = struct { | ... | @@ -1559,19 +1561,15 @@ pub const Dir = struct { |
| 1559 | /// | 1561 | /// |
| 1560 | /// Asserts that the path parameter has no null bytes. | 1562 | /// Asserts that the path parameter has no null bytes. |
| 1561 | pub fn openIterableDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!IterableDir { | 1563 | pub fn openIterableDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!IterableDir { |
| 1562 | var adjusted_args = args; | 1564 | if (builtin.os.tag == .windows) { |
| 1563 | adjusted_args.iterate = true; | 1565 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); |
| 1564 | const new_dir = try self.openDir(sub_path, adjusted_args); | 1566 | return IterableDir{ .dir = try self.openDirW(sub_path_w.span().ptr, args, true) }; |
| 1565 | return IterableDir{ .dir = new_dir }; | 1567 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| 1566 | } | 1568 | return IterableDir{ .dir = try self.openDirWasi(sub_path, args) }; |
| 1567 | 1569 | } else { | |
| 1568 | /// Convert `self` into an iterable directory. | 1570 | const sub_path_c = try os.toPosixPath(sub_path); |
| 1569 | /// Asserts that `self` was opened with `iterate = true`. | 1571 | return IterableDir{ .dir = try self.openDirZ(&sub_path_c, args, true) }; |
| 1570 | pub fn intoIterable(self: Dir) IterableDir { | ||
| 1571 | if (builtin.mode == .Debug) { | ||
| 1572 | assert(self.iterable); | ||
| 1573 | } | 1572 | } |
| 1574 | return .{ .dir = self }; | ||
| 1575 | } | 1573 | } |
| 1576 | 1574 | ||
| 1577 | /// Same as `openDir` except only WASI. | 1575 | /// Same as `openDir` except only WASI. |
| ... | @@ -1619,36 +1617,33 @@ pub const Dir = struct { | ... | @@ -1619,36 +1617,33 @@ pub const Dir = struct { |
| 1619 | error.FileBusy => unreachable, // can't happen for directories | 1617 | error.FileBusy => unreachable, // can't happen for directories |
| 1620 | else => |e| return e, | 1618 | else => |e| return e, |
| 1621 | }; | 1619 | }; |
| 1622 | return Dir{ .fd = fd, .iterable = if (builtin.mode == .Debug) args.iterate else {} }; | 1620 | return Dir{ .fd = fd }; |
| 1623 | } | 1621 | } |
| 1624 | 1622 | ||
| 1625 | /// Same as `openDir` except the parameter is null-terminated. | 1623 | /// Same as `openDir` except the parameter is null-terminated. |
| 1626 | pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions) OpenError!Dir { | 1624 | pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions, iterable: bool) OpenError!Dir { |
| 1627 | if (builtin.os.tag == .windows) { | 1625 | if (builtin.os.tag == .windows) { |
| 1628 | const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); | 1626 | const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); |
| 1629 | return self.openDirW(sub_path_w.span().ptr, args); | 1627 | return self.openDirW(sub_path_w.span().ptr, args); |
| 1630 | } | 1628 | } |
| 1631 | const symlink_flags: u32 = if (args.no_follow) os.O.NOFOLLOW else 0x0; | 1629 | const symlink_flags: u32 = if (args.no_follow) os.O.NOFOLLOW else 0x0; |
| 1632 | if (!args.iterate) { | 1630 | if (!iterable) { |
| 1633 | const O_PATH = if (@hasDecl(os.O, "PATH")) os.O.PATH else 0; | 1631 | const O_PATH = if (@hasDecl(os.O, "PATH")) os.O.PATH else 0; |
| 1634 | return self.openDirFlagsZ(sub_path_c, os.O.DIRECTORY | os.O.RDONLY | os.O.CLOEXEC | O_PATH | symlink_flags); | 1632 | return self.openDirFlagsZ(sub_path_c, os.O.DIRECTORY | os.O.RDONLY | os.O.CLOEXEC | O_PATH | symlink_flags); |
| 1635 | } else { | 1633 | } else { |
| 1636 | var dir = try self.openDirFlagsZ(sub_path_c, os.O.DIRECTORY | os.O.RDONLY | os.O.CLOEXEC | symlink_flags); | 1634 | return self.openDirFlagsZ(sub_path_c, os.O.DIRECTORY | os.O.RDONLY | os.O.CLOEXEC | symlink_flags); |
| 1637 | if (builtin.mode == .Debug) dir.iterable = true; | ||
| 1638 | return dir; | ||
| 1639 | } | 1635 | } |
| 1640 | } | 1636 | } |
| 1641 | 1637 | ||
| 1642 | /// Same as `openDir` except the path parameter is WTF-16 encoded, NT-prefixed. | 1638 | /// Same as `openDir` except the path parameter is WTF-16 encoded, NT-prefixed. |
| 1643 | /// This function asserts the target OS is Windows. | 1639 | /// This function asserts the target OS is Windows. |
| 1644 | pub fn openDirW(self: Dir, sub_path_w: [*:0]const u16, args: OpenDirOptions) OpenError!Dir { | 1640 | pub fn openDirW(self: Dir, sub_path_w: [*:0]const u16, args: OpenDirOptions, iterable: bool) OpenError!Dir { |
| 1645 | const w = os.windows; | 1641 | const w = os.windows; |
| 1646 | // TODO remove some of these flags if args.access_sub_paths is false | 1642 | // TODO remove some of these flags if args.access_sub_paths is false |
| 1647 | const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | | 1643 | const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | |
| 1648 | w.SYNCHRONIZE | w.FILE_TRAVERSE; | 1644 | w.SYNCHRONIZE | w.FILE_TRAVERSE; |
| 1649 | const flags: u32 = if (args.iterate) base_flags | w.FILE_LIST_DIRECTORY else base_flags; | 1645 | const flags: u32 = if (iterable) base_flags | w.FILE_LIST_DIRECTORY else base_flags; |
| 1650 | var dir = try self.openDirAccessMaskW(sub_path_w, flags, args.no_follow); | 1646 | var dir = try self.openDirAccessMaskW(sub_path_w, flags, args.no_follow); |
| 1651 | if (builtin.mode == .Debug) dir.iterable = args.iterate; | ||
| 1652 | return dir; | 1647 | return dir; |
| 1653 | } | 1648 | } |
| 1654 | 1649 |
lib/std/fs/test.zig+14-13| ... | @@ -11,6 +11,7 @@ const Dir = std.fs.Dir; | ... | @@ -11,6 +11,7 @@ const Dir = std.fs.Dir; |
| 11 | const IterableDir = std.fs.IterableDir; | 11 | const IterableDir = std.fs.IterableDir; |
| 12 | const File = std.fs.File; | 12 | const File = std.fs.File; |
| 13 | const tmpDir = testing.tmpDir; | 13 | const tmpDir = testing.tmpDir; |
| 14 | const tmpIterableDir = testing.tmpIterableDir; | ||
| 14 | 15 | ||
| 15 | test "Dir.readLink" { | 16 | test "Dir.readLink" { |
| 16 | var tmp = tmpDir(.{}); | 17 | var tmp = tmpDir(.{}); |
| ... | @@ -156,14 +157,14 @@ fn testReadLinkAbsolute(target_path: []const u8, symlink_path: []const u8) !void | ... | @@ -156,14 +157,14 @@ fn testReadLinkAbsolute(target_path: []const u8, symlink_path: []const u8) !void |
| 156 | } | 157 | } |
| 157 | 158 | ||
| 158 | test "Dir.Iterator" { | 159 | test "Dir.Iterator" { |
| 159 | var tmp_dir = tmpDir(.{ .iterate = true }); | 160 | var tmp_dir = tmpIterableDir(.{}); |
| 160 | defer tmp_dir.cleanup(); | 161 | defer tmp_dir.cleanup(); |
| 161 | 162 | ||
| 162 | // First, create a couple of entries to iterate over. | 163 | // First, create a couple of entries to iterate over. |
| 163 | const file = try tmp_dir.dir.createFile("some_file", .{}); | 164 | const file = try tmp_dir.iterable_dir.dir.createFile("some_file", .{}); |
| 164 | file.close(); | 165 | file.close(); |
| 165 | 166 | ||
| 166 | try tmp_dir.dir.makeDir("some_dir"); | 167 | try tmp_dir.iterable_dir.dir.makeDir("some_dir"); |
| 167 | 168 | ||
| 168 | var arena = ArenaAllocator.init(testing.allocator); | 169 | var arena = ArenaAllocator.init(testing.allocator); |
| 169 | defer arena.deinit(); | 170 | defer arena.deinit(); |
| ... | @@ -172,7 +173,7 @@ test "Dir.Iterator" { | ... | @@ -172,7 +173,7 @@ test "Dir.Iterator" { |
| 172 | var entries = std.ArrayList(IterableDir.Entry).init(allocator); | 173 | var entries = std.ArrayList(IterableDir.Entry).init(allocator); |
| 173 | 174 | ||
| 174 | // Create iterator. | 175 | // Create iterator. |
| 175 | var iter = tmp_dir.dir.intoIterable().iterate(); | 176 | var iter = tmp_dir.iterable_dir.iterate(); |
| 176 | while (try iter.next()) |entry| { | 177 | while (try iter.next()) |entry| { |
| 177 | // We cannot just store `entry` as on Windows, we're re-using the name buffer | 178 | // We cannot just store `entry` as on Windows, we're re-using the name buffer |
| 178 | // which means we'll actually share the `name` pointer between entries! | 179 | // which means we'll actually share the `name` pointer between entries! |
| ... | @@ -186,14 +187,14 @@ test "Dir.Iterator" { | ... | @@ -186,14 +187,14 @@ test "Dir.Iterator" { |
| 186 | } | 187 | } |
| 187 | 188 | ||
| 188 | test "Dir.Iterator twice" { | 189 | test "Dir.Iterator twice" { |
| 189 | var tmp_dir = tmpDir(.{ .iterate = true }); | 190 | var tmp_dir = tmpIterableDir(.{}); |
| 190 | defer tmp_dir.cleanup(); | 191 | defer tmp_dir.cleanup(); |
| 191 | 192 | ||
| 192 | // First, create a couple of entries to iterate over. | 193 | // First, create a couple of entries to iterate over. |
| 193 | const file = try tmp_dir.dir.createFile("some_file", .{}); | 194 | const file = try tmp_dir.iterable_dir.dir.createFile("some_file", .{}); |
| 194 | file.close(); | 195 | file.close(); |
| 195 | 196 | ||
| 196 | try tmp_dir.dir.makeDir("some_dir"); | 197 | try tmp_dir.iterable_dir.dir.makeDir("some_dir"); |
| 197 | 198 | ||
| 198 | var arena = ArenaAllocator.init(testing.allocator); | 199 | var arena = ArenaAllocator.init(testing.allocator); |
| 199 | defer arena.deinit(); | 200 | defer arena.deinit(); |
| ... | @@ -204,7 +205,7 @@ test "Dir.Iterator twice" { | ... | @@ -204,7 +205,7 @@ test "Dir.Iterator twice" { |
| 204 | var entries = std.ArrayList(IterableDir.Entry).init(allocator); | 205 | var entries = std.ArrayList(IterableDir.Entry).init(allocator); |
| 205 | 206 | ||
| 206 | // Create iterator. | 207 | // Create iterator. |
| 207 | var iter = tmp_dir.dir.intoIterable().iterate(); | 208 | var iter = tmp_dir.iterable_dir.iterate(); |
| 208 | while (try iter.next()) |entry| { | 209 | while (try iter.next()) |entry| { |
| 209 | // We cannot just store `entry` as on Windows, we're re-using the name buffer | 210 | // We cannot just store `entry` as on Windows, we're re-using the name buffer |
| 210 | // which means we'll actually share the `name` pointer between entries! | 211 | // which means we'll actually share the `name` pointer between entries! |
| ... | @@ -986,7 +987,7 @@ test "walker" { | ... | @@ -986,7 +987,7 @@ test "walker" { |
| 986 | if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest; | 987 | if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest; |
| 987 | if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); | 988 | if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); |
| 988 | 989 | ||
| 989 | var tmp = tmpDir(.{ .iterate = true }); | 990 | var tmp = tmpIterableDir(.{}); |
| 990 | defer tmp.cleanup(); | 991 | defer tmp.cleanup(); |
| 991 | 992 | ||
| 992 | // iteration order of walker is undefined, so need lookup maps to check against | 993 | // iteration order of walker is undefined, so need lookup maps to check against |
| ... | @@ -1012,10 +1013,10 @@ test "walker" { | ... | @@ -1012,10 +1013,10 @@ test "walker" { |
| 1012 | }); | 1013 | }); |
| 1013 | 1014 | ||
| 1014 | for (expected_paths.kvs) |kv| { | 1015 | for (expected_paths.kvs) |kv| { |
| 1015 | try tmp.dir.makePath(kv.key); | 1016 | try tmp.iterable_dir.dir.makePath(kv.key); |
| 1016 | } | 1017 | } |
| 1017 | 1018 | ||
| 1018 | var walker = try tmp.dir.intoIterable().walk(testing.allocator); | 1019 | var walker = try tmp.iterable_dir.walk(testing.allocator); |
| 1019 | defer walker.deinit(); | 1020 | defer walker.deinit(); |
| 1020 | 1021 | ||
| 1021 | var num_walked: usize = 0; | 1022 | var num_walked: usize = 0; |
| ... | @@ -1126,7 +1127,7 @@ test "chmod" { | ... | @@ -1126,7 +1127,7 @@ test "chmod" { |
| 1126 | defer iterable_dir.close(); | 1127 | defer iterable_dir.close(); |
| 1127 | 1128 | ||
| 1128 | try iterable_dir.chmod(0o700); | 1129 | try iterable_dir.chmod(0o700); |
| 1129 | try testing.expect((try iterable_dir.stat()).mode & 0o7777 == 0o700); | 1130 | try testing.expect((try iterable_dir.dir.stat()).mode & 0o7777 == 0o700); |
| 1130 | } | 1131 | } |
| 1131 | 1132 | ||
| 1132 | test "chown" { | 1133 | test "chown" { |
| ... | @@ -1142,7 +1143,7 @@ test "chown" { | ... | @@ -1142,7 +1143,7 @@ test "chown" { |
| 1142 | 1143 | ||
| 1143 | try tmp.dir.makeDir("test_dir"); | 1144 | try tmp.dir.makeDir("test_dir"); |
| 1144 | 1145 | ||
| 1145 | var iterable_dir = try tmp.dir.openDir("test_dir", .{}); | 1146 | var iterable_dir = try tmp.dir.openIterableDir("test_dir", .{}); |
| 1146 | defer iterable_dir.close(); | 1147 | defer iterable_dir.close(); |
| 1147 | try iterable_dir.chown(null, null); | 1148 | try iterable_dir.chown(null, null); |
| 1148 | } | 1149 | } |
lib/std/os.zig+2-2| ... | @@ -308,7 +308,7 @@ pub fn fchmod(fd: fd_t, mode: mode_t) FChmodError!void { | ... | @@ -308,7 +308,7 @@ pub fn fchmod(fd: fd_t, mode: mode_t) FChmodError!void { |
| 308 | switch (system.getErrno(res)) { | 308 | switch (system.getErrno(res)) { |
| 309 | .SUCCESS => return, | 309 | .SUCCESS => return, |
| 310 | .INTR => continue, | 310 | .INTR => continue, |
| 311 | .BADF => unreachable, // Can be reached if the fd refers to a directory opened without `OpenDirOptions{ .iterate = true }` | 311 | .BADF => unreachable, // Can be reached if the fd refers to a non-iterable directory. |
| 312 | 312 | ||
| 313 | .FAULT => unreachable, | 313 | .FAULT => unreachable, |
| 314 | .INVAL => unreachable, | 314 | .INVAL => unreachable, |
| ... | @@ -349,7 +349,7 @@ pub fn fchown(fd: fd_t, owner: ?uid_t, group: ?gid_t) FChownError!void { | ... | @@ -349,7 +349,7 @@ pub fn fchown(fd: fd_t, owner: ?uid_t, group: ?gid_t) FChownError!void { |
| 349 | switch (system.getErrno(res)) { | 349 | switch (system.getErrno(res)) { |
| 350 | .SUCCESS => return, | 350 | .SUCCESS => return, |
| 351 | .INTR => continue, | 351 | .INTR => continue, |
| 352 | .BADF => unreachable, // Can be reached if the fd refers to a directory opened without `OpenDirOptions{ .iterate = true }` | 352 | .BADF => unreachable, // Can be reached if the fd refers to a non-iterable directory. |
| 353 | 353 | ||
| 354 | .FAULT => unreachable, | 354 | .FAULT => unreachable, |
| 355 | .INVAL => unreachable, | 355 | .INVAL => unreachable, |
lib/std/testing.zig+38| ... | @@ -363,6 +363,22 @@ pub const TmpDir = struct { | ... | @@ -363,6 +363,22 @@ pub const TmpDir = struct { |
| 363 | } | 363 | } |
| 364 | }; | 364 | }; |
| 365 | 365 | ||
| 366 | pub const TmpIterableDir = struct { | ||
| 367 | iterable_dir: std.fs.IterableDir, | ||
| 368 | parent_dir: std.fs.Dir, | ||
| 369 | sub_path: [sub_path_len]u8, | ||
| 370 | |||
| 371 | const random_bytes_count = 12; | ||
| 372 | const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count); | ||
| 373 | |||
| 374 | pub fn cleanup(self: *TmpIterableDir) void { | ||
| 375 | self.iterable_dir.close(); | ||
| 376 | self.parent_dir.deleteTree(&self.sub_path) catch {}; | ||
| 377 | self.parent_dir.close(); | ||
| 378 | self.* = undefined; | ||
| 379 | } | ||
| 380 | }; | ||
| 381 | |||
| 366 | fn getCwdOrWasiPreopen() std.fs.Dir { | 382 | fn getCwdOrWasiPreopen() std.fs.Dir { |
| 367 | if (builtin.os.tag == .wasi and !builtin.link_libc) { | 383 | if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| 368 | var preopens = std.fs.wasi.PreopenList.init(allocator); | 384 | var preopens = std.fs.wasi.PreopenList.init(allocator); |
| ... | @@ -400,6 +416,28 @@ pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir { | ... | @@ -400,6 +416,28 @@ pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir { |
| 400 | }; | 416 | }; |
| 401 | } | 417 | } |
| 402 | 418 | ||
| 419 | pub fn tmpIterableDir(opts: std.fs.Dir.OpenDirOptions) TmpIterableDir { | ||
| 420 | var random_bytes: [TmpIterableDir.random_bytes_count]u8 = undefined; | ||
| 421 | std.crypto.random.bytes(&random_bytes); | ||
| 422 | var sub_path: [TmpIterableDir.sub_path_len]u8 = undefined; | ||
| 423 | _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes); | ||
| 424 | |||
| 425 | var cwd = getCwdOrWasiPreopen(); | ||
| 426 | var cache_dir = cwd.makeOpenPath("zig-cache", .{}) catch | ||
| 427 | @panic("unable to make tmp dir for testing: unable to make and open zig-cache dir"); | ||
| 428 | defer cache_dir.close(); | ||
| 429 | var parent_dir = cache_dir.makeOpenPath("tmp", .{}) catch | ||
| 430 | @panic("unable to make tmp dir for testing: unable to make and open zig-cache/tmp dir"); | ||
| 431 | var dir = parent_dir.makeOpenPathIterable(&sub_path, opts) catch | ||
| 432 | @panic("unable to make tmp dir for testing: unable to make and open the tmp dir"); | ||
| 433 | |||
| 434 | return .{ | ||
| 435 | .iterable_dir = dir, | ||
| 436 | .parent_dir = parent_dir, | ||
| 437 | .sub_path = sub_path, | ||
| 438 | }; | ||
| 439 | } | ||
| 440 | |||
| 403 | test "expectEqual nested array" { | 441 | test "expectEqual nested array" { |
| 404 | const a = [2][2]f32{ | 442 | const a = [2][2]f32{ |
| 405 | [_]f32{ 1.0, 0.0 }, | 443 | [_]f32{ 1.0, 0.0 }, |
src/test.zig+5-5| ... | @@ -54,7 +54,7 @@ test { | ... | @@ -54,7 +54,7 @@ test { |
| 54 | std.fs.path.dirname(@src().file).?, "..", "test", "cases", | 54 | std.fs.path.dirname(@src().file).?, "..", "test", "cases", |
| 55 | }); | 55 | }); |
| 56 | 56 | ||
| 57 | var dir = try std.fs.cwd().openDir(dir_path, .{ .iterate = true }); | 57 | var dir = try std.fs.cwd().openIterableDir(dir_path, .{}); |
| 58 | defer dir.close(); | 58 | defer dir.close(); |
| 59 | 59 | ||
| 60 | ctx.addTestCasesFromDir(dir); | 60 | ctx.addTestCasesFromDir(dir); |
| ... | @@ -1080,7 +1080,7 @@ pub const TestContext = struct { | ... | @@ -1080,7 +1080,7 @@ pub const TestContext = struct { |
| 1080 | /// Each file should include a test manifest as a contiguous block of comments at | 1080 | /// Each file should include a test manifest as a contiguous block of comments at |
| 1081 | /// the end of the file. The first line should be the test type, followed by a set of | 1081 | /// the end of the file. The first line should be the test type, followed by a set of |
| 1082 | /// key-value config values, followed by a blank line, then the expected output. | 1082 | /// key-value config values, followed by a blank line, then the expected output. |
| 1083 | pub fn addTestCasesFromDir(ctx: *TestContext, dir: std.fs.Dir) void { | 1083 | pub fn addTestCasesFromDir(ctx: *TestContext, dir: std.fs.IterableDir) void { |
| 1084 | var current_file: []const u8 = "none"; | 1084 | var current_file: []const u8 = "none"; |
| 1085 | ctx.addTestCasesFromDirInner(dir, &current_file) catch |err| { | 1085 | ctx.addTestCasesFromDirInner(dir, &current_file) catch |err| { |
| 1086 | std.debug.panic("test harness failed to process file '{s}': {s}\n", .{ | 1086 | std.debug.panic("test harness failed to process file '{s}': {s}\n", .{ |
| ... | @@ -1091,12 +1091,12 @@ pub const TestContext = struct { | ... | @@ -1091,12 +1091,12 @@ pub const TestContext = struct { |
| 1091 | 1091 | ||
| 1092 | fn addTestCasesFromDirInner( | 1092 | fn addTestCasesFromDirInner( |
| 1093 | ctx: *TestContext, | 1093 | ctx: *TestContext, |
| 1094 | dir: std.fs.Dir, | 1094 | iterable_dir: std.fs.IterableDir, |
| 1095 | /// This is kept up to date with the currently being processed file so | 1095 | /// This is kept up to date with the currently being processed file so |
| 1096 | /// that if any errors occur the caller knows it happened during this file. | 1096 | /// that if any errors occur the caller knows it happened during this file. |
| 1097 | current_file: *[]const u8, | 1097 | current_file: *[]const u8, |
| 1098 | ) !void { | 1098 | ) !void { |
| 1099 | var it = try dir.intoIterable().walk(ctx.arena); | 1099 | var it = try iterable_dir.walk(ctx.arena); |
| 1100 | var filenames = std.ArrayList([]const u8).init(ctx.arena); | 1100 | var filenames = std.ArrayList([]const u8).init(ctx.arena); |
| 1101 | 1101 | ||
| 1102 | while (try it.next()) |entry| { | 1102 | while (try it.next()) |entry| { |
| ... | @@ -1123,7 +1123,7 @@ pub const TestContext = struct { | ... | @@ -1123,7 +1123,7 @@ pub const TestContext = struct { |
| 1123 | current_file.* = filename; | 1123 | current_file.* = filename; |
| 1124 | 1124 | ||
| 1125 | const max_file_size = 10 * 1024 * 1024; | 1125 | const max_file_size = 10 * 1024 * 1024; |
| 1126 | const src = try dir.readFileAllocOptions(ctx.arena, filename, max_file_size, null, 1, 0); | 1126 | const src = try iterable_dir.dir.readFileAllocOptions(ctx.arena, filename, max_file_size, null, 1, 0); |
| 1127 | 1127 | ||
| 1128 | // Parse the manifest | 1128 | // Parse the manifest |
| 1129 | var manifest = try TestManifest.parse(ctx.arena, src); | 1129 | var manifest = try TestManifest.parse(ctx.arena, src); |