#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::*;
use crate::services::mutation::types::{
Mutant, MutantStatus, MutationOperator, SourceLocation,
};
use tempfile::TempDir;
fn create_temp_rust_file() -> (TempDir, std::path::PathBuf) {
let temp = TempDir::new().unwrap();
let file_path = temp.path().join("test.rs");
std::fs::write(
&file_path,
r#"fn main() {
let x = 5;
let y = 10;
println!("{}", x + y);
}
fn add(a: i32, b: i32) -> i32 {
a + b
}
"#,
)
.unwrap();
(temp, file_path)
}
fn create_test_mutant(file_path: &Path, line: usize) -> Mutant {
Mutant {
id: format!("mutant_{}", line),
original_file: file_path.to_path_buf(),
location: SourceLocation {
line,
column: 1,
end_line: line,
end_column: 10,
},
operator: MutationOperator::ArithmeticReplace,
original_source: "a + b".to_string(),
mutated_source: "a - b".to_string(),
}
}
fn create_test_mutation_result(file_path: &Path, status: MutantStatus) -> MutationResult {
MutationResult {
mutant: create_test_mutant(file_path, 8),
status,
execution_time_ms: 100,
test_output: Some("test output".to_string()),
}
}
#[test]
fn test_extract_code_snippet_success() {
let (temp, file_path) = create_temp_rust_file();
let location = SourceLocation {
line: 1,
column: 1,
end_line: 2,
end_column: 10,
};
let snippet = extract_code_snippet(&file_path, &location).unwrap();
assert!(snippet.contains("fn main()"));
drop(temp);
}
#[test]
fn test_extract_code_snippet_single_line() {
let (temp, file_path) = create_temp_rust_file();
let location = SourceLocation {
line: 7,
column: 1,
end_line: 7,
end_column: 30,
};
let snippet = extract_code_snippet(&file_path, &location).unwrap();
assert!(!snippet.is_empty());
drop(temp);
}
#[test]
fn test_extract_code_snippet_out_of_bounds() {
let (temp, file_path) = create_temp_rust_file();
let location = SourceLocation {
line: 1000,
column: 1,
end_line: 1001,
end_column: 10,
};
let snippet = extract_code_snippet(&file_path, &location).unwrap();
assert!(snippet.contains("<code location out of bounds>"));
drop(temp);
}
#[test]
fn test_extract_code_snippet_file_not_found() {
let location = SourceLocation {
line: 1,
column: 1,
end_line: 2,
end_column: 10,
};
let result = extract_code_snippet(Path::new("/nonexistent/file.rs"), &location);
assert!(result.is_err());
}
#[test]
fn test_print_progress_zero_total() {
print_progress(0, 0);
}
#[test]
fn test_print_progress_partial() {
print_progress(5, 10);
}
#[test]
fn test_print_progress_complete() {
print_progress(10, 10);
}
#[test]
fn test_mutation_score_from_empty_results() {
let results: Vec<MutationResult> = vec![];
let score = MutationScore::from_results(&results);
assert_eq!(score.total, 0);
assert_eq!(score.killed, 0);
}
#[test]
fn test_mutation_score_from_results_killed() {
let (temp, file_path) = create_temp_rust_file();
let results = vec![create_test_mutation_result(
&file_path,
MutantStatus::Killed,
)];
let score = MutationScore::from_results(&results);
assert_eq!(score.total, 1);
assert_eq!(score.killed, 1);
assert_eq!(score.survived, 0);
drop(temp);
}
#[test]
fn test_mutation_score_from_results_survived() {
let (temp, file_path) = create_temp_rust_file();
let results = vec![create_test_mutation_result(
&file_path,
MutantStatus::Survived,
)];
let score = MutationScore::from_results(&results);
assert_eq!(score.survived, 1);
drop(temp);
}
#[test]
fn test_mutation_score_from_results_mixed() {
let (temp, file_path) = create_temp_rust_file();
let results = vec![
create_test_mutation_result(&file_path, MutantStatus::Killed),
create_test_mutation_result(&file_path, MutantStatus::Survived),
create_test_mutation_result(&file_path, MutantStatus::CompileError),
create_test_mutation_result(&file_path, MutantStatus::Timeout),
];
let score = MutationScore::from_results(&results);
assert_eq!(score.total, 4);
assert_eq!(score.killed, 1);
assert_eq!(score.survived, 1);
assert_eq!(score.compile_errors, 1);
assert_eq!(score.timeouts, 1);
drop(temp);
}
#[test]
fn test_enhanced_mutation_result_serialization() {
let (temp, file_path) = create_temp_rust_file();
let result = create_test_mutation_result(&file_path, MutantStatus::Killed);
let enhanced = EnhancedMutationResult {
result,
original_code_snippet: Some("a + b".to_string()),
mutated_code_snippet: Some("a - b".to_string()),
};
let json = serde_json::to_string(&enhanced).unwrap();
assert!(json.contains("a + b"));
assert!(json.contains("a - b"));
drop(temp);
}
#[test]
fn test_mutation_test_output_serialization() {
let score = MutationScore {
total: 10,
killed: 8,
survived: 2,
compile_errors: 0,
timeouts: 0,
equivalent: 0,
score: 0.8,
};
let output = MutationTestOutput {
score,
results: vec![],
};
let json = serde_json::to_string(&output).unwrap();
assert!(json.contains("\"total\":10"));
assert!(json.contains("\"killed\":8"));
assert!(json.contains("\"score\":0.8"));
}
#[test]
fn test_source_location_creation() {
let location = SourceLocation {
line: 10,
column: 5,
end_line: 12,
end_column: 20,
};
assert_eq!(location.line, 10);
assert_eq!(location.column, 5);
assert_eq!(location.end_line, 12);
assert_eq!(location.end_column, 20);
}
#[test]
fn test_mutant_creation() {
let (temp, file_path) = create_temp_rust_file();
let mutant = create_test_mutant(&file_path, 8);
assert_eq!(mutant.original_source, "a + b");
assert_eq!(mutant.mutated_source, "a - b");
assert_eq!(mutant.operator, MutationOperator::ArithmeticReplace);
drop(temp);
}
#[test]
fn test_mutation_result_creation() {
let (temp, file_path) = create_temp_rust_file();
let result = create_test_mutation_result(&file_path, MutantStatus::Killed);
assert_eq!(result.status, MutantStatus::Killed);
assert_eq!(result.execution_time_ms, 100);
assert!(result.test_output.is_some());
drop(temp);
}
#[test]
fn test_mutation_result_serialization() {
let (temp, file_path) = create_temp_rust_file();
let result = create_test_mutation_result(&file_path, MutantStatus::Survived);
let json = serde_json::to_string(&result).unwrap();
assert!(json.contains("Survived"));
assert!(json.contains("execution_time_ms"));
drop(temp);
}
#[test]
fn test_filter_failures_only_keeps_survived() {
let (temp, file_path) = create_temp_rust_file();
let results = vec![
create_test_mutation_result(&file_path, MutantStatus::Killed),
create_test_mutation_result(&file_path, MutantStatus::Survived),
];
let filtered: Vec<_> = results
.iter()
.filter(|r| {
matches!(
r.status,
MutantStatus::Survived | MutantStatus::CompileError | MutantStatus::Timeout
)
})
.collect();
assert_eq!(filtered.len(), 1);
assert_eq!(filtered[0].status, MutantStatus::Survived);
drop(temp);
}
#[test]
fn test_filter_failures_only_keeps_compile_error() {
let (temp, file_path) = create_temp_rust_file();
let results = vec![
create_test_mutation_result(&file_path, MutantStatus::Killed),
create_test_mutation_result(&file_path, MutantStatus::CompileError),
];
let filtered: Vec<_> = results
.iter()
.filter(|r| {
matches!(
r.status,
MutantStatus::Survived | MutantStatus::CompileError | MutantStatus::Timeout
)
})
.collect();
assert_eq!(filtered.len(), 1);
assert_eq!(filtered[0].status, MutantStatus::CompileError);
drop(temp);
}
#[test]
fn test_filter_failures_only_keeps_timeout() {
let (temp, file_path) = create_temp_rust_file();
let results = vec![
create_test_mutation_result(&file_path, MutantStatus::Killed),
create_test_mutation_result(&file_path, MutantStatus::Timeout),
];
let filtered: Vec<_> = results
.iter()
.filter(|r| {
matches!(
r.status,
MutantStatus::Survived | MutantStatus::CompileError | MutantStatus::Timeout
)
})
.collect();
assert_eq!(filtered.len(), 1);
assert_eq!(filtered[0].status, MutantStatus::Timeout);
drop(temp);
}
#[test]
fn test_mutation_operator_equality() {
assert_eq!(
MutationOperator::ArithmeticReplace,
MutationOperator::ArithmeticReplace
);
assert_ne!(
MutationOperator::ArithmeticReplace,
MutationOperator::ComparisonReplace
);
}
#[test]
fn test_mutant_status_equality() {
assert_eq!(MutantStatus::Killed, MutantStatus::Killed);
assert_ne!(MutantStatus::Killed, MutantStatus::Survived);
}
}