| 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 | 1637 | event.wait(); |
| 1638 | 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 | const fs = std.fs; |
| 8 | 8 | const mem = std.mem; |
| 9 | 9 | const elf = std.elf; |
| 10 | const Thread = std.Thread; | |
| 11 | 10 | const linux = std.os.linux; |
| 12 | 11 | |
| 13 | 12 | const a = std.testing.allocator; |
| ... | ... | @@ -18,6 +17,9 @@ const AtomicOrder = std.builtin.AtomicOrder; |
| 18 | 17 | const native_os = builtin.target.os.tag; |
| 19 | 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 | 23 | // https://github.com/ziglang/zig/issues/20288 |
| 22 | 24 | test "WTF-8 to WTF-16 conversion buffer overflows" { |
| 23 | 25 | if (native_os != .windows) return error.SkipZigTest; |
| ... | ... | @@ -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 | 45 | test "open smoke test" { |
| 106 | 46 | if (native_os == .wasi) return error.SkipZigTest; |
| 107 | 47 | if (native_os == .windows) return error.SkipZigTest; |
| ... | ... | @@ -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 | 170 | test "readlink on Windows" { |
| 270 | 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 | 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 | 184 | test "linkat with different directories" { |
| 333 | 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 | 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 | 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 | 301 | test "getrandom" { |
| 514 | 302 | var buf_a: [50]u8 = undefined; |
| 515 | 303 | var buf_b: [50]u8 = undefined; |
| ... | ... | @@ -520,12 +308,6 @@ test "getrandom" { |
| 520 | 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 | 311 | test "getuid" { |
| 530 | 312 | if (native_os == .windows or native_os == .wasi) return error.SkipZigTest; |
| 531 | 313 | _ = posix.getuid(); |
| ... | ... | @@ -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 | 522 | test "fcntl" { |
| 759 | 523 | if (native_os == .windows or native_os == .wasi) |
| 760 | 524 | return error.SkipZigTest; |
| ... | ... | @@ -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 | 706 | test "dup & dup2" { |
| 1091 | 707 | switch (native_os) { |
| 1092 | 708 | .linux, .solaris, .illumos => {}, |
test/standalone/build.zig.zon+3| ... | ... | @@ -214,6 +214,9 @@ |
| 214 | 214 | .tsan = .{ |
| 215 | 215 | .path = "tsan", |
| 216 | 216 | }, |
| 217 | .posix = .{ | |
| 218 | .path = "posix", | |
| 219 | }, | |
| 217 | 220 | }, |
| 218 | 221 | .paths = .{ |
| 219 | 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 | } |