Skip to content

Commit c53d977

Browse files
committed
run-make-support: introduce macro for common methods to avoid repetition
Add a helper macro for adding common methods to command wrappers. Common methods include helpers that delegate to `Command` and running methods. - `arg` and `args` (delegates to `Command`) - `env`, `env_remove` and `env_clear` (delegates to `Command`) - `output`, `run` and `run_fail` This helps to avoid needing to copy-pasta / reimplement these common methods on a new command wrapper, which hopefully reduces the friction for run-make test writers wanting to introduce new command wrappers.
1 parent fd3b5d5 commit c53d977

File tree

1 file changed

+124
-0
lines changed
  • src/tools/run-make-support/src

1 file changed

+124
-0
lines changed

src/tools/run-make-support/src/lib.rs

+124
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,127 @@ fn handle_failed_output(cmd: &Command, output: Output, caller_line_number: u32)
115115
eprintln!("=== STDERR ===\n{}\n\n", String::from_utf8(output.stderr).unwrap());
116116
std::process::exit(1)
117117
}
118+
119+
/// Implement common helpers for command wrappers. This assumes that the command wrapper is a struct
120+
/// containing a `cmd: Command` field. The provided helpers are:
121+
///
122+
/// 1. Generic argument acceptors: `arg` and `args` (delegated to [`Command`]). These are intended
123+
/// to be *fallback* argument acceptors, when specific helpers don't make sense. Prefer to add
124+
/// new specific helper methods over relying on these generic argument providers.
125+
/// 2. Environment manipulation methods: `env`, `env_remove` and `env_clear`: these delegate to
126+
/// methods of the same name on [`Command`].
127+
/// 3. Output and execution: `output`, `run` and `run_fail` are provided. `output` waits for the
128+
/// command to finish running and returns the process's [`Output`]. `run` and `run_fail` are
129+
/// higher-level convenience methods which waits for the command to finish running and assert
130+
/// that the command successfully ran or failed as expected. Prefer `run` and `run_fail` when
131+
/// possible.
132+
///
133+
/// Example usage:
134+
///
135+
/// ```ignore (illustrative)
136+
/// struct CommandWrapper { cmd: Command }
137+
///
138+
/// crate::impl_common_helpers!(CommandWrapper);
139+
///
140+
/// impl CommandWrapper {
141+
/// // ... additional specific helper methods
142+
/// }
143+
/// ```
144+
///
145+
/// [`Command`]: ::std::process::Command
146+
/// [`Output`]: ::std::process::Output
147+
macro_rules! impl_common_helpers {
148+
($wrapper: ident) => {
149+
impl $wrapper {
150+
/// Specify an environment variable.
151+
pub fn env<K, V>(&mut self, key: K, value: V) -> &mut Self
152+
where
153+
K: AsRef<::std::ffi::OsStr>,
154+
V: AsRef<::std::ffi::OsStr>,
155+
{
156+
self.cmd.env(key, value);
157+
self
158+
}
159+
160+
/// Remove an environmental variable.
161+
pub fn env_remove<K>(&mut self, key: K) -> &mut Self
162+
where
163+
K: AsRef<::std::ffi::OsStr>,
164+
{
165+
self.cmd.env_remove(key);
166+
self
167+
}
168+
169+
/// Clear all environmental variables.
170+
pub fn env_var(&mut self) -> &mut Self {
171+
self.cmd.env_clear();
172+
self
173+
}
174+
175+
/// Generic command argument provider. Prefer specific helper methods if possible.
176+
/// Note that for some executables, arguments might be platform specific. For C/C++
177+
/// compilers, arguments might be platform *and* compiler specific.
178+
pub fn arg<S>(&mut self, arg: S) -> &mut Self
179+
where
180+
S: AsRef<::std::ffi::OsStr>,
181+
{
182+
self.cmd.arg(arg);
183+
self
184+
}
185+
186+
/// Generic command arguments provider. Prefer specific helper methods if possible.
187+
/// Note that for some executables, arguments might be platform specific. For C/C++
188+
/// compilers, arguments might be platform *and* compiler specific.
189+
pub fn args<S>(&mut self, args: &[S]) -> &mut Self
190+
where
191+
S: AsRef<::std::ffi::OsStr>,
192+
{
193+
self.cmd.args(args);
194+
self
195+
}
196+
197+
/// Inspect what the underlying [`Command`][::std::process::Command] is up to the
198+
/// current construction.
199+
pub fn inspect<I>(&mut self, inspector: I) -> &mut Self
200+
where
201+
I: FnOnce(&::std::process::Command),
202+
{
203+
inspector(&self.cmd);
204+
self
205+
}
206+
207+
/// Get the [`Output`][::std::process::Output] of the finished process.
208+
pub fn output(&mut self) -> ::std::process::Output {
209+
self.cmd.output().expect("failed to get output of finished process")
210+
}
211+
212+
/// Run the constructed command and assert that it is successfully run.
213+
#[track_caller]
214+
pub fn run(&mut self) -> ::std::process::Output {
215+
let caller_location = ::std::panic::Location::caller();
216+
let caller_line_number = caller_location.line();
217+
218+
let output = self.cmd.output().unwrap();
219+
if !output.status.success() {
220+
handle_failed_output(&self.cmd, output, caller_line_number);
221+
}
222+
output
223+
}
224+
225+
/// Run the constructed command and assert that it does not successfully run.
226+
#[track_caller]
227+
pub fn run_fail(&mut self) -> ::std::process::Output {
228+
let caller_location = ::std::panic::Location::caller();
229+
let caller_line_number = caller_location.line();
230+
231+
let output = self.cmd.output().unwrap();
232+
if output.status.success() {
233+
handle_failed_output(&self.cmd, output, caller_line_number);
234+
}
235+
output
236+
}
237+
}
238+
};
239+
}
240+
241+
pub(crate) use impl_common_helpers;

0 commit comments

Comments
 (0)