use super::*;
#[serial_test::serial(tty_arbiter)]
#[test]
fn mark_fds_cloexec_preserves_stdio() {
mark_fds_cloexec();
for fd in 0..3i32 {
let flags = unsafe { libc::fcntl(fd, libc::F_GETFD) };
assert!(
flags >= 0,
"stdio fd {fd} must remain open after mark_fds_cloexec"
);
assert_eq!(
flags & libc::FD_CLOEXEC,
0,
"stdio fd {fd} must not have FD_CLOEXEC set"
);
}
}
#[serial_test::serial(tty_arbiter)]
#[test]
fn mark_fds_cloexec_sets_flag_on_new_fd() {
let f = std::fs::File::open("/dev/null").expect("open /dev/null");
let fd = std::os::unix::io::AsRawFd::as_raw_fd(&f);
unsafe {
let flags = libc::fcntl(fd, libc::F_GETFD);
libc::fcntl(fd, libc::F_SETFD, flags & !libc::FD_CLOEXEC);
}
assert_eq!(
unsafe { libc::fcntl(fd, libc::F_GETFD) } & libc::FD_CLOEXEC,
0,
"pre-condition: CLOEXEC should be clear"
);
mark_fds_cloexec();
let flags_after = unsafe { libc::fcntl(fd, libc::F_GETFD) };
assert_ne!(
flags_after & libc::FD_CLOEXEC,
0,
"mark_fds_cloexec must set FD_CLOEXEC on an open fd (fd={fd})"
);
}
#[test]
fn terminal_fd_available_true_normally() {
assert!(
terminal_fd_available(),
"fd table should have free slots normally"
);
}
#[test]
fn terminal_fd_available_false_when_probe_open_fails() {
assert!(
!terminal_fd_available_from_probe(|| {
Err(std::io::Error::from_raw_os_error(libc::EMFILE))
}),
"terminal_fd_available must return false when the probe cannot open"
);
assert!(
terminal_fd_available(),
"fd table should still have free slots after the synthetic failure"
);
}
#[serial_test::serial(tty_arbiter)]
#[test]
fn mark_fds_cloexec_is_idempotent() {
let f = std::fs::File::open("/dev/null").expect("open /dev/null");
let fd = std::os::unix::io::AsRawFd::as_raw_fd(&f);
mark_fds_cloexec();
let flags_first = unsafe { libc::fcntl(fd, libc::F_GETFD) };
mark_fds_cloexec();
let flags_second = unsafe { libc::fcntl(fd, libc::F_GETFD) };
assert_eq!(
flags_first, flags_second,
"second call must not change fd flags"
);
}