@@ -10,6 +10,7 @@ const ArenaAllocator = std.heap.ArenaAllocator;
10
10
const Dir = std .fs .Dir ;
11
11
const File = std .fs .File ;
12
12
const tmpDir = testing .tmpDir ;
13
+ const SymLinkFlags = std .fs .Dir .SymLinkFlags ;
13
14
14
15
const PathType = enum {
15
16
relative ,
@@ -119,6 +120,25 @@ fn testWithAllSupportedPathTypes(test_func: anytype) !void {
119
120
}
120
121
}
121
122
123
+ // For use in test setup. If the symlink creation fails on Windows with
124
+ // AccessDenied, then make the test failure silent (it is not a Zig failure).
125
+ fn setupSymlink (dir : Dir , target : []const u8 , link : []const u8 , flags : SymLinkFlags ) ! void {
126
+ return dir .symLink (target , link , flags ) catch | err | switch (err ) {
127
+ // Symlink requires admin privileges on windows, so this test can legitimately fail.
128
+ error .AccessDenied = > if (builtin .os .tag == .windows ) return error .SkipZigTest else return err ,
129
+ else = > return err ,
130
+ };
131
+ }
132
+
133
+ // For use in test setup. If the symlink creation fails on Windows with
134
+ // AccessDenied, then make the test failure silent (it is not a Zig failure).
135
+ fn setupSymlinkAbsolute (target : []const u8 , link : []const u8 , flags : SymLinkFlags ) ! void {
136
+ return fs .symLinkAbsolute (target , link , flags ) catch | err | switch (err ) {
137
+ error .AccessDenied = > if (builtin .os .tag == .windows ) return error .SkipZigTest else return err ,
138
+ else = > return err ,
139
+ };
140
+ }
141
+
122
142
test "Dir.readLink" {
123
143
try testWithAllSupportedPathTypes (struct {
124
144
fn impl (ctx : * TestContext ) ! void {
@@ -128,31 +148,33 @@ test "Dir.readLink" {
128
148
const dir_target_path = try ctx .transformPath ("subdir" );
129
149
try ctx .dir .makeDir (dir_target_path );
130
150
131
- {
132
- // Create symbolic link by path
133
- ctx .dir .symLink (file_target_path , "symlink1" , .{}) catch | err | switch (err ) {
134
- // Symlink requires admin privileges on windows, so this test can legitimately fail.
135
- error .AccessDenied = > return error .SkipZigTest ,
136
- else = > return err ,
137
- };
138
- try testReadLink (ctx .dir , file_target_path , "symlink1" );
139
- }
140
- {
141
- // Create symbolic link by path
142
- ctx .dir .symLink (dir_target_path , "symlink2" , .{ .is_directory = true }) catch | err | switch (err ) {
143
- // Symlink requires admin privileges on windows, so this test can legitimately fail.
144
- error .AccessDenied = > return error .SkipZigTest ,
145
- else = > return err ,
146
- };
147
- try testReadLink (ctx .dir , dir_target_path , "symlink2" );
148
- }
151
+ // test 1: symlink to a file
152
+ try setupSymlink (ctx .dir , file_target_path , "symlink1" , .{});
153
+ try testReadLink (ctx .dir , file_target_path , "symlink1" );
154
+
155
+ // test 2: symlink to a directory (can be different on Windows)
156
+ try setupSymlink (ctx .dir , dir_target_path , "symlink2" , .{ .is_directory = true });
157
+ try testReadLink (ctx .dir , dir_target_path , "symlink2" );
158
+
159
+ // test 3: relative path symlink
160
+ const parent_file = ".." ++ fs .path .sep_str ++ "target.txt" ;
161
+ var subdir = try ctx .dir .makeOpenPath ("subdir" , .{});
162
+ defer subdir .close ();
163
+ try setupSymlink (subdir , parent_file , "relative-link.txt" , .{});
164
+ try testReadLink (subdir , parent_file , "relative-link.txt" );
149
165
}
150
166
}.impl );
151
167
}
152
168
153
169
fn testReadLink (dir : Dir , target_path : []const u8 , symlink_path : []const u8 ) ! void {
154
170
var buffer : [fs .MAX_PATH_BYTES ]u8 = undefined ;
155
- const given = try dir .readLink (symlink_path , buffer [0.. ]);
171
+ const actual = try dir .readLink (symlink_path , buffer [0.. ]);
172
+ try testing .expectEqualStrings (target_path , actual );
173
+ }
174
+
175
+ fn testReadLinkAbsolute (target_path : []const u8 , symlink_path : []const u8 ) ! void {
176
+ var buffer : [fs .MAX_PATH_BYTES ]u8 = undefined ;
177
+ const given = try fs .readLinkAbsolute (symlink_path , buffer [0.. ]);
156
178
try testing .expectEqualStrings (target_path , given );
157
179
}
158
180
@@ -169,11 +191,7 @@ test "File.stat on a File that is a symlink returns Kind.sym_link" {
169
191
const dir_target_path = try ctx .transformPath ("subdir" );
170
192
try ctx .dir .makeDir (dir_target_path );
171
193
172
- ctx .dir .symLink (dir_target_path , "symlink" , .{ .is_directory = true }) catch | err | switch (err ) {
173
- // Symlink requires admin privileges on windows, so this test can legitimately fail.
174
- error .AccessDenied = > return error .SkipZigTest ,
175
- else = > return err ,
176
- };
194
+ try setupSymlink (ctx .dir , dir_target_path , "symlink" , .{ .is_directory = true });
177
195
178
196
var symlink = switch (builtin .target .os .tag ) {
179
197
.windows = > windows_symlink : {
@@ -238,23 +256,6 @@ test "File.stat on a File that is a symlink returns Kind.sym_link" {
238
256
}.impl );
239
257
}
240
258
241
- test "relative symlink to parent directory" {
242
- var tmp = tmpDir (.{});
243
- defer tmp .cleanup ();
244
-
245
- var subdir = try tmp .dir .makeOpenPath ("subdir" , .{});
246
- defer subdir .close ();
247
-
248
- const expected_link_name = ".." ++ std .fs .path .sep_str ++ "b.txt" ;
249
-
250
- try subdir .symLink (expected_link_name , "a.txt" , .{});
251
-
252
- var buf : [1000 ]u8 = undefined ;
253
- const link_name = try subdir .readLink ("a.txt" , & buf );
254
-
255
- try testing .expectEqualStrings (expected_link_name , link_name );
256
- }
257
-
258
259
test "openDir" {
259
260
try testWithAllSupportedPathTypes (struct {
260
261
fn impl (ctx : * TestContext ) ! void {
@@ -373,33 +374,19 @@ test "readLinkAbsolute" {
373
374
const symlink_path = try fs .path .join (allocator , &.{ base_path , "symlink1" });
374
375
375
376
// Create symbolic link by path
376
- fs .symLinkAbsolute (target_path , symlink_path , .{}) catch | err | switch (err ) {
377
- // Symlink requires admin privileges on windows, so this test can legitimately fail.
378
- error .AccessDenied = > return error .SkipZigTest ,
379
- else = > return err ,
380
- };
377
+ try setupSymlinkAbsolute (target_path , symlink_path , .{});
381
378
try testReadLinkAbsolute (target_path , symlink_path );
382
379
}
383
380
{
384
381
const target_path = try fs .path .join (allocator , &.{ base_path , "subdir" });
385
382
const symlink_path = try fs .path .join (allocator , &.{ base_path , "symlink2" });
386
383
387
- // Create symbolic link by path
388
- fs .symLinkAbsolute (target_path , symlink_path , .{ .is_directory = true }) catch | err | switch (err ) {
389
- // Symlink requires admin privileges on windows, so this test can legitimately fail.
390
- error .AccessDenied = > return error .SkipZigTest ,
391
- else = > return err ,
392
- };
384
+ // Create symbolic link to a directory by path
385
+ try setupSymlinkAbsolute (target_path , symlink_path , .{ .is_directory = true });
393
386
try testReadLinkAbsolute (target_path , symlink_path );
394
387
}
395
388
}
396
389
397
- fn testReadLinkAbsolute (target_path : []const u8 , symlink_path : []const u8 ) ! void {
398
- var buffer : [fs .MAX_PATH_BYTES ]u8 = undefined ;
399
- const given = try fs .readLinkAbsolute (symlink_path , buffer [0.. ]);
400
- try testing .expectEqualStrings (target_path , given );
401
- }
402
-
403
390
test "Dir.Iterator" {
404
391
var tmp_dir = tmpDir (.{ .iterate = true });
405
392
defer tmp_dir .cleanup ();
@@ -1005,11 +992,7 @@ test "deleteTree does not follow symlinks" {
1005
992
var a = try tmp .dir .makeOpenPath ("a" , .{});
1006
993
defer a .close ();
1007
994
1008
- a .symLink ("../b" , "b" , .{ .is_directory = true }) catch | err | switch (err ) {
1009
- // Symlink requires admin privileges on windows, so this test can legitimately fail.
1010
- error .AccessDenied = > return error .SkipZigTest ,
1011
- else = > return err ,
1012
- };
995
+ try setupSymlink (a , "../b" , "b" , .{ .is_directory = true });
1013
996
}
1014
997
1015
998
try tmp .dir .deleteTree ("a" );
@@ -1024,23 +1007,15 @@ test "deleteTree on a symlink" {
1024
1007
1025
1008
// Symlink to a file
1026
1009
try tmp .dir .writeFile ("file" , "" );
1027
- tmp .dir .symLink ("file" , "filelink" , .{}) catch | err | switch (err ) {
1028
- // Symlink requires admin privileges on windows, so this test can legitimately fail.
1029
- error .AccessDenied = > return error .SkipZigTest ,
1030
- else = > return err ,
1031
- };
1010
+ try setupSymlink (tmp .dir , "file" , "filelink" , .{});
1032
1011
1033
1012
try tmp .dir .deleteTree ("filelink" );
1034
1013
try testing .expectError (error .FileNotFound , tmp .dir .access ("filelink" , .{}));
1035
1014
try tmp .dir .access ("file" , .{});
1036
1015
1037
1016
// Symlink to a directory
1038
1017
try tmp .dir .makePath ("dir" );
1039
- tmp .dir .symLink ("dir" , "dirlink" , .{ .is_directory = true }) catch | err | switch (err ) {
1040
- // Symlink requires admin privileges on windows, so this test can legitimately fail.
1041
- error .AccessDenied = > return error .SkipZigTest ,
1042
- else = > return err ,
1043
- };
1018
+ try setupSymlink (tmp .dir , "dir" , "dirlink" , .{ .is_directory = true });
1044
1019
1045
1020
try tmp .dir .deleteTree ("dirlink" );
1046
1021
try testing .expectError (error .FileNotFound , tmp .dir .access ("dirlink" , .{}));
@@ -1123,7 +1098,7 @@ test "makepath through existing valid symlink" {
1123
1098
defer tmp .cleanup ();
1124
1099
1125
1100
try tmp .dir .makeDir ("realfolder" );
1126
- try tmp .dir . symLink ( "." ++ fs .path .sep_str ++ "realfolder" , "working-symlink" , .{});
1101
+ try setupSymlink ( tmp .dir , "." ++ fs .path .sep_str ++ "realfolder" , "working-symlink" , .{});
1127
1102
1128
1103
try tmp .dir .makePath ("working-symlink" ++ fs .path .sep_str ++ "in-realfolder" );
1129
1104
0 commit comments