tokio_process_tools/
lib.rs

1mod collector;
2mod inspector;
3mod output_stream;
4mod panic_on_drop;
5mod process_handle;
6mod signal;
7mod terminate_on_drop;
8
9pub use collector::{Collector, CollectorError, Sink};
10pub use inspector::{Inspector, InspectorError};
11pub use output_stream::{
12    LineParsingOptions, Next, NumBytes, NumBytesExt, OutputStream, broadcast, single_subscriber,
13};
14pub use process_handle::{ProcessHandle, RunningState, TerminationError};
15pub use terminate_on_drop::TerminateOnDrop;
16
17#[cfg(test)]
18mod test {
19    use crate::output_stream::broadcast::BroadcastOutputStream;
20    use crate::{LineParsingOptions, ProcessHandle, RunningState};
21    use assertr::prelude::*;
22    use std::time::Duration;
23    use tokio::process::Command;
24
25    #[tokio::test]
26    async fn wait_with_output() {
27        let cmd = Command::new("ls");
28        let mut process = ProcessHandle::<BroadcastOutputStream>::spawn("ls", cmd)
29            .expect("Failed to spawn `ls` command");
30        let (status, stdout, stderr) = process
31            .wait_with_output(LineParsingOptions::default())
32            .await
33            .unwrap();
34        assert_that(status.success()).is_true();
35        assert_that(stdout).is_equal_to(&[
36            "Cargo.lock",
37            "Cargo.toml",
38            "LICENSE-APACHE",
39            "LICENSE-MIT",
40            "README.md",
41            "src",
42            "target",
43        ]);
44        assert_that(stderr).is_empty();
45    }
46
47    #[tokio::test]
48    async fn is_running() {
49        let mut cmd = Command::new("sleep");
50        cmd.arg("1");
51        let mut process = ProcessHandle::<BroadcastOutputStream>::spawn("sleep", cmd)
52            .expect("Failed to spawn `sleep` command");
53
54        match process.is_running() {
55            RunningState::Running => {}
56            RunningState::Terminated(exit_status) => {
57                assert_that(exit_status).fail("Process should be running");
58            }
59            RunningState::Uncertain(_) => {
60                assert_that_ref(&process).fail("Process state should not be uncertain");
61            }
62        };
63
64        let _exit_status = process.wait_for_completion(None).await.unwrap();
65
66        match process.is_running() {
67            RunningState::Running => {
68                assert_that(process).fail("Process should not be running anymore");
69            }
70            RunningState::Terminated(exit_status) => {
71                assert_that(exit_status.code()).is_some().is_equal_to(0);
72                assert_that(exit_status.success()).is_true();
73            }
74            RunningState::Uncertain(_) => {
75                assert_that(process).fail("Process state should not be uncertain");
76            }
77        };
78    }
79
80    #[tokio::test]
81    async fn terminate() {
82        let mut cmd = Command::new("sleep");
83        cmd.arg("1000");
84        let mut process = ProcessHandle::<BroadcastOutputStream>::spawn("sleep", cmd)
85            .expect("Failed to spawn `sleep` command");
86        process
87            .terminate(Duration::from_secs(1), Duration::from_secs(1))
88            .await
89            .unwrap();
90        match process.is_running() {
91            RunningState::Running => {
92                assert_that(process).fail("Process should not be running anymore");
93            }
94            RunningState::Terminated(exit_status) => {
95                // Terminating a process with a signal results in no code being emitted (on linux).
96                assert_that(exit_status.code()).is_none();
97                assert_that(exit_status.success()).is_false();
98            }
99            RunningState::Uncertain(_) => {
100                assert_that(process).fail("Process state should not be uncertain");
101            }
102        };
103    }
104}