blob: 68a6e221c4e5d776d368b2c33f7f416cb4c8e0c3 [file] [log] [blame]
#![cfg(all(unix, feature = "with-tokio"))]
use command_group::{AsyncCommandGroup, Signal, UnixChildExt};
use std::{io::Result, os::unix::process::ExitStatusExt, process::Stdio, time::Duration};
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
process::Command,
time::sleep,
};
const DIE_TIME: Duration = Duration::from_millis(100);
// each test has a _normal variant that uses the Tokio non-group API for comparison/debugging.
#[tokio::test]
async fn inner_read_stdout_normal() -> Result<()> {
let mut child = Command::new("echo")
.arg("hello")
.stdout(Stdio::piped())
.spawn()?;
let mut output = String::new();
if let Some(mut out) = child.stdout.take() {
out.read_to_string(&mut output).await?;
}
assert_eq!(output.as_str(), "hello\n");
Ok(())
}
#[tokio::test]
async fn inner_read_stdout_group() -> Result<()> {
let mut child = Command::new("echo")
.arg("hello")
.stdout(Stdio::piped())
.group_spawn()?;
let mut output = String::new();
if let Some(mut out) = child.inner().stdout.take() {
out.read_to_string(&mut output).await?;
}
assert_eq!(output.as_str(), "hello\n");
Ok(())
}
#[tokio::test]
async fn into_inner_write_stdin_normal() -> Result<()> {
let mut child = Command::new("cat")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()?;
if let Some(mut din) = child.stdin.take() {
din.write_all(b"hello").await?;
}
let mut output = String::new();
if let Some(mut out) = child.stdout.take() {
out.read_to_string(&mut output).await?;
}
assert_eq!(output.as_str(), "hello");
Ok(())
}
#[tokio::test]
async fn into_inner_write_stdin_group() -> Result<()> {
let mut child = Command::new("cat")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.group_spawn()?
.into_inner();
if let Some(mut din) = child.stdin.take() {
din.write_all(b"hello").await?;
}
let mut output = String::new();
if let Some(mut out) = child.stdout.take() {
out.read_to_string(&mut output).await?;
}
assert_eq!(output.as_str(), "hello");
Ok(())
}
#[tokio::test]
async fn kill_and_try_wait_normal() -> Result<()> {
let mut child = Command::new("yes").stdout(Stdio::null()).spawn()?;
assert!(child.try_wait()?.is_none());
child.kill().await?;
sleep(DIE_TIME).await;
assert!(child.try_wait()?.is_some());
sleep(DIE_TIME).await;
assert!(child.try_wait()?.is_some());
Ok(())
}
#[tokio::test]
async fn kill_and_try_wait_group() -> Result<()> {
let mut child = Command::new("yes").stdout(Stdio::null()).group_spawn()?;
assert!(child.try_wait()?.is_none());
child.kill()?;
sleep(DIE_TIME).await;
assert!(child.try_wait()?.is_some());
sleep(DIE_TIME).await;
assert!(child.try_wait()?.is_some());
Ok(())
}
#[tokio::test]
async fn try_wait_twice_after_sigterm_normal() -> Result<()> {
let mut child = Command::new("yes").stdout(Stdio::null()).spawn()?;
assert!(child.try_wait()?.is_none(), "pre try_wait");
child.signal(Signal::SIGTERM)?;
sleep(DIE_TIME).await;
assert!(child.try_wait()?.is_some(), "first try_wait");
sleep(DIE_TIME).await;
assert!(child.try_wait()?.is_some(), "second try_wait");
Ok(())
}
#[tokio::test]
async fn try_wait_twice_after_sigterm_group() -> Result<()> {
let mut child = Command::new("yes").stdout(Stdio::null()).group_spawn()?;
assert!(child.try_wait()?.is_none(), "pre try_wait");
child.signal(Signal::SIGTERM)?;
sleep(DIE_TIME).await;
assert!(child.try_wait()?.is_some(), "first try_wait");
sleep(DIE_TIME).await;
assert!(child.try_wait()?.is_some(), "second try_wait");
Ok(())
}
#[tokio::test]
async fn wait_twice_after_sigterm_normal() -> Result<()> {
let mut child = Command::new("yes").stdout(Stdio::null()).spawn()?;
assert!(child.try_wait()?.is_none(), "pre try_wait");
child.signal(Signal::SIGTERM)?;
let status = child.wait().await?;
assert_eq!(
status.signal(),
Some(Signal::SIGTERM as i32),
"first wait status"
);
let status = child.wait().await?;
assert_eq!(
status.signal(),
Some(Signal::SIGTERM as i32),
"second wait status"
);
Ok(())
}
#[tokio::test]
async fn wait_twice_after_sigterm_group() -> Result<()> {
let mut child = Command::new("yes").stdout(Stdio::null()).group_spawn()?;
assert!(child.try_wait()?.is_none(), "pre try_wait");
child.signal(Signal::SIGTERM)?;
let status = child.wait().await?;
assert_eq!(
status.signal(),
Some(Signal::SIGTERM as i32),
"first wait status"
);
let status = child.wait().await?;
assert_eq!(
status.signal(),
Some(Signal::SIGTERM as i32),
"second wait status"
);
Ok(())
}
#[tokio::test]
async fn wait_after_die_normal() -> Result<()> {
let mut child = Command::new("echo").stdout(Stdio::null()).spawn()?;
sleep(DIE_TIME).await;
let status = child.wait().await?;
assert!(status.success());
Ok(())
}
#[tokio::test]
async fn wait_after_die_group() -> Result<()> {
let mut child = Command::new("echo").stdout(Stdio::null()).group_spawn()?;
sleep(DIE_TIME).await;
let status = child.wait().await?;
assert!(status.success());
Ok(())
}
#[tokio::test]
async fn try_wait_after_die_normal() -> Result<()> {
let mut child = Command::new("echo").stdout(Stdio::null()).spawn()?;
sleep(DIE_TIME).await;
let status = child.try_wait()?;
assert!(status.is_some());
assert!(status.unwrap().success());
Ok(())
}
#[tokio::test]
async fn try_wait_after_die_group() -> Result<()> {
let mut child = Command::new("echo").stdout(Stdio::null()).group_spawn()?;
sleep(DIE_TIME).await;
let status = child.try_wait()?;
assert!(status.is_some());
assert!(status.unwrap().success());
Ok(())
}
#[tokio::test]
async fn wait_normal() -> Result<()> {
let mut command = Command::new("echo");
let mut child = command.spawn()?;
let status = child.wait().await?;
assert!(status.success());
let status = child.wait().await?;
assert!(status.success());
Ok(())
}
#[tokio::test]
async fn wait_group() -> Result<()> {
let mut command = Command::new("echo");
let mut child = command.group_spawn()?;
let status = child.wait().await?;
assert!(status.success());
let status = child.wait().await?;
assert!(status.success());
Ok(())
}
#[tokio::test]
async fn wait_with_output_normal() -> Result<()> {
let child = Command::new("echo")
.arg("hello")
.stdout(Stdio::piped())
.spawn()?;
let output = child.wait_with_output().await?;
assert!(output.status.success());
assert_eq!(output.stdout, b"hello\n".to_vec());
assert_eq!(output.stderr, Vec::new());
Ok(())
}
#[tokio::test]
async fn wait_with_output_group() -> Result<()> {
let child = Command::new("echo")
.arg("hello")
.stdout(Stdio::piped())
.group_spawn()?;
let output = child.wait_with_output().await?;
assert!(output.status.success());
assert_eq!(output.stdout, b"hello\n".to_vec());
assert_eq!(output.stderr, Vec::new());
Ok(())
}
#[tokio::test]
async fn id_same_as_inner_group() -> Result<()> {
let mut command = Command::new("echo");
let mut child = command.group_spawn()?;
assert_eq!(child.id(), child.inner().id());
Ok(())
}
#[tokio::test]
async fn signal_normal() -> Result<()> {
let mut child = Command::new("yes").stdout(Stdio::null()).spawn()?;
child.signal(Signal::SIGCONT)?;
sleep(DIE_TIME).await;
assert!(child.try_wait()?.is_none(), "not exited with sigcont");
child.signal(Signal::SIGTERM)?;
sleep(DIE_TIME).await;
assert!(child.try_wait()?.is_some(), "exited with sigterm");
Ok(())
}
#[tokio::test]
async fn signal_group() -> Result<()> {
let mut child = Command::new("yes").stdout(Stdio::null()).group_spawn()?;
child.signal(Signal::SIGCONT)?;
sleep(DIE_TIME).await;
assert!(child.try_wait()?.is_none(), "not exited with sigcont");
child.signal(Signal::SIGTERM)?;
sleep(DIE_TIME).await;
assert!(child.try_wait()?.is_some(), "exited with sigterm");
Ok(())
}