diff --git a/src/benchmark/mod.rs b/src/benchmark/mod.rs index e3534a7bc..04e4ad9a3 100644 --- a/src/benchmark/mod.rs +++ b/src/benchmark/mod.rs @@ -266,12 +266,18 @@ impl<'a> Benchmark<'a> { let count = { let min = cmp::max(runs_in_min_time, self.options.run_bounds.min); - self.options + let count = self + .options .run_bounds .max .as_ref() .map(|max| cmp::min(min, *max)) - .unwrap_or(min) + .unwrap_or(min); + + // The initial timing run above has already been performed, so we can never + // end up with fewer than one run (this could happen with '--min-runs 0' for + // a command that is slower than the minimum benchmarking time). + cmp::max(count, 1) }; let count_remaining = count - 1; diff --git a/src/error.rs b/src/error.rs index 53d39a57d..63330bfb3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -39,6 +39,10 @@ pub enum OptionsError<'a> { "Conflicting requirements for the number of runs (empty range, min is larger than max)" )] EmptyRunsRange, + #[error( + "At least one run has to be performed. Please specify a value larger than zero for '--{0}'" + )] + ZeroRuns(&'a str), #[error("Too many --command-name options: Expected {0} at most")] TooManyCommandNames(usize), #[error("'--command-name' has been specified {0} times. It has to appear exactly once, or exactly {1} times (number of benchmarks)")] diff --git a/src/options.rs b/src/options.rs index 7c83da1c5..f37a07285 100644 --- a/src/options.rs +++ b/src/options.rs @@ -291,8 +291,13 @@ impl Options { let mut max_runs = param_to_u64("max-runs")?; if let Some(runs) = param_to_u64("runs")? { + if runs == 0 { + return Err(OptionsError::ZeroRuns("runs")); + } min_runs = Some(runs); max_runs = Some(runs); + } else if max_runs == Some(0) { + return Err(OptionsError::ZeroRuns("max-runs")); } match (min_runs, max_runs) { diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 2de3a5049..974114a10 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -33,6 +33,30 @@ fn one_run_is_supported() { .success(); } +#[test] +fn fails_with_zero_runs() { + for option in ["--runs=0", "--max-runs=0"] { + hyperfine() + .arg(option) + .arg("echo dummy benchmark") + .assert() + .failure() + .stderr(predicate::str::contains( + "At least one run has to be performed", + )); + } +} + +#[test] +fn min_runs_of_zero_still_performs_one_run() { + hyperfine_debug() + .arg("--min-runs=0") + .arg("sleep 4") + .assert() + .success() + .stdout(predicate::str::contains("Time (abs ≡)")); +} + #[test] fn can_run_commands_without_a_shell() { hyperfine()