authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2024-02-29 17:54:04-08:00
committergravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2024-02-29 17:55:27-08:00
logf1dd1ee5ed43732a7a671fb438810462136b83eb
tree20a064e8b58cbe1cec3b4606659701ff3f8af228
parente80d4bc6f88d477af802c9e4db413814d0888c78

fs/test: Make testWithAllSupportedPathTypes also test all supported path separators

Now, all the tests that use `testWithAllSupportedPathTypes` will also run each test with both `/` and `\` as the path separator on Windows. Also, removes the now-redundant "Dir.symLink with relative target that has a / path separator" since the same thing is now tested in the "Dir.readLink" test

1 files changed, 49 insertions(+), 25 deletions(-)

lib/std/fs/test.zig+49-25
...@@ -72,15 +72,17 @@ const PathType = enum {...@@ -72,15 +72,17 @@ const PathType = enum {
7272
73const TestContext = struct {73const TestContext = struct {
74 path_type: PathType,74 path_type: PathType,
75 path_sep: u8,
75 arena: ArenaAllocator,76 arena: ArenaAllocator,
76 tmp: testing.TmpDir,77 tmp: testing.TmpDir,
77 dir: std.fs.Dir,78 dir: std.fs.Dir,
78 transform_fn: *const PathType.TransformFn,79 transform_fn: *const PathType.TransformFn,
7980
80 pub fn init(path_type: PathType, allocator: mem.Allocator, transform_fn: *const PathType.TransformFn) TestContext {81 pub fn init(path_type: PathType, path_sep: u8, allocator: mem.Allocator, transform_fn: *const PathType.TransformFn) TestContext {
81 const tmp = tmpDir(.{ .iterate = true });82 const tmp = tmpDir(.{ .iterate = true });
82 return .{83 return .{
83 .path_type = path_type,84 .path_type = path_type,
85 .path_sep = path_sep,
84 .arena = ArenaAllocator.init(allocator),86 .arena = ArenaAllocator.init(allocator),
85 .tmp = tmp,87 .tmp = tmp,
86 .dir = tmp.dir,88 .dir = tmp.dir,
...@@ -93,11 +95,37 @@ const TestContext = struct {...@@ -93,11 +95,37 @@ const TestContext = struct {
93 self.tmp.cleanup();95 self.tmp.cleanup();
94 }96 }
9597
96 /// Returns the `relative_path` transformed into the TestContext's `path_type`.98 /// Returns the `relative_path` transformed into the TestContext's `path_type`,
99 /// with any supported path separators replaced by `path_sep`.
97 /// The result is allocated by the TestContext's arena and will be free'd during100 /// The result is allocated by the TestContext's arena and will be free'd during
98 /// `TestContext.deinit`.101 /// `TestContext.deinit`.
99 pub fn transformPath(self: *TestContext, relative_path: [:0]const u8) ![:0]const u8 {102 pub fn transformPath(self: *TestContext, relative_path: [:0]const u8) ![:0]const u8 {
100 return self.transform_fn(self.arena.allocator(), self.dir, relative_path);103 const allocator = self.arena.allocator();
104 const transformed_path = try self.transform_fn(allocator, self.dir, relative_path);
105 if (builtin.os.tag == .windows) {
106 const transformed_sep_path = try allocator.dupeZ(u8, transformed_path);
107 std.mem.replaceScalar(u8, transformed_sep_path, switch (self.path_sep) {
108 '/' => '\\',
109 '\\' => '/',
110 else => unreachable,
111 }, self.path_sep);
112 return transformed_sep_path;
113 }
114 return transformed_path;
115 }
116
117 /// Replaces any path separators with the canonical path separator for the platform
118 /// (e.g. all path separators are converted to `\` on Windows).
119 /// If path separators are replaced, then the result is allocated by the
120 /// TestContext's arena and will be free'd during `TestContext.deinit`.
121 pub fn toCanonicalPathSep(self: *TestContext, path: [:0]const u8) ![:0]const u8 {
122 if (builtin.os.tag == .windows) {
123 const allocator = self.arena.allocator();
124 const transformed_sep_path = try allocator.dupeZ(u8, path);
125 std.mem.replaceScalar(u8, transformed_sep_path, '/', '\\');
126 return transformed_sep_path;
127 }
128 return path;
101 }129 }
102};130};
103131
...@@ -106,15 +134,19 @@ const TestContext = struct {...@@ -106,15 +134,19 @@ const TestContext = struct {
106/// and will be passed a TestContext that can transform a relative path into the path type under test.134/// and will be passed a TestContext that can transform a relative path into the path type under test.
107/// The TestContext will also create a tmp directory for you (and will clean it up for you too).135/// The TestContext will also create a tmp directory for you (and will clean it up for you too).
108fn testWithAllSupportedPathTypes(test_func: anytype) !void {136fn testWithAllSupportedPathTypes(test_func: anytype) !void {
109 try testWithPathTypeIfSupported(.relative, test_func);137 try testWithPathTypeIfSupported(.relative, '/', test_func);
110 try testWithPathTypeIfSupported(.absolute, test_func);138 try testWithPathTypeIfSupported(.absolute, '/', test_func);
111 try testWithPathTypeIfSupported(.unc, test_func);139 try testWithPathTypeIfSupported(.unc, '/', test_func);
140 try testWithPathTypeIfSupported(.relative, '\\', test_func);
141 try testWithPathTypeIfSupported(.absolute, '\\', test_func);
142 try testWithPathTypeIfSupported(.unc, '\\', test_func);
112}143}
113144
114fn testWithPathTypeIfSupported(comptime path_type: PathType, test_func: anytype) !void {145fn testWithPathTypeIfSupported(comptime path_type: PathType, comptime path_sep: u8, test_func: anytype) !void {
115 if (!(comptime path_type.isSupported(builtin.os))) return;146 if (!(comptime path_type.isSupported(builtin.os))) return;
147 if (!(comptime fs.path.isSep(path_sep))) return;
116148
117 var ctx = TestContext.init(path_type, testing.allocator, path_type.getTransformFn());149 var ctx = TestContext.init(path_type, path_sep, testing.allocator, path_type.getTransformFn());
118 defer ctx.deinit();150 defer ctx.deinit();
119151
120 try test_func(&ctx);152 try test_func(&ctx);
...@@ -148,20 +180,25 @@ test "Dir.readLink" {...@@ -148,20 +180,25 @@ test "Dir.readLink" {
148 const dir_target_path = try ctx.transformPath("subdir");180 const dir_target_path = try ctx.transformPath("subdir");
149 try ctx.dir.makeDir(dir_target_path);181 try ctx.dir.makeDir(dir_target_path);
150182
183 // On Windows, symlink targets always use the canonical path separator
184 const canonical_file_target_path = try ctx.toCanonicalPathSep(file_target_path);
185 const canonical_dir_target_path = try ctx.toCanonicalPathSep(dir_target_path);
186
151 // test 1: symlink to a file187 // test 1: symlink to a file
152 try setupSymlink(ctx.dir, file_target_path, "symlink1", .{});188 try setupSymlink(ctx.dir, file_target_path, "symlink1", .{});
153 try testReadLink(ctx.dir, file_target_path, "symlink1");189 try testReadLink(ctx.dir, canonical_file_target_path, "symlink1");
154190
155 // test 2: symlink to a directory (can be different on Windows)191 // test 2: symlink to a directory (can be different on Windows)
156 try setupSymlink(ctx.dir, dir_target_path, "symlink2", .{ .is_directory = true });192 try setupSymlink(ctx.dir, dir_target_path, "symlink2", .{ .is_directory = true });
157 try testReadLink(ctx.dir, dir_target_path, "symlink2");193 try testReadLink(ctx.dir, canonical_dir_target_path, "symlink2");
158194
159 // test 3: relative path symlink195 // test 3: relative path symlink
160 const parent_file = ".." ++ fs.path.sep_str ++ "target.txt";196 const parent_file = ".." ++ fs.path.sep_str ++ "target.txt";
197 const canonical_parent_file = try ctx.toCanonicalPathSep(parent_file);
161 var subdir = try ctx.dir.makeOpenPath("subdir", .{});198 var subdir = try ctx.dir.makeOpenPath("subdir", .{});
162 defer subdir.close();199 defer subdir.close();
163 try setupSymlink(subdir, parent_file, "relative-link.txt", .{});200 try setupSymlink(subdir, canonical_parent_file, "relative-link.txt", .{});
164 try testReadLink(subdir, parent_file, "relative-link.txt");201 try testReadLink(subdir, canonical_parent_file, "relative-link.txt");
165 }202 }
166 }.impl);203 }.impl);
167}204}
...@@ -178,19 +215,6 @@ fn testReadLinkAbsolute(target_path: []const u8, symlink_path: []const u8) !void...@@ -178,19 +215,6 @@ fn testReadLinkAbsolute(target_path: []const u8, symlink_path: []const u8) !void
178 try testing.expectEqualStrings(target_path, given);215 try testing.expectEqualStrings(target_path, given);
179}216}
180217
181test "Dir.symLink with relative target that has a / path separator" {
182 var tmp = testing.tmpDir(.{});
183 defer tmp.cleanup();
184
185 try tmp.dir.makePath("a");
186 try tmp.dir.writeFile("a/file", "");
187 try tmp.dir.symLink("a/file", "symlink", .{});
188
189 const stat = try tmp.dir.statFile("symlink");
190 // statFile follows symlinks
191 try testing.expectEqual(File.Kind.file, stat.kind);
192}
193
194test "File.stat on a File that is a symlink returns Kind.sym_link" {218test "File.stat on a File that is a symlink returns Kind.sym_link" {
195 // This test requires getting a file descriptor of a symlink which219 // This test requires getting a file descriptor of a symlink which
196 // is not possible on all targets220 // is not possible on all targets