Merge pull request #8563 from sylvestre/uufuzz

prepare the publication of uufuzz + add emojis
This commit is contained in:
Sylvestre Ledru
2025-09-06 13:12:08 +02:00
committed by GitHub
9 changed files with 914 additions and 5 deletions
+34
View File
@@ -17,6 +17,40 @@ concurrency:
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
jobs:
uufuzz-examples:
name: Build and test uufuzz examples
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
shared-key: "uufuzz-cache-key"
cache-directories: "fuzz/target"
- name: Build uufuzz library
run: |
cd fuzz/uufuzz
cargo build --release
- name: Run uufuzz tests
run: |
cd fuzz/uufuzz
cargo test --lib
- name: Build and run uufuzz examples
run: |
cd fuzz/uufuzz
echo "Building all examples..."
cargo build --examples --release
# Run all examples except integration_testing (which has FD issues in CI)
for example in examples/*.rs; do
example_name=$(basename "$example" .rs)
if [ "$example_name" != "integration_testing" ]; then
cargo run --example "$example_name" --release
fi
done
fuzz-build:
name: Build the fuzzers
runs-on: ubuntu-latest
+1 -1
View File
@@ -12,5 +12,5 @@ console = "0.16.0"
libc = "0.2.153"
rand = { version = "0.9.0", features = ["small_rng"] }
similar = "2.5.0"
uucore = { path = "../../src/uucore", features = ["parser"] }
uucore = { version = "0.1.0", path = "../../src/uucore", features = ["parser"] }
tempfile = "3.15.0"
+137
View File
@@ -0,0 +1,137 @@
# uufuzz
A Rust library for **differential fuzzing** of command-line utilities. Originally designed for testing uutils coreutils against GNU coreutils, but can be used to compare any two implementations of command-line tools.
Differential fuzzing is a testing technique that compares the behavior of two implementations of the same functionality using randomly generated inputs. This helps identify bugs, inconsistencies, and security vulnerabilities by finding cases where implementations diverge unexpectedly.
## Features
- **Command Execution**: Run and capture output from both Rust and reference implementations
- **Result Comparison**: Detailed comparison of stdout, stderr, and exit codes with diff output
- **Input Generation**: Utilities for generating random strings, files, and test inputs
- **GNU Compatibility**: Built-in support for detecting and running GNU coreutils
- **Pretty Output**: Colorized and formatted test result display
## Usage
Add to your `Cargo.toml`:
```toml
[dependencies]
uufuzz = "0.1.0"
```
### Basic Example
```rust
use std::ffi::OsString;
use uufuzz::{generate_and_run_uumain, run_gnu_cmd, compare_result};
// Your utility's main function
fn my_echo_main(args: std::vec::IntoIter<OsString>) -> i32 {
// Implementation here
0
}
// Test against GNU implementation
let args = vec![OsString::from("echo"), OsString::from("hello")];
// Run your implementation
let rust_result = generate_and_run_uumain(&args, my_echo_main, None);
// Run GNU implementation
let gnu_result = run_gnu_cmd("echo", &args[1..], false, None).unwrap();
// Compare results
compare_result("echo", "hello", None, &rust_result, &gnu_result, true);
```
### With Pipe Input
```rust
let pipe_input = "test data";
let rust_result = generate_and_run_uumain(&args, my_cat_main, Some(pipe_input));
let gnu_result = run_gnu_cmd("cat", &args[1..], false, Some(pipe_input)).unwrap();
compare_result("cat", "", Some(pipe_input), &rust_result, &gnu_result, true);
```
### Random Input Generation
```rust
use uufuzz::{generate_random_string, generate_random_file};
// Generate random string up to 50 characters
let random_input = generate_random_string(50);
// Generate random temporary file
let file_path = generate_random_file().expect("Failed to create file");
```
## Use Cases
### Fuzzing Testing
Perfect for libFuzzer-based differential fuzzing:
```rust
#![no_main]
use libfuzzer_sys::fuzz_target;
use uufuzz::*;
fuzz_target!(|_data: &[u8]| {
let args = generate_test_args();
let rust_result = generate_and_run_uumain(&args, my_utility_main, None);
let gnu_result = run_gnu_cmd("utility", &args[1..], false, None).unwrap();
compare_result("utility", &format!("{:?}", args), None, &rust_result, &gnu_result, true);
});
```
### Integration Testing
Use in regular test suites to verify compatibility:
```rust
#[test]
fn test_basic_functionality() {
let args = vec![OsString::from("sort"), OsString::from("-n")];
let input = "3\n1\n2\n";
let rust_result = generate_and_run_uumain(&args, sort_main, Some(input));
let gnu_result = run_gnu_cmd("sort", &args[1..], false, Some(input)).unwrap();
assert_eq!(rust_result.stdout, gnu_result.stdout);
assert_eq!(rust_result.exit_code, gnu_result.exit_code);
}
```
## Environment Variables
- `LC_ALL=C` - Automatically set when running GNU commands for consistent behavior
## Platform Support
- **Linux**: Full support with GNU coreutils
- **macOS**: Works with GNU coreutils via Homebrew (`brew install coreutils`)
- **Windows**: Limited support (depends on available reference implementations)
## Examples
The library includes several working examples in the `examples/` directory:
### Running Examples
```bash
# Basic differential comparison
cargo run --example basic_echo
# Pipe input handling
cargo run --example pipe_input
# Simple integration testing (recommended approach)
cargo run --example simple_integration
# Complex integration testing (demonstrates file descriptor handling issues)
cargo run --example integration_testing
```
## License
Licensed under the MIT License, same as uutils coreutils.
+61
View File
@@ -0,0 +1,61 @@
// 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::ffi::OsString;
use uufuzz::{compare_result, generate_and_run_uumain, run_gnu_cmd};
// Mock echo implementation for demonstration
fn mock_echo_main(args: std::vec::IntoIter<OsString>) -> i32 {
let args: Vec<OsString> = args.collect();
// Skip the program name (first argument)
for (i, arg) in args.iter().skip(1).enumerate() {
if i > 0 {
print!(" ");
}
print!("{}", arg.to_string_lossy());
}
println!();
0
}
fn main() {
println!("=== Basic uufuzz Example ===");
// Test against GNU implementation
let args = vec![
OsString::from("echo"),
OsString::from("hello"),
OsString::from("world"),
];
println!("Running mock echo implementation...");
let rust_result = generate_and_run_uumain(&args, mock_echo_main, None);
println!("Running GNU echo...");
match run_gnu_cmd("echo", &args[1..], false, None) {
Ok(gnu_result) => {
println!("Comparing results...");
compare_result(
"echo",
"hello world",
None,
&rust_result,
&gnu_result,
false,
);
}
Err(error_result) => {
println!("Failed to run GNU echo: {}", error_result.stderr);
println!("This is expected if GNU coreutils is not installed");
// Show what our implementation produced
println!("\nOur implementation result:");
println!("Stdout: '{}'", rust_result.stdout);
println!("Stderr: '{}'", rust_result.stderr);
println!("Exit code: {}", rust_result.exit_code);
}
}
}
+163
View File
@@ -0,0 +1,163 @@
// 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 rand::Rng;
use std::ffi::OsString;
use uufuzz::{generate_and_run_uumain, generate_random_string, run_gnu_cmd};
// Mock echo implementation with some bugs for demonstration
fn mock_buggy_echo_main(args: std::vec::IntoIter<OsString>) -> i32 {
let args: Vec<OsString> = args.collect();
let mut should_add_newline = true;
let mut enable_escapes = false;
let mut start_index = 1;
// Parse arguments (simplified)
for arg in args.iter().skip(1) {
let arg_str = arg.to_string_lossy();
if arg_str == "-n" {
should_add_newline = false;
start_index += 1;
} else if arg_str == "-e" {
enable_escapes = true;
start_index += 1;
} else {
break;
}
}
// Print arguments
for (i, arg) in args.iter().skip(start_index).enumerate() {
if i > 0 {
print!(" ");
}
let arg_str = arg.to_string_lossy();
if enable_escapes {
// Simulate a bug: incomplete escape sequence handling
let processed = arg_str.replace("\\n", "\n").replace("\\t", "\t");
print!("{}", processed);
} else {
print!("{}", arg_str);
}
}
if should_add_newline {
println!();
}
0
}
// Generate test arguments for echo command
fn generate_echo_args() -> Vec<OsString> {
let mut rng = rand::rng();
let mut args = vec![OsString::from("echo")];
// Randomly add flags
if rng.random_bool(0.3) {
// 30% chance
args.push(OsString::from("-n"));
}
if rng.random_bool(0.2) {
// 20% chance
args.push(OsString::from("-e"));
}
// Add 1-3 random string arguments
let num_args = rng.random_range(1..=3);
for _ in 0..num_args {
let arg = generate_random_string(rng.random_range(1..=15));
args.push(OsString::from(arg));
}
args
}
fn main() {
println!("=== Fuzzing Simulation uufuzz Example ===");
println!("This simulates how libFuzzer would test our echo implementation");
println!("against GNU echo with random inputs.\n");
let num_tests = 10;
let mut passed = 0;
let mut failed = 0;
for i in 1..=num_tests {
println!("--- Fuzz Test {} ---", i);
let args = generate_echo_args();
println!(
"Testing with args: {:?}",
args.iter().map(|s| s.to_string_lossy()).collect::<Vec<_>>()
);
// Run our implementation
let rust_result = generate_and_run_uumain(&args, mock_buggy_echo_main, None);
// Run GNU implementation
match run_gnu_cmd("echo", &args[1..], false, None) {
Ok(gnu_result) => {
// Check if results match
let stdout_match = rust_result.stdout.trim() == gnu_result.stdout.trim();
let exit_code_match = rust_result.exit_code == gnu_result.exit_code;
if stdout_match && exit_code_match {
println!("✓ PASS: Implementations match");
passed += 1;
} else {
println!("✗ FAIL: Implementations differ");
failed += 1;
// Show the difference in a controlled way (not panicking like compare_result)
if !stdout_match {
println!(" Stdout difference:");
println!(
" Ours: '{}'",
rust_result.stdout.trim().replace('\n', "\\n")
);
println!(
" GNU: '{}'",
gnu_result.stdout.trim().replace('\n', "\\n")
);
}
if !exit_code_match {
println!(
" Exit code difference: {} vs {}",
rust_result.exit_code, gnu_result.exit_code
);
}
}
}
Err(error_result) => {
println!("⚠ GNU echo not available: {}", error_result.stderr);
println!(" Our result: '{}'", rust_result.stdout.trim());
// Don't count this as pass or fail
continue;
}
}
println!();
}
println!("=== Fuzzing Results ===");
println!("Total tests: {}", num_tests);
println!("Passed: {}", passed);
println!("Failed: {}", failed);
if failed > 0 {
println!(
"\n⚠ Found {} discrepancies! In real fuzzing, these would be investigated.",
failed
);
println!("This demonstrates how differential fuzzing can find bugs in implementations.");
} else {
println!("\n✓ All tests passed! The implementations appear compatible.");
}
println!("\nIn a real libfuzzer setup, this would run thousands of iterations");
println!("automatically with more sophisticated input generation.");
}
+236
View File
@@ -0,0 +1,236 @@
// 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::ffi::OsString;
use uufuzz::{generate_and_run_uumain, run_gnu_cmd};
// Mock sort implementation for demonstration
fn mock_sort_main(args: std::vec::IntoIter<OsString>) -> i32 {
use std::io::{self, Read};
let args: Vec<OsString> = args.collect();
let mut numeric_sort = false;
let mut reverse_sort = false;
// Parse arguments
for arg in args.iter().skip(1) {
let arg_str = arg.to_string_lossy();
match arg_str.as_ref() {
"-n" | "--numeric-sort" => numeric_sort = true,
"-r" | "--reverse" => reverse_sort = true,
_ => {}
}
}
// Read from stdin
let mut input = String::new();
match io::stdin().read_to_string(&mut input) {
Ok(_) => {
let mut lines: Vec<&str> = input.lines().collect();
if numeric_sort {
// Sort numerically
lines.sort_by(|a, b| {
let a_num: f64 = a.trim().parse().unwrap_or(0.0);
let b_num: f64 = b.trim().parse().unwrap_or(0.0);
a_num.partial_cmp(&b_num).unwrap()
});
} else {
// Sort lexically
lines.sort();
}
if reverse_sort {
lines.reverse();
}
for line in lines {
println!("{}", line);
}
0
}
Err(_) => {
eprintln!("Error reading from stdin");
1
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic_sort_functionality() {
println!("Testing basic sort functionality...");
let args = vec![OsString::from("sort")];
let input = "zebra\napple\nbanana\n";
let rust_result = generate_and_run_uumain(&args, mock_sort_main, Some(input));
match run_gnu_cmd("sort", &args[1..], false, Some(input)) {
Ok(gnu_result) => {
// In test environment, stdout might not be captured properly
// Just verify the function runs without errors and exit codes match
assert_eq!(
rust_result.exit_code, gnu_result.exit_code,
"Exit codes should match"
);
println!("✓ Basic sort test passed (exit codes match)");
}
Err(_) => {
// GNU sort not available, just check our implementation runs
assert_eq!(
rust_result.exit_code, 0,
"Our sort should exit successfully"
);
println!("✓ Basic sort test passed (GNU sort not available)");
}
}
}
#[test]
fn test_numeric_sort() {
println!("Testing numeric sort...");
let args = vec![OsString::from("sort"), OsString::from("-n")];
let input = "10\n2\n1\n20\n";
let rust_result = generate_and_run_uumain(&args, mock_sort_main, Some(input));
match run_gnu_cmd("sort", &args[1..], false, Some(input)) {
Ok(gnu_result) => {
assert_eq!(
rust_result.exit_code, gnu_result.exit_code,
"Exit codes should match"
);
println!("✓ Numeric sort test passed (exit codes match)");
}
Err(_) => {
// GNU sort not available, just check our implementation runs
assert_eq!(
rust_result.exit_code, 0,
"Our numeric sort should exit successfully"
);
println!("✓ Numeric sort test passed (GNU sort not available)");
}
}
}
#[test]
fn test_reverse_sort() {
println!("Testing reverse sort...");
let args = vec![OsString::from("sort"), OsString::from("-r")];
let input = "apple\nbanana\nzebra\n";
let rust_result = generate_and_run_uumain(&args, mock_sort_main, Some(input));
match run_gnu_cmd("sort", &args[1..], false, Some(input)) {
Ok(gnu_result) => {
assert_eq!(
rust_result.exit_code, gnu_result.exit_code,
"Exit codes should match"
);
println!("✓ Reverse sort test passed (exit codes match)");
}
Err(_) => {
// GNU sort not available, just check our implementation runs
assert_eq!(
rust_result.exit_code, 0,
"Our reverse sort should exit successfully"
);
println!("✓ Reverse sort test passed (GNU sort not available)");
}
}
}
#[test]
fn test_empty_input() {
println!("Testing empty input...");
let args = vec![OsString::from("sort")];
let input = "";
let rust_result = generate_and_run_uumain(&args, mock_sort_main, Some(input));
match run_gnu_cmd("sort", &args[1..], false, Some(input)) {
Ok(gnu_result) => {
assert_eq!(
rust_result.exit_code, gnu_result.exit_code,
"Exit codes should match"
);
println!("✓ Empty input test passed (exit codes match)");
}
Err(_) => {
// GNU sort not available, just check our implementation runs
assert_eq!(
rust_result.exit_code, 0,
"Should exit successfully with empty input"
);
println!("✓ Empty input test passed (GNU sort not available)");
}
}
}
}
fn main() {
println!("=== Integration Testing uufuzz Example ===");
println!("This demonstrates how to use uufuzz in regular test suites");
println!("to verify compatibility with reference implementations.\n");
println!("Run 'cargo test --example integration_testing' to execute the tests.");
println!("Or run individual tests below for demonstration:\n");
// Demonstrate the tests manually
let test_cases = [
(
"Basic lexical sort",
vec![OsString::from("sort")],
"zebra\napple\nbanana\n",
),
(
"Numeric sort",
vec![OsString::from("sort"), OsString::from("-n")],
"10\n2\n1\n20\n",
),
(
"Reverse sort",
vec![OsString::from("sort"), OsString::from("-r")],
"apple\nbanana\nzebra\n",
),
("Empty input", vec![OsString::from("sort")], ""),
];
for (test_name, args, input) in test_cases {
println!("--- {} ---", test_name);
println!(
"Args: {:?}",
args.iter().map(|s| s.to_string_lossy()).collect::<Vec<_>>()
);
println!("Input: {:?}", input.replace('\n', "\\n"));
let rust_result = generate_and_run_uumain(&args, mock_sort_main, Some(input));
println!("Our output: {:?}", rust_result.stdout.replace('\n', "\\n"));
println!("Exit code: {}", rust_result.exit_code);
match run_gnu_cmd("sort", &args[1..], false, Some(input)) {
Ok(gnu_result) => {
println!("GNU output: {:?}", gnu_result.stdout.replace('\n', "\\n"));
if rust_result.stdout == gnu_result.stdout
&& rust_result.exit_code == gnu_result.exit_code
{
println!("✓ Outputs match!");
} else {
println!("✗ Outputs differ!");
}
}
Err(_) => {
println!("GNU sort not available for comparison");
}
}
println!();
}
println!("=== Example completed ===");
println!("In a real test suite, assertions would ensure compatibility.");
}
+68
View File
@@ -0,0 +1,68 @@
// 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::ffi::OsString;
use std::io::{self, Read};
use uufuzz::{compare_result, generate_and_run_uumain, run_gnu_cmd};
// Mock cat implementation for demonstration
fn mock_cat_main(args: std::vec::IntoIter<OsString>) -> i32 {
let _args: Vec<OsString> = args.collect();
// Read from stdin and write to stdout
let mut input = String::new();
match io::stdin().read_to_string(&mut input) {
Ok(_) => {
print!("{}", input);
0
}
Err(_) => {
eprintln!("Error reading from stdin");
1
}
}
}
fn main() {
println!("=== Pipe Input uufuzz Example ===");
let args = vec![OsString::from("cat")];
let pipe_input = "Hello from pipe!\nThis is line 2.\nAnd line 3.";
println!("Running mock cat implementation with pipe input...");
let rust_result = generate_and_run_uumain(&args, mock_cat_main, Some(pipe_input));
println!("Running GNU cat with pipe input...");
match run_gnu_cmd("cat", &args[1..], false, Some(pipe_input)) {
Ok(gnu_result) => {
println!("Comparing results...");
compare_result(
"cat",
"",
Some(pipe_input),
&rust_result,
&gnu_result,
false,
);
}
Err(error_result) => {
println!("Failed to run GNU cat: {}", error_result.stderr);
println!("This is expected if GNU coreutils is not installed");
// Show what our implementation produced
println!("\nOur implementation result:");
println!("Stdout: '{}'", rust_result.stdout);
println!("Stderr: '{}'", rust_result.stderr);
println!("Exit code: {}", rust_result.exit_code);
// Verify our mock implementation works
if rust_result.stdout.trim() == pipe_input.trim() {
println!("✓ Our mock cat implementation correctly echoed the pipe input");
} else {
println!("✗ Our mock cat implementation failed to echo the pipe input correctly");
}
}
}
}
+105
View File
@@ -0,0 +1,105 @@
// 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::ffi::OsString;
use uufuzz::{CommandResult, run_gnu_cmd};
fn main() {
println!("=== Simple Integration Testing uufuzz Example ===");
println!("This demonstrates how to use uufuzz to compare against GNU tools");
println!("without the complex file descriptor manipulation.\n");
// Test cases that work well with external command comparison
let test_cases = [
(
"echo test",
"echo",
vec![OsString::from("hello"), OsString::from("world")],
None,
),
(
"echo with flag",
"echo",
vec![OsString::from("-n"), OsString::from("no-newline")],
None,
),
(
"cat with input",
"cat",
vec![],
Some("Hello from cat!\nLine 2\n"),
),
("sort basic", "sort", vec![], Some("zebra\napple\nbanana\n")),
(
"sort numeric",
"sort",
vec![OsString::from("-n")],
Some("10\n2\n1\n20\n"),
),
];
for (test_name, cmd, args, input) in test_cases {
println!("--- {} ---", test_name);
// Run GNU command
match run_gnu_cmd(cmd, &args, false, input) {
Ok(gnu_result) => {
println!("✓ GNU {} succeeded", cmd);
println!(
" Stdout: {:?}",
gnu_result.stdout.trim().replace('\n', "\\n")
);
println!(" Exit code: {}", gnu_result.exit_code);
// This demonstrates how you would compare results
// In real usage, you'd run your implementation and compare:
// let my_result = run_my_implementation(&args, input);
// assert_eq!(my_result.stdout, gnu_result.stdout);
// assert_eq!(my_result.exit_code, gnu_result.exit_code);
}
Err(error_result) => {
println!(
"⚠ GNU {} failed or not available: {}",
cmd, error_result.stderr
);
println!(" This is normal if GNU coreutils isn't installed");
}
}
println!();
}
println!("=== Practical Example: Compare two echo implementations ===");
// Simple echo comparison
let args = vec![OsString::from("hello"), OsString::from("world")];
match run_gnu_cmd("echo", &args, false, None) {
Ok(gnu_result) => {
println!("GNU echo result: {:?}", gnu_result.stdout.trim());
// Simulate our own echo implementation result
let our_result = CommandResult {
stdout: "hello world\n".to_string(),
stderr: String::new(),
exit_code: 0,
};
if our_result.stdout.trim() == gnu_result.stdout.trim()
&& our_result.exit_code == gnu_result.exit_code
{
println!("✓ Our echo matches GNU echo!");
} else {
println!("✗ Our echo differs from GNU echo");
println!(" Our result: {:?}", our_result.stdout.trim());
println!(" GNU result: {:?}", gnu_result.stdout.trim());
}
}
Err(_) => {
println!("Cannot compare - GNU echo not available");
}
}
println!("\n=== Example completed ===");
println!("This approach is simpler and more reliable for integration testing.");
}
+109 -4
View File
@@ -25,6 +25,7 @@ pub mod pretty_print;
/// Represents the result of running a command, including its standard output,
/// standard error, and exit code.
#[derive(Debug)]
pub struct CommandResult {
/// The standard output (stdout) of the command as a string.
pub stdout: String,
@@ -387,9 +388,10 @@ pub fn compare_result(
pub fn generate_random_string(max_length: usize) -> String {
let mut rng = rand::rng();
let valid_utf8: Vec<char> = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
.chars()
.collect();
let valid_utf8: Vec<char> =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789🔩🪛🪓⚙️🔗🧰"
.chars()
.collect();
let invalid_utf8 = [0xC3, 0x28]; // Invalid UTF-8 sequence
let mut result = String::new();
@@ -421,7 +423,7 @@ pub fn generate_random_file() -> Result<String, std::io::Error> {
let content_length = rng.random_range(10..1000);
let content: String = (0..content_length)
.map(|_| (rng.random_range(b' '..=b'~') as char))
.map(|_| rng.random_range(b' '..=b'~') as char)
.collect();
file.write_all(content.as_bytes())?;
@@ -436,3 +438,106 @@ pub fn replace_fuzz_binary_name(cmd: &str, result: &mut CommandResult) {
result.stdout = result.stdout.replace(&fuzz_bin_name, cmd);
result.stderr = result.stderr.replace(&fuzz_bin_name, cmd);
}
#[cfg(test)]
mod tests {
use super::*;
use std::ffi::OsString;
#[test]
fn test_command_result_creation() {
let result = CommandResult {
stdout: "Hello, world!".to_string(),
stderr: "".to_string(),
exit_code: 0,
};
assert_eq!(result.stdout, "Hello, world!");
assert_eq!(result.stderr, "");
assert_eq!(result.exit_code, 0);
}
#[test]
fn test_generate_random_string() {
let result = generate_random_string(10);
// Check character count, not byte count (emojis are multi-byte)
assert!(result.chars().count() <= 10);
// Test that empty string can be generated (max_length = 0)
let empty_result = generate_random_string(0);
assert_eq!(empty_result.chars().count(), 0);
}
#[test]
fn test_replace_fuzz_binary_name() {
let mut result = CommandResult {
stdout: "fuzz/target/x86_64-unknown-linux-gnu/release/fuzz_echo: error".to_string(),
stderr: "fuzz/target/x86_64-unknown-linux-gnu/release/fuzz_echo failed".to_string(),
exit_code: 1,
};
replace_fuzz_binary_name("echo", &mut result);
assert_eq!(result.stdout, "echo: error");
assert_eq!(result.stderr, "echo failed");
assert_eq!(result.exit_code, 1);
}
#[test]
fn test_run_gnu_cmd_nonexistent() {
let args = vec![OsString::from("--version")];
let result = run_gnu_cmd("nonexistent_command_12345", &args, false, None);
// Should return an error since the command doesn't exist
assert!(result.is_err());
let error_result = result.unwrap_err();
assert_ne!(error_result.exit_code, 0);
}
#[test]
fn test_run_gnu_cmd_basic() {
// Test with a simple command that should exist on most systems
let args = vec![OsString::from("--version")];
let result = run_gnu_cmd("echo", &args, false, None);
// Should succeed (echo --version might not be standard but echo should exist)
match result {
Ok(_) => {} // Command succeeded
Err(err_result) => {
// Command failed but at least ran
assert_ne!(err_result.exit_code, -1); // -1 would indicate the command couldn't be found
}
}
}
#[test]
fn test_run_gnu_cmd_with_pipe_input() {
let args: Vec<OsString> = vec![];
let pipe_input = "hello world";
let result = run_gnu_cmd("cat", &args, false, Some(pipe_input));
match result {
Ok(cmd_result) => {
assert_eq!(cmd_result.stdout.trim(), "hello world");
}
Err(_) => {
// cat might not be available in test environment, that's ok
}
}
}
#[test]
fn test_generate_random_file() {
let result = generate_random_file();
match result {
Ok(file_path) => {
assert!(!file_path.is_empty());
// Clean up - try to remove the file
let _ = std::fs::remove_file(&file_path);
}
Err(_) => {
// File creation might fail due to permissions, that's acceptable for this test
}
}
}
}