| ... | @@ -44,13 +44,12 @@ test "check WASI CWD" { | ... | @@ -44,13 +44,12 @@ test "check WASI CWD" { |
| 44 | } | 44 | } |
| 45 | } | 45 | } |
| 46 | | 46 | |
| 47 | test "chdir smoke test" { | 47 | test "chdir absolute parent" { |
| 48 | if (native_os == .wasi) return error.SkipZigTest; | 48 | if (native_os == .wasi) return error.SkipZigTest; |
| 49 | | 49 | |
| 50 | if (true) { | 50 | // Restore default CWD at end of test. |
| 51 | // https://github.com/ziglang/zig/issues/14968 | 51 | const orig_cwd = try fs.cwd().openDir(".", .{}); |
| 52 | return error.SkipZigTest; | 52 | defer orig_cwd.setAsCwd() catch unreachable; |
| 53 | } | | |
| 54 | | 53 | |
| 55 | // Get current working directory path | 54 | // Get current working directory path |
| 56 | var old_cwd_buf: [fs.max_path_bytes]u8 = undefined; | 55 | var old_cwd_buf: [fs.max_path_bytes]u8 = undefined; |
| ... | @@ -65,47 +64,46 @@ test "chdir smoke test" { | ... | @@ -65,47 +64,46 @@ test "chdir smoke test" { |
| 65 | } | 64 | } |
| 66 | | 65 | |
| 67 | // Next, change current working directory to one level above | 66 | // Next, change current working directory to one level above |
| 68 | if (native_os != .wasi) { // WASI does not support navigating outside of Preopens | 67 | const parent = fs.path.dirname(old_cwd) orelse unreachable; // old_cwd should be absolute |
| 69 | const parent = fs.path.dirname(old_cwd) orelse unreachable; // old_cwd should be absolute | 68 | try posix.chdir(parent); |
| 70 | try posix.chdir(parent); | | |
| 71 | | 69 | |
| 72 | // Restore cwd because process may have other tests that do not tolerate chdir. | 70 | var new_cwd_buf: [fs.max_path_bytes]u8 = undefined; |
| 73 | defer posix.chdir(old_cwd) catch unreachable; | 71 | const new_cwd = try posix.getcwd(new_cwd_buf[0..]); |
| | 72 | try expect(mem.eql(u8, parent, new_cwd)); |
| | 73 | } |
| 74 | | 74 | |
| 75 | var new_cwd_buf: [fs.max_path_bytes]u8 = undefined; | 75 | test "chdir relative" { |
| 76 | const new_cwd = try posix.getcwd(new_cwd_buf[0..]); | 76 | if (native_os == .wasi) return error.SkipZigTest; |
| 77 | try expect(mem.eql(u8, parent, new_cwd)); | | |
| 78 | } | | |
| 79 | | 77 | |
| 80 | // Next, change current working directory to a temp directory one level below | 78 | var tmp = tmpDir(.{}); |
| 81 | { | 79 | defer tmp.cleanup(); |
| 82 | // Create a tmp directory | | |
| 83 | var tmp_dir_buf: [fs.max_path_bytes]u8 = undefined; | | |
| 84 | const tmp_dir_path = path: { | | |
| 85 | var allocator = std.heap.FixedBufferAllocator.init(&tmp_dir_buf); | | |
| 86 | break :path try fs.path.resolve(allocator.allocator(), &[_][]const u8{ old_cwd, "zig-test-tmp" }); | | |
| 87 | }; | | |
| 88 | var tmp_dir = try fs.cwd().makeOpenPath("zig-test-tmp", .{}); | | |
| 89 | | 80 | |
| 90 | // Change current working directory to tmp directory | 81 | // Restore default CWD at end of test. |
| 91 | try posix.chdir("zig-test-tmp"); | 82 | const orig_cwd = try fs.cwd().openDir(".", .{}); |
| | 83 | defer orig_cwd.setAsCwd() catch unreachable; |
| 92 | | 84 | |
| 93 | var new_cwd_buf: [fs.max_path_bytes]u8 = undefined; | 85 | // Use the tmpDir parent_dir as the "base" for the test. Then cd into the child |
| 94 | const new_cwd = try posix.getcwd(new_cwd_buf[0..]); | 86 | try tmp.parent_dir.setAsCwd(); |
| 95 | | 87 | |
| 96 | // On Windows, fs.path.resolve returns an uppercase drive letter, but the drive letter returned by getcwd may be lowercase | 88 | // Capture base working directory path, to build expected full path |
| 97 | var resolved_cwd_buf: [fs.max_path_bytes]u8 = undefined; | 89 | var base_cwd_buf: [fs.max_path_bytes]u8 = undefined; |
| 98 | const resolved_cwd = path: { | 90 | const base_cwd = try posix.getcwd(base_cwd_buf[0..]); |
| 99 | var allocator = std.heap.FixedBufferAllocator.init(&resolved_cwd_buf); | | |
| 100 | break :path try fs.path.resolve(allocator.allocator(), &[_][]const u8{new_cwd}); | | |
| 101 | }; | | |
| 102 | try expect(mem.eql(u8, tmp_dir_path, resolved_cwd)); | | |
| 103 | | 91 | |
| 104 | // Restore cwd because process may have other tests that do not tolerate chdir. | 92 | const dir_name = &tmp.sub_path; |
| 105 | tmp_dir.close(); | 93 | const expected_path = try fs.path.resolve(a, &.{ base_cwd, dir_name }); |
| 106 | posix.chdir(old_cwd) catch unreachable; | 94 | defer a.free(expected_path); |
| 107 | try fs.cwd().deleteDir("zig-test-tmp"); | 95 | |
| 108 | } | 96 | // change current working directory to new directory |
| | 97 | try posix.chdir(dir_name); |
| | 98 | |
| | 99 | var new_cwd_buf: [fs.max_path_bytes]u8 = undefined; |
| | 100 | const new_cwd = try posix.getcwd(new_cwd_buf[0..]); |
| | 101 | |
| | 102 | // On Windows, fs.path.resolve returns an uppercase drive letter, but the drive letter returned by getcwd may be lowercase |
| | 103 | const resolved_cwd = try fs.path.resolve(a, &.{new_cwd}); |
| | 104 | defer a.free(resolved_cwd); |
| | 105 | |
| | 106 | try expect(mem.eql(u8, expected_path, resolved_cwd)); |
| 109 | } | 107 | } |
| 110 | | 108 | |
| 111 | test "open smoke test" { | 109 | test "open smoke test" { |
| ... | @@ -222,44 +220,42 @@ test "openat smoke test" { | ... | @@ -222,44 +220,42 @@ test "openat smoke test" { |
| 222 | } | 220 | } |
| 223 | | 221 | |
| 224 | test "symlink with relative paths" { | 222 | test "symlink with relative paths" { |
| 225 | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; | 223 | if (native_os == .wasi) return error.SkipZigTest; // Can symlink, but can't change into tmpDir |
| 226 | | 224 | |
| 227 | if (true) { | 225 | var tmp = tmpDir(.{}); |
| 228 | // https://github.com/ziglang/zig/issues/14968 | 226 | defer tmp.cleanup(); |
| 229 | return error.SkipZigTest; | 227 | |
| 230 | } | 228 | const target_name = "symlink-target"; |
| 231 | const cwd = fs.cwd(); | 229 | const symlink_name = "symlinker"; |
| 232 | cwd.deleteFile("file.txt") catch {}; | | |
| 233 | cwd.deleteFile("symlinked") catch {}; | | |
| 234 | | 230 | |
| 235 | // First, try relative paths in cwd | 231 | // Restore default CWD at end of test. |
| 236 | try cwd.writeFile(.{ .sub_path = "file.txt", .data = "nonsense" }); | 232 | const orig_cwd = try fs.cwd().openDir(".", .{}); |
| | 233 | defer orig_cwd.setAsCwd() catch unreachable; |
| | 234 | |
| | 235 | // Create the target file |
| | 236 | try tmp.dir.writeFile(.{ .sub_path = target_name, .data = "nonsense" }); |
| | 237 | |
| | 238 | // Want to test relative paths, so cd into the tmpdir for this test |
| | 239 | try tmp.dir.setAsCwd(); |
| 237 | | 240 | |
| 238 | if (native_os == .windows) { | 241 | if (native_os == .windows) { |
| 239 | std.os.windows.CreateSymbolicLink( | 242 | const wtarget_name = try std.unicode.wtf8ToWtf16LeAllocZ(a, target_name); |
| 240 | cwd.fd, | 243 | const wsymlink_name = try std.unicode.wtf8ToWtf16LeAllocZ(a, symlink_name); |
| 241 | &[_]u16{ 's', 'y', 'm', 'l', 'i', 'n', 'k', 'e', 'd' }, | 244 | defer a.free(wtarget_name); |
| 242 | &[_:0]u16{ 'f', 'i', 'l', 'e', '.', 't', 'x', 't' }, | 245 | defer a.free(wsymlink_name); |
| 243 | false, | 246 | |
| 244 | ) catch |err| switch (err) { | 247 | std.os.windows.CreateSymbolicLink(tmp.dir.fd, wsymlink_name, wtarget_name, false) catch |err| switch (err) { |
| 245 | // Symlink requires admin privileges on windows, so this test can legitimately fail. | 248 | // Symlink requires admin privileges on windows, so this test can legitimately fail. |
| 246 | error.AccessDenied => { | 249 | error.AccessDenied => return error.SkipZigTest, |
| 247 | try cwd.deleteFile("file.txt"); | | |
| 248 | try cwd.deleteFile("symlinked"); | | |
| 249 | return error.SkipZigTest; | | |
| 250 | }, | | |
| 251 | else => return err, | 250 | else => return err, |
| 252 | }; | 251 | }; |
| 253 | } else { | 252 | } else { |
| 254 | try posix.symlink("file.txt", "symlinked"); | 253 | try posix.symlink(target_name, symlink_name); |
| 255 | } | 254 | } |
| 256 | | 255 | |
| 257 | var buffer: [fs.max_path_bytes]u8 = undefined; | 256 | var buffer: [fs.max_path_bytes]u8 = undefined; |
| 258 | const given = try posix.readlink("symlinked", buffer[0..]); | 257 | const given = try posix.readlink(symlink_name, buffer[0..]); |
| 259 | try expect(mem.eql(u8, "file.txt", given)); | 258 | try expect(mem.eql(u8, target_name, given)); |
| 260 | | | |
| 261 | try cwd.deleteFile("file.txt"); | | |
| 262 | try cwd.deleteFile("symlinked"); | | |
| 263 | } | 259 | } |
| 264 | | 260 | |
| 265 | test "readlink on Windows" { | 261 | test "readlink on Windows" { |
| ... | @@ -277,90 +273,95 @@ fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void { | ... | @@ -277,90 +273,95 @@ fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void { |
| 277 | } | 273 | } |
| 278 | | 274 | |
| 279 | test "link with relative paths" { | 275 | test "link with relative paths" { |
| 280 | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; | 276 | if (native_os == .wasi) return error.SkipZigTest; // Can link, but can't change into tmpDir |
| | 277 | if (builtin.cpu.arch == .riscv32 and builtin.os.tag == .linux and !builtin.link_libc) return error.SkipZigTest; // No `fstat()`. |
| 281 | | 278 | |
| 282 | switch (native_os) { | 279 | switch (native_os) { |
| 283 | .wasi, .linux, .solaris, .illumos => {}, | 280 | .wasi, .linux, .solaris, .illumos => {}, |
| 284 | else => return error.SkipZigTest, | 281 | else => return error.SkipZigTest, |
| 285 | } | 282 | } |
| 286 | if (true) { | | |
| 287 | // https://github.com/ziglang/zig/issues/14968 | | |
| 288 | return error.SkipZigTest; | | |
| 289 | } | | |
| 290 | var cwd = fs.cwd(); | | |
| 291 | | 283 | |
| 292 | cwd.deleteFile("example.txt") catch {}; | 284 | var tmp = tmpDir(.{}); |
| 293 | cwd.deleteFile("new.txt") catch {}; | 285 | defer tmp.cleanup(); |
| | 286 | |
| | 287 | // Restore default CWD at end of test. |
| | 288 | const orig_cwd = try fs.cwd().openDir(".", .{}); |
| | 289 | defer orig_cwd.setAsCwd() catch unreachable; |
| | 290 | |
| | 291 | const target_name = "link-target"; |
| | 292 | const link_name = "newlink"; |
| | 293 | |
| | 294 | try tmp.dir.writeFile(.{ .sub_path = target_name, .data = "example" }); |
| 294 | | 295 | |
| 295 | try cwd.writeFile(.{ .sub_path = "example.txt", .data = "example" }); | 296 | // Test 1: create the relative link from inside tmp |
| 296 | try posix.link("example.txt", "new.txt"); | 297 | try tmp.dir.setAsCwd(); |
| | 298 | try posix.link(target_name, link_name); |
| 297 | | 299 | |
| 298 | const efd = try cwd.openFile("example.txt", .{}); | 300 | // Verify |
| | 301 | const efd = try tmp.dir.openFile(target_name, .{}); |
| 299 | defer efd.close(); | 302 | defer efd.close(); |
| 300 | | 303 | |
| 301 | const nfd = try cwd.openFile("new.txt", .{}); | 304 | const nfd = try tmp.dir.openFile(link_name, .{}); |
| 302 | defer nfd.close(); | 305 | defer nfd.close(); |
| 303 | | 306 | |
| 304 | { | 307 | { |
| 305 | const estat = try posix.fstat(efd.handle); | 308 | const estat = try posix.fstat(efd.handle); |
| 306 | const nstat = try posix.fstat(nfd.handle); | 309 | const nstat = try posix.fstat(nfd.handle); |
| 307 | | | |
| 308 | try testing.expectEqual(estat.ino, nstat.ino); | 310 | try testing.expectEqual(estat.ino, nstat.ino); |
| 309 | try testing.expectEqual(@as(@TypeOf(nstat.nlink), 2), nstat.nlink); | 311 | try testing.expectEqual(@as(@TypeOf(nstat.nlink), 2), nstat.nlink); |
| 310 | } | 312 | } |
| 311 | | 313 | |
| 312 | try posix.unlink("new.txt"); | 314 | // Test 2: Remove the link and see the stats update |
| | 315 | try posix.unlink(link_name); |
| 313 | | 316 | |
| 314 | { | 317 | { |
| 315 | const estat = try posix.fstat(efd.handle); | 318 | const estat = try posix.fstat(efd.handle); |
| 316 | try testing.expectEqual(@as(@TypeOf(estat.nlink), 1), estat.nlink); | 319 | try testing.expectEqual(@as(@TypeOf(estat.nlink), 1), estat.nlink); |
| 317 | } | 320 | } |
| 318 | | | |
| 319 | try cwd.deleteFile("example.txt"); | | |
| 320 | } | 321 | } |
| 321 | | 322 | |
| 322 | test "linkat with different directories" { | 323 | test "linkat with different directories" { |
| 323 | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; | 324 | if (builtin.cpu.arch == .riscv32 and builtin.os.tag == .linux and !builtin.link_libc) return error.SkipZigTest; // No `fstatat()`. |
| 324 | | 325 | |
| 325 | switch (native_os) { | 326 | switch (native_os) { |
| 326 | .wasi, .linux, .solaris, .illumos => {}, | 327 | .wasi, .linux, .solaris, .illumos => {}, |
| 327 | else => return error.SkipZigTest, | 328 | else => return error.SkipZigTest, |
| 328 | } | 329 | } |
| 329 | if (true) { | 330 | |
| 330 | // https://github.com/ziglang/zig/issues/14968 | | |
| 331 | return error.SkipZigTest; | | |
| 332 | } | | |
| 333 | var cwd = fs.cwd(); | | |
| 334 | var tmp = tmpDir(.{}); | 331 | var tmp = tmpDir(.{}); |
| | 332 | defer tmp.cleanup(); |
| | 333 | |
| | 334 | const target_name = "link-target"; |
| | 335 | const link_name = "newlink"; |
| 335 | | 336 | |
| 336 | cwd.deleteFile("example.txt") catch {}; | 337 | const subdir = try tmp.dir.makeOpenPath("subdir", .{}); |
| 337 | tmp.dir.deleteFile("new.txt") catch {}; | | |
| 338 | | 338 | |
| 339 | try cwd.writeFile(.{ .sub_path = "example.txt", .data = "example" }); | 339 | defer tmp.dir.deleteFile(target_name) catch {}; |
| 340 | try posix.linkat(cwd.fd, "example.txt", tmp.dir.fd, "new.txt", 0); | 340 | try tmp.dir.writeFile(.{ .sub_path = target_name, .data = "example" }); |
| 341 | | 341 | |
| 342 | const efd = try cwd.openFile("example.txt", .{}); | 342 | // Test 1: link from file in subdir back up to target in parent directory |
| | 343 | try posix.linkat(tmp.dir.fd, target_name, subdir.fd, link_name, 0); |
| | 344 | |
| | 345 | const efd = try tmp.dir.openFile(target_name, .{}); |
| 343 | defer efd.close(); | 346 | defer efd.close(); |
| 344 | | 347 | |
| 345 | const nfd = try tmp.dir.openFile("new.txt", .{}); | 348 | const nfd = try subdir.openFile(link_name, .{}); |
| | 349 | defer nfd.close(); |
| 346 | | 350 | |
| 347 | { | 351 | { |
| 348 | defer nfd.close(); | | |
| 349 | const estat = try posix.fstat(efd.handle); | 352 | const estat = try posix.fstat(efd.handle); |
| 350 | const nstat = try posix.fstat(nfd.handle); | 353 | const nstat = try posix.fstat(nfd.handle); |
| 351 | | | |
| 352 | try testing.expectEqual(estat.ino, nstat.ino); | 354 | try testing.expectEqual(estat.ino, nstat.ino); |
| 353 | try testing.expectEqual(@as(@TypeOf(nstat.nlink), 2), nstat.nlink); | 355 | try testing.expectEqual(@as(@TypeOf(nstat.nlink), 2), nstat.nlink); |
| 354 | } | 356 | } |
| 355 | | 357 | |
| 356 | try posix.unlinkat(tmp.dir.fd, "new.txt", 0); | 358 | // Test 2: remove link |
| | 359 | try posix.unlinkat(subdir.fd, link_name, 0); |
| 357 | | 360 | |
| 358 | { | 361 | { |
| 359 | const estat = try posix.fstat(efd.handle); | 362 | const estat = try posix.fstat(efd.handle); |
| 360 | try testing.expectEqual(@as(@TypeOf(estat.nlink), 1), estat.nlink); | 363 | try testing.expectEqual(@as(@TypeOf(estat.nlink), 1), estat.nlink); |
| 361 | } | 364 | } |
| 362 | | | |
| 363 | try cwd.deleteFile("example.txt"); | | |
| 364 | } | 365 | } |
| 365 | | 366 | |
| 366 | test "fstatat" { | 367 | test "fstatat" { |
| ... | @@ -979,7 +980,7 @@ test "POSIX file locking with fcntl" { | ... | @@ -979,7 +980,7 @@ test "POSIX file locking with fcntl" { |
| 979 | return error.SkipZigTest; | 980 | return error.SkipZigTest; |
| 980 | } | 981 | } |
| 981 | | 982 | |
| 982 | var tmp = std.testing.tmpDir(.{}); | 983 | var tmp = tmpDir(.{}); |
| 983 | defer tmp.cleanup(); | 984 | defer tmp.cleanup(); |
| 984 | | 985 | |
| 985 | // Create a temporary lock file | 986 | // Create a temporary lock file |