mirror of
https://github.com/sharkdp/hyperfine.git
synced 2024-11-25 19:19:31 +03:00
Export exit codes to JSON output
This commit is contained in:
parent
9e57bf613c
commit
3fcc6a187b
@ -1,6 +1,7 @@
|
||||
# unreleased
|
||||
|
||||
- Add command exit code to output if it fails, see #342 (@KaindlJulian)
|
||||
- Export command exit code to JSON output, see #371 (@JordiChauzi)
|
||||
|
||||
## Features
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::cmp;
|
||||
use std::io;
|
||||
use std::process::Stdio;
|
||||
use std::process::{ExitStatus, Stdio};
|
||||
|
||||
use colored::*;
|
||||
use statistical::{mean, median, standard_deviation};
|
||||
@ -46,7 +46,7 @@ pub fn time_shell_command(
|
||||
show_output: bool,
|
||||
failure_action: CmdFailureAction,
|
||||
shell_spawning_time: Option<TimingResult>,
|
||||
) -> io::Result<(TimingResult, bool)> {
|
||||
) -> io::Result<(TimingResult, ExitStatus)> {
|
||||
let (stdout, stderr) = if show_output {
|
||||
(Stdio::inherit(), Stdio::inherit())
|
||||
} else {
|
||||
@ -88,7 +88,7 @@ pub fn time_shell_command(
|
||||
time_user,
|
||||
time_system,
|
||||
},
|
||||
result.status.success(),
|
||||
result.status,
|
||||
))
|
||||
}
|
||||
|
||||
@ -201,6 +201,26 @@ fn run_cleanup_command(
|
||||
run_intermediate_command(shell, command, show_output, error_output)
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn extract_exit_code(status: ExitStatus) -> i32 {
|
||||
use std::os::unix::process::ExitStatusExt;
|
||||
|
||||
/* From the ExitStatus::code documentation:
|
||||
"On Unix, this will return None if the process was terminated by a signal."
|
||||
In that case, ExitStatusExt::signal should never return None.
|
||||
*/
|
||||
status.code().unwrap_or_else(|| status.signal().unwrap())
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
fn extract_exit_code(status: ExitStatus) -> i32 {
|
||||
/* From the ExitStatus::code documentation:
|
||||
"On Unix, this will return None if the process was terminated by a signal."
|
||||
On the other configurations, ExitStatus::code should never return None.
|
||||
*/
|
||||
status.code().unwrap()
|
||||
}
|
||||
|
||||
/// Run the benchmark for a single shell command
|
||||
pub fn run_benchmark(
|
||||
num: usize,
|
||||
@ -228,6 +248,7 @@ pub fn run_benchmark(
|
||||
let mut times_real: Vec<Second> = vec![];
|
||||
let mut times_user: Vec<Second> = vec![];
|
||||
let mut times_system: Vec<Second> = vec![];
|
||||
let mut exit_codes: Vec<i32> = vec![];
|
||||
let mut all_succeeded = true;
|
||||
|
||||
// Run init command
|
||||
@ -280,13 +301,14 @@ pub fn run_benchmark(
|
||||
let prepare_res = run_preparation_command(&options.shell, &prepare_cmd, options.show_output)?;
|
||||
|
||||
// Initial timing run
|
||||
let (res, success) = time_shell_command(
|
||||
let (res, status) = time_shell_command(
|
||||
&options.shell,
|
||||
cmd,
|
||||
options.show_output,
|
||||
options.failure_action,
|
||||
Some(shell_spawning_time),
|
||||
)?;
|
||||
let success = status.success();
|
||||
|
||||
// Determine number of benchmark runs
|
||||
let runs_in_min_time = (options.min_time_sec
|
||||
@ -310,6 +332,7 @@ pub fn run_benchmark(
|
||||
times_real.push(res.time_real);
|
||||
times_user.push(res.time_user);
|
||||
times_system.push(res.time_system);
|
||||
exit_codes.push(extract_exit_code(status));
|
||||
|
||||
all_succeeded = all_succeeded && success;
|
||||
|
||||
@ -328,17 +351,19 @@ pub fn run_benchmark(
|
||||
|
||||
progress_bar.as_ref().map(|bar| bar.set_message(msg.to_owned()));
|
||||
|
||||
let (res, success) = time_shell_command(
|
||||
let (res, status) = time_shell_command(
|
||||
&options.shell,
|
||||
cmd,
|
||||
options.show_output,
|
||||
options.failure_action,
|
||||
Some(shell_spawning_time),
|
||||
)?;
|
||||
let success = status.success();
|
||||
|
||||
times_real.push(res.time_real);
|
||||
times_user.push(res.time_user);
|
||||
times_system.push(res.time_system);
|
||||
exit_codes.push(extract_exit_code(status));
|
||||
|
||||
all_succeeded = all_succeeded && success;
|
||||
|
||||
@ -438,6 +463,7 @@ pub fn run_benchmark(
|
||||
t_min,
|
||||
t_max,
|
||||
times_real,
|
||||
exit_codes,
|
||||
cmd.get_parameters()
|
||||
.iter()
|
||||
.map(|(name, value)| ((*name).to_string(), value.to_string()))
|
||||
|
@ -99,6 +99,7 @@ fn test_asciidoc_table_row() {
|
||||
0.10745223440000001,
|
||||
0.10697327940000001,
|
||||
],
|
||||
vec![0, 0, 0], // exit codes
|
||||
BTreeMap::new(), // param
|
||||
);
|
||||
|
||||
@ -151,6 +152,7 @@ fn test_asciidoc_table_row_command_escape() {
|
||||
0.10745223440000001,
|
||||
0.10697327940000001,
|
||||
],
|
||||
vec![0, 0, 0], // exit codes
|
||||
BTreeMap::new(), // param
|
||||
);
|
||||
let exps = format!(
|
||||
@ -185,6 +187,7 @@ fn test_asciidoc() {
|
||||
5.0,
|
||||
6.0,
|
||||
vec![7.0, 8.0, 9.0],
|
||||
vec![0, 0, 0],
|
||||
{
|
||||
let mut params = BTreeMap::new();
|
||||
params.insert("foo".into(), "1".into());
|
||||
@ -202,6 +205,7 @@ fn test_asciidoc() {
|
||||
15.0,
|
||||
16.0,
|
||||
vec![17.0, 18.0, 19.0],
|
||||
vec![0, 0, 0],
|
||||
{
|
||||
let mut params = BTreeMap::new();
|
||||
params.insert("foo".into(), "1".into());
|
||||
|
@ -17,7 +17,7 @@ impl Exporter for CsvExporter {
|
||||
|
||||
{
|
||||
let mut headers: Vec<Cow<[u8]>> = [
|
||||
// The list of times cannot be exported to the CSV file - omit it.
|
||||
// The list of times and exit codes cannot be exported to the CSV file - omit them.
|
||||
"command", "mean", "stddev", "median", "user", "system", "min", "max",
|
||||
]
|
||||
.iter()
|
||||
@ -68,6 +68,7 @@ fn test_csv() {
|
||||
5.0,
|
||||
6.0,
|
||||
vec![7.0, 8.0, 9.0],
|
||||
vec![0, 0, 0],
|
||||
{
|
||||
let mut params = BTreeMap::new();
|
||||
params.insert("foo".into(), "one".into());
|
||||
@ -85,6 +86,7 @@ fn test_csv() {
|
||||
15.0,
|
||||
16.5,
|
||||
vec![17.0, 18.0, 19.0],
|
||||
vec![0, 0, 0],
|
||||
{
|
||||
let mut params = BTreeMap::new();
|
||||
params.insert("foo".into(), "one".into());
|
||||
|
@ -102,6 +102,7 @@ fn test_markdown_format_ms() {
|
||||
0.1023, // min
|
||||
0.1080, // max
|
||||
vec![0.1, 0.1, 0.1], // times
|
||||
vec![0, 0, 0], // exit codes
|
||||
BTreeMap::new(), // parameter
|
||||
));
|
||||
|
||||
@ -115,6 +116,7 @@ fn test_markdown_format_ms() {
|
||||
2.0020, // min
|
||||
2.0080, // max
|
||||
vec![2.0, 2.0, 2.0], // times
|
||||
vec![0, 0, 0], // exit codes
|
||||
BTreeMap::new(), // parameter
|
||||
));
|
||||
|
||||
@ -150,6 +152,7 @@ fn test_markdown_format_s() {
|
||||
2.0020, // min
|
||||
2.0080, // max
|
||||
vec![2.0, 2.0, 2.0], // times
|
||||
vec![0, 0, 0], // exit codes
|
||||
BTreeMap::new(), // parameter
|
||||
));
|
||||
|
||||
@ -163,6 +166,7 @@ fn test_markdown_format_s() {
|
||||
0.1023, // min
|
||||
0.1080, // max
|
||||
vec![0.1, 0.1, 0.1], // times
|
||||
vec![0, 0, 0], // exit codes
|
||||
BTreeMap::new(), // parameter
|
||||
));
|
||||
|
||||
@ -197,6 +201,7 @@ fn test_markdown_format_time_unit_s() {
|
||||
0.1023, // min
|
||||
0.1080, // max
|
||||
vec![0.1, 0.1, 0.1], // times
|
||||
vec![0, 0, 0], // exit codes
|
||||
BTreeMap::new(), // parameter
|
||||
));
|
||||
|
||||
@ -210,6 +215,7 @@ fn test_markdown_format_time_unit_s() {
|
||||
2.0020, // min
|
||||
2.0080, // max
|
||||
vec![2.0, 2.0, 2.0], // times
|
||||
vec![0, 0, 0], // exit codes
|
||||
BTreeMap::new(), // parameter
|
||||
));
|
||||
|
||||
@ -250,6 +256,7 @@ fn test_markdown_format_time_unit_ms() {
|
||||
2.0020, // min
|
||||
2.0080, // max
|
||||
vec![2.0, 2.0, 2.0], // times
|
||||
vec![0, 0, 0], // exit codes
|
||||
BTreeMap::new(), // parameter
|
||||
));
|
||||
|
||||
@ -263,6 +270,7 @@ fn test_markdown_format_time_unit_ms() {
|
||||
0.1023, // min
|
||||
0.1080, // max
|
||||
vec![0.1, 0.1, 0.1], // times
|
||||
vec![0, 0, 0], // exit codes
|
||||
BTreeMap::new(), // parameter
|
||||
));
|
||||
|
||||
|
@ -157,6 +157,7 @@ fn create_result(name: &str, mean: Scalar) -> BenchmarkResult {
|
||||
min: mean,
|
||||
max: mean,
|
||||
times: None,
|
||||
exit_codes: Vec::new(),
|
||||
parameters: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
@ -268,6 +268,9 @@ pub struct BenchmarkResult {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub times: Option<Vec<Second>>,
|
||||
|
||||
/// All run exit codes
|
||||
pub exit_codes: Vec<i32>,
|
||||
|
||||
/// Any parameter values used
|
||||
#[serde(skip_serializing_if = "BTreeMap::is_empty")]
|
||||
pub parameters: BTreeMap<String, String>,
|
||||
@ -285,6 +288,7 @@ impl BenchmarkResult {
|
||||
min: Second,
|
||||
max: Second,
|
||||
times: Vec<Second>,
|
||||
exit_codes: Vec<i32>,
|
||||
parameters: BTreeMap<String, String>,
|
||||
) -> Self {
|
||||
BenchmarkResult {
|
||||
@ -297,6 +301,7 @@ impl BenchmarkResult {
|
||||
min,
|
||||
max,
|
||||
times: Some(times),
|
||||
exit_codes,
|
||||
parameters,
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user