aboutsummaryrefslogtreecommitdiff
path: root/test/standalone/posix/relpaths.zig
blob: f7ced28cdbf1b654cd9921211393f112143c4bab (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
// 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, .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);
    }
}