1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
//! Hermetic test temp directories for the `tm` BIN target.
//!
//! Why: the `trusty-mpm` lib already owns this fixture
//! (`trusty_mpm::test_support::hermetic_temp_dir`, #3382/#3390), but it is
//! declared `#[cfg(test)] pub(crate) mod test_support` — compiled only into the
//! lib's own test binary, so no visibility change can make it reachable from
//! here. The `tm` binary is a separate compilation target and needs its own
//! copy, the same conclusion `commands::session::start_tests::HomeGuard`
//! already reached for `$HOME`.
//!
//! What it buys: a bare `tempfile::tempdir()` resolves through
//! `std::env::temp_dir()`, which honors an inherited `$TMPDIR`. That makes
//! every such call site hostage to whatever set the variable — a polluted
//! harness environment (#3382, litter in a project tree) or a sibling test that
//! mutated it mid-run (PR #4914 run 31023632348: five `pm_guard_budget` tests
//! panicked `NotFound` on a Linux runner because another test in this same
//! binary had `TMPDIR` pinned to a macOS-only path). Rooting under the
//! hardcoded system temp path removes the variable from the equation entirely.
//!
//! Deliberately NOT duplicated from the lib's copy: the stale-directory sweep.
//! Both targets emit the same `tm-test-` prefix into the same `/tmp`, so the
//! lib's once-per-process sweep already reaps anything this module leaks.
//! Test: `tests` below.
use PathBuf;
use TempDir;
/// RAII ownership of a real tmux session created by a test (#6116).
///
/// Unlike `hermetic_temp_dir` above, this one is NOT duplicated for the binary:
/// the lib's copy and this one are the same source file, included from both.
/// A kill-on-drop guardrail written twice is one edit away from drifting, and
/// the file needs nothing from either crate root, so `#[path]` costs nothing
/// here that the duplication above pays for.
pub
/// The spawn primitive [`tmux_session`] runs every tmux invocation through
/// (#7060) — the binary target's spelling of the lib `test_support`'s
/// re-export of the same function; see that module for why the seam exists.
pub use disclaimed_output as tmux_spawn;
/// The one lock every `$PATH` mutation and every PATH-resolved spawn in THIS
/// target takes (#7996).
///
/// Why: `gh_identity`'s tests prepend a fake-`gh` directory to `$PATH` with
/// `std::env::set_var`, and [`tmux_session`]'s fixtures spawn a bare `tmux`
/// the OS resolves through `$PATH` at exec time. `setenv` is not atomic
/// against a concurrent `execvp` reading the same block, so an exec that
/// straddles one of those writes finds no `tmux` — reported once as
/// `spawn tmux new-session: No such file or directory`, and once as a
/// `Drop`-time `kill-session` that silently never ran and left the session
/// alive. A module-local mutex in `gh_identity` could not close it, because
/// the other side of the race is in another module.
/// What: one process-wide mutex; poisoning is recovered so a panicking test
/// cannot wedge its siblings. The LIB target's `test_support` supplies the
/// same name by re-exporting `core::trusty_tools_config::env_test_lock`,
/// already its own single PATH regime — which is what lets the shared fixture
/// file spell this `super::lock_path_env()` and compile into both targets.
/// Test: `gh_identity::tests::gh_path_override_and_the_tmux_fixture_share_one_lock`.
static PATH_ENV_LOCK: Mutex = new;
/// The mutex behind [`lock_path_env`], for a test that must observe contention
/// on it rather than take it.
pub
/// Hold the target's PATH regime for the caller's scope. See [`PATH_ENV_LOCK`].
pub
/// Arm `core::home_write_fence` for this whole test binary, before `main`.
///
/// Why (#8545): `cargo test -p trusty-mpm --bin tm` deployed the full skill and
/// agent roster into the operator's `~/.trusty-tools/trusty-mpm/claude-config`
/// and `~/.trusty-mpm/framework`. A process-wide `$HOME` redirect would stop
/// it, but this target bans `HOME` writes (`env_isolation_tests`, #5544) and a
/// redirected `$HOME` makes the #5784 host-state gate refuse tmux to every
/// tmux fixture here. The fence writes no environment at all.
/// What: a pre-`main` constructor, so the fence is armed before libtest starts
/// any test thread and parallel tests only ever read it. It fences the `$HOME`
/// and password-database home config paths; a test that reaches a home-config
/// writer panics there, by name, before the write.
/// Test: `tests::the_home_write_fence_is_armed_for_this_binary`.
/// Give this test binary its own default tmux server, before `main` (#6542).
///
/// Aborts the binary when the private directory cannot be created, rather than
/// let a test reach the operator's server. See `core::tmux_test_isolation`.
/// Test: `tests::this_test_binary_runs_on_a_relocated_tmux_server`.
/// Kill the private tmux servers and remove their directory at exit (#6542).
/// Same prefix the lib's fixture uses, so its sweep reaps these too.
const TEST_DIR_PREFIX: &str = "tm-test-";
/// An absolute, installed-looking `tm` path every hook-writing test can pin
/// (#7244) — this target's spelling of `trusty_mpm::test_support::STABLE_HOOK_EXE`,
/// duplicated for the same reason `hermetic_temp_dir` is: the lib's copy is
/// `#[cfg(test)] pub(crate)` and no visibility change reaches this target.
///
/// Why the value matters: the hooks writer refuses a build-artifact binary, and
/// a test process is one; a CI runner then has no installed `tm` to fall back
/// to. This path passes both of the writer's gates and is never created or
/// executed — only its spelling is inspected. It must stay outside any temp
/// root, which `is_ephemeral_build_path` also refuses.
/// Test: `install_claude_hooks_at_is_idempotent`, `update_cmd_errors_if_not_loaded`.
pub const STABLE_HOOK_EXE: &str = "/usr/local/bin/tm";
/// The real, hardcoded OS temp root — deliberately NOT `std::env::temp_dir()`.
///
/// Why: `env::temp_dir()` reads `$TMPDIR`, which is the exact indirection this
/// module exists to remove. `/tmp` exists on both targets the suite runs on
/// (macOS locally, `ubuntu-latest` in CI), is never inside a project tree, and
/// no environment variable can redirect it.
/// Create a test `TempDir` immune to an inherited or sibling-mutated `$TMPDIR`.
///
/// What: the one replacement for a bare `tempfile::tempdir()` in this binary's
/// test code.
/// Test: [`tests::hermetic_temp_dir_ignores_tmpdir`].
pub
/// Raise the process-global tracing level so a thread-local capture can see
/// `warn!` (#4931).
///
/// Why: `tracing`'s macros short-circuit on a process-global `MAX_LEVEL` that
/// only a GLOBAL default subscriber raises, so a `with_default`/`set_default`
/// capture records nothing unless something in the binary installed one first.
/// `trusty_mpm::test_support::enable_event_capture` is the library's copy and is
/// not reachable from this bin target; this is the bin's one copy (#8405 review).
/// What: installs a bare registry once per process, then asserts the resulting
/// level admits `WARN`, so a filtered global installed elsewhere fails here by
/// name instead of as an empty capture.
/// Test: `compress`'s warning-capture test and
/// `session_start_in_place_proceeds_with_a_warning_on_an_unreadable_config`
/// are vacuous without it.
pub
/// A launch spec's assignments and argv as one line of `K=V ` words then args
/// (#8308), so a seam test can assert with substrings.
pub
/// A state root whose `config.yaml` sets `tmux.alternate_screen` (#8405).
///
/// Why: every CLI launch seam reads the renderer from a config root; its
/// wiring test needs one holding a known value.
/// What: a hermetic temp dir containing `config.yaml` with
/// `tmux: { alternate_screen: <value> }`.
/// Test: used by each `*_follows_the_configured_renderer` test.
pub
/// The renderer operand a launch line carries for `alternate_screen` (#8405).
pub