1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-27 19:17:43 +00:00

fuzz: improve readability of fuzzer output, add colors

This commit is contained in:
Dorian Peron 2025-02-08 00:38:35 +01:00
parent d86a7fb593
commit 5d952afa3d
4 changed files with 135 additions and 24 deletions

20
fuzz/Cargo.lock generated
View file

@ -292,6 +292,19 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "120133d4db2ec47efe2e26502ee984747630c67f51974fca0b6c1340cf2368d3"
[[package]]
name = "console"
version = "0.15.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b"
dependencies = [
"encode_unicode",
"libc",
"once_cell",
"unicode-width 0.2.0",
"windows-sys",
]
[[package]]
name = "const-random"
version = "0.1.18"
@ -450,6 +463,12 @@ version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "encode_unicode"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
[[package]]
name = "errno"
version = "0.3.10"
@ -1355,6 +1374,7 @@ dependencies = [
name = "uucore-fuzz"
version = "0.0.0"
dependencies = [
"console",
"libc",
"libfuzzer-sys",
"rand 0.9.0",

View file

@ -8,6 +8,7 @@ edition = "2021"
cargo-fuzz = true
[dependencies]
console = "0.15.0"
libfuzzer-sys = "0.4.7"
libc = "0.2.153"
tempfile = "3.15.0"

View file

@ -3,11 +3,14 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use console::Style;
use libc::STDIN_FILENO;
use libc::{close, dup, dup2, pipe, STDERR_FILENO, STDOUT_FILENO};
use pretty_print::{
print_diff, print_end_with_status, print_or_empty, print_section, print_with_style,
};
use rand::prelude::IndexedRandom;
use rand::Rng;
use similar::TextDiff;
use std::env::temp_dir;
use std::ffi::OsString;
use std::fs::File;
@ -18,6 +21,8 @@ use std::sync::atomic::Ordering;
use std::sync::{atomic::AtomicBool, Once};
use std::{io, thread};
pub mod pretty_print;
/// Represents the result of running a command, including its standard output,
/// standard error, and exit code.
pub struct CommandResult {
@ -315,8 +320,8 @@ pub fn compare_result(
gnu_result: &CommandResult,
fail_on_stderr_diff: bool,
) {
println!("Test Type: {}", test_type);
println!("Input: {}", input);
print_section(format!("Compare result for: {} {}", test_type, input));
if let Some(pipe) = pipe_input {
println!("Pipe: {}", pipe);
}
@ -326,49 +331,58 @@ pub fn compare_result(
if rust_result.stdout.trim() != gnu_result.stdout.trim() {
discrepancies.push("stdout differs");
println!("Rust stdout: {}", rust_result.stdout);
println!("GNU stdout: {}", gnu_result.stdout);
println!("Rust stdout:",);
print_or_empty(rust_result.stdout.as_str());
println!("GNU stdout:",);
print_or_empty(gnu_result.stdout.as_ref());
print_diff(&rust_result.stdout, &gnu_result.stdout);
should_panic = true;
}
if rust_result.stderr.trim() != gnu_result.stderr.trim() {
discrepancies.push("stderr differs");
println!("Rust stderr: {}", rust_result.stderr);
println!("GNU stderr: {}", gnu_result.stderr);
println!("Rust stderr:",);
print_or_empty(rust_result.stderr.as_str());
println!("GNU stderr:",);
print_or_empty(gnu_result.stderr.as_str());
print_diff(&rust_result.stderr, &gnu_result.stderr);
if fail_on_stderr_diff {
should_panic = true;
}
}
if rust_result.exit_code != gnu_result.exit_code {
discrepancies.push("exit code differs");
println!("Rust exit code: {}", rust_result.exit_code);
println!("GNU exit code: {}", gnu_result.exit_code);
println!(
"Different exit code: (Rust: {}, GNU: {})",
rust_result.exit_code, gnu_result.exit_code
);
should_panic = true;
}
if discrepancies.is_empty() {
println!("All outputs and exit codes matched.");
print_end_with_status("Same behavior", true);
} else {
println!("Discrepancy detected: {}", discrepancies.join(", "));
print_with_style(
format!("Discrepancies detected: {}", discrepancies.join(", ")),
Style::new().red(),
);
if should_panic {
panic!("Test failed for {}: {}", test_type, input);
print_end_with_status(
format!("Test failed and will panic for: {} {}", test_type, input),
false,
);
panic!("Test failed for: {} {}", test_type, input);
} else {
println!(
"Test completed with discrepancies for {}: {}",
test_type, input
print_end_with_status(
format!(
"Test completed with discrepancies for: {} {}",
test_type, input
),
false,
);
}
}
}
/// When we have different outputs, print the diff
fn print_diff(rust_output: &str, gnu_output: &str) {
println!("Diff=");
let diff = TextDiff::from_lines(rust_output, gnu_output);
for change in diff.iter_all_changes() {
print!("{}{}", change.tag(), change);
}
println!();
}
@ -414,3 +428,10 @@ pub fn generate_random_file() -> Result<String, std::io::Error> {
Ok(file_path.to_str().unwrap().to_string())
}
pub fn replace_fuzz_binary_name(cmd: &str, result: &mut CommandResult) {
let fuzz_bin_name = format!("fuzz/target/x86_64-unknown-linux-gnu/release/fuzz_{cmd}");
result.stdout = result.stdout.replace(&fuzz_bin_name, cmd);
result.stderr = result.stderr.replace(&fuzz_bin_name, cmd);
}

View file

@ -0,0 +1,69 @@
// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use std::fmt;
use console::{style, Style};
use similar::TextDiff;
pub fn print_section<S: fmt::Display>(s: S) {
println!("{}", style(format!("=== {}", s)).bold());
}
pub fn print_subsection<S: fmt::Display>(s: S) {
println!("{}", style(format!("--- {}", s)).bright());
}
pub fn print_test_begin<S: fmt::Display>(msg: S) {
println!(
"{} {} {}",
style("===").bold(), // Kind of gray
style("TEST").black().on_yellow().bold(),
style(msg).bold()
);
}
pub fn print_end_with_status<S: fmt::Display>(msg: S, ok: bool) {
let ok = if ok {
style(" OK ").black().on_green().bold()
} else {
style(" KO ").black().on_red().bold()
};
println!(
"{} {} {}",
style("===").bold(), // Kind of gray
ok,
style(msg).bold()
);
}
pub fn print_or_empty(s: &str) {
let to_print = if s.is_empty() { "(empty)" } else { s };
println!("{}", style(to_print).dim());
}
pub fn print_with_style<S: fmt::Display>(msg: S, style: Style) {
println!("{}", style.apply_to(msg));
}
pub fn print_diff<'a, 'b>(got: &'a str, expected: &'b str) {
let diff = TextDiff::from_lines(got, expected);
print_subsection("START diff");
for change in diff.iter_all_changes() {
let (sign, style) = match change.tag() {
similar::ChangeTag::Equal => (" ", Style::new().dim()),
similar::ChangeTag::Delete => ("-", Style::new().red()),
similar::ChangeTag::Insert => ("+", Style::new().green()),
};
print!("{}{}", style.apply_to(sign).bold(), style.apply_to(change));
}
print_subsection("END diff");
println!();
}