Compare only settable descriptor status flags in the PTY session test
Class
Two PTY restoration tests compared raw F_GETFL flags: term::session::tests::delayed_old_state_drop_preserves_reopened_pty and the integration test controlling_tty_probe_and_os_state_restore (tests/terminal.rs). Both fail on macOS (ivar) on clean main and pass on Linux (bb1):
left: OFlags(NONBLOCK | RDWR | 0x10000)
right: OFlags(NONBLOCK | RDWR)
Root cause
The comparison was too strict. The reopened PTY does not differ.
0x10000 is XNU's kernel-private FWASWRITTEN ("descriptor was written"). F_GETFL reports it once anything has been written through the open file description, and F_SETFL can neither set nor clear it. The test captured flags before the session wrote its terminal probe, so every later read on macOS had the extra bit.
A direct check on ivar (Python fcntl, on a fresh pty):
| step | F_GETFL |
|---|---|
| open | 0x2 |
F_SETFL with NONBLOCK | 0x6 |
| after one write | 0x10006 |
F_SETFL with the saved flags | 0x10002 |
dup of it | 0x10002 |
Production (tty.rs) restores with F_SETFL from the saved flags, which is correct. The bit is not state a session can save or restore.
Change
-
One helper,
settable_status_flagsintests/support/status_flags.rs, is the only way either restoration test reads descriptor flags. The unit test includes it with#[path], the integration test as a module, and neither test callsfcntl_getflany more. -
The helper selects exactly the flags
F_SETFLcan change, rather than excluding known extras:- Darwin: xnu
FCNTLFLAGS(bsd/sys/fcntl.h), which isFAPPEND | FASYNC | FFSYNC | FFDSYNC | FNONBLOCK. - Linux: the
fcntl(2)settable set, which isO_APPEND | O_ASYNC | O_DIRECT | O_NOATIME | O_NONBLOCK. - Other unix:
O_APPEND | O_NONBLOCK.
The access mode (
O_RDWR) andFWASWRITTENare fixed for the open file, so they are no longer compared. - Darwin: xnu
Validation
| where | command | result |
|---|---|---|
| ivar (macOS 15.6), at 1f11a1f9d | --lib term::session; --test terminal controlling_tty | 1 passed each, 0.00 s / 0.60 s (both failed before) |
| ivar | negative control: restore leaves NONBLOCK set (tty.rs:55) | both tests fail, NONBLOCK vs 0x0 |
| bb1 (Linux), at 1f11a1f9d | the same two tests | 1 passed each, 0.00 s |
| bb1 | Windows GNU cargo check -p acyclic-harness-tui --features tui --lib --tests | ok |
| ivar (macOS 15.6), first revision | cargo test --locked -p acyclic-harness-tui --features tui --lib term:: | 2 passed, 3 reruns (failed before the change) |
| ivar | the full acyclic-harness-tui --features tui lib suite | 14 passed |
| ivar | negative control: restore leaves NONBLOCK set (tty.rs) | the test fails, NONBLOCK | RDWR vs RDWR |
| ivar | rustfmt --check, clippy | clean for session.rs |
| bb1 (Linux) | cargo test --locked -p acyclic-harness-tui --features tui --lib term:: | 2 passed |
Decisions
- Select the settable set, rather than mask known extras or capture the baseline after a write. Selecting what
F_SETFLcan change is exactly the state a session saves and restores, so a new kernel bookkeeping bit cannot break the tests and a leaked settable flag (such asNONBLOCK) still fails them. A baseline taken after a write would hide the reason and depend on test ordering. - Keep the helper in
tests/support, shared by#[path]. That keeps it out of the library's public surface, and there is one definition for both tests. - No production change. Restoring through
F_SETFLalready ignores bits that cannot be set.
š¤ Generated with Claude Code