#![cfg(feature = "mutation-testing")]
use pmat::services::mutation::{MutationConfig, MutationEngine, RustAdapter};
use std::io::Write;
use std::sync::Arc;
use tempfile::NamedTempFile;
#[tokio::test]
async fn test_unary_operator_negation_detected() {
let source = r#"
fn validate(x: bool) -> bool {
!x
}
"#;
let mut temp_file = NamedTempFile::new().unwrap();
temp_file.write_all(source.as_bytes()).unwrap();
let temp_path = temp_file.path().to_path_buf();
let adapter = Arc::new(RustAdapter::new());
let config = MutationConfig::default();
let engine = MutationEngine::new(adapter, config);
let mutants = engine.generate_mutants_from_file(&temp_path).await.unwrap();
println!("\nUnary test generated {} mutants:", mutants.len());
for (i, mutant) in mutants.iter().enumerate() {
println!(
" {}. {:?} at line {}",
i + 1,
mutant.operator,
mutant.location.line
);
println!(
" Source: {}",
&mutant.mutated_source[..mutant.mutated_source.len().min(50)]
);
}
assert!(
!mutants.is_empty(),
"Expected at least 1 mutant for unary negation (!), got 0"
);
let has_unary_mutation = mutants
.iter()
.any(|m| format!("{:?}", m.operator).contains("Unary"));
assert!(
has_unary_mutation,
"Expected at least one UnaryReplacement mutant, got: {:?}",
mutants
.iter()
.map(|m| format!("{:?}", m.operator))
.collect::<Vec<_>>()
);
}
#[tokio::test]
async fn test_boolean_literal_mutation_detected() {
let source = r#"
fn is_valid() -> bool {
true
}
fn is_invalid() -> bool {
false
}
"#;
let mut temp_file = NamedTempFile::new().unwrap();
temp_file.write_all(source.as_bytes()).unwrap();
let temp_path = temp_file.path().to_path_buf();
let adapter = Arc::new(RustAdapter::new());
let config = MutationConfig::default();
let engine = MutationEngine::new(adapter, config);
let mutants = engine.generate_mutants_from_file(&temp_path).await.unwrap();
assert!(
mutants.len() >= 2,
"Expected at least 2 mutants for boolean literals, got {}",
mutants.len()
);
}
#[tokio::test]
async fn test_method_call_mutations_detected() {
let source = r#"
fn validate(s: &str) -> bool {
!s.is_empty()
}
"#;
let mut temp_file = NamedTempFile::new().unwrap();
temp_file.write_all(source.as_bytes()).unwrap();
let temp_path = temp_file.path().to_path_buf();
let adapter = Arc::new(RustAdapter::new());
let config = MutationConfig::default();
let engine = MutationEngine::new(adapter, config);
let mutants = engine.generate_mutants_from_file(&temp_path).await.unwrap();
assert!(
!mutants.is_empty(),
"Expected at least 1 mutant for !s.is_empty(), got 0"
);
}
#[tokio::test]
async fn test_pforge_validator_generates_mutants() {
let source = r#"
use std::collections::HashSet;
pub fn validate_config(tools: &[String]) -> Result<(), String> {
let mut tool_names = HashSet::new();
for tool in tools {
if !tool_names.insert(tool) {
return Err(format!("Duplicate tool: {}", tool));
}
}
Ok(())
}
fn validate_handler_path(path: &str) -> Result<(), String> {
if path.is_empty() {
return Err("empty path".to_string());
}
if !path.contains("::") {
return Err(format!("invalid format: {}", path));
}
Ok(())
}
"#;
let mut temp_file = NamedTempFile::new().unwrap();
temp_file.write_all(source.as_bytes()).unwrap();
let temp_path = temp_file.path().to_path_buf();
let adapter = Arc::new(RustAdapter::new());
let config = MutationConfig::default();
let engine = MutationEngine::new(adapter, config);
let mutants = engine.generate_mutants_from_file(&temp_path).await.unwrap();
assert!(
mutants.len() >= 2,
"Expected at least 2 mutants for pforge-style code (cargo-mutants found 4), got {}",
mutants.len()
);
println!("Generated {} mutants:", mutants.len());
for (i, mutant) in mutants.iter().enumerate() {
println!(
" {}. {:?} at line {}",
i + 1,
mutant.operator,
mutant.location.line
);
}
}
#[tokio::test]
async fn test_arithmetic_mutations_detected() {
let source = r#"
fn add(a: i32, b: i32) -> i32 {
a + b
}
fn subtract(a: i32, b: i32) -> i32 {
a - b
}
"#;
let mut temp_file = NamedTempFile::new().unwrap();
temp_file.write_all(source.as_bytes()).unwrap();
let temp_path = temp_file.path().to_path_buf();
let adapter = Arc::new(RustAdapter::new());
let config = MutationConfig::default();
let engine = MutationEngine::new(adapter, config);
let mutants = engine.generate_mutants_from_file(&temp_path).await.unwrap();
println!("\nArithmetic test generated {} mutants:", mutants.len());
for (i, mutant) in mutants.iter().enumerate() {
println!(
" {}. {:?} at line {}",
i + 1,
mutant.operator,
mutant.location.line
);
println!(
" Source: {}",
&mutant.mutated_source[..mutant.mutated_source.len().min(50)]
);
}
assert!(
mutants.len() >= 2,
"Expected at least 2 mutants for arithmetic operators, got {}",
mutants.len()
);
}