1use crate::utils::PathExt;
2use std::borrow::Cow;
3use std::ffi::{OsStr, OsString};
4use std::io::Write;
5use std::path::{Path, PathBuf};
6use std::process::{Child, ExitStatus};
7
8pub struct Command {
9 program: OsString,
11 program_args: Vec<OsString>,
13 current_dir: PathBuf,
15 child: Option<Child>,
17 stdin: Option<Vec<u8>>,
19 stdout: Vec<u8>,
21 stderr: Vec<u8>,
23 status: ExitStatus,
25 expected_success: Option<bool>,
27 expected_failure: Option<bool>,
29 expected_status: Option<i32>,
31 expected_stdout: Option<Vec<u8>>,
33 expected_stderr: Option<Vec<u8>>,
35}
36
37impl Command {
38 pub fn new(program: impl AsRef<OsStr>, caller_file: impl AsRef<str>, manifest_dir: impl AsRef<str>) -> Self {
39 let manifest_path = Path::new(manifest_dir.as_ref());
40 let caller_path = Path::new(caller_file.as_ref()).parent().unwrap();
41 let current_dir = match manifest_path.rem(caller_path) {
43 None => caller_path.into(),
44 Some(path_buf) => {
45 if path_buf.components().count() == 0 {
46 PathBuf::from(".")
47 } else {
48 path_buf
49 }
50 }
51 };
52 Self {
53 program: program.as_ref().into(),
54 program_args: vec![],
55 current_dir,
56 child: None,
57 stdin: None,
58 stdout: vec![],
59 stderr: vec![],
60 status: ExitStatus::default(),
61 expected_success: None,
62 expected_failure: None,
63 expected_status: None,
64 expected_stdout: None,
65 expected_stderr: None,
66 }
67 }
68
69 pub fn arg(mut self, arg: impl AsRef<OsStr>) -> Self {
70 self.program_args.push(arg.as_ref().into());
71 self
72 }
73
74 pub fn success(mut self) -> Self {
75 self.expected_success = Some(true);
76 self.expected_failure = None;
77 self
78 }
79
80 pub fn failure(mut self) -> Self {
81 self.expected_failure = Some(true);
82 self.expected_success = None;
83 self
84 }
85
86 pub fn code(mut self, code: i32) -> Self {
87 self.expected_status = Some(code);
88 self
89 }
90
91 pub fn stdin(mut self, bytes: impl AsRef<[u8]>) -> Self {
92 self.stdin = Some(bytes.as_ref().to_vec());
93 self
94 }
95
96 pub fn stdout(mut self, bytes: impl AsRef<[u8]>) -> Self {
97 self.expected_stdout = Some(bytes.as_ref().to_vec());
98 self
99 }
100
101 pub fn stderr(mut self, bytes: impl AsRef<[u8]>) -> Self {
102 self.expected_stderr = Some(bytes.as_ref().to_vec());
103 self
104 }
105
106 pub fn spawn(&mut self) {
107 if self.child.is_some() {
108 panic!("command is already spawned");
109 }
110 let mut command = std::process::Command::new(self.program.clone());
111 let mut child = command
112 .args(self.program_args.clone())
113 .current_dir(self.current_dir.clone())
114 .stdin(std::process::Stdio::piped())
115 .stdout(std::process::Stdio::piped())
116 .stderr(std::process::Stdio::piped())
117 .spawn()
118 .unwrap();
119 if let Some(bytes) = &self.stdin {
121 let mut stdin = child.stdin.take().unwrap(); stdin.write_all(bytes).expect("failed to write stdin");
123 }
124 self.child = Some(child);
125 }
126
127 pub fn wait(&mut self) {
128 if let Some(child) = self.child.take() {
129 let output = child.wait_with_output().unwrap();
130 self.stdout = output.stdout;
132 self.stderr = output.stderr;
133 self.status = output.status;
134 self.assert();
135 } else {
136 panic!("command is not spawned");
137 }
138 }
139
140 pub fn execute(&mut self) {
141 self.spawn();
142 self.wait();
143 }
144
145 pub fn stop(&mut self) {
146 if let Some(child) = &mut self.child {
147 child.kill().unwrap(); } else {
149 panic!("command is not spawned");
150 }
151 }
152
153 pub fn get_program(&self) -> &OsStr {
154 &self.program
155 }
156
157 pub fn get_current_dir(&self) -> &Path {
158 &self.current_dir
159 }
160
161 pub fn get_stdout(&'_ self) -> Cow<'_, str> {
162 String::from_utf8_lossy(&self.stdout)
163 }
164
165 pub fn get_stderr(&'_ self) -> Cow<'_, str> {
166 String::from_utf8_lossy(&self.stderr)
167 }
168
169 pub fn get_stdout_raw(&self) -> &[u8] {
170 &self.stdout
171 }
172
173 pub fn get_stderr_raw(&self) -> &[u8] {
174 &self.stderr
175 }
176
177 pub fn get_status(&self) -> ExitStatus {
178 self.status
179 }
180
181 fn assert(&self) {
183 if let Some(true) = self.expected_success {
184 assert!(self.status.success(), "expected success");
185 }
186 if let Some(true) = self.expected_failure {
187 assert!(!self.status.success(), "expected failure");
188 }
189 if let Some(expected) = self.expected_status {
190 let actual = self.status.code().expect("failed to retrieve status code");
191 assert_eq!(
192 expected, actual,
193 "\nexpected status code: {}\n actual status code: {}",
194 expected, actual
195 );
196 }
197 if let Some(expected) = &self.expected_stdout {
198 let actual = self.get_stdout_raw();
199 assert_eq!(
200 expected, actual,
201 "\nexpected stdout: {:?}\n actual stdout: {:?}",
202 expected, actual
203 )
204 }
205 if let Some(expected) = &self.expected_stderr {
206 let actual = self.get_stderr_raw();
207 assert_eq!(
208 expected, actual,
209 "\nexpected stderr: {:?}\n actual stderr: {:?}",
210 expected, actual
211 )
212 }
213 }
214}