#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod fuzzing_red_tests {
use crate::services::mutation::{
FuzzConfig, FuzzMutationStrategy, FuzzResult, InputGeneratorType, MutationEngine,
};
use std::time::Duration;
#[test]
fn red_fuzz_config_must_have_default() {
let config = FuzzConfig::default();
assert_eq!(config.iterations, 1000);
assert!(matches!(config.input_generator, InputGeneratorType::Random));
assert!(config.crash_detection);
assert_eq!(config.iteration_timeout, Duration::from_millis(100));
}
#[test]
fn red_fuzz_strategy_must_be_creatable() {
let engine = MutationEngine::default();
let config = FuzzConfig::default();
let strategy = FuzzMutationStrategy::new(engine, config);
assert_eq!(strategy.config().iterations, 1000);
}
#[test]
fn red_input_generator_types_must_exist() {
let random = InputGeneratorType::Random;
let grammar = InputGeneratorType::GrammarBased;
let mutation = InputGeneratorType::MutationBased;
let coverage = InputGeneratorType::CoverageGuided;
assert!(matches!(random, InputGeneratorType::Random));
assert!(matches!(grammar, InputGeneratorType::GrammarBased));
assert!(matches!(mutation, InputGeneratorType::MutationBased));
assert!(matches!(coverage, InputGeneratorType::CoverageGuided));
}
#[tokio::test]
async fn red_fuzz_result_must_track_crashes() {
let result = FuzzResult {
crashes: vec!["crash1".to_string(), "crash2".to_string()],
hangs: vec![],
coverage_increase: 0.0,
};
assert_eq!(result.crashes.len(), 2);
assert!(result.has_crashes());
}
#[tokio::test]
async fn red_fuzz_result_must_track_hangs() {
let result = FuzzResult {
crashes: vec![],
hangs: vec![vec![0x00, 0x01], vec![0xFF, 0xFE]],
coverage_increase: 0.0,
};
assert_eq!(result.hangs.len(), 2);
assert!(result.has_hangs());
}
#[tokio::test]
async fn red_fuzz_strategy_must_generate_random_inputs() {
let engine = MutationEngine::default();
let config = FuzzConfig {
iterations: 10,
input_generator: InputGeneratorType::Random,
crash_detection: true,
iteration_timeout: Duration::from_millis(100),
};
let strategy = FuzzMutationStrategy::new(engine, config);
let inputs = strategy.generate_inputs(10);
assert_eq!(inputs.len(), 10);
assert!(inputs.iter().any(|i| i != &inputs[0]));
}
#[tokio::test]
async fn red_fuzz_mutant_must_detect_crashes() {
let source = r#"
fn parse(input: &[u8]) -> Result<u32, String> {
if input.is_empty() {
return Err("empty".to_string());
}
// This mutant will cause out-of-bounds access
Ok(input[100] as u32)
}
"#;
let engine = MutationEngine::default();
let config = FuzzConfig {
iterations: 100,
input_generator: InputGeneratorType::Random,
crash_detection: true,
iteration_timeout: Duration::from_millis(100),
};
let strategy = FuzzMutationStrategy::new(engine, config);
let mutants = strategy
.engine()
.generate_mutants_from_source(std::path::Path::new("test.rs"), source)
.await;
assert!(mutants.is_ok());
let mutants = mutants.unwrap();
if !mutants.is_empty() {
let result = strategy.fuzz_mutant(&mutants[0]).await;
assert!(result.is_ok());
let _fuzz_result = result.unwrap();
}
}
#[tokio::test]
async fn red_fuzz_strategy_must_timeout_hanging_mutants() {
let source = r#"
fn infinite_loop(input: &[u8]) -> u32 {
loop {
// Infinite loop
}
}
"#;
let engine = MutationEngine::default();
let config = FuzzConfig {
iterations: 5,
input_generator: InputGeneratorType::Random,
crash_detection: true,
iteration_timeout: Duration::from_millis(50), };
let strategy = FuzzMutationStrategy::new(engine, config);
let mutants = strategy
.engine()
.generate_mutants_from_source(std::path::Path::new("test.rs"), source)
.await;
assert!(mutants.is_ok());
let mutants = mutants.unwrap();
if !mutants.is_empty() {
let result = strategy.fuzz_mutant(&mutants[0]).await;
assert!(result.is_ok());
let _fuzz_result = result.unwrap();
}
}
#[tokio::test]
async fn red_coverage_guided_fuzzing_must_increase_coverage() {
let source = r#"
fn complex_function(input: &[u8]) -> u32 {
if input.len() > 0 && input[0] == 0xAA {
if input.len() > 1 && input[1] == 0xBB {
if input.len() > 2 && input[2] == 0xCC {
return 1; // Hard to reach
}
}
}
return 0;
}
"#;
let engine = MutationEngine::default();
let config = FuzzConfig {
iterations: 1000,
input_generator: InputGeneratorType::CoverageGuided,
crash_detection: true,
iteration_timeout: Duration::from_millis(100),
};
let strategy = FuzzMutationStrategy::new(engine, config);
let mutants = strategy
.engine()
.generate_mutants_from_source(std::path::Path::new("test.rs"), source)
.await;
assert!(mutants.is_ok());
let mutants = mutants.unwrap();
if !mutants.is_empty() {
let result = strategy.fuzz_mutant(&mutants[0]).await;
assert!(result.is_ok());
let fuzz_result = result.unwrap();
assert!(fuzz_result.coverage_increase >= 0.0);
}
}
#[tokio::test]
async fn red_fuzz_mutation_report_must_aggregate_results() {
let engine = MutationEngine::default();
let config = FuzzConfig {
iterations: 10,
input_generator: InputGeneratorType::Random,
crash_detection: true,
iteration_timeout: Duration::from_millis(100),
};
let strategy = FuzzMutationStrategy::new(engine, config);
let source = r#"
fn add(a: i32, b: i32) -> i32 {
a + b
}
"#;
let report = strategy.execute_from_source(source).await;
assert!(report.is_ok());
let _report = report.unwrap();
}
#[tokio::test]
async fn red_fuzz_strategy_must_support_parallel_execution() {
let engine = MutationEngine::default();
let config = FuzzConfig {
iterations: 100,
input_generator: InputGeneratorType::Random,
crash_detection: true,
iteration_timeout: Duration::from_millis(100),
};
let strategy = FuzzMutationStrategy::new(engine, config);
let source = r#"
fn test1() -> u32 { 1 }
fn test2() -> u32 { 2 }
fn test3() -> u32 { 3 }
"#;
let start = std::time::Instant::now();
let report = strategy.execute_from_source_parallel(source, 4).await;
let elapsed = start.elapsed();
assert!(report.is_ok());
assert!(elapsed.as_secs() < 60);
}
#[test]
fn red_fuzz_config_must_validate_iterations() {
let config = FuzzConfig {
iterations: 0, input_generator: InputGeneratorType::Random,
crash_detection: true,
iteration_timeout: Duration::from_millis(100),
};
let result = config.validate();
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("iterations"));
}
#[test]
fn red_fuzz_config_must_validate_timeout() {
let config = FuzzConfig {
iterations: 100,
input_generator: InputGeneratorType::Random,
crash_detection: true,
iteration_timeout: Duration::from_millis(0), };
let result = config.validate();
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("timeout"));
}
#[tokio::test]
async fn red_fuzz_strategy_must_handle_compilation_errors() {
let engine = MutationEngine::default();
let config = FuzzConfig::default();
let strategy = FuzzMutationStrategy::new(engine, config);
let invalid_source = "this is not valid rust code";
let result = strategy.execute_from_source(invalid_source).await;
assert!(result.is_ok() || result.is_err());
}
#[tokio::test]
async fn red_grammar_based_generation_must_produce_valid_inputs() {
let config = FuzzConfig {
iterations: 10,
input_generator: InputGeneratorType::GrammarBased,
crash_detection: true,
iteration_timeout: Duration::from_millis(100),
};
let engine = MutationEngine::default();
let strategy = FuzzMutationStrategy::new(engine, config);
let inputs = strategy.generate_grammar_based_inputs(10, "json");
assert_eq!(inputs.len(), 10);
for input in inputs {
assert!(input.starts_with(b"{") || input.starts_with(b"[") || input.is_empty());
}
}
}