mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
uufuzz: add examples and them in the CI
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.");
|
||||
}
|
||||
@@ -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.");
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.");
|
||||
}
|
||||
Reference in New Issue
Block a user