Files
enricobuehler 18bbe50d9f
apple / swift (pull_request) Skipped
apple / distribute (pull_request) Skipped
macos-host / check (pull_request) Skipped
apple / screenshots (pull_request) Skipped
ci / rust-arm64 (pull_request) Successful in 3m38s
ci / sdk-plugin-kit (pull_request) Successful in 55s
ci / web (pull_request) Successful in 4m23s
ci / decky-typecheck (pull_request) Successful in 20s
ci / docs-site (pull_request) Successful in 2m39s
ci / rust (pull_request) Failing after 5m43s
ci / docs-drift (pull_request) Successful in 35s
ci / bun-nix (pull_request) Successful in 57s
secret-scan / gitleaks (pull_request) Successful in 31s
setup-binary / build (aarch64) (pull_request) Successful in 4m9s
android / android (pull_request) Successful in 12m40s
nix / flake (pull_request) Successful in 7m57s
windows-client / client (arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (pull_request) Skipped
windows-client / client (x64, , x86_64-pc-windows-msvc, C:\t) (pull_request) Skipped
setup-binary / build (x86_64) (pull_request) Successful in 14m4s
setup-binary / probe the published channel (pull_request) Skipped
setup-binary / publish to the generic registry (pull_request) Skipped
test(ci): read the smoke binaries with env! again
The `option_env!` detour was a workaround for the sccache shim dropping
`CARGO_BIN_EXE_*` names with a hyphen; it traded a compile-time guarantee
for a runtime panic. The shim keeps those names now, so the tests go back
to the one-line form.
2026-09-02 15:49:48 +02:00

68 lines
2.6 KiB
Rust

//! Runs the REAL `punktfunk-session` binary and asserts it speaks the stdout contract.
//!
//! 0.22.0 shipped a stub as `punktfunk-session` — a stray copy of the GTK shell replaced
//! this crate's `main.rs`, and every build gate stayed green because the wrong program
//! compiled perfectly. Nothing in CI ever *ran* the binary; the failure only existed at
//! runtime, as a connect that silently bounced back to the host list. This test is the
//! gate that would have caught it: whatever the binary does, it must SAY so on stdout.
#![cfg(any(target_os = "linux", windows))]
use std::io::Read as _;
use std::process::{Command, Stdio};
use std::time::{Duration, Instant};
/// A refused loopback connect emits one `{"error"…}` line on stdout. Presenter
/// initialization may fail first on a headless runner, but either path satisfies
/// the same contract before the deadline.
#[test]
fn a_failing_connect_still_speaks_the_contract() {
let mut child = Command::new(env!("CARGO_BIN_EXE_punktfunk-session"))
.args([
"--connect",
"127.0.0.1:1",
"--fp",
&"0".repeat(64),
"--connect-timeout",
"5",
])
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::null())
.spawn()
.expect("spawn punktfunk-session");
let mut stdout = child.stdout.take().expect("piped stdout");
let reader = std::thread::spawn(move || {
let mut buf = String::new();
let _ = stdout.read_to_string(&mut buf);
buf
});
// Generous bound: presenter init on a cold CI runner can be slow, but a healthy
// binary answers in seconds. Only a hang (or a stub that inherited our stdout and
// blocked) gets anywhere near it.
let deadline = Instant::now() + Duration::from_secs(120);
loop {
match child.try_wait().expect("wait on punktfunk-session") {
Some(_) => break,
None if Instant::now() > deadline => {
let _ = child.kill();
let _ = child.wait();
panic!("punktfunk-session neither exited nor spoke within 120 s");
}
None => std::thread::sleep(Duration::from_millis(200)),
}
}
let out = reader.join().expect("stdout reader");
let spoke = out.lines().map(str::trim).any(|l| {
l.starts_with('{')
&& (l.contains("\"error\"") || l.contains("\"ready\"") || l.contains("\"ended\""))
});
assert!(
spoke,
"no stdout-contract line — the wrong program may be wearing this binary's name. \
stdout was:\n{out}"
);
}