diff options
author | Steve Nease <stephen.h.nease@gmail.com> | 2022-09-11 16:20:22 -0500 |
---|---|---|
committer | David Peter <sharkdp@users.noreply.github.com> | 2023-03-15 09:33:49 +0100 |
commit | e38acdb68313d76028e3da37bd8574320f585c98 (patch) | |
tree | 17f6dfc634f78a9f3433704e07d6e2530b7ec4f5 | |
parent | 531fee318817967fc0d6d6eb227c77abd5148499 (diff) |
Added --stdin-data argument
This argument accepts a path to a file. The data contained in the file will be passed to the command via stdin.
Closes sharkdp/hyperfine#541
-rw-r--r-- | src/benchmark/executor.rs | 8 | ||||
-rw-r--r-- | src/cli.rs | 8 | ||||
-rw-r--r-- | src/error.rs | 2 | ||||
-rw-r--r-- | src/options.rs | 47 | ||||
-rw-r--r-- | tests/example_stdin_data | 1 | ||||
-rw-r--r-- | tests/integration_tests.rs | 28 |
6 files changed, 91 insertions, 3 deletions
diff --git a/src/benchmark/executor.rs b/src/benchmark/executor.rs index 096c622..64d621d 100644 --- a/src/benchmark/executor.rs +++ b/src/benchmark/executor.rs @@ -1,7 +1,7 @@ use std::process::{ExitStatus, Stdio}; use crate::command::Command; -use crate::options::{CmdFailureAction, CommandOutputPolicy, Options, OutputStyleOption, Shell}; +use crate::options::{CmdFailureAction, CommandInputPolicy, CommandOutputPolicy, Options, OutputStyleOption, Shell}; use crate::output::progress_bar::get_progress_bar; use crate::timer::{execute_and_measure, TimerResult}; use crate::util::randomized_environment_offset; @@ -36,11 +36,13 @@ pub trait Executor { fn run_command_and_measure_common( mut command: std::process::Command, command_failure_action: CmdFailureAction, + command_input_policy: &CommandInputPolicy, command_output_policy: &CommandOutputPolicy, command_name: &str, ) -> Result<TimerResult> { let (stdout, stderr) = command_output_policy.get_stdout_stderr()?; - command.stdin(Stdio::null()).stdout(stdout).stderr(stderr); + let stdin = command_input_policy.get_stdin()?; + command.stdin(stdin).stdout(stdout).stderr(stderr); command.env( "HYPERFINE_RANDOMIZED_ENVIRONMENT_OFFSET", @@ -83,6 +85,7 @@ impl<'a> Executor for RawExecutor<'a> { let result = run_command_and_measure_common( command.get_command()?, command_failure_action.unwrap_or(self.options.command_failure_action), + &self.options.command_input_policy, &self.options.command_output_policy, &command.get_command_line(), )?; @@ -142,6 +145,7 @@ impl<'a> Executor for ShellExecutor<'a> { let mut result = run_command_and_measure_common( command_builder, command_failure_action.unwrap_or(self.options.command_failure_action), + &self.options.command_input_policy, &self.options.command_output_policy, &command.get_command_line(), )?; @@ -312,6 +312,14 @@ fn build_command() -> Command { .hide(true) .help("Enable debug mode which does not actually run commands, but returns fake times when the command is 'sleep <time>'.") ) + .arg( + Arg::new("stdin-data") + .long("stdin-data") + .takes_value(true) + .number_of_values(1) + .value_name("FILE") + .help("Path to file containing stdin data to provide to the command"), + ) } #[test] diff --git a/src/error.rs b/src/error.rs index a38bbd3..2e6d279 100644 --- a/src/error.rs +++ b/src/error.rs @@ -53,4 +53,6 @@ pub enum OptionsError<'a> { ShellParseError(shell_words::ParseError), #[error("Unknown output policy '{0}'. Use './{0}' to output to a file named '{0}'.")] UnknownOutputPolicy(String), + #[error("File containing stdin data '{0}' does not exist")] + StdinDataFileDoesNotExist(String), } diff --git a/src/options.rs b/src/options.rs index 086991e..32a9636 100644 --- a/src/options.rs +++ b/src/options.rs @@ -110,6 +110,36 @@ impl Default for RunBounds { } } +#[derive(Debug, Clone, PartialEq)] +pub enum CommandInputPolicy { + /// Redirect from the null device + Null, + + /// Redirect input from a file + File(PathBuf), +} + +impl Default for CommandInputPolicy { + fn default() -> Self { + CommandInputPolicy::Null + } +} + +impl CommandInputPolicy { + pub fn get_stdin(&self) -> io::Result<Stdio> { + let stream: Stdio = match self { + CommandInputPolicy::Null => Stdio::null(), + + CommandInputPolicy::File(path) => { + let file: File = File::open(&path)?; + Stdio::from(file) + } + }; + + Ok(stream) + } +} + /// How to handle the output of benchmarked commands #[derive(Debug, Clone, PartialEq, Eq)] pub enum CommandOutputPolicy { @@ -196,8 +226,12 @@ pub struct Options { /// What to do with the output of the benchmarked command pub command_output_policy: CommandOutputPolicy, - /// Which time unit to use when displaying resuls + /// Which time unit to use when displaying results pub time_unit: Option<Unit>, + + /// Where input to the benchmarked command comes from + pub command_input_policy: CommandInputPolicy, + } impl Default for Options { @@ -214,6 +248,7 @@ impl Default for Options { executor_kind: ExecutorKind::default(), command_output_policy: CommandOutputPolicy::Null, time_unit: None, + command_input_policy: CommandInputPolicy::Null, } } } @@ -354,6 +389,16 @@ impl Options { .map_err(|e| OptionsError::FloatParsingError("min-benchmarking-time", e))?; } + options.command_input_policy = if let Some(path_str) = matches.value_of("stdin-data") { + let path = PathBuf::from(path_str); + if !path.exists() { + return Err(OptionsError::StdinDataFileDoesNotExist(path_str.to_string())) + } + CommandInputPolicy::File(path) + } else { + CommandInputPolicy::Null + }; + Ok(options) } diff --git a/tests/example_stdin_data b/tests/example_stdin_data new file mode 100644 index 0000000..2483820 --- /dev/null +++ b/tests/example_stdin_data @@ -0,0 +1 @@ +This data is passed to the command via stdin diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 8a215c9..073740c 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -216,6 +216,34 @@ fn runs_commands_using_user_defined_shell() { } #[test] +fn can_pass_file_data_to_command_via_stdin(){ + hyperfine() + .arg("--runs=1") + .arg("--stdin-data=example_stdin_data") + .arg("--show-output") + .arg("cat") + .assert() + .success() + .stdout( + predicate::str::contains("This data is passed to the command via stdin") + ); +} + +#[test] +fn fails_if_invalid_stdin_data_file_provided(){ + hyperfine() + .arg("--runs=1") + .arg("--stdin-data=example_stdin_data_invalid") + .arg("--show-output") + .arg("cat") + .assert() + .failure() + .stderr( + predicate::str::contains("File containing stdin data 'example_stdin_data_invalid' does not exist") + ); +} + +#[test] fn returns_mean_time_in_correct_unit() { hyperfine_debug() .arg("sleep 1.234") |