mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
use a command result structure
This commit is contained in:
parent
104e707b07
commit
2746c199cf
3 changed files with 107 additions and 45 deletions
|
@ -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,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -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
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue