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