test: the owed Stop Share SIGINT gate, against the real pixelpass

0c half (ii) had never been field-run: SIGINT sent, child exits within
the bound, no fallback kill on the normal path. Now it's a repeatable
live gate instead of a one-off manual check: a real whole-desktop host
(idle — no viewer, so no capture) is stopped and must reach
ScreenShareStopped inside STOP_GRACE. The SIGKILL fallback is
indistinguishable from success in the event stream, so time is the
discriminator: the fallback first waits out the full 2 s grace, while a
host honouring SIGINT exits in milliseconds.

Also holds the SIGINTed host's late stdout EOF to the same staleness
contract as the fake-host gate. Verified green on this desktop; no
stray pixelpass processes after the run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-31 01:10:11 -04:00
co-authored by Claude Fable 5
parent 81c230a09c
commit 3df2378831
+98
View File
@@ -195,3 +195,101 @@ async fn a_dead_host_is_torn_down_and_a_clean_stop_stays_clean() {
assert!(controller.send(CoreCommand::Leave));
std::fs::remove_dir_all(&dir).ok();
}
/// The long-owed Stop Share SIGINT gate (0c half (ii)), against the REAL
/// pixelpass binary: a Stop Share must end the host through the graceful
/// SIGINT path — child exits within [`STOP_GRACE`], no SIGKILL fallback, no
/// "couldn't confirm" warning — because SIGKILL would skip pixelpass's own
/// teardown (it unloads its capture sink on the way out in sink-owning modes).
///
/// The fallback is indistinguishable from success in the event stream (both
/// end in a confirmed reap), so the discriminator is TIME: the fallback path
/// first waits out the full 2 s grace, while a host honouring SIGINT exits in
/// milliseconds. The bound asserts the stop completed inside the grace.
///
/// Live: needs `pixelpass` on `$PATH` plus a real solo room (audio + network).
#[tokio::test]
#[ignore = "live: real pixelpass host + a real solo room (audio backend, network bind)"]
async fn stop_share_ends_the_real_host_via_sigint_within_the_grace() {
/// Mirrors `core::teardown::STOP_GRACE` (private): the graceful wait
/// before the SIGKILL fallback.
const STOP_GRACE: Duration = Duration::from_secs(2);
let (ui_tx, mut ui_rx) = tokio::sync::mpsc::channel(256);
let controller = CoreController::new(ui_tx);
// No override: resolve the real binary from $PATH.
assert!(controller.send(CoreCommand::SetPixelpassPath(None)));
assert!(controller.send(CoreCommand::Join {
name: "sigint-gate".into(),
ticket: "create".into(),
room_name: "s2".into(),
input_device: None,
output_device: None,
echo_cancellation: false,
avatar: Default::default(),
}));
wait_for(&mut ui_rx, "RoomJoined", |ev| match ev {
UiEvent::RoomJoined { .. } => Some(()),
UiEvent::Error(e) => panic!("join failed: {e}"),
_ => None,
})
.await;
// Whole-desktop share: no viewers ever connect, so the real host sits idle
// after its ticket (capture starts on first viewer) — exactly the state a
// Stop Share most often hits.
assert!(controller.send(CoreCommand::StartScreenShare {
audio_app: None,
settings: Default::default(),
quality: Default::default(),
}));
wait_for(
&mut ui_rx,
"ScreenShareStarted (real pixelpass)",
|ev| match ev {
UiEvent::ScreenShareStarted => Some(()),
UiEvent::Error(e) => panic!("real pixelpass host failed to start: {e}"),
_ => None,
},
)
.await;
let stop_started = std::time::Instant::now();
assert!(controller.send(CoreCommand::StopScreenShare));
wait_for(
&mut ui_rx,
"ScreenShareStopped (real pixelpass)",
|ev| match ev {
UiEvent::ScreenShareStopped => Some(()),
// An Unconfirmed reap surfaces exactly this way; it means the
// SIGINT AND the SIGKILL both failed to end the host.
UiEvent::Error(e) => panic!("stop of the real host was not clean: {e}"),
_ => None,
},
)
.await;
let elapsed = stop_started.elapsed();
assert!(
elapsed < STOP_GRACE,
"stop took {elapsed:?} — at or past the {STOP_GRACE:?} grace, i.e. the \
SIGKILL fallback fired instead of pixelpass honouring SIGINT"
);
// And the late stdout EOF from the SIGINTed host must stay silent (same
// staleness contract the fake-host half pins).
let deadline = tokio::time::Instant::now() + QUIET_WINDOW;
while let Ok(Some(ev)) = tokio::time::timeout_at(deadline, ui_rx.recv()).await {
match ev {
UiEvent::ScreenShareStopped => {
panic!("stale fault from the SIGINTed real host re-emitted ScreenShareStopped")
}
UiEvent::Error(e) if e.contains("unexpectedly") => {
panic!("stale fault from the SIGINTed real host surfaced as an error: {e}")
}
_ => {}
}
}
assert!(controller.send(CoreCommand::Leave));
}