tokio_process_tools/
lib.rs1#![warn(missing_docs)]
2
3#![doc = include_str!("../README.md")]
5mod async_drop;
8mod collector;
9mod error;
10mod inspector;
11mod output;
12mod output_stream;
13mod panic_on_drop;
14mod process;
15mod process_handle;
16mod signal;
17mod terminate_on_drop;
18
19pub use collector::{Collector, CollectorError, Sink};
21pub use error::{OutputError, SpawnError, TerminationError, WaitError};
22pub use inspector::{Inspector, InspectorError};
23pub use output::Output;
24pub use output_stream::{
25 DEFAULT_CHANNEL_CAPACITY, DEFAULT_CHUNK_SIZE, LineOverflowBehavior, LineParsingOptions, Next,
26 NumBytes, NumBytesExt, OutputStream, broadcast, single_subscriber,
27};
28pub use process::{AutoName, AutoNameSettings, Process, ProcessName};
29pub use process_handle::{ProcessHandle, RunningState};
30pub use terminate_on_drop::TerminateOnDrop;
31
32#[cfg(test)]
33mod test {
34 use crate::output::Output;
35 use crate::{LineParsingOptions, Next, Process, RunningState};
36 use assertr::prelude::*;
37 use std::time::Duration;
38 use tokio::process::Command;
39
40 #[tokio::test]
41 async fn wait_with_output() {
42 let mut process = Process::new(Command::new("ls"))
43 .name("ls")
44 .spawn_broadcast()
45 .expect("Failed to spawn `ls` command");
46 let Output {
47 status,
48 stdout,
49 stderr,
50 } = process
51 .wait_for_completion_with_output(None, LineParsingOptions::default())
52 .await
53 .unwrap();
54 assert_that(status.success()).is_true();
55 assert_that(stdout).is_equal_to([
56 "Cargo.lock",
57 "Cargo.toml",
58 "LICENSE-APACHE",
59 "LICENSE-MIT",
60 "README.md",
61 "src",
62 "target",
63 ]);
64 assert_that(stderr).is_empty();
65 }
66
67 #[tokio::test]
68 async fn single_subscriber_panics_on_multiple_consumers() {
69 let mut process = Process::new(Command::new("ls"))
70 .name("ls")
71 .spawn_single_subscriber()
72 .expect("Failed to spawn `ls` command");
73
74 let _inspector = process
75 .stdout()
76 .inspect_lines(|_line| Next::Continue, LineParsingOptions::default());
77
78 assert_that_panic_by(|| {
79 let _inspector = process
80 .stdout()
81 .inspect_lines(|_line| Next::Continue, LineParsingOptions::default());
82 })
83 .has_type::<String>()
84 .is_equal_to("Cannot create multiple consumers on SingleSubscriberOutputStream (stream: 'stdout'). Only one inspector or collector can be active at a time. Use .spawn_broadcast() instead of .spawn_single_subscriber() to support multiple consumers.");
85
86 process.wait_for_completion(None).await.unwrap();
87 }
88
89 #[tokio::test]
90 async fn is_running() {
91 let mut cmd = Command::new("sleep");
92 cmd.arg("1");
93 let mut process = Process::new(cmd)
94 .name("sleep")
95 .spawn_broadcast()
96 .expect("Failed to spawn `sleep` command");
97
98 match process.is_running() {
99 RunningState::Running => {}
100 RunningState::Terminated(exit_status) => {
101 assert_that(exit_status).fail("Process should be running");
102 }
103 RunningState::Uncertain(_) => {
104 assert_that_ref(&process).fail("Process state should not be uncertain");
105 }
106 };
107
108 let _exit_status = process.wait_for_completion(None).await.unwrap();
109
110 match process.is_running() {
111 RunningState::Running => {
112 assert_that(process).fail("Process should not be running anymore");
113 }
114 RunningState::Terminated(exit_status) => {
115 assert_that(exit_status.code()).is_some().is_equal_to(0);
116 assert_that(exit_status.success()).is_true();
117 }
118 RunningState::Uncertain(_) => {
119 assert_that(process).fail("Process state should not be uncertain");
120 }
121 };
122 }
123
124 #[tokio::test]
125 async fn terminate() {
126 let mut cmd = Command::new("sleep");
127 cmd.arg("1000");
128 let mut process = Process::new(cmd)
129 .name("sleep")
130 .spawn_broadcast()
131 .expect("Failed to spawn `sleep` command");
132 process
133 .terminate(Duration::from_secs(1), Duration::from_secs(1))
134 .await
135 .unwrap();
136 match process.is_running() {
137 RunningState::Running => {
138 assert_that(process).fail("Process should not be running anymore");
139 }
140 RunningState::Terminated(exit_status) => {
141 assert_that(exit_status.code()).is_none();
143 assert_that(exit_status.success()).is_false();
144 }
145 RunningState::Uncertain(_) => {
146 assert_that(process).fail("Process state should not be uncertain");
147 }
148 };
149 }
150}