mirror of
https://github.com/max-sixty/worktrunk.git
synced 2026-09-14 20:00:38 +08:00
715af4cd72
Two test-suite flakes fixed at the root, both dependencies on machine
load and concurrent builds.
**Concurrent-cargo spawn `NotFound`.** Cargo uplifts `target/debug/wt`
by removing the path and recreating it, so a second `cargo` against the
same target directory leaves the binary every test spawns absent for a
fraction of a millisecond per rebuild — the one-off `NotFound` spawn
failures that pass on re-run. `wt_bin()` now returns a hardlink pinned
under `target/debug/wt-test-bin/<mtime>-<len>/`: the uplift unlinks only
the uplifted name, so the pin keeps serving the observed binary through
any number of concurrent rebuilds, at no disk cost beyond the `deps/`
artifact whose inode it shares. `test_wt_spawns_are_pinned` keeps every
spawn routed through it. Reproduced by re-creating the uplift every 200
ms alongside a full `cargo nextest run`: 302 of 4583 tests failed before
this change (147 as direct `NotFound` spawn panics, five of them
byte-identical to the original shell-wrapper report), 4585 of 4585 after
— with an unrelated external cargo also rebuilding `wt` mid-validation,
absorbed the same way.
**`--reap` probe races.** `test_remove_reap_kills_process` predicted the
reap guard's verdict with its own `lsof`/`ps` snapshot, and under load
either probe's spawn can stall past the 5 s bound, whose fail-safe empty
result flips the outcome — a prediction `wt` then contradicts, or `wt`
reporting "No processes to reap" for a live child. The prediction now
reads the session's controlling terminal directly (`/dev/tty` opens iff
the session has one — the property the child inherits at spawn), the
probe timeout is env-pinnable (`WORKTRUNK_TEST_PROBE_TIMEOUT_MS`, set to
60 s in the static test baseline; production keeps its 5 s bound), and
the discovery poll uses the suite's 60 s presence-poll convention.
Looped 15/15 green at load average ~60, where the previous shape failed
2/10.
Not covered here, noted as follow-ups: benches still spawn
`env!("CARGO_BIN_EXE_wt")` directly (same hazard, separate runner,
outside the guard's scan), and the reap "spared" branch has no
deterministic end-to-end test (needs a PTY-held child).
> _This was written by Claude Code on behalf of max-sixty_
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
57 lines
2.0 KiB
Rust
57 lines
2.0 KiB
Rust
//! Windows-specific shell integration tests.
|
|
//!
|
|
//! These tests verify that shell integration works correctly on Windows.
|
|
//! On Windows, binaries have `.exe` extension but shell integration uses the base name
|
|
//! (e.g., `wt` not `wt.exe`) because MSYS2/Git Bash handles the resolution automatically.
|
|
|
|
#![cfg(windows)]
|
|
|
|
use crate::common::wt_bin;
|
|
use std::process::Command;
|
|
|
|
/// Verify shell function uses base name (without .exe) on Windows.
|
|
///
|
|
/// When the binary is invoked as `wt.exe`, the generated bash script should:
|
|
/// 1. Define a function named `wt()` (not `wt.exe()`)
|
|
/// 2. Check for `command -v wt` (not `wt.exe`)
|
|
/// 3. Set up completions for `wt`
|
|
///
|
|
/// Users should use `alias wt="wt"` (or just have `wt` in PATH) rather than
|
|
/// `alias wt="wt.exe"`. MSYS2/Git Bash automatically resolves `wt` to `wt.exe`.
|
|
#[test]
|
|
fn test_shell_init_strips_exe_suffix_on_windows() {
|
|
// Run wt.exe config shell init bash
|
|
// Note: This command doesn't need a git repo - it just generates shell init code
|
|
let output = Command::new(wt_bin())
|
|
.args(["config", "shell", "init", "bash"])
|
|
.output()
|
|
.expect("Failed to run wt config shell init");
|
|
|
|
assert!(output.status.success(), "Command failed: {:?}", output);
|
|
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
|
|
// The binary is wt.exe but function should be wt() (base name)
|
|
assert!(
|
|
stdout.contains("wt()"),
|
|
"Expected function definition 'wt()' not found in output.\n\
|
|
Shell integration should use base name without .exe.\n\
|
|
Output:\n{}",
|
|
stdout
|
|
);
|
|
|
|
// Verify command -v check uses base name
|
|
assert!(
|
|
stdout.contains("command -v wt"),
|
|
"Expected 'command -v wt' check not found in output.\nOutput:\n{}",
|
|
stdout
|
|
);
|
|
|
|
// Should NOT contain .exe in function/command names
|
|
assert!(
|
|
!stdout.contains("wt.exe()"),
|
|
"Function should be 'wt()' not 'wt.exe()'.\nOutput:\n{}",
|
|
stdout
|
|
);
|
|
}
|