mirror of
https://github.com/sharkdp/hyperfine.git
synced 2026-11-03 01:21:25 +00:00
Support list of exit codes in --ignore-failure
This commit is contained in:
1 parent
16348dcf35
commit
b5a6860d96
4 files changed
+137
-25
No files matched your search
+36
-18
@@ -79,23 +79,41 @@ fn run_command_and_measure_common(
|
|||||||
let result = execute_and_measure(command)
|
let result = execute_and_measure(command)
|
||||||
.with_context(|| format!("Failed to run command '{command_name}'"))?;
|
.with_context(|| format!("Failed to run command '{command_name}'"))?;
|
||||||
|
|
||||||
if command_failure_action == CmdFailureAction::RaiseError && !result.status.success() {
|
if !result.status.success() {
|
||||||
let when = match iteration {
|
use crate::util::exit_code::extract_exit_code;
|
||||||
BenchmarkIteration::NonBenchmarkRun => "a non-benchmark run".to_string(),
|
|
||||||
BenchmarkIteration::Warmup(0) => "the first warmup run".to_string(),
|
|
||||||
BenchmarkIteration::Warmup(i) => format!("warmup iteration {i}"),
|
|
||||||
BenchmarkIteration::Benchmark(0) => "the first benchmark run".to_string(),
|
|
||||||
BenchmarkIteration::Benchmark(i) => format!("benchmark iteration {i}"),
|
|
||||||
};
|
|
||||||
bail!(
|
|
||||||
"{cause} in {when}. Use the '-i'/'--ignore-failure' option if you want to ignore this. \
|
|
||||||
Alternatively, use the '--show-output' option to debug what went wrong.",
|
|
||||||
cause=result.status.code().map_or(
|
|
||||||
"The process has been terminated by a signal".into(),
|
|
||||||
|c| format!("Command terminated with non-zero exit code {c}")
|
|
||||||
|
|
||||||
),
|
let should_fail = match command_failure_action {
|
||||||
);
|
CmdFailureAction::RaiseError => true,
|
||||||
|
CmdFailureAction::IgnoreAllFailures => false,
|
||||||
|
CmdFailureAction::IgnoreSpecificFailures(ref codes) => {
|
||||||
|
// Only fail if the exit code is not in the list of codes to ignore
|
||||||
|
if let Some(exit_code) = extract_exit_code(result.status) {
|
||||||
|
!codes.contains(&exit_code)
|
||||||
|
} else {
|
||||||
|
// If we can't extract an exit code, treat it as a failure
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if should_fail {
|
||||||
|
let when = match iteration {
|
||||||
|
BenchmarkIteration::NonBenchmarkRun => "a non-benchmark run".to_string(),
|
||||||
|
BenchmarkIteration::Warmup(0) => "the first warmup run".to_string(),
|
||||||
|
BenchmarkIteration::Warmup(i) => format!("warmup iteration {i}"),
|
||||||
|
BenchmarkIteration::Benchmark(0) => "the first benchmark run".to_string(),
|
||||||
|
BenchmarkIteration::Benchmark(i) => format!("benchmark iteration {i}"),
|
||||||
|
};
|
||||||
|
bail!(
|
||||||
|
"{cause} in {when}. Use the '-i'/'--ignore-failure' option if you want to ignore this. \
|
||||||
|
Alternatively, use the '--show-output' option to debug what went wrong.",
|
||||||
|
cause=result.status.code().map_or(
|
||||||
|
"The process has been terminated by a signal".into(),
|
||||||
|
|c| format!("Command terminated with non-zero exit code {c}")
|
||||||
|
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
@@ -122,7 +140,7 @@ impl Executor for RawExecutor<'_> {
|
|||||||
let result = run_command_and_measure_common(
|
let result = run_command_and_measure_common(
|
||||||
command.get_command()?,
|
command.get_command()?,
|
||||||
iteration,
|
iteration,
|
||||||
command_failure_action.unwrap_or(self.options.command_failure_action),
|
command_failure_action.unwrap_or_else(|| self.options.command_failure_action.clone()),
|
||||||
&self.options.command_input_policy,
|
&self.options.command_input_policy,
|
||||||
output_policy,
|
output_policy,
|
||||||
&command.get_command_line(),
|
&command.get_command_line(),
|
||||||
@@ -187,7 +205,7 @@ impl Executor for ShellExecutor<'_> {
|
|||||||
let mut result = run_command_and_measure_common(
|
let mut result = run_command_and_measure_common(
|
||||||
command_builder,
|
command_builder,
|
||||||
iteration,
|
iteration,
|
||||||
command_failure_action.unwrap_or(self.options.command_failure_action),
|
command_failure_action.unwrap_or_else(|| self.options.command_failure_action.clone()),
|
||||||
&self.options.command_input_policy,
|
&self.options.command_input_policy,
|
||||||
output_policy,
|
output_policy,
|
||||||
&command.get_command_line(),
|
&command.get_command_line(),
|
||||||
|
|||||||
+8
-2
@@ -226,9 +226,15 @@ fn build_command() -> Command {
|
|||||||
.arg(
|
.arg(
|
||||||
Arg::new("ignore-failure")
|
Arg::new("ignore-failure")
|
||||||
.long("ignore-failure")
|
.long("ignore-failure")
|
||||||
.action(ArgAction::SetTrue)
|
.action(ArgAction::Set)
|
||||||
|
.value_name("MODE")
|
||||||
|
.num_args(0..=1)
|
||||||
|
.default_missing_value("all-non-zero")
|
||||||
|
.require_equals(true)
|
||||||
.short('i')
|
.short('i')
|
||||||
.help("Ignore non-zero exit codes of the benchmarked programs."),
|
.help("Ignore failures of the benchmarked programs. Without a value or with \
|
||||||
|
'all-non-zero', all non-zero exit codes are ignored. You can also provide \
|
||||||
|
a comma-separated list of exit codes to ignore (e.g., --ignore-failure=1,2)."),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("style")
|
Arg::new("style")
|
||||||
|
|||||||
+21
-5
@@ -67,13 +67,16 @@ impl Shell {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Action to take when an executed command fails.
|
/// Action to take when an executed command fails.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum CmdFailureAction {
|
pub enum CmdFailureAction {
|
||||||
/// Exit with an error message
|
/// Exit with an error message
|
||||||
RaiseError,
|
RaiseError,
|
||||||
|
|
||||||
/// Simply ignore the non-zero exit code
|
/// Ignore all non-zero exit codes
|
||||||
Ignore,
|
IgnoreAllFailures,
|
||||||
|
|
||||||
|
/// Ignore specific exit codes
|
||||||
|
IgnoreSpecificFailures(Vec<i32>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Output style type option
|
/// Output style type option
|
||||||
@@ -415,8 +418,21 @@ impl Options {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if matches.get_flag("ignore-failure") {
|
if let Some(mode) = matches.get_one::<String>("ignore-failure") {
|
||||||
options.command_failure_action = CmdFailureAction::Ignore;
|
options.command_failure_action = match mode.as_str() {
|
||||||
|
"all-non-zero" | "" => CmdFailureAction::IgnoreAllFailures,
|
||||||
|
codes => {
|
||||||
|
let exit_codes: Result<Vec<i32>, _> = codes
|
||||||
|
.split(',')
|
||||||
|
.map(|s| {
|
||||||
|
s.trim()
|
||||||
|
.parse::<i32>()
|
||||||
|
.map_err(|e| OptionsError::IntParsingError("ignore-failure", e))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
CmdFailureAction::IgnoreSpecificFailures(exit_codes?)
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
options.time_unit = match matches.get_one::<String>("time-unit").map(|s| s.as_str()) {
|
options.time_unit = match matches.get_one::<String>("time-unit").map(|s| s.as_str()) {
|
||||||
|
|||||||
@@ -277,6 +277,78 @@ fn can_run_failing_commands_with_ignore_failure_option() {
|
|||||||
.success();
|
.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
#[test]
|
||||||
|
fn can_ignore_specific_exit_codes() {
|
||||||
|
// Test that specifying exit code 1 ignores it
|
||||||
|
hyperfine()
|
||||||
|
.arg("--runs=1")
|
||||||
|
.arg("--ignore-failure=1")
|
||||||
|
.arg("exit 1")
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
// Test that other exit codes still fail
|
||||||
|
hyperfine()
|
||||||
|
.arg("--runs=1")
|
||||||
|
.arg("--ignore-failure=1")
|
||||||
|
.arg("exit 2")
|
||||||
|
.assert()
|
||||||
|
.failure()
|
||||||
|
.stderr(predicate::str::contains(
|
||||||
|
"Command terminated with non-zero exit code 2",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
#[test]
|
||||||
|
fn can_ignore_multiple_exit_codes() {
|
||||||
|
// Test that all specified exit codes are ignored
|
||||||
|
hyperfine()
|
||||||
|
.arg("--runs=1")
|
||||||
|
.arg("--ignore-failure=1,2,3")
|
||||||
|
.arg("exit 1")
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
hyperfine()
|
||||||
|
.arg("--runs=1")
|
||||||
|
.arg("--ignore-failure=1,2,3")
|
||||||
|
.arg("exit 2")
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
hyperfine()
|
||||||
|
.arg("--runs=1")
|
||||||
|
.arg("--ignore-failure=1,2,3")
|
||||||
|
.arg("exit 3")
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
// Test that other exit codes still fail
|
||||||
|
hyperfine()
|
||||||
|
.arg("--runs=1")
|
||||||
|
.arg("--ignore-failure=1,2,3")
|
||||||
|
.arg("exit 4")
|
||||||
|
.assert()
|
||||||
|
.failure()
|
||||||
|
.stderr(predicate::str::contains(
|
||||||
|
"Command terminated with non-zero exit code 4",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
#[test]
|
||||||
|
fn ignore_failure_with_all_non_zero() {
|
||||||
|
// Test explicit "all-non-zero" mode
|
||||||
|
hyperfine()
|
||||||
|
.arg("--runs=1")
|
||||||
|
.arg("--ignore-failure=all-non-zero")
|
||||||
|
.arg("exit 5")
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn shows_output_of_benchmarked_command() {
|
fn shows_output_of_benchmarked_command() {
|
||||||
hyperfine()
|
hyperfine()
|
||||||
|
|||||||
Reference in new issue
Block a user