summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteve Nease <stephen.h.nease@gmail.com>2022-09-11 16:20:22 -0500
committerDavid Peter <sharkdp@users.noreply.github.com>2023-03-15 09:33:49 +0100
commite38acdb68313d76028e3da37bd8574320f585c98 (patch)
tree17f6dfc634f78a9f3433704e07d6e2530b7ec4f5
parent531fee318817967fc0d6d6eb227c77abd5148499 (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.rs8
-rw-r--r--src/cli.rs8
-rw-r--r--src/error.rs2
-rw-r--r--src/options.rs47
-rw-r--r--tests/example_stdin_data1
-rw-r--r--tests/integration_tests.rs28
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(),
)?;
diff --git a/src/cli.rs b/src/cli.rs
index d05455d..9abfc19 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -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")