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
//! Shared helpers for cuttlefish's daemon-facing tests (`daemon.rs`).
//!
//! Mirrors the equivalent helpers in `crates/cuttlefishd/tests/api.rs`: these
//! tests spawn a *real* `cuttlefishd` process and talk to it over a real
//! transport, nothing mocked.
use ;
use Stdio;
/// A private endpoint for one test.
///
/// Tests run concurrently, so each needs its own. On unix that is a socket
/// inside the test's own tempdir; on Windows a bare filesystem path is not a
/// valid/usable named pipe name — `cuttlefishd` on Windows listens on a named
/// pipe, not a filesystem path — so a pipe name is constructed instead,
/// unique via the process id plus a counter (a pipe name has no tempdir to
/// live in). Mirrors `crates/cuttlefishd/tests/api.rs`'s own
/// `unique_endpoint`.
/// Build `cuttlefishd` once per test binary and return its compiled path.
///
/// `env!("CARGO_BIN_EXE_cuttlefishd")` doesn't work here — that's only
/// populated for a binary belonging to the *same* package as the test
/// binary, not a sibling workspace crate's. Build and locate manually
/// instead, same pattern `crates/cuttlefishd/tests/api.rs`'s own
/// `example_block()` helper already uses for a sibling crate's artifact.
/// Build the example block once per test binary, the same fixture
/// `crates/cuttlefishd/tests/api.rs`'s own `example_block()` builds — reused
/// here rather than duplicated as a distinct wasm source, since these tests
/// only need *some* real, loadable block to satisfy the daemon's startup
/// typecheck, not any particular behavior from it.
/// Point `$CUTTLEFISH_HOME` at one tempdir shared by every test in this
/// binary, so a spawned `cuttlefishd`'s job ledger and catalog never touch a
/// developer's real home directory. `std::process::Command::spawn` inherits
/// the calling process's environment by default, so setting this here is
/// enough for every `spawn_daemon` call afterward to pick it up — no need to
/// thread it through as an explicit argument. Mirrors
/// `crates/cuttlefishd/tests/api.rs`'s own `ensure_test_cuttlefish_home`.
///
/// A `OnceLock` plus its own internal `Once`-style init (via `get_or_init`,
/// which itself synchronizes concurrent callers) means every concurrently
/// running `#[tokio::test]` in this binary computes and sets the exact same
/// value at most once, rather than racing distinct `std::env::set_var` calls
/// against each other.
/// Serializes every test in this binary that spawns a real `cuttlefishd`.
///
/// Each spawned daemon does real cold-start work (wasmtime engine init,
/// sqlite ledger setup) before it can answer its first request. On a
/// developer's fast, mostly-idle machine, five of these cold-starting
/// concurrently (this binary's default `cargo test` parallelism) comfortably
/// clears the readiness-poll bound below. On a resource-constrained CI
/// runner, the same five daemons genuinely contend for CPU, and each one's
/// cold start can individually run long enough to blow that bound — this
/// showed up in CI as 3-4 of 5 daemon tests failing with "did not become
/// ready in time" on every platform in the matrix at once, not as an
/// isolated flake. Acquiring this guard before spawning ensures at most one
/// `cuttlefishd` is ever cold-starting (or running) at a time in this binary,
/// which is the fundamental fix; the generous readiness bound below is
/// defense in depth on top of that, not a substitute for it.
///
/// `tokio::sync::Mutex`, not `std::sync::Mutex`: callers hold the returned
/// guard across `.await` points for a test's entire body, which is exactly
/// the pattern `clippy::await_holding_lock` flags for a std mutex. Compare
/// `crates/cuttlefish-host/tests/ledger.rs`'s `ENV_GUARD`, which uses a std
/// `Mutex` because it's only ever held across non-async sections.
static DAEMON_SERIAL_GUARD: Mutex = const_new;
/// Acquire the daemon-spawning serialization guard described on
/// [`DAEMON_SERIAL_GUARD`]. Call this as the first line of any test that
/// calls [`spawn_daemon`], and keep the returned guard alive (bound to a
/// local, e.g. `let _daemon_guard = ...`) for the test's entire body — not
/// just around the `spawn_daemon` call — so no other daemon-spawning test can
/// start its own cold start until this one's daemon is fully done with, not
/// merely started.
pub async
/// A spawned `cuttlefishd` child that is killed automatically when this value
/// is dropped — on a normal return, an early return, *or* a panic unwinding
/// through the test that owns it.
///
/// `cuttlefishd` never exits on its own; it just listens forever. Without
/// this guard, any fallible step between [`spawn_daemon`] and a test's own
/// cleanup (e.g. an `.unwrap()` on a warm-up call) would skip straight past
/// the manual `child.kill()` at the end and orphan the process.
/// Spawn `cuttlefishd` against `spec_path`/`endpoint`, wait (bounded, but
/// generously — see the 30s bound below) for it to actually accept
/// connections before returning, and return a [`DaemonGuard`] that kills it
/// on drop no matter how the caller's test function exits.
///
/// Callers should hold [`daemon_test_guard`]'s guard before calling this —
/// see its doc comment for why.
///
/// The child's stdout/stderr are piped (not inherited), mirroring
/// `crates/cuttlefishd/tests/api.rs`'s own `spawn_and_wait_ready` — otherwise
/// `cuttlefishd`'s own log lines (e.g. "listening on ...") print straight
/// into test output, bypassing the test harness's usual output capture.
///
/// Panics (after killing the child) if the daemon never becomes ready, so
/// callers only need to handle the happy path.
pub async