authorgravatar for pat.github@tullmann.orgPat Tullmann <pat.github@tullmann.org> 2025-09-08 22:52:32-07:00
committergravatar for pat.github@tullmann.orgPat Tullmann <pat.github@tullmann.org> 2025-09-09 22:07:44-07:00
logca09629beeb899c418b16e5c180daf4d3f5ba3f9
tree394139a4fb0feb40bd028cc131d0a58106c55d94
parentaa1d2adffc78167b38b34afeb683c152ad5767f4

standalone posix tests for relative path linking


2 files changed, 94 insertions(+), 89 deletions(-)

lib/std/posix/test.zig-88
......@@ -167,45 +167,6 @@ test "openat smoke test" {
167167 }
168168}
169169
170test "symlink with relative paths" {
171 if (native_os == .wasi) return error.SkipZigTest; // Can symlink, but can't change into tmpDir
172
173 var tmp = tmpDir(.{});
174 defer tmp.cleanup();
175
176 const target_name = "symlink-target";
177 const symlink_name = "symlinker";
178
179 // Restore default CWD at end of test.
180 const orig_cwd = try fs.cwd().openDir(".", .{});
181 defer orig_cwd.setAsCwd() catch unreachable;
182
183 // Create the target file
184 try tmp.dir.writeFile(.{ .sub_path = target_name, .data = "nonsense" });
185
186 // Want to test relative paths, so cd into the tmpdir for this test
187 try tmp.dir.setAsCwd();
188
189 if (native_os == .windows) {
190 const wtarget_name = try std.unicode.wtf8ToWtf16LeAllocZ(a, target_name);
191 const wsymlink_name = try std.unicode.wtf8ToWtf16LeAllocZ(a, symlink_name);
192 defer a.free(wtarget_name);
193 defer a.free(wsymlink_name);
194
195 std.os.windows.CreateSymbolicLink(tmp.dir.fd, wsymlink_name, wtarget_name, false) catch |err| switch (err) {
196 // Symlink requires admin privileges on windows, so this test can legitimately fail.
197 error.AccessDenied => return error.SkipZigTest,
198 else => return err,
199 };
200 } else {
201 try posix.symlink(target_name, symlink_name);
202 }
203
204 var buffer: [fs.max_path_bytes]u8 = undefined;
205 const given = try posix.readlink(symlink_name, buffer[0..]);
206 try expect(mem.eql(u8, target_name, given));
207}
208
209170test "readlink on Windows" {
210171 if (native_os != .windows) return error.SkipZigTest;
211172
......@@ -220,55 +181,6 @@ fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void {
220181 try expect(mem.eql(u8, target_path, given));
221182}
222183
223test "link with relative paths" {
224 if (native_os == .wasi) return error.SkipZigTest; // Can link, but can't change into tmpDir
225 if ((builtin.cpu.arch == .riscv32 or builtin.cpu.arch.isLoongArch()) and builtin.os.tag == .linux and !builtin.link_libc) return error.SkipZigTest; // No `fstat()`.
226 if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; // `nstat.nlink` assertion is failing with LLVM 20+ for unclear reasons.
227
228 switch (native_os) {
229 .wasi, .linux, .solaris, .illumos => {},
230 else => return error.SkipZigTest,
231 }
232
233 var tmp = tmpDir(.{});
234 defer tmp.cleanup();
235
236 // Restore default CWD at end of test.
237 const orig_cwd = try fs.cwd().openDir(".", .{});
238 defer orig_cwd.setAsCwd() catch unreachable;
239
240 const target_name = "link-target";
241 const link_name = "newlink";
242
243 try tmp.dir.writeFile(.{ .sub_path = target_name, .data = "example" });
244
245 // Test 1: create the relative link from inside tmp
246 try tmp.dir.setAsCwd();
247 try posix.link(target_name, link_name);
248
249 // Verify
250 const efd = try tmp.dir.openFile(target_name, .{});
251 defer efd.close();
252
253 const nfd = try tmp.dir.openFile(link_name, .{});
254 defer nfd.close();
255
256 {
257 const estat = try posix.fstat(efd.handle);
258 const nstat = try posix.fstat(nfd.handle);
259 try testing.expectEqual(estat.ino, nstat.ino);
260 try testing.expectEqual(@as(@TypeOf(nstat.nlink), 2), nstat.nlink);
261 }
262
263 // Test 2: Remove the link and see the stats update
264 try posix.unlink(link_name);
265
266 {
267 const estat = try posix.fstat(efd.handle);
268 try testing.expectEqual(@as(@TypeOf(estat.nlink), 1), estat.nlink);
269 }
270}
271
272184test "linkat with different directories" {
273185 if ((builtin.cpu.arch == .riscv32 or builtin.cpu.arch.isLoongArch()) and builtin.os.tag == .linux and !builtin.link_libc) return error.SkipZigTest; // No `fstatat()`.
274186 if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; // `nstat.nlink` assertion is failing with LLVM 20+ for unclear reasons.
test/standalone/posix/relpaths.zig+94-1
......@@ -1 +1,94 @@
1pub fn main() !void {}
1// Test relative paths through POSIX APIS. These tests have to change the cwd, so
2// they shouldn't be Zig unit tests.
3
4const std = @import("std");
5const builtin = @import("builtin");
6
7pub fn main() !void {
8 if (builtin.target.os.tag == .wasi) return; // Can link, but can't change into tmpDir
9
10 var Allocator = std.heap.DebugAllocator(.{}){};
11 const a = Allocator.allocator();
12 defer std.debug.assert(Allocator.deinit() == .ok);
13
14 var tmp = std.testing.tmpDir(.{});
15 defer tmp.cleanup();
16
17 // Want to test relative paths, so cd into the tmpdir for these tests
18 try tmp.dir.setAsCwd();
19
20 try test_symlink(a, tmp);
21 try test_link(tmp);
22}
23
24fn test_symlink(a: std.mem.Allocator, tmp: std.testing.TmpDir) !void {
25 const target_name = "symlink-target";
26 const symlink_name = "symlinker";
27
28 // Create the target file
29 try tmp.dir.writeFile(.{ .sub_path = target_name, .data = "nonsense" });
30
31 if (builtin.target.os.tag == .windows) {
32 const wtarget_name = try std.unicode.wtf8ToWtf16LeAllocZ(a, target_name);
33 const wsymlink_name = try std.unicode.wtf8ToWtf16LeAllocZ(a, symlink_name);
34 defer a.free(wtarget_name);
35 defer a.free(wsymlink_name);
36
37 std.os.windows.CreateSymbolicLink(tmp.dir.fd, wsymlink_name, wtarget_name, false) catch |err| switch (err) {
38 // Symlink requires admin privileges on windows, so this test can legitimately fail.
39 error.AccessDenied => return,
40 else => return err,
41 };
42 } else {
43 try std.posix.symlink(target_name, symlink_name);
44 }
45
46 var buffer: [std.fs.max_path_bytes]u8 = undefined;
47 const given = try std.posix.readlink(symlink_name, buffer[0..]);
48 try std.testing.expectEqualStrings(target_name, given);
49}
50
51fn test_link(tmp: std.testing.TmpDir) !void {
52 switch (builtin.target.os.tag) {
53 .linux, .solaris, .illumos => {},
54 else => return,
55 }
56
57 if ((builtin.cpu.arch == .riscv32 or builtin.cpu.arch.isLoongArch()) and builtin.target.os.tag == .linux and !builtin.link_libc) {
58 return; // No `fstat()`.
59 }
60
61 if (builtin.cpu.arch.isMIPS64()) {
62 return; // `nstat.nlink` assertion is failing with LLVM 20+ for unclear reasons.
63 }
64
65 const target_name = "link-target";
66 const link_name = "newlink";
67
68 try tmp.dir.writeFile(.{ .sub_path = target_name, .data = "example" });
69
70 // Test 1: create the relative link from inside tmp
71 try std.posix.link(target_name, link_name);
72
73 // Verify
74 const efd = try tmp.dir.openFile(target_name, .{});
75 defer efd.close();
76
77 const nfd = try tmp.dir.openFile(link_name, .{});
78 defer nfd.close();
79
80 {
81 const estat = try std.posix.fstat(efd.handle);
82 const nstat = try std.posix.fstat(nfd.handle);
83 try std.testing.expectEqual(estat.ino, nstat.ino);
84 try std.testing.expectEqual(@as(@TypeOf(nstat.nlink), 2), nstat.nlink);
85 }
86
87 // Test 2: Remove the link and see the stats update
88 try std.posix.unlink(link_name);
89
90 {
91 const estat = try std.posix.fstat(efd.handle);
92 try std.testing.expectEqual(@as(@TypeOf(estat.nlink), 1), estat.nlink);
93 }
94}