1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 03:27:44 +00:00

use a command result structure

This commit is contained in:
Sylvestre Ledru 2023-11-10 16:01:38 +01:00
parent 104e707b07
commit 2746c199cf
3 changed files with 107 additions and 45 deletions

View file

@ -12,6 +12,19 @@ use std::process::Command;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use std::sync::{atomic::AtomicBool, Once}; use std::sync::{atomic::AtomicBool, Once};
/// Represents the result of running a command, including its standard output,
/// standard error, and exit code.
pub struct CommandResult {
/// The standard output (stdout) of the command as a string.
pub stdout: String,
/// The standard error (stderr) of the command as a string.
pub stderr: String,
/// The exit code of the command.
pub exit_code: i32,
}
static CHECK_GNU: Once = Once::new(); static CHECK_GNU: Once = Once::new();
static IS_GNU: AtomicBool = AtomicBool::new(false); static IS_GNU: AtomicBool = AtomicBool::new(false);
@ -34,7 +47,7 @@ pub fn is_gnu_cmd(cmd_path: &str) -> Result<(), std::io::Error> {
} }
} }
pub fn generate_and_run_uumain<F>(args: &[OsString], uumain_function: F) -> (String, String, i32) pub fn generate_and_run_uumain<F>(args: &[OsString], uumain_function: F) -> CommandResult
where where
F: FnOnce(std::vec::IntoIter<OsString>) -> i32, F: FnOnce(std::vec::IntoIter<OsString>) -> i32,
{ {
@ -42,11 +55,11 @@ where
let original_stdout_fd = unsafe { dup(STDOUT_FILENO) }; let original_stdout_fd = unsafe { dup(STDOUT_FILENO) };
let original_stderr_fd = unsafe { dup(STDERR_FILENO) }; let original_stderr_fd = unsafe { dup(STDERR_FILENO) };
if original_stdout_fd == -1 || original_stderr_fd == -1 { if original_stdout_fd == -1 || original_stderr_fd == -1 {
return ( return CommandResult {
"Failed to duplicate STDOUT_FILENO or STDERR_FILENO".to_string(), stdout: "Failed to duplicate STDOUT_FILENO or STDERR_FILENO".to_string(),
"".to_string(), stderr: "".to_string(),
-1, exit_code: -1,
); };
} }
println!("Running test {:?}", &args[0..]); println!("Running test {:?}", &args[0..]);
let mut pipe_stdout_fds = [-1; 2]; let mut pipe_stdout_fds = [-1; 2];
@ -56,7 +69,11 @@ where
if unsafe { pipe(pipe_stdout_fds.as_mut_ptr()) } == -1 if unsafe { pipe(pipe_stdout_fds.as_mut_ptr()) } == -1
|| unsafe { pipe(pipe_stderr_fds.as_mut_ptr()) } == -1 || unsafe { pipe(pipe_stderr_fds.as_mut_ptr()) } == -1
{ {
return ("Failed to create pipes".to_string(), "".to_string(), -1); return CommandResult {
stdout: "Failed to create pipes".to_string(),
stderr: "".to_string(),
exit_code: -1,
};
} }
// Redirect stdout and stderr to their respective pipes // Redirect stdout and stderr to their respective pipes
@ -69,11 +86,11 @@ where
close(pipe_stderr_fds[0]); close(pipe_stderr_fds[0]);
close(pipe_stderr_fds[1]); close(pipe_stderr_fds[1]);
} }
return ( return CommandResult {
"Failed to redirect STDOUT_FILENO or STDERR_FILENO".to_string(), stdout: "Failed to redirect STDOUT_FILENO or STDERR_FILENO".to_string(),
"".to_string(), stderr: "".to_string(),
-1, exit_code: -1,
); };
} }
let uumain_exit_status = uumain_function(args.to_owned().into_iter()); let uumain_exit_status = uumain_function(args.to_owned().into_iter());
@ -85,18 +102,19 @@ where
if unsafe { dup2(original_stdout_fd, STDOUT_FILENO) } == -1 if unsafe { dup2(original_stdout_fd, STDOUT_FILENO) } == -1
|| unsafe { dup2(original_stderr_fd, STDERR_FILENO) } == -1 || unsafe { dup2(original_stderr_fd, STDERR_FILENO) } == -1
{ {
return ( return CommandResult {
"Failed to restore the original STDOUT_FILENO or STDERR_FILENO".to_string(), stdout: "Failed to restore the original STDOUT_FILENO or STDERR_FILENO".to_string(),
"".to_string(), stderr: "".to_string(),
-1, exit_code: -1,
); };
} }
unsafe { unsafe {
close(original_stdout_fd); close(original_stdout_fd);
close(original_stderr_fd); close(original_stderr_fd);
close(pipe_stdout_fds[1]);
close(pipe_stderr_fds[1]);
} }
unsafe { close(pipe_stdout_fds[1]) };
unsafe { close(pipe_stderr_fds[1]) };
let captured_stdout = read_from_fd(pipe_stdout_fds[0]).trim().to_string(); let captured_stdout = read_from_fd(pipe_stdout_fds[0]).trim().to_string();
let captured_stderr = read_from_fd(pipe_stderr_fds[0]).to_string(); let captured_stderr = read_from_fd(pipe_stderr_fds[0]).to_string();
@ -107,7 +125,11 @@ where
.trim() .trim()
.to_string(); .to_string();
(captured_stdout, captured_stderr, uumain_exit_status) CommandResult {
stdout: captured_stdout,
stderr: captured_stderr,
exit_code: uumain_exit_status,
}
} }
fn read_from_fd(fd: RawFd) -> String { fn read_from_fd(fd: RawFd) -> String {
@ -141,13 +163,17 @@ pub fn run_gnu_cmd(
cmd_path: &str, cmd_path: &str,
args: &[OsString], args: &[OsString],
check_gnu: bool, check_gnu: bool,
) -> Result<(String, String, i32), (String, String, i32)> { ) -> Result<CommandResult, CommandResult> {
if check_gnu { if check_gnu {
match is_gnu_cmd(cmd_path) { match is_gnu_cmd(cmd_path) {
Ok(_) => {} // if the check passes, do nothing Ok(_) => {} // if the check passes, do nothing
Err(e) => { Err(e) => {
// Convert the io::Error into the function's error type // Convert the io::Error into the function's error type
return Err((String::new(), e.to_string(), -1)); return Err(CommandResult {
stdout: String::new(),
stderr: e.to_string(),
exit_code: -1,
});
} }
} }
} }
@ -159,7 +185,13 @@ pub fn run_gnu_cmd(
let output = match command.output() { let output = match command.output() {
Ok(output) => output, Ok(output) => output,
Err(e) => return Err((String::new(), e.to_string(), -1)), Err(e) => {
return Err(CommandResult {
stdout: String::new(),
stderr: e.to_string(),
exit_code: -1,
});
}
}; };
let exit_code = output.status.code().unwrap_or(-1); let exit_code = output.status.code().unwrap_or(-1);
@ -174,9 +206,17 @@ pub fn run_gnu_cmd(
.to_string(); .to_string();
if output.status.success() || !check_gnu { if output.status.success() || !check_gnu {
Ok((stdout, stderr, exit_code)) Ok(CommandResult {
stdout,
stderr,
exit_code,
})
} else { } else {
Err((stdout, stderr, exit_code)) Err(CommandResult {
stdout,
stderr,
exit_code,
})
} }
} }

View file

@ -13,8 +13,8 @@ use rand::Rng;
use std::{env, ffi::OsString}; use std::{env, ffi::OsString};
mod fuzz_common; mod fuzz_common;
use crate::fuzz_common::CommandResult;
use crate::fuzz_common::{compare_result, generate_and_run_uumain, run_gnu_cmd}; use crate::fuzz_common::{compare_result, generate_and_run_uumain, run_gnu_cmd};
static CMD_PATH: &str = "expr"; static CMD_PATH: &str = "expr";
fn generate_random_string(max_length: usize) -> String { fn generate_random_string(max_length: usize) -> String {
@ -88,21 +88,31 @@ fuzz_target!(|_data: &[u8]| {
// because uutils expr doesn't support localization yet // because uutils expr doesn't support localization yet
// TODO remove once uutils expr supports localization // TODO remove once uutils expr supports localization
env::set_var("LC_COLLATE", "C"); env::set_var("LC_COLLATE", "C");
let rust_result = generate_and_run_uumain(&args, uumain);
let (rust_stdout, rust_stderr, uumain_exit_code) = generate_and_run_uumain(&args, uumain); let gnu_result = match run_gnu_cmd(CMD_PATH, &args[1..], false) {
Ok(result) => result,
let (gnu_stdout, gnu_stderr, gnu_exit_code) = Err(error_result) => {
run_gnu_cmd(CMD_PATH, &args[1..], false).unwrap_or_else(|e| e); eprintln!("Failed to run GNU command:");
eprintln!("Stderr: {}", error_result.stderr);
eprintln!("Exit Code: {}", error_result.exit_code);
CommandResult {
stdout: String::new(),
stderr: error_result.stderr,
exit_code: error_result.exit_code,
}
}
};
compare_result( compare_result(
"expr", "expr",
&format!("{:?}", &args[1..]), &format!("{:?}", &args[1..]),
&rust_stdout, &rust_result.stdout,
&gnu_stdout, &gnu_result.stdout,
&rust_stderr, &rust_result.stderr,
&gnu_stderr, &gnu_result.stderr,
uumain_exit_code, rust_result.exit_code,
gnu_exit_code, gnu_result.exit_code,
false, // Set to true if you want to fail on stderr diff false, // Set to true if you want to fail on stderr diff
); );
}); });

View file

@ -13,6 +13,7 @@ use rand::Rng;
use std::ffi::OsString; use std::ffi::OsString;
mod fuzz_common; mod fuzz_common;
use crate::fuzz_common::CommandResult;
use crate::fuzz_common::{compare_result, generate_and_run_uumain, run_gnu_cmd}; use crate::fuzz_common::{compare_result, generate_and_run_uumain, run_gnu_cmd};
#[derive(PartialEq, Debug, Clone)] #[derive(PartialEq, Debug, Clone)]
@ -204,20 +205,31 @@ fuzz_target!(|_data: &[u8]| {
args.push(OsString::from(generate_test_arg())); args.push(OsString::from(generate_test_arg()));
} }
let (rust_stdout, rust_stderr, uumain_exit_code) = generate_and_run_uumain(&args, uumain); let rust_result = generate_and_run_uumain(&args, uumain);
let (gnu_stdout, gnu_stderr, gnu_exit_code) = let gnu_result = match run_gnu_cmd(CMD_PATH, &args[1..], false) {
run_gnu_cmd(CMD_PATH, &args[1..], false).unwrap_or_else(|e| e); Ok(result) => result,
Err(error_result) => {
eprintln!("Failed to run GNU command:");
eprintln!("Stderr: {}", error_result.stderr);
eprintln!("Exit Code: {}", error_result.exit_code);
CommandResult {
stdout: String::new(),
stderr: error_result.stderr,
exit_code: error_result.exit_code,
}
}
};
compare_result( compare_result(
"test", "test",
&format!("{:?}", &args[1..]), &format!("{:?}", &args[1..]),
&rust_stdout, &rust_result.stdout,
&gnu_stdout, &gnu_result.stdout,
&rust_stderr, &rust_result.stderr,
&gnu_stderr, &gnu_result.stderr,
uumain_exit_code, rust_result.exit_code,
gnu_exit_code, gnu_result.exit_code,
false, // Set to true if you want to fail on stderr diff false, // Set to true if you want to fail on stderr diff
); );
}); });