zig/test/standalone/posix/relpaths.zig
2025-09-09 22:07:44 -07:00

95 lines
3.1 KiB
Zig

// Test relative paths through POSIX APIS. These tests have to change the cwd, so
// they shouldn't be Zig unit tests.
const std = @import("std");
const builtin = @import("builtin");
pub fn main() !void {
if (builtin.target.os.tag == .wasi) return; // Can link, but can't change into tmpDir
var Allocator = std.heap.DebugAllocator(.{}){};
const a = Allocator.allocator();
defer std.debug.assert(Allocator.deinit() == .ok);
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
// Want to test relative paths, so cd into the tmpdir for these tests
try tmp.dir.setAsCwd();
try test_symlink(a, tmp);
try test_link(tmp);
}
fn test_symlink(a: std.mem.Allocator, tmp: std.testing.TmpDir) !void {
const target_name = "symlink-target";
const symlink_name = "symlinker";
// Create the target file
try tmp.dir.writeFile(.{ .sub_path = target_name, .data = "nonsense" });
if (builtin.target.os.tag == .windows) {
const wtarget_name = try std.unicode.wtf8ToWtf16LeAllocZ(a, target_name);
const wsymlink_name = try std.unicode.wtf8ToWtf16LeAllocZ(a, symlink_name);
defer a.free(wtarget_name);
defer a.free(wsymlink_name);
std.os.windows.CreateSymbolicLink(tmp.dir.fd, wsymlink_name, wtarget_name, false) catch |err| switch (err) {
// Symlink requires admin privileges on windows, so this test can legitimately fail.
error.AccessDenied => return,
else => return err,
};
} else {
try std.posix.symlink(target_name, symlink_name);
}
var buffer: [std.fs.max_path_bytes]u8 = undefined;
const given = try std.posix.readlink(symlink_name, buffer[0..]);
try std.testing.expectEqualStrings(target_name, given);
}
fn test_link(tmp: std.testing.TmpDir) !void {
switch (builtin.target.os.tag) {
.linux, .solaris, .illumos => {},
else => return,
}
if ((builtin.cpu.arch == .riscv32 or builtin.cpu.arch.isLoongArch()) and builtin.target.os.tag == .linux and !builtin.link_libc) {
return; // No `fstat()`.
}
if (builtin.cpu.arch.isMIPS64()) {
return; // `nstat.nlink` assertion is failing with LLVM 20+ for unclear reasons.
}
const target_name = "link-target";
const link_name = "newlink";
try tmp.dir.writeFile(.{ .sub_path = target_name, .data = "example" });
// Test 1: create the relative link from inside tmp
try std.posix.link(target_name, link_name);
// Verify
const efd = try tmp.dir.openFile(target_name, .{});
defer efd.close();
const nfd = try tmp.dir.openFile(link_name, .{});
defer nfd.close();
{
const estat = try std.posix.fstat(efd.handle);
const nstat = try std.posix.fstat(nfd.handle);
try std.testing.expectEqual(estat.ino, nstat.ino);
try std.testing.expectEqual(@as(@TypeOf(nstat.nlink), 2), nstat.nlink);
}
// Test 2: Remove the link and see the stats update
try std.posix.unlink(link_name);
{
const estat = try std.posix.fstat(efd.handle);
try std.testing.expectEqual(@as(@TypeOf(estat.nlink), 1), estat.nlink);
}
}