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
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.
68 lines
2.6 KiB
Rust
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}"
|
|
);
|
|
}
|