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.
This commit is contained in:
许杰友 Jieyou Xu (Joe) 2024-04-13 14:49:44 +00:00
parent e994534242
commit b22099d4e0

View File

@ -129,3 +129,127 @@ pub fn set_host_rpath(cmd: &mut Command) {
env::join_paths(paths.iter()).unwrap()
});
}
/// Implement common helpers for command wrappers. This assumes that the command wrapper is a struct
/// containing a `cmd: Command` field. The provided helpers are:
///
/// 1. Generic argument acceptors: `arg` and `args` (delegated to [`Command`]). These are intended
/// to be *fallback* argument acceptors, when specific helpers don't make sense. Prefer to add
/// new specific helper methods over relying on these generic argument providers.
/// 2. Environment manipulation methods: `env`, `env_remove` and `env_clear`: these delegate to
/// methods of the same name on [`Command`].
/// 3. Output and execution: `output`, `run` and `run_fail` are provided. `output` waits for the
/// command to finish running and returns the process's [`Output`]. `run` and `run_fail` are
/// higher-level convenience methods which waits for the command to finish running and assert
/// that the command successfully ran or failed as expected. Prefer `run` and `run_fail` when
/// possible.
///
/// Example usage:
///
/// ```ignore (illustrative)
/// struct CommandWrapper { cmd: Command }
///
/// crate::impl_common_helpers!(CommandWrapper);
///
/// impl CommandWrapper {
/// // ... additional specific helper methods
/// }
/// ```
///
/// [`Command`]: ::std::process::Command
/// [`Output`]: ::std::process::Output
macro_rules! impl_common_helpers {
($wrapper: ident) => {
impl $wrapper {
/// Specify an environment variable.
pub fn env<K, V>(&mut self, key: K, value: V) -> &mut Self
where
K: AsRef<::std::ffi::OsStr>,
V: AsRef<::std::ffi::OsStr>,
{
self.cmd.env(key, value);
self
}
/// Remove an environmental variable.
pub fn env_remove<K>(&mut self, key: K) -> &mut Self
where
K: AsRef<::std::ffi::OsStr>,
{
self.cmd.env_remove(key);
self
}
/// Clear all environmental variables.
pub fn env_var(&mut self) -> &mut Self {
self.cmd.env_clear();
self
}
/// Generic command argument provider. Prefer specific helper methods if possible.
/// Note that for some executables, arguments might be platform specific. For C/C++
/// compilers, arguments might be platform *and* compiler specific.
pub fn arg<S>(&mut self, arg: S) -> &mut Self
where
S: AsRef<::std::ffi::OsStr>,
{
self.cmd.arg(arg);
self
}
/// Generic command arguments provider. Prefer specific helper methods if possible.
/// Note that for some executables, arguments might be platform specific. For C/C++
/// compilers, arguments might be platform *and* compiler specific.
pub fn args<S>(&mut self, args: &[S]) -> &mut Self
where
S: AsRef<::std::ffi::OsStr>,
{
self.cmd.args(args);
self
}
/// Inspect what the underlying [`Command`][::std::process::Command] is up to the
/// current construction.
pub fn inspect<I>(&mut self, inspector: I) -> &mut Self
where
I: FnOnce(&::std::process::Command),
{
inspector(&self.cmd);
self
}
/// Get the [`Output`][::std::process::Output] of the finished process.
pub fn output(&mut self) -> ::std::process::Output {
self.cmd.output().expect("failed to get output of finished process")
}
/// Run the constructed command and assert that it is successfully run.
#[track_caller]
pub fn run(&mut self) -> ::std::process::Output {
let caller_location = ::std::panic::Location::caller();
let caller_line_number = caller_location.line();
let output = self.cmd.output().unwrap();
if !output.status.success() {
handle_failed_output(&self.cmd, output, caller_line_number);
}
output
}
/// Run the constructed command and assert that it does not successfully run.
#[track_caller]
pub fn run_fail(&mut self) -> ::std::process::Output {
let caller_location = ::std::panic::Location::caller();
let caller_line_number = caller_location.line();
let output = self.cmd.output().unwrap();
if output.status.success() {
handle_failed_output(&self.cmd, output, caller_line_number);
}
output
}
}
};
}
pub(crate) use impl_common_helpers;