Files
lightcraft/apps/lightcraft-cli/tests/it/mcp_shutdown.rs
T
aiongg f3bb7a9f30 CI: one test binary per crate, line tables only, one WASM check (#2710)
* CI: dev profile keeps line tables for workspace crates and no debug info for dependencies

* Control server tests: persistent_requests_and_slow_replies_outlive_the_idle_read_timeout no longer races the idle timeout

Each request (and the closing half-shutdown) reaches the socket before the
worker reads, so on a slow machine only the reply waits outlast the 100 ms
idle timeout, as the test intends. The idle test sends its input the same
way, and the no-frames test allows 5 s instead of 2 s to give up.

* CI: one integration-test binary per crate

Each crate's tests/*.rs become modules of tests/it/main.rs (51 test files
into 13 binaries, plus 6 that stay their own). Inner #![cfg] attributes
move to the mod lines. Files that change state for the whole process keep
a binary of their own: codecs jxl_tail_heap and raw sr2_heap (dhat global
allocator), gpu shutdown, shutdown_first and memory, ui-egui shutdown.
Docs, comments and the GPU probe name the new paths and targets
(--test it <module>::); the CI report names an it binary's skips after
the crate.

* CI: tests under nextest, doctests apart, one wasm check

xtask ci runs the tests with cargo nextest run --lib --bins --tests when
cargo-nextest is installed, else cargo test with the same targets and a
one-line notice; doctests run after them with cargo test --workspace --doc.
The heif and hardware GPU steps use the same runner. Examples stay out of
the test build (clippy --all-targets still checks them). Concurrent tests
under nextest follow the RUST_TEST_THREADS cap (NEXTEST_TEST_THREADS).
The wasm check is one cargo check --keep-going over the same crates, and
clippy runs with --keep-going.

* CI: tests run under cargo test by default, nextest with LIGHTCRAFT_NEXTEST=1 (per-process scene cache)
2026-10-23 13:34:30 -04:00

39 lines
1.5 KiB
Rust

//! A broken protocol output must not require the client to close its input pipe.
use std::io::{Read, Write};
use std::process::{Command, Stdio};
use std::time::{Duration, Instant};
#[test]
fn broken_stdout_exits_with_stdin_still_open() {
let mut child = Command::new(env!("CARGO_BIN_EXE_lightcraft-cli"))
.arg("mcp")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap();
let mut input = child.stdin.take().unwrap();
drop(child.stdout.take());
input.write_all(b"{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\"}\n{\"jsonrpc\":").unwrap();
input.flush().unwrap();
let deadline = Instant::now() + Duration::from_secs(10);
let status = loop {
if let Some(status) = child.try_wait().unwrap() {
break status;
}
if Instant::now() >= deadline {
child.kill().unwrap();
child.wait().unwrap();
panic!("MCP did not exit after stdout failed while stdin stayed open");
}
std::thread::sleep(Duration::from_millis(10));
};
let mut stderr = String::new();
child.stderr.take().unwrap().read_to_string(&mut stderr).unwrap();
assert!(stderr.contains("serving MCP on stdio"), "CLI must reach the MCP transport: {stderr}");
assert!(!status.success(), "broken protocol output must be reported: {stderr}");
assert!(stderr.contains("lightcraft-cli:"), "CLI must report the transport error: {stderr}");
drop(input);
}