Use `std::process::ExitCode` in main instead of `std::process:exit` to fix valgrind errors.

See <https://github.com/jfrimmel/cargo-valgrind/issues/131>
This commit is contained in:
Jean-Christophe Amiel 2025-11-30 13:54:32 +01:00
parent f540e2e735
commit c509a0504a
No known key found for this signature in database
GPG Key ID: 07FF11CFD55356CC
3 changed files with 50 additions and 48 deletions

View File

@ -4,8 +4,7 @@ set -Eeuo pipefail
valgrind --version
cargo-valgrind --help
# Disable valgrind for the moment, see <https://github.com/jfrimmel/cargo-valgrind/issues/131>
#cat <<END | cargo valgrind run -p hurl -- --test
#GET https://unpkg.com/vue@3.4.27/dist/vue.global.prod.js
#HTTP 200
#END
cat <<END | cargo valgrind run -p hurl -- --test
GET https://unpkg.com/vue@3.4.27/dist/vue.global.prod.js
HTTP 200
END

View File

@ -52,6 +52,9 @@ impl BaseLogger {
/// Prints an error `message` on standard error.
pub fn error(&self, message: &str) {
if message.is_empty() {
return;
}
let mut s = StyledString::new();
s.push_with("error", Style::new().red().bold());
s.push(": ");

View File

@ -22,8 +22,9 @@ use std::collections::HashSet;
use std::io::prelude::*;
use std::io::IsTerminal;
use std::path::Path;
use std::process::ExitCode;
use std::time::Instant;
use std::{env, io, process, thread};
use std::{env, io, thread};
use hurl::report::{curl, html, json, junit, tap};
use hurl::runner;
@ -35,12 +36,12 @@ use hurl_core::text;
use crate::cli::options::{CliOptions, CliOptionsError, RunContext};
use crate::cli::{BaseLogger, CliError};
const EXIT_OK: i32 = 0;
const EXIT_ERROR_COMMANDLINE: i32 = 1;
const EXIT_ERROR_PARSING: i32 = 2;
const EXIT_ERROR_RUNTIME: i32 = 3;
const EXIT_ERROR_ASSERT: i32 = 4;
const EXIT_ERROR_UNDEFINED: i32 = 127;
const EXIT_OK: u8 = 0;
const EXIT_ERROR_COMMANDLINE: u8 = 1;
const EXIT_ERROR_PARSING: u8 = 2;
const EXIT_ERROR_RUNTIME: u8 = 3;
const EXIT_ERROR_ASSERT: u8 = 4;
const EXIT_ERROR_UNDEFINED: u8 = 127;
/// Structure that stores the result of an Hurl file execution, and the content of the file.
#[derive(Clone, Debug, PartialEq, Eq)]
@ -53,7 +54,7 @@ struct HurlRun {
}
/// Executes Hurl entry point.
fn main() {
fn main() -> ExitCode {
text::init_crate_colored();
// Construct the run context environment, this should be the sole place where we read
@ -71,11 +72,11 @@ fn main() {
Err(e) => match e {
CliOptionsError::DisplayHelp(e) | CliOptionsError::DisplayVersion(e) => {
print!("{e}");
process::exit(EXIT_OK);
return ExitCode::from(EXIT_OK);
}
_ => {
eprintln!("{e}");
process::exit(EXIT_ERROR_COMMANDLINE);
return ExitCode::from(EXIT_ERROR_COMMANDLINE);
}
},
};
@ -84,17 +85,24 @@ fn main() {
// We'll use a more advanced logger for rich error report when running Hurl files.
let verbose = opts.verbose || opts.very_verbose || opts.interactive;
let base_logger = BaseLogger::new(opts.color, verbose);
let current_dir = env::current_dir();
let current_dir = unwrap_or_exit(current_dir, EXIT_ERROR_UNDEFINED, &base_logger);
let current_dir = match env::current_dir() {
Ok(c) => c,
Err(err) => {
base_logger.error(&err.to_string());
return ExitCode::from(EXIT_ERROR_UNDEFINED);
}
};
let current_dir = current_dir.as_path();
let start = Instant::now();
let runs = if opts.parallel {
let available = unwrap_or_exit(
thread::available_parallelism(),
EXIT_ERROR_UNDEFINED,
&base_logger,
);
let available = match thread::available_parallelism() {
Ok(a) => a,
Err(err) => {
base_logger.error(&err.to_string());
return ExitCode::from(EXIT_ERROR_UNDEFINED);
}
};
let workers_count = opts.jobs.unwrap_or(available.get());
base_logger.debug(&format!("Parallel run using {workers_count} workers"));
@ -109,12 +117,19 @@ fn main() {
Ok(r) => r,
// So, we're dealing here with I/O errors: input reading, parsing etc...
// We consider input read as "parsing" and don't have a specific exit code for the moment.
Err(CliError::InputRead(msg)) => exit_with_error(&msg, EXIT_ERROR_PARSING, &base_logger),
Err(CliError::InputRead(msg)) | Err(CliError::GenericIO(msg)) => {
base_logger.error(&msg);
return ExitCode::from(EXIT_ERROR_PARSING);
}
// In case of parsing error, there is no error because the display of parsing error has been
// done in the execution of the Hurl files, inside the crates (and not in the main).
Err(CliError::Parsing) => exit_with_error("", EXIT_ERROR_PARSING, &base_logger),
Err(CliError::OutputWrite(msg)) => exit_with_error(&msg, EXIT_ERROR_RUNTIME, &base_logger),
Err(CliError::GenericIO(msg)) => exit_with_error(&msg, EXIT_ERROR_PARSING, &base_logger),
Err(CliError::Parsing) => {
return ExitCode::from(EXIT_ERROR_PARSING);
}
Err(CliError::OutputWrite(msg)) => {
base_logger.error(&msg);
return ExitCode::from(EXIT_ERROR_RUNTIME);
}
};
// Compute duration of the test here to not take reports writings into account.
@ -123,7 +138,10 @@ fn main() {
// Write HTML, JUnit, TAP reports on disk.
if has_report(&opts) {
let ret = export_results(&runs, &opts, &base_logger);
unwrap_or_exit(ret, EXIT_ERROR_UNDEFINED, &base_logger);
if let Err(err) = ret {
base_logger.error(&err.to_string());
return ExitCode::from(EXIT_ERROR_UNDEFINED);
}
}
if opts.test {
@ -131,26 +149,8 @@ fn main() {
base_logger.info(summary.as_str());
}
process::exit(exit_code(&runs));
}
/// Unwraps a `result` or exit with message.
fn unwrap_or_exit<T, E>(result: Result<T, E>, code: i32, logger: &BaseLogger) -> T
where
E: std::fmt::Display,
{
match result {
Ok(v) => v,
Err(e) => exit_with_error(&e.to_string(), code, logger),
}
}
/// Prints an error message and exits the current process with an exit code.
fn exit_with_error(message: &str, code: i32, logger: &BaseLogger) -> ! {
if !message.is_empty() {
logger.error(message);
}
process::exit(code);
let exit_code = exit_code(&runs);
ExitCode::from(exit_code)
}
/// Returns `true` if any kind of report should be created, `false` otherwise.
@ -271,7 +271,7 @@ fn create_json_report(runs: &[HurlRun], dir_path: &Path, secrets: &[&str]) -> Re
}
/// Returns an exit code for a list of HurlResult.
fn exit_code(runs: &[HurlRun]) -> i32 {
fn exit_code(runs: &[HurlRun]) -> u8 {
let mut count_errors_runner = 0;
let mut count_errors_assert = 0;
for run in runs.iter() {