use std::io::Write;
use std::time::Duration;
use processkit::{Command, Error, Stdin};
use crate::common::raw_stdin_echo;
const INNER_MARKER: &str = "PK_T100_INHERIT_STDIN_INNER";
const PAYLOAD: &str = "processkit-T100-inherited-stdin-payload-9f3a7c\n";
const BEGIN: &str = "<<<PK_T100_BEGIN>>>";
const END: &str = "<<<PK_T100_END>>>";
#[tokio::test]
#[ignore = "re-execs the test binary and spawns a real inherited-stdin child"]
async fn inherited_stdin_is_read_by_the_child() {
if std::env::var_os(INNER_MARKER).is_some() {
run_inner().await;
return;
}
let exe = std::env::current_exe().expect("current_exe for self re-exec");
let result = Command::new(exe)
.args([
"stdin_inherit::inherited_stdin_is_read_by_the_child",
"--exact",
"--ignored",
"--nocapture",
])
.env(INNER_MARKER, "1")
.stdin(Stdin::from_string(PAYLOAD))
.timeout(Duration::from_secs(60))
.output_string()
.await
.expect("the inner re-exec must run to completion");
let out = result.stdout();
let begin = out
.find(BEGIN)
.unwrap_or_else(|| panic!("inner produced no BEGIN sentinel; stdout: {out:?}"));
let end = out
.find(END)
.unwrap_or_else(|| panic!("inner produced no END sentinel; stdout: {out:?}"));
let echoed = &out[begin + BEGIN.len()..end];
assert_eq!(
echoed, PAYLOAD,
"the inherit_stdin child must have read the parent's piped stdin verbatim; \
full inner stdout: {out:?}"
);
}
async fn run_inner() {
let result = raw_stdin_echo()
.inherit_stdin()
.timeout(Duration::from_secs(30))
.output_bytes()
.await
.expect("inner: an inherit_stdin echo child must run");
assert!(
result.is_success(),
"inner: the echo child must exit cleanly; got {:?}",
result.code()
);
let mut stdout = std::io::stdout();
stdout.write_all(BEGIN.as_bytes()).unwrap();
stdout.write_all(result.stdout()).unwrap();
stdout.write_all(END.as_bytes()).unwrap();
stdout.flush().unwrap();
}
#[tokio::test]
#[ignore = "drives the real launch path (though it rejects before spawning)"]
async fn inherit_stdin_with_keep_stdin_open_is_rejected() {
let err = raw_stdin_echo()
.inherit_stdin()
.keep_stdin_open()
.output_string()
.await
.expect_err("inherit_stdin + keep_stdin_open must be rejected at launch");
assert!(
matches!(&err, Error::Io(io) if io.kind() == std::io::ErrorKind::InvalidInput),
"expected Error::Io(InvalidInput), got {err:?}"
);
}
#[tokio::test]
#[ignore = "drives the real launch path (though it rejects before spawning)"]
async fn inherit_stdin_with_a_source_is_rejected() {
let err = raw_stdin_echo()
.stdin(Stdin::from_string("payload"))
.inherit_stdin()
.output_string()
.await
.expect_err("inherit_stdin + a stdin source must be rejected at launch");
assert!(
matches!(&err, Error::Io(io) if io.kind() == std::io::ErrorKind::InvalidInput),
"expected Error::Io(InvalidInput), got {err:?}"
);
}