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
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
//! Deterministic **fault injection at the OS-primitive boundary** of the
//! containment backends — compiled only under `cfg(test)`.
//!
//! The happy paths of `sys::linux` / `sys::windows` / `sys::pgroup` are covered by
//! real-subprocess integration tests, but their *error* paths are not: a failed
//! `cgroup.freeze` write, a `SetInformationJobObject` rejected on the second info
//! class after the first succeeded, an `EPERM` from `killpg` against a uid-changed
//! child. Each needs a privileged, degraded or otherwise hand-built host to
//! reproduce, so none of them is regression-tested today. This module lets a unit
//! test order one specific call of one specific primitive to fail with one specific
//! errno, and assert what the crate reports for it.
//!
//! # Why this shape (and not the alternatives)
//!
//! The crate already has four hermetic-seam precedents, and this seam was chosen by
//! elimination against them rather than invented fresh:
//!
//! - **`ScriptedRunner` / the `ProcessRunner` doubles** (`src/doubles.rs`) — a trait
//! double at a boundary that *is already an abstraction* (the runner), replacing
//! the OS wholesale. It does not fit here: the OS-primitive boundary inside a
//! backend is a handful of FFI calls owned by `Job`/`Cgroup`/`Tracked`, and making
//! it a trait means either a `dyn` field on every containment object (an indirect
//! call on every happy-path `kill`/`write`, and a pointer per group — the runtime
//! cost this seam must not have) or a generic parameter that goes viral through
//! `sys::Job` into the **public** `ProcessGroup` type. Faking the OS is also more
//! than is wanted: these tests should exercise the *real* probe, identity and
//! liveness logic and only substitute the one call whose failure is under test.
//! - **The `cfg(loom)` `#[path]`-included cores** (`sys/skip_drop_kill.rs`,
//! `sys/pid_gate.rs`, `running/deadline.rs`) — extract a *pure* algorithmic core
//! so a separate harness can model-check it. Inapplicable by construction: a
//! cgroup write or a `killpg` is the effect itself; there is no pure core to lift
//! out, and the crate has already applied that technique everywhere one exists.
//! - **The `*_with(read: impl Fn(&Path) -> io::Result<String>)` closure seams**
//! (`Cgroup::members_with` / `signal_with` / `kill_with` / `stats_with` /
//! `limit_evidence_with`) — the closest existing relative, and deliberately kept:
//! it is the right tool when the injected primitive is already threaded as a
//! parameter and the assertion is at the backend's own return value. It does not
//! reach these cases, because the failing calls sit on *write*/FFI paths behind
//! public constructors (`ProcessGroup::with_options` → `Job::new` →
//! `SetInformationJobObject`) that cannot take a closure without changing the
//! public API, and because the assertion wanted here is the *crate's* error
//! contract at the end of the whole call, not an `io::Error` at the backend edge.
//! - **The `cfg(test)` task-local probes in `src/pump.rs`** (T-128) — an *ambient*,
//! test-only channel consulted at an interesting point deep inside a call whose
//! public entry point takes no test parameter, compiled out of production
//! entirely. That is exactly this problem, one step further along: T-128 needed to
//! **observe** an internal event, this needs to **substitute its result**. So this
//! module is the T-128 probe pattern generalized from observation to injection —
//! ambient (nothing threaded through production signatures), scoped (parallel
//! tests stay isolated), and absent outside `cfg(test)`.
//!
//! Storage is a `thread_local` rather than `pump.rs`'s `tokio::task_local`: every
//! path this covers is synchronous and stays on the caller's thread
//! (`ProcessGroup::with_options`/`update_limits`/`suspend`/`resume`/`signal` are
//! plain `fn`s calling straight into the backend), and a thread-local additionally
//! works in a `#[test]` with no tokio runtime. libtest gives each test its own
//! thread and `cargo nextest` its own process, so armed rules never leak between
//! tests. The one limitation this implies is deliberate and named: a primitive
//! invoked on a *different* thread than the one that armed the rules sees no rules
//! and calls the real OS — the safe direction.
//!
//! # How a backend is wired in
//!
//! Each backend routes the primitive through **one** owning wrapper (the convention
//! the crate already uses for launch/config boundaries) and consults [`check`] there
//! under `#[cfg(test)]`, so production keeps the bare call and there is exactly one
//! place per primitive to audit:
//!
//! - `sys::linux::cgroup_write` — every `write(2)` to a cgroup v2 interface file;
//! the target label is the file name (`memory.max`, `cgroup.freeze`, …).
//! - `sys::windows::set_information_job_object` — every `SetInformationJobObject`
//! call; the target label is the info-class axis (`extended-limit`, `cpu-rate`).
//! - `sys::pgroup::deliver_signal` — every real signal *delivery* in the tracked
//! sweep; the target label is the syscall (`killpg`, `kill`).
//! - `sys::pty::unix::{clone_master, AsyncPtyMaster::new}` — each fallible step
//! that runs *after* a PTY child already exists and is contained (a master
//! `dup`, a reactor registration); the target label names the owner of the fd
//! (`reader`, `writer`, `resize`). This is what makes the post-spawn rollback
//! path — otherwise reachable only on an fd-exhausted host — testable.
//!
//! A faulted call never reaches the OS at all, which is also what makes a test able
//! to name a signal it must not actually send.
use RefCell;
use io;
/// An OS primitive a backend routes every call through, and that an armed
/// [`Rule`] can therefore make fail.
///
/// One variant per wrapped primitive, gated to the platforms that have it so an
/// unreachable variant can never be armed by mistake.
pub
/// One armed fault: which primitive to fail, which of its calls, and with what.
thread_local!
/// Consulted by a wrapped OS primitive **before** it calls the OS: `Some(err)` means
/// this call must fail with `err` and never reach the kernel, `None` means proceed.
///
/// The first rule that matches `site` (and, when it names one, `target`) decides —
/// later rules do not also see the call.
pub
/// A set of faults being built up, armed with [`arm`](Self::arm).
pub
/// The live scope of a set of armed faults: it disarms them on drop (including on
/// unwind, so a failing assertion cannot leave a fault armed for the next test on
/// this thread) and reports what the seam saw while it was armed.
pub