| author | |
| committer | |
| log | 220c6795233bfdc9b93dfb3364b21705b1e6c903 |
| tree | 8d59b2842374a10ceac6648f37a954b19dfa33f5 |
| parent | 09bc118c9f2349f162f181487f8e5a01eb5e734c |
| parent | bd4617033e09979398e65907a1d9c7f1b6e1d124 |
| signature |
Re-enable std.posix "sigset_t bits" test9 files changed, 485 insertions(+), 387 deletions(-)
lib/std/Thread.zig+37| ... | @@ -1637,3 +1637,40 @@ test detach { | ... | @@ -1637,3 +1637,40 @@ test detach { |
| 1637 | event.wait(); | 1637 | event.wait(); |
| 1638 | try std.testing.expectEqual(value, 1); | 1638 | try std.testing.expectEqual(value, 1); |
| 1639 | } | 1639 | } |
| 1640 | |||
| 1641 | test "Thread.getCpuCount" { | ||
| 1642 | if (native_os == .wasi) return error.SkipZigTest; | ||
| 1643 | |||
| 1644 | const cpu_count = try Thread.getCpuCount(); | ||
| 1645 | try std.testing.expect(cpu_count >= 1); | ||
| 1646 | } | ||
| 1647 | |||
| 1648 | fn testThreadIdFn(thread_id: *Thread.Id) void { | ||
| 1649 | thread_id.* = Thread.getCurrentId(); | ||
| 1650 | } | ||
| 1651 | |||
| 1652 | test "Thread.getCurrentId" { | ||
| 1653 | if (builtin.single_threaded) return error.SkipZigTest; | ||
| 1654 | |||
| 1655 | var thread_current_id: Thread.Id = undefined; | ||
| 1656 | const thread = try Thread.spawn(.{}, testThreadIdFn, .{&thread_current_id}); | ||
| 1657 | thread.join(); | ||
| 1658 | try std.testing.expect(Thread.getCurrentId() != thread_current_id); | ||
| 1659 | } | ||
| 1660 | |||
| 1661 | test "thread local storage" { | ||
| 1662 | if (builtin.single_threaded) return error.SkipZigTest; | ||
| 1663 | |||
| 1664 | const thread1 = try Thread.spawn(.{}, testTls, .{}); | ||
| 1665 | const thread2 = try Thread.spawn(.{}, testTls, .{}); | ||
| 1666 | try testTls(); | ||
| 1667 | thread1.join(); | ||
| 1668 | thread2.join(); | ||
| 1669 | } | ||
| 1670 | |||
| 1671 | threadlocal var x: i32 = 1234; | ||
| 1672 | fn testTls() !void { | ||
| 1673 | if (x != 1234) return error.TlsBadStartValue; | ||
| 1674 | x += 1; | ||
| 1675 | if (x != 1235) return error.TlsBadEndValue; | ||
| 1676 | } |
lib/std/posix/test.zig+3-387| ... | @@ -7,7 +7,6 @@ const expectError = testing.expectError; | ... | @@ -7,7 +7,6 @@ const expectError = testing.expectError; |
| 7 | const fs = std.fs; | 7 | const fs = std.fs; |
| 8 | const mem = std.mem; | 8 | const mem = std.mem; |
| 9 | const elf = std.elf; | 9 | const elf = std.elf; |
| 10 | const Thread = std.Thread; | ||
| 11 | const linux = std.os.linux; | 10 | const linux = std.os.linux; |
| 12 | 11 | ||
| 13 | const a = std.testing.allocator; | 12 | const a = std.testing.allocator; |
| ... | @@ -18,6 +17,9 @@ const AtomicOrder = std.builtin.AtomicOrder; | ... | @@ -18,6 +17,9 @@ const AtomicOrder = std.builtin.AtomicOrder; |
| 18 | const native_os = builtin.target.os.tag; | 17 | const native_os = builtin.target.os.tag; |
| 19 | const tmpDir = std.testing.tmpDir; | 18 | const tmpDir = std.testing.tmpDir; |
| 20 | 19 | ||
| 20 | // NOTE: several additional tests are in test/standalone/posix/. Any tests that mutate | ||
| 21 | // process-wide POSIX state (cwd, signals, etc) cannot be Zig unit tests and should be over there. | ||
| 22 | |||
| 21 | // https://github.com/ziglang/zig/issues/20288 | 23 | // https://github.com/ziglang/zig/issues/20288 |
| 22 | test "WTF-8 to WTF-16 conversion buffer overflows" { | 24 | test "WTF-8 to WTF-16 conversion buffer overflows" { |
| 23 | if (native_os != .windows) return error.SkipZigTest; | 25 | if (native_os != .windows) return error.SkipZigTest; |
| ... | @@ -40,68 +42,6 @@ test "check WASI CWD" { | ... | @@ -40,68 +42,6 @@ test "check WASI CWD" { |
| 40 | } | 42 | } |
| 41 | } | 43 | } |
| 42 | 44 | ||
| 43 | test "chdir absolute parent" { | ||
| 44 | if (native_os == .wasi) return error.SkipZigTest; | ||
| 45 | |||
| 46 | // Restore default CWD at end of test. | ||
| 47 | const orig_cwd = try fs.cwd().openDir(".", .{}); | ||
| 48 | defer orig_cwd.setAsCwd() catch unreachable; | ||
| 49 | |||
| 50 | // Get current working directory path | ||
| 51 | var old_cwd_buf: [fs.max_path_bytes]u8 = undefined; | ||
| 52 | const old_cwd = try posix.getcwd(old_cwd_buf[0..]); | ||
| 53 | |||
| 54 | { | ||
| 55 | // Firstly, changing to itself should have no effect | ||
| 56 | try posix.chdir(old_cwd); | ||
| 57 | var new_cwd_buf: [fs.max_path_bytes]u8 = undefined; | ||
| 58 | const new_cwd = try posix.getcwd(new_cwd_buf[0..]); | ||
| 59 | try expect(mem.eql(u8, old_cwd, new_cwd)); | ||
| 60 | } | ||
| 61 | |||
| 62 | // Next, change current working directory to one level above | ||
| 63 | const parent = fs.path.dirname(old_cwd) orelse unreachable; // old_cwd should be absolute | ||
| 64 | try posix.chdir(parent); | ||
| 65 | |||
| 66 | var new_cwd_buf: [fs.max_path_bytes]u8 = undefined; | ||
| 67 | const new_cwd = try posix.getcwd(new_cwd_buf[0..]); | ||
| 68 | try expect(mem.eql(u8, parent, new_cwd)); | ||
| 69 | } | ||
| 70 | |||
| 71 | test "chdir relative" { | ||
| 72 | if (native_os == .wasi) return error.SkipZigTest; | ||
| 73 | |||
| 74 | var tmp = tmpDir(.{}); | ||
| 75 | defer tmp.cleanup(); | ||
| 76 | |||
| 77 | // Restore default CWD at end of test. | ||
| 78 | const orig_cwd = try fs.cwd().openDir(".", .{}); | ||
| 79 | defer orig_cwd.setAsCwd() catch unreachable; | ||
| 80 | |||
| 81 | // Use the tmpDir parent_dir as the "base" for the test. Then cd into the child | ||
| 82 | try tmp.parent_dir.setAsCwd(); | ||
| 83 | |||
| 84 | // Capture base working directory path, to build expected full path | ||
| 85 | var base_cwd_buf: [fs.max_path_bytes]u8 = undefined; | ||
| 86 | const base_cwd = try posix.getcwd(base_cwd_buf[0..]); | ||
| 87 | |||
| 88 | const dir_name = &tmp.sub_path; | ||
| 89 | const expected_path = try fs.path.resolve(a, &.{ base_cwd, dir_name }); | ||
| 90 | defer a.free(expected_path); | ||
| 91 | |||
| 92 | // change current working directory to new directory | ||
| 93 | try posix.chdir(dir_name); | ||
| 94 | |||
| 95 | var new_cwd_buf: [fs.max_path_bytes]u8 = undefined; | ||
| 96 | const new_cwd = try posix.getcwd(new_cwd_buf[0..]); | ||
| 97 | |||
| 98 | // On Windows, fs.path.resolve returns an uppercase drive letter, but the drive letter returned by getcwd may be lowercase | ||
| 99 | const resolved_cwd = try fs.path.resolve(a, &.{new_cwd}); | ||
| 100 | defer a.free(resolved_cwd); | ||
| 101 | |||
| 102 | try expect(mem.eql(u8, expected_path, resolved_cwd)); | ||
| 103 | } | ||
| 104 | |||
| 105 | test "open smoke test" { | 45 | test "open smoke test" { |
| 106 | if (native_os == .wasi) return error.SkipZigTest; | 46 | if (native_os == .wasi) return error.SkipZigTest; |
| 107 | if (native_os == .windows) return error.SkipZigTest; | 47 | if (native_os == .windows) return error.SkipZigTest; |
| ... | @@ -227,45 +167,6 @@ test "openat smoke test" { | ... | @@ -227,45 +167,6 @@ test "openat smoke test" { |
| 227 | } | 167 | } |
| 228 | } | 168 | } |
| 229 | 169 | ||
| 230 | test "symlink with relative paths" { | ||
| 231 | if (native_os == .wasi) return error.SkipZigTest; // Can symlink, but can't change into tmpDir | ||
| 232 | |||
| 233 | var tmp = tmpDir(.{}); | ||
| 234 | defer tmp.cleanup(); | ||
| 235 | |||
| 236 | const target_name = "symlink-target"; | ||
| 237 | const symlink_name = "symlinker"; | ||
| 238 | |||
| 239 | // Restore default CWD at end of test. | ||
| 240 | const orig_cwd = try fs.cwd().openDir(".", .{}); | ||
| 241 | defer orig_cwd.setAsCwd() catch unreachable; | ||
| 242 | |||
| 243 | // Create the target file | ||
| 244 | try tmp.dir.writeFile(.{ .sub_path = target_name, .data = "nonsense" }); | ||
| 245 | |||
| 246 | // Want to test relative paths, so cd into the tmpdir for this test | ||
| 247 | try tmp.dir.setAsCwd(); | ||
| 248 | |||
| 249 | if (native_os == .windows) { | ||
| 250 | const wtarget_name = try std.unicode.wtf8ToWtf16LeAllocZ(a, target_name); | ||
| 251 | const wsymlink_name = try std.unicode.wtf8ToWtf16LeAllocZ(a, symlink_name); | ||
| 252 | defer a.free(wtarget_name); | ||
| 253 | defer a.free(wsymlink_name); | ||
| 254 | |||
| 255 | std.os.windows.CreateSymbolicLink(tmp.dir.fd, wsymlink_name, wtarget_name, false) catch |err| switch (err) { | ||
| 256 | // Symlink requires admin privileges on windows, so this test can legitimately fail. | ||
| 257 | error.AccessDenied => return error.SkipZigTest, | ||
| 258 | else => return err, | ||
| 259 | }; | ||
| 260 | } else { | ||
| 261 | try posix.symlink(target_name, symlink_name); | ||
| 262 | } | ||
| 263 | |||
| 264 | var buffer: [fs.max_path_bytes]u8 = undefined; | ||
| 265 | const given = try posix.readlink(symlink_name, buffer[0..]); | ||
| 266 | try expect(mem.eql(u8, target_name, given)); | ||
| 267 | } | ||
| 268 | |||
| 269 | test "readlink on Windows" { | 170 | test "readlink on Windows" { |
| 270 | if (native_os != .windows) return error.SkipZigTest; | 171 | if (native_os != .windows) return error.SkipZigTest; |
| 271 | 172 | ||
| ... | @@ -280,55 +181,6 @@ fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void { | ... | @@ -280,55 +181,6 @@ fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void { |
| 280 | try expect(mem.eql(u8, target_path, given)); | 181 | try expect(mem.eql(u8, target_path, given)); |
| 281 | } | 182 | } |
| 282 | 183 | ||
| 283 | test "link with relative paths" { | ||
| 284 | if (native_os == .wasi) return error.SkipZigTest; // Can link, but can't change into tmpDir | ||
| 285 | if ((builtin.cpu.arch == .riscv32 or builtin.cpu.arch.isLoongArch()) and builtin.os.tag == .linux and !builtin.link_libc) return error.SkipZigTest; // No `fstat()`. | ||
| 286 | if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; // `nstat.nlink` assertion is failing with LLVM 20+ for unclear reasons. | ||
| 287 | |||
| 288 | switch (native_os) { | ||
| 289 | .wasi, .linux, .solaris, .illumos => {}, | ||
| 290 | else => return error.SkipZigTest, | ||
| 291 | } | ||
| 292 | |||
| 293 | var tmp = tmpDir(.{}); | ||
| 294 | defer tmp.cleanup(); | ||
| 295 | |||
| 296 | // Restore default CWD at end of test. | ||
| 297 | const orig_cwd = try fs.cwd().openDir(".", .{}); | ||
| 298 | defer orig_cwd.setAsCwd() catch unreachable; | ||
| 299 | |||
| 300 | const target_name = "link-target"; | ||
| 301 | const link_name = "newlink"; | ||
| 302 | |||
| 303 | try tmp.dir.writeFile(.{ .sub_path = target_name, .data = "example" }); | ||
| 304 | |||
| 305 | // Test 1: create the relative link from inside tmp | ||
| 306 | try tmp.dir.setAsCwd(); | ||
| 307 | try posix.link(target_name, link_name); | ||
| 308 | |||
| 309 | // Verify | ||
| 310 | const efd = try tmp.dir.openFile(target_name, .{}); | ||
| 311 | defer efd.close(); | ||
| 312 | |||
| 313 | const nfd = try tmp.dir.openFile(link_name, .{}); | ||
| 314 | defer nfd.close(); | ||
| 315 | |||
| 316 | { | ||
| 317 | const estat = try posix.fstat(efd.handle); | ||
| 318 | const nstat = try posix.fstat(nfd.handle); | ||
| 319 | try testing.expectEqual(estat.ino, nstat.ino); | ||
| 320 | try testing.expectEqual(@as(@TypeOf(nstat.nlink), 2), nstat.nlink); | ||
| 321 | } | ||
| 322 | |||
| 323 | // Test 2: Remove the link and see the stats update | ||
| 324 | try posix.unlink(link_name); | ||
| 325 | |||
| 326 | { | ||
| 327 | const estat = try posix.fstat(efd.handle); | ||
| 328 | try testing.expectEqual(@as(@TypeOf(estat.nlink), 1), estat.nlink); | ||
| 329 | } | ||
| 330 | } | ||
| 331 | |||
| 332 | test "linkat with different directories" { | 184 | test "linkat with different directories" { |
| 333 | if ((builtin.cpu.arch == .riscv32 or builtin.cpu.arch.isLoongArch()) and builtin.os.tag == .linux and !builtin.link_libc) return error.SkipZigTest; // No `fstatat()`. | 185 | if ((builtin.cpu.arch == .riscv32 or builtin.cpu.arch.isLoongArch()) and builtin.os.tag == .linux and !builtin.link_libc) return error.SkipZigTest; // No `fstatat()`. |
| 334 | if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; // `nstat.nlink` assertion is failing with LLVM 20+ for unclear reasons. | 186 | if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; // `nstat.nlink` assertion is failing with LLVM 20+ for unclear reasons. |
| ... | @@ -446,70 +298,6 @@ test "readlinkat" { | ... | @@ -446,70 +298,6 @@ test "readlinkat" { |
| 446 | try expect(mem.eql(u8, "file.txt", read_link)); | 298 | try expect(mem.eql(u8, "file.txt", read_link)); |
| 447 | } | 299 | } |
| 448 | 300 | ||
| 449 | fn testThreadIdFn(thread_id: *Thread.Id) void { | ||
| 450 | thread_id.* = Thread.getCurrentId(); | ||
| 451 | } | ||
| 452 | |||
| 453 | test "Thread.getCurrentId" { | ||
| 454 | if (builtin.single_threaded) return error.SkipZigTest; | ||
| 455 | |||
| 456 | var thread_current_id: Thread.Id = undefined; | ||
| 457 | const thread = try Thread.spawn(.{}, testThreadIdFn, .{&thread_current_id}); | ||
| 458 | thread.join(); | ||
| 459 | try expect(Thread.getCurrentId() != thread_current_id); | ||
| 460 | } | ||
| 461 | |||
| 462 | test "spawn threads" { | ||
| 463 | if (builtin.single_threaded) return error.SkipZigTest; | ||
| 464 | |||
| 465 | var shared_ctx: i32 = 1; | ||
| 466 | |||
| 467 | const thread1 = try Thread.spawn(.{}, start1, .{}); | ||
| 468 | const thread2 = try Thread.spawn(.{}, start2, .{&shared_ctx}); | ||
| 469 | const thread3 = try Thread.spawn(.{}, start2, .{&shared_ctx}); | ||
| 470 | const thread4 = try Thread.spawn(.{}, start2, .{&shared_ctx}); | ||
| 471 | |||
| 472 | thread1.join(); | ||
| 473 | thread2.join(); | ||
| 474 | thread3.join(); | ||
| 475 | thread4.join(); | ||
| 476 | |||
| 477 | try expect(shared_ctx == 4); | ||
| 478 | } | ||
| 479 | |||
| 480 | fn start1() u8 { | ||
| 481 | return 0; | ||
| 482 | } | ||
| 483 | |||
| 484 | fn start2(ctx: *i32) u8 { | ||
| 485 | _ = @atomicRmw(i32, ctx, AtomicRmwOp.Add, 1, AtomicOrder.seq_cst); | ||
| 486 | return 0; | ||
| 487 | } | ||
| 488 | |||
| 489 | test "cpu count" { | ||
| 490 | if (native_os == .wasi) return error.SkipZigTest; | ||
| 491 | |||
| 492 | const cpu_count = try Thread.getCpuCount(); | ||
| 493 | try expect(cpu_count >= 1); | ||
| 494 | } | ||
| 495 | |||
| 496 | test "thread local storage" { | ||
| 497 | if (builtin.single_threaded) return error.SkipZigTest; | ||
| 498 | |||
| 499 | const thread1 = try Thread.spawn(.{}, testTls, .{}); | ||
| 500 | const thread2 = try Thread.spawn(.{}, testTls, .{}); | ||
| 501 | try testTls(); | ||
| 502 | thread1.join(); | ||
| 503 | thread2.join(); | ||
| 504 | } | ||
| 505 | |||
| 506 | threadlocal var x: i32 = 1234; | ||
| 507 | fn testTls() !void { | ||
| 508 | if (x != 1234) return error.TlsBadStartValue; | ||
| 509 | x += 1; | ||
| 510 | if (x != 1235) return error.TlsBadEndValue; | ||
| 511 | } | ||
| 512 | |||
| 513 | test "getrandom" { | 301 | test "getrandom" { |
| 514 | var buf_a: [50]u8 = undefined; | 302 | var buf_a: [50]u8 = undefined; |
| 515 | var buf_b: [50]u8 = undefined; | 303 | var buf_b: [50]u8 = undefined; |
| ... | @@ -520,12 +308,6 @@ test "getrandom" { | ... | @@ -520,12 +308,6 @@ test "getrandom" { |
| 520 | try expect(!mem.eql(u8, &buf_a, &buf_b)); | 308 | try expect(!mem.eql(u8, &buf_a, &buf_b)); |
| 521 | } | 309 | } |
| 522 | 310 | ||
| 523 | test "getcwd" { | ||
| 524 | // at least call it so it gets compiled | ||
| 525 | var buf: [std.fs.max_path_bytes]u8 = undefined; | ||
| 526 | _ = posix.getcwd(&buf) catch undefined; | ||
| 527 | } | ||
| 528 | |||
| 529 | test "getuid" { | 311 | test "getuid" { |
| 530 | if (native_os == .windows or native_os == .wasi) return error.SkipZigTest; | 312 | if (native_os == .windows or native_os == .wasi) return error.SkipZigTest; |
| 531 | _ = posix.getuid(); | 313 | _ = posix.getuid(); |
| ... | @@ -737,24 +519,6 @@ test "mmap" { | ... | @@ -737,24 +519,6 @@ test "mmap" { |
| 737 | } | 519 | } |
| 738 | } | 520 | } |
| 739 | 521 | ||
| 740 | test "getenv" { | ||
| 741 | if (native_os == .wasi and !builtin.link_libc) { | ||
| 742 | // std.posix.getenv is not supported on WASI due to the need of allocation | ||
| 743 | return error.SkipZigTest; | ||
| 744 | } | ||
| 745 | |||
| 746 | if (native_os == .windows) { | ||
| 747 | try expect(std.process.getenvW(&[_:0]u16{ 'B', 'O', 'G', 'U', 'S', 0x11, 0x22, 0x33, 0x44, 0x55 }) == null); | ||
| 748 | } else { | ||
| 749 | try expect(posix.getenv("") == null); | ||
| 750 | try expect(posix.getenv("BOGUSDOESNOTEXISTENVVAR") == null); | ||
| 751 | if (builtin.link_libc) { | ||
| 752 | try testing.expectEqualStrings(posix.getenv("USER") orelse "", mem.span(std.c.getenv("USER") orelse "")); | ||
| 753 | } | ||
| 754 | try expect(posix.getenvZ("BOGUSDOESNOTEXISTENVVAR") == null); | ||
| 755 | } | ||
| 756 | } | ||
| 757 | |||
| 758 | test "fcntl" { | 522 | test "fcntl" { |
| 759 | if (native_os == .windows or native_os == .wasi) | 523 | if (native_os == .windows or native_os == .wasi) |
| 760 | return error.SkipZigTest; | 524 | return error.SkipZigTest; |
| ... | @@ -939,154 +703,6 @@ test "sigset add/del" { | ... | @@ -939,154 +703,6 @@ test "sigset add/del" { |
| 939 | } | 703 | } |
| 940 | } | 704 | } |
| 941 | 705 | ||
| 942 | test "sigaction" { | ||
| 943 | if (native_os == .wasi or native_os == .windows) | ||
| 944 | return error.SkipZigTest; | ||
| 945 | |||
| 946 | // https://github.com/ziglang/zig/issues/15381 | ||
| 947 | if (native_os == .macos and builtin.target.cpu.arch == .x86_64) { | ||
| 948 | return error.SkipZigTest; | ||
| 949 | } | ||
| 950 | |||
| 951 | const test_signo = posix.SIG.URG; // URG only because it is ignored by default in debuggers | ||
| 952 | |||
| 953 | const S = struct { | ||
| 954 | var handler_called_count: u32 = 0; | ||
| 955 | |||
| 956 | fn handler(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopaque) callconv(.c) void { | ||
| 957 | _ = ctx_ptr; | ||
| 958 | // Check that we received the correct signal. | ||
| 959 | const info_sig = switch (native_os) { | ||
| 960 | .netbsd => info.info.signo, | ||
| 961 | else => info.signo, | ||
| 962 | }; | ||
| 963 | if (sig == test_signo and sig == info_sig) { | ||
| 964 | handler_called_count += 1; | ||
| 965 | } | ||
| 966 | } | ||
| 967 | }; | ||
| 968 | |||
| 969 | var sa: posix.Sigaction = .{ | ||
| 970 | .handler = .{ .sigaction = &S.handler }, | ||
| 971 | .mask = posix.sigemptyset(), | ||
| 972 | .flags = posix.SA.SIGINFO | posix.SA.RESETHAND, | ||
| 973 | }; | ||
| 974 | |||
| 975 | var old_sa: posix.Sigaction = undefined; | ||
| 976 | |||
| 977 | // Install the new signal handler. | ||
| 978 | posix.sigaction(test_signo, &sa, null); | ||
| 979 | |||
| 980 | // Check that we can read it back correctly. | ||
| 981 | posix.sigaction(test_signo, null, &old_sa); | ||
| 982 | try testing.expectEqual(&S.handler, old_sa.handler.sigaction.?); | ||
| 983 | try testing.expect((old_sa.flags & posix.SA.SIGINFO) != 0); | ||
| 984 | |||
| 985 | // Invoke the handler. | ||
| 986 | try posix.raise(test_signo); | ||
| 987 | try testing.expectEqual(1, S.handler_called_count); | ||
| 988 | |||
| 989 | // Check if passing RESETHAND correctly reset the handler to SIG_DFL | ||
| 990 | posix.sigaction(test_signo, null, &old_sa); | ||
| 991 | try testing.expectEqual(posix.SIG.DFL, old_sa.handler.handler); | ||
| 992 | |||
| 993 | // Reinstall the signal w/o RESETHAND and re-raise | ||
| 994 | sa.flags = posix.SA.SIGINFO; | ||
| 995 | posix.sigaction(test_signo, &sa, null); | ||
| 996 | try posix.raise(test_signo); | ||
| 997 | try testing.expectEqual(2, S.handler_called_count); | ||
| 998 | |||
| 999 | // Now set the signal to ignored | ||
| 1000 | sa.handler = .{ .handler = posix.SIG.IGN }; | ||
| 1001 | sa.flags = 0; | ||
| 1002 | posix.sigaction(test_signo, &sa, null); | ||
| 1003 | |||
| 1004 | // Re-raise to ensure handler is actually ignored | ||
| 1005 | try posix.raise(test_signo); | ||
| 1006 | try testing.expectEqual(2, S.handler_called_count); | ||
| 1007 | |||
| 1008 | // Ensure that ignored state is returned when querying | ||
| 1009 | posix.sigaction(test_signo, null, &old_sa); | ||
| 1010 | try testing.expectEqual(posix.SIG.IGN, old_sa.handler.handler); | ||
| 1011 | } | ||
| 1012 | |||
| 1013 | test "sigset_t bits" { | ||
| 1014 | if (native_os == .wasi or native_os == .windows) | ||
| 1015 | return error.SkipZigTest; | ||
| 1016 | |||
| 1017 | if (true) { | ||
| 1018 | // https://github.com/ziglang/zig/issues/24380 | ||
| 1019 | return error.SkipZigTest; | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | const S = struct { | ||
| 1023 | var expected_sig: i32 = undefined; | ||
| 1024 | var handler_called_count: u32 = 0; | ||
| 1025 | |||
| 1026 | fn handler(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopaque) callconv(.c) void { | ||
| 1027 | _ = ctx_ptr; | ||
| 1028 | |||
| 1029 | const info_sig = switch (native_os) { | ||
| 1030 | .netbsd => info.info.signo, | ||
| 1031 | else => info.signo, | ||
| 1032 | }; | ||
| 1033 | if (sig == expected_sig and sig == info_sig) { | ||
| 1034 | handler_called_count += 1; | ||
| 1035 | } | ||
| 1036 | } | ||
| 1037 | }; | ||
| 1038 | |||
| 1039 | const self_pid = posix.system.getpid(); | ||
| 1040 | |||
| 1041 | // To check that sigset_t mapping matches kernel (think u32/u64 mismatches on | ||
| 1042 | // big-endian), try sending a blocked signal to make sure the mask matches the | ||
| 1043 | // signal. (Send URG and CHLD because they're ignored by default in the | ||
| 1044 | // debugger, vs. USR1 or other named signals) | ||
| 1045 | inline for ([_]usize{ posix.SIG.URG, posix.SIG.CHLD, 62, 94, 126 }) |test_signo| { | ||
| 1046 | if (test_signo >= posix.NSIG) continue; | ||
| 1047 | |||
| 1048 | S.expected_sig = test_signo; | ||
| 1049 | S.handler_called_count = 0; | ||
| 1050 | |||
| 1051 | const sa: posix.Sigaction = .{ | ||
| 1052 | .handler = .{ .sigaction = &S.handler }, | ||
| 1053 | .mask = posix.sigemptyset(), | ||
| 1054 | .flags = posix.SA.SIGINFO | posix.SA.RESETHAND, | ||
| 1055 | }; | ||
| 1056 | |||
| 1057 | var old_sa: posix.Sigaction = undefined; | ||
| 1058 | |||
| 1059 | // Install the new signal handler. | ||
| 1060 | posix.sigaction(test_signo, &sa, &old_sa); | ||
| 1061 | |||
| 1062 | // block the signal and see that its delayed until unblocked | ||
| 1063 | var block_one: posix.sigset_t = posix.sigemptyset(); | ||
| 1064 | posix.sigaddset(&block_one, test_signo); | ||
| 1065 | posix.sigprocmask(posix.SIG.BLOCK, &block_one, null); | ||
| 1066 | |||
| 1067 | // qemu maps target signals to host signals 1-to-1, so targets | ||
| 1068 | // with more signals than the host will fail to send the signal. | ||
| 1069 | const rc = posix.system.kill(self_pid, test_signo); | ||
| 1070 | switch (posix.errno(rc)) { | ||
| 1071 | .SUCCESS => { | ||
| 1072 | // See that the signal is blocked, then unblocked | ||
| 1073 | try testing.expectEqual(0, S.handler_called_count); | ||
| 1074 | posix.sigprocmask(posix.SIG.UNBLOCK, &block_one, null); | ||
| 1075 | try testing.expectEqual(1, S.handler_called_count); | ||
| 1076 | }, | ||
| 1077 | .INVAL => { | ||
| 1078 | // Signal won't get delviered. Just clean up. | ||
| 1079 | posix.sigprocmask(posix.SIG.UNBLOCK, &block_one, null); | ||
| 1080 | try testing.expectEqual(0, S.handler_called_count); | ||
| 1081 | }, | ||
| 1082 | else => |errno| return posix.unexpectedErrno(errno), | ||
| 1083 | } | ||
| 1084 | |||
| 1085 | // Restore original handler | ||
| 1086 | posix.sigaction(test_signo, &old_sa, null); | ||
| 1087 | } | ||
| 1088 | } | ||
| 1089 | |||
| 1090 | test "dup & dup2" { | 706 | test "dup & dup2" { |
| 1091 | switch (native_os) { | 707 | switch (native_os) { |
| 1092 | .linux, .solaris, .illumos => {}, | 708 | .linux, .solaris, .illumos => {}, |
test/standalone/build.zig.zon+3| ... | @@ -214,6 +214,9 @@ | ... | @@ -214,6 +214,9 @@ |
| 214 | .tsan = .{ | 214 | .tsan = .{ |
| 215 | .path = "tsan", | 215 | .path = "tsan", |
| 216 | }, | 216 | }, |
| 217 | .posix = .{ | ||
| 218 | .path = "posix", | ||
| 219 | }, | ||
| 217 | }, | 220 | }, |
| 218 | .paths = .{ | 221 | .paths = .{ |
| 219 | "build.zig", | 222 | "build.zig", |
test/standalone/posix/README.md created+8| ... | @@ -0,0 +1,8 @@ | ||
| 1 | ## Zig standalone POSIX tests | ||
| 2 | |||
| 3 | This directory is just for std.posix-related test cases that depend on | ||
| 4 | process-wide state like the current-working directory, signal handlers, | ||
| 5 | fork, the main thread, environment variables, etc. Most tests (e.g, | ||
| 6 | around file descriptors, etc) are with the unit tests in | ||
| 7 | `lib/std/posix/test.zig`. New tests should be with the unit tests, unless | ||
| 8 | there is a specific reason they cannot. | ||
test/standalone/posix/build.zig created+80| ... | @@ -0,0 +1,80 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | |||
| 4 | const Case = struct { | ||
| 5 | src_path: []const u8, | ||
| 6 | set_env_vars: bool = false, | ||
| 7 | }; | ||
| 8 | |||
| 9 | const cases = [_]Case{ | ||
| 10 | .{ | ||
| 11 | .src_path = "cwd.zig", | ||
| 12 | }, | ||
| 13 | .{ | ||
| 14 | .src_path = "getenv.zig", | ||
| 15 | .set_env_vars = true, | ||
| 16 | }, | ||
| 17 | .{ | ||
| 18 | .src_path = "sigaction.zig", | ||
| 19 | }, | ||
| 20 | .{ | ||
| 21 | .src_path = "relpaths.zig", | ||
| 22 | }, | ||
| 23 | }; | ||
| 24 | |||
| 25 | pub fn build(b: *std.Build) void { | ||
| 26 | const test_step = b.step("test", "Run POSIX standalone test cases"); | ||
| 27 | b.default_step = test_step; | ||
| 28 | |||
| 29 | const optimize = b.standardOptimizeOption(.{}); | ||
| 30 | |||
| 31 | const default_target = b.resolveTargetQuery(.{}); | ||
| 32 | |||
| 33 | // Run each test case built against libc-less, glibc, and musl. | ||
| 34 | for (cases) |case| { | ||
| 35 | const run_def = run_exe(b, optimize, &case, default_target, false); | ||
| 36 | test_step.dependOn(&run_def.step); | ||
| 37 | |||
| 38 | if (default_target.result.os.tag == .linux) { | ||
| 39 | const gnu_target = b.resolveTargetQuery(.{ .abi = .gnu }); | ||
| 40 | const musl_target = b.resolveTargetQuery(.{ .abi = .musl }); | ||
| 41 | |||
| 42 | const run_gnu = run_exe(b, optimize, &case, gnu_target, true); | ||
| 43 | const run_musl = run_exe(b, optimize, &case, musl_target, true); | ||
| 44 | |||
| 45 | test_step.dependOn(&run_gnu.step); | ||
| 46 | test_step.dependOn(&run_musl.step); | ||
| 47 | } else { | ||
| 48 | const run_libc = run_exe(b, optimize, &case, default_target, true); | ||
| 49 | test_step.dependOn(&run_libc.step); | ||
| 50 | } | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | fn run_exe(b: *std.Build, optimize: std.builtin.OptimizeMode, case: *const Case, target: std.Build.ResolvedTarget, link_libc: bool) *std.Build.Step.Run { | ||
| 55 | const exe_name = b.fmt("test-posix-{s}{s}{s}", .{ | ||
| 56 | std.fs.path.stem(case.src_path), | ||
| 57 | if (link_libc) "-libc" else "", | ||
| 58 | if (link_libc and target.result.isGnuLibC()) "-gnu" else if (link_libc and target.result.isMuslLibC()) "-musl" else "", | ||
| 59 | }); | ||
| 60 | |||
| 61 | const exe = b.addExecutable(.{ | ||
| 62 | .name = exe_name, | ||
| 63 | .root_module = b.createModule(.{ | ||
| 64 | .root_source_file = b.path(case.src_path), | ||
| 65 | .link_libc = link_libc, | ||
| 66 | .optimize = optimize, | ||
| 67 | .target = target, | ||
| 68 | }), | ||
| 69 | }); | ||
| 70 | |||
| 71 | const run_cmd = b.addRunArtifact(exe); | ||
| 72 | |||
| 73 | if (case.set_env_vars) { | ||
| 74 | run_cmd.setEnvironmentVariable("ZIG_TEST_POSIX_1EQ", "test=variable"); | ||
| 75 | run_cmd.setEnvironmentVariable("ZIG_TEST_POSIX_3EQ", "=test=variable="); | ||
| 76 | run_cmd.setEnvironmentVariable("ZIG_TEST_POSIX_EMPTY", ""); | ||
| 77 | } | ||
| 78 | |||
| 79 | return run_cmd; | ||
| 80 | } | ||
test/standalone/posix/cwd.zig created+75| ... | @@ -0,0 +1,75 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | |||
| 4 | const path_max = std.fs.max_path_bytes; | ||
| 5 | |||
| 6 | pub fn main() !void { | ||
| 7 | if (builtin.target.os.tag == .wasi) { | ||
| 8 | // WASI doesn't support changing the working directory at all. | ||
| 9 | return; | ||
| 10 | } | ||
| 11 | |||
| 12 | var Allocator = std.heap.DebugAllocator(.{}){}; | ||
| 13 | const a = Allocator.allocator(); | ||
| 14 | defer std.debug.assert(Allocator.deinit() == .ok); | ||
| 15 | |||
| 16 | try test_chdir_self(); | ||
| 17 | try test_chdir_absolute(); | ||
| 18 | try test_chdir_relative(a); | ||
| 19 | } | ||
| 20 | |||
| 21 | // get current working directory and expect it to match given path | ||
| 22 | fn expect_cwd(expected_cwd: []const u8) !void { | ||
| 23 | var cwd_buf: [path_max]u8 = undefined; | ||
| 24 | const actual_cwd = try std.posix.getcwd(cwd_buf[0..]); | ||
| 25 | try std.testing.expectEqualStrings(actual_cwd, expected_cwd); | ||
| 26 | } | ||
| 27 | |||
| 28 | fn test_chdir_self() !void { | ||
| 29 | var old_cwd_buf: [path_max]u8 = undefined; | ||
| 30 | const old_cwd = try std.posix.getcwd(old_cwd_buf[0..]); | ||
| 31 | |||
| 32 | // Try changing to the current directory | ||
| 33 | try std.posix.chdir(old_cwd); | ||
| 34 | try expect_cwd(old_cwd); | ||
| 35 | } | ||
| 36 | |||
| 37 | fn test_chdir_absolute() !void { | ||
| 38 | var old_cwd_buf: [path_max]u8 = undefined; | ||
| 39 | const old_cwd = try std.posix.getcwd(old_cwd_buf[0..]); | ||
| 40 | |||
| 41 | const parent = std.fs.path.dirname(old_cwd) orelse unreachable; // old_cwd should be absolute | ||
| 42 | |||
| 43 | // Try changing to the parent via a full path | ||
| 44 | try std.posix.chdir(parent); | ||
| 45 | |||
| 46 | try expect_cwd(parent); | ||
| 47 | } | ||
| 48 | |||
| 49 | fn test_chdir_relative(a: std.mem.Allocator) !void { | ||
| 50 | var tmp = std.testing.tmpDir(.{}); | ||
| 51 | defer tmp.cleanup(); | ||
| 52 | |||
| 53 | // Use the tmpDir parent_dir as the "base" for the test. Then cd into the child | ||
| 54 | try tmp.parent_dir.setAsCwd(); | ||
| 55 | |||
| 56 | // Capture base working directory path, to build expected full path | ||
| 57 | var base_cwd_buf: [path_max]u8 = undefined; | ||
| 58 | const base_cwd = try std.posix.getcwd(base_cwd_buf[0..]); | ||
| 59 | |||
| 60 | const relative_dir_name = &tmp.sub_path; | ||
| 61 | const expected_path = try std.fs.path.resolve(a, &.{ base_cwd, relative_dir_name }); | ||
| 62 | defer a.free(expected_path); | ||
| 63 | |||
| 64 | // change current working directory to new test directory | ||
| 65 | try std.posix.chdir(relative_dir_name); | ||
| 66 | |||
| 67 | var new_cwd_buf: [path_max]u8 = undefined; | ||
| 68 | const new_cwd = try std.posix.getcwd(new_cwd_buf[0..]); | ||
| 69 | |||
| 70 | // On Windows, fs.path.resolve returns an uppercase drive letter, but the drive letter returned by getcwd may be lowercase | ||
| 71 | const resolved_cwd = try std.fs.path.resolve(a, &.{new_cwd}); | ||
| 72 | defer a.free(resolved_cwd); | ||
| 73 | |||
| 74 | try std.testing.expectEqualStrings(expected_path, resolved_cwd); | ||
| 75 | } | ||
test/standalone/posix/getenv.zig created+32| ... | @@ -0,0 +1,32 @@ | ||
| 1 | // test getting environment variables | ||
| 2 | |||
| 3 | const std = @import("std"); | ||
| 4 | const builtin = @import("builtin"); | ||
| 5 | |||
| 6 | pub fn main() !void { | ||
| 7 | if (builtin.target.os.tag == .windows) { | ||
| 8 | return; // Windows env strings are WTF-16, so not supported by Zig's std.posix.getenv() | ||
| 9 | } | ||
| 10 | |||
| 11 | if (builtin.target.os.tag == .wasi and !builtin.link_libc) { | ||
| 12 | return; // std.posix.getenv is not supported on WASI due to the need of allocation | ||
| 13 | } | ||
| 14 | |||
| 15 | // Test some unset env vars: | ||
| 16 | |||
| 17 | try std.testing.expectEqual(std.posix.getenv(""), null); | ||
| 18 | try std.testing.expectEqual(std.posix.getenv("BOGUSDOESNOTEXISTENVVAR"), null); | ||
| 19 | try std.testing.expectEqual(std.posix.getenvZ("BOGUSDOESNOTEXISTENVVAR"), null); | ||
| 20 | |||
| 21 | if (builtin.link_libc) { | ||
| 22 | // Test if USER matches what C library sees | ||
| 23 | const expected = std.mem.span(std.c.getenv("USER") orelse ""); | ||
| 24 | const actual = std.posix.getenv("USER") orelse ""; | ||
| 25 | try std.testing.expectEqualStrings(expected, actual); | ||
| 26 | } | ||
| 27 | |||
| 28 | // env vars set by our build.zig run step: | ||
| 29 | try std.testing.expectEqualStrings("", std.posix.getenv("ZIG_TEST_POSIX_EMPTY") orelse "invalid"); | ||
| 30 | try std.testing.expectEqualStrings("test=variable", std.posix.getenv("ZIG_TEST_POSIX_1EQ") orelse "invalid"); | ||
| 31 | try std.testing.expectEqualStrings("=test=variable=", std.posix.getenv("ZIG_TEST_POSIX_3EQ") orelse "invalid"); | ||
| 32 | } | ||
test/standalone/posix/relpaths.zig created+94| ... | @@ -0,0 +1,94 @@ | ||
| 1 | // Test relative paths through POSIX APIS. These tests have to change the cwd, so | ||
| 2 | // they shouldn't be Zig unit tests. | ||
| 3 | |||
| 4 | const std = @import("std"); | ||
| 5 | const builtin = @import("builtin"); | ||
| 6 | |||
| 7 | pub 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 | |||
| 24 | fn 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 | |||
| 51 | fn 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 | } | ||
test/standalone/posix/sigaction.zig created+153| ... | @@ -0,0 +1,153 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | |||
| 4 | const native_os = builtin.target.os.tag; | ||
| 5 | |||
| 6 | pub fn main() !void { | ||
| 7 | if (native_os == .wasi or native_os == .windows) { | ||
| 8 | return; // no sigaction | ||
| 9 | } | ||
| 10 | |||
| 11 | try test_sigaction(); | ||
| 12 | try test_sigset_bits(); | ||
| 13 | } | ||
| 14 | |||
| 15 | fn test_sigaction() !void { | ||
| 16 | if (native_os == .macos and builtin.target.cpu.arch == .x86_64) { | ||
| 17 | return; // https://github.com/ziglang/zig/issues/15381 | ||
| 18 | } | ||
| 19 | |||
| 20 | const test_signo = std.posix.SIG.URG; // URG only because it is ignored by default in debuggers | ||
| 21 | |||
| 22 | const S = struct { | ||
| 23 | var handler_called_count: u32 = 0; | ||
| 24 | |||
| 25 | fn handler(sig: i32, info: *const std.posix.siginfo_t, ctx_ptr: ?*anyopaque) callconv(.c) void { | ||
| 26 | _ = ctx_ptr; | ||
| 27 | // Check that we received the correct signal. | ||
| 28 | const info_sig = switch (native_os) { | ||
| 29 | .netbsd => info.info.signo, | ||
| 30 | else => info.signo, | ||
| 31 | }; | ||
| 32 | if (sig == test_signo and sig == info_sig) { | ||
| 33 | handler_called_count += 1; | ||
| 34 | } | ||
| 35 | } | ||
| 36 | }; | ||
| 37 | |||
| 38 | var sa: std.posix.Sigaction = .{ | ||
| 39 | .handler = .{ .sigaction = &S.handler }, | ||
| 40 | .mask = std.posix.sigemptyset(), | ||
| 41 | .flags = std.posix.SA.SIGINFO | std.posix.SA.RESETHAND, | ||
| 42 | }; | ||
| 43 | |||
| 44 | var old_sa: std.posix.Sigaction = undefined; | ||
| 45 | |||
| 46 | // Install the new signal handler. | ||
| 47 | std.posix.sigaction(test_signo, &sa, null); | ||
| 48 | |||
| 49 | // Check that we can read it back correctly. | ||
| 50 | std.posix.sigaction(test_signo, null, &old_sa); | ||
| 51 | try std.testing.expectEqual(&S.handler, old_sa.handler.sigaction.?); | ||
| 52 | try std.testing.expect((old_sa.flags & std.posix.SA.SIGINFO) != 0); | ||
| 53 | |||
| 54 | // Invoke the handler. | ||
| 55 | try std.posix.raise(test_signo); | ||
| 56 | try std.testing.expectEqual(1, S.handler_called_count); | ||
| 57 | |||
| 58 | // Check if passing RESETHAND correctly reset the handler to SIG_DFL | ||
| 59 | std.posix.sigaction(test_signo, null, &old_sa); | ||
| 60 | try std.testing.expectEqual(std.posix.SIG.DFL, old_sa.handler.handler); | ||
| 61 | |||
| 62 | // Reinstall the signal w/o RESETHAND and re-raise | ||
| 63 | sa.flags = std.posix.SA.SIGINFO; | ||
| 64 | std.posix.sigaction(test_signo, &sa, null); | ||
| 65 | try std.posix.raise(test_signo); | ||
| 66 | try std.testing.expectEqual(2, S.handler_called_count); | ||
| 67 | |||
| 68 | // Now set the signal to ignored | ||
| 69 | sa.handler = .{ .handler = std.posix.SIG.IGN }; | ||
| 70 | sa.flags = 0; | ||
| 71 | std.posix.sigaction(test_signo, &sa, null); | ||
| 72 | |||
| 73 | // Re-raise to ensure handler is actually ignored | ||
| 74 | try std.posix.raise(test_signo); | ||
| 75 | try std.testing.expectEqual(2, S.handler_called_count); | ||
| 76 | |||
| 77 | // Ensure that ignored state is returned when querying | ||
| 78 | std.posix.sigaction(test_signo, null, &old_sa); | ||
| 79 | try std.testing.expectEqual(std.posix.SIG.IGN, old_sa.handler.handler); | ||
| 80 | } | ||
| 81 | |||
| 82 | fn test_sigset_bits() !void { | ||
| 83 | const NO_SIG: i32 = 0; | ||
| 84 | |||
| 85 | const S = struct { | ||
| 86 | var expected_sig: i32 = undefined; | ||
| 87 | var seen_sig: i32 = NO_SIG; | ||
| 88 | |||
| 89 | fn handler(sig: i32, info: *const std.posix.siginfo_t, ctx_ptr: ?*anyopaque) callconv(.c) void { | ||
| 90 | _ = ctx_ptr; | ||
| 91 | |||
| 92 | const info_sig = switch (native_os) { | ||
| 93 | .netbsd => info.info.signo, | ||
| 94 | else => info.signo, | ||
| 95 | }; | ||
| 96 | if (seen_sig == NO_SIG and sig == expected_sig and sig == info_sig) { | ||
| 97 | seen_sig = sig; | ||
| 98 | } | ||
| 99 | } | ||
| 100 | }; | ||
| 101 | |||
| 102 | // Assume this is a single-threaded process where the current thread has the | ||
| 103 | // 'pid' thread id. (The sigprocmask calls are thread-private state.) | ||
| 104 | const self_tid = std.posix.system.getpid(); | ||
| 105 | |||
| 106 | // To check that sigset_t mapping matches kernel (think u32/u64 mismatches on | ||
| 107 | // big-endian), try sending a blocked signal to make sure the mask matches the | ||
| 108 | // signal. (Send URG and CHLD because they're ignored by default in the | ||
| 109 | // debugger, vs. USR1 or other named signals) | ||
| 110 | inline for ([_]i32{ std.posix.SIG.URG, std.posix.SIG.CHLD, 62, 94, 126 }) |test_signo| { | ||
| 111 | if (test_signo >= std.posix.NSIG) continue; | ||
| 112 | |||
| 113 | S.expected_sig = test_signo; | ||
| 114 | S.seen_sig = NO_SIG; | ||
| 115 | |||
| 116 | const sa: std.posix.Sigaction = .{ | ||
| 117 | .handler = .{ .sigaction = &S.handler }, | ||
| 118 | .mask = std.posix.sigemptyset(), | ||
| 119 | .flags = std.posix.SA.SIGINFO | std.posix.SA.RESETHAND, | ||
| 120 | }; | ||
| 121 | |||
| 122 | var old_sa: std.posix.Sigaction = undefined; | ||
| 123 | |||
| 124 | // Install the new signal handler. | ||
| 125 | std.posix.sigaction(test_signo, &sa, &old_sa); | ||
| 126 | |||
| 127 | // block the signal and see that its delayed until unblocked | ||
| 128 | var block_one: std.posix.sigset_t = std.posix.sigemptyset(); | ||
| 129 | std.posix.sigaddset(&block_one, test_signo); | ||
| 130 | std.posix.sigprocmask(std.posix.SIG.BLOCK, &block_one, null); | ||
| 131 | |||
| 132 | // qemu maps target signals to host signals 1-to-1, so targets | ||
| 133 | // with more signals than the host will fail to send the signal. | ||
| 134 | const rc = std.posix.system.kill(self_tid, test_signo); | ||
| 135 | switch (std.posix.errno(rc)) { | ||
| 136 | .SUCCESS => { | ||
| 137 | // See that the signal is blocked, then unblocked | ||
| 138 | try std.testing.expectEqual(NO_SIG, S.seen_sig); | ||
| 139 | std.posix.sigprocmask(std.posix.SIG.UNBLOCK, &block_one, null); | ||
| 140 | try std.testing.expectEqual(test_signo, S.seen_sig); | ||
| 141 | }, | ||
| 142 | .INVAL => { | ||
| 143 | // Signal won't get delviered. Just clean up. | ||
| 144 | std.posix.sigprocmask(std.posix.SIG.UNBLOCK, &block_one, null); | ||
| 145 | try std.testing.expectEqual(NO_SIG, S.seen_sig); | ||
| 146 | }, | ||
| 147 | else => |errno| return std.posix.unexpectedErrno(errno), | ||
| 148 | } | ||
| 149 | |||
| 150 | // Restore original handler | ||
| 151 | std.posix.sigaction(test_signo, &old_sa, null); | ||
| 152 | } | ||
| 153 | } | ||