Skip to content

test(e2e): unique tmux socket per test + RAII server disposal#25

Merged
jakemassoth merged 1 commit into
mainfrom
uniq-test-socket
Jul 9, 2026
Merged

test(e2e): unique tmux socket per test + RAII server disposal#25
jakemassoth merged 1 commit into
mainfrom
uniq-test-socket

Conversation

@jakemassoth

Copy link
Copy Markdown
Owner

Problem

A prior fix pointed the e2e suite at a throwaway -L tmux socket so it never touched the operator's live yeschef server. But that socket was minted once per test-binary run (a OnceLock), which left two problems:

  1. Not per-test. Every test shared one socket/server, so parallel tests (and concurrent yeschef/test instances) stepped on each other — one test's sessions were visible to another, and a kill-session/kill-server in one could disrupt another. That forced --test-threads=1.
  2. The server leaked. Nothing ever ran kill-server on the throwaway socket, so every run left a live tmux server (and its socket file) orphaned in /tmp/tmux-*/. They accumulated.

Change

Each TestEnv is now fully self-isolating:

  • Unique socket per test. unique_socket() combines PID + nanosecond timestamp + a process-wide AtomicU64 counter, so no two envs ever collide — not two created back-to-back in one thread (the counter breaks any timestamp tie), not two separate binary runs (distinct PIDs), and not a run beside a live yeschef instance (yeschef-test- prefix ≠ production yeschef). Replaces the shared per-run OnceLock.
  • RAII server disposal. TestEnv::new() mints the socket, cmd() exports it as YESCHEF_TMUX_SOCKET, and a new Drop impl runs kill-server on that socket — tearing the private server down automatically on pass, fail, or panic. The free helpers tmux()/capture()/window_exists() became TestEnv methods on the per-env socket; test_socket() and the now-redundant WindowCleanup guard are gone.
  • Socket-file cleanup too. tmux leaves a stale socket file behind after kill-server (verified on macOS). So each env points TMUX_TMPDIR inside its own TempDir, and the socket file is removed with the temp dir on drop — no fragile socket-path math. The temp dir is rooted at /tmp because a unix socket path has a hard ~104-byte limit (sun_path) and macOS's default temp dir (/var/folders/…) is deep enough to overflow it.
  • Parallel-safe. With a fully independent server per test, --test-threads=1 is dropped from DEVELOPMENT.md, README.md, and the .#e2e flake app. The suite is green under cargo's default parallel execution.

Verification

cargo fmt --check          # OK
cargo clippy --all-targets -- -D warnings -D clippy::pedantic   # clean
cargo test --bin yeschef   # 67 passed
cargo test --test e2e -- --ignored   # 17 passed IN PARALLEL (no --test-threads=1)
nix run nixpkgs#nixfmt -- --check flake.nix   # OK

No orphaned servers or sockets after the run (before/after, filtering by this suite's config signature so pre-existing orphans from old runs don't muddy it):

================= BEFORE =================
yeschef-test-* socket files:        0
live tmux servers from this suite:  0

Running full e2e suite in PARALLEL (cargo default, no --test-threads=1)...
test result: ok. 17 passed; 0 failed; ...

================= AFTER ==================
yeschef-test-* socket files:        0   <- RAII removed them
live tmux servers from this suite:  0   <- kill-server disposed them

operator's live 'yeschef' server (must be untouched):
80954 tmux -L yeschef -f /Users/jake/yeschef/tmux.conf attach-session -t headchef

The operator's live yeschef server is untouched, and running the suite from inside a live yeschef session (as here) leaves it alone.

Note: the environment still had pre-existing orphaned test servers from old-code runs (2-segment socket names, deep-temp-dir configs) — exactly the leak this PR eliminates going forward. This branch's own run adds none.

Coordination

A separate line cook is reworking tests/e2e.rs + the backend for the tmux-native TUI on another branch — expect a conflict in tests/e2e.rs. This change is tightly scoped to the socket/isolation harness. Rebased onto the latest origin/main (cd8c78c) before opening; that work hasn't landed yet, so no conflict at this time.

Each TestEnv now mints its own uniquely-named -L socket (PID + nanos +
process-wide atomic counter) instead of a shared per-run OnceLock, and a
Drop guard runs kill-server on that socket so the throwaway tmux server
is torn down automatically on pass, fail, or panic. The socket is
relocated under the env's temp dir via TMUX_TMPDIR so its socket file is
cleaned up with the temp dir too -- nothing leaks into /tmp/tmux-*/.

With fully independent per-test servers the suite is parallel-safe, so
--test-threads=1 is dropped from docs, README, and the .#e2e flake app.
The free helpers (tmux/capture/window_exists) become TestEnv methods on
the per-env socket; test_socket() and the now-redundant WindowCleanup
guard are removed.
@jakemassoth
jakemassoth merged commit 12d8320 into main Jul 9, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant