#![cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::super::predictor::SurvivabilityPredictor;
use super::super::types::TrainingData;
use crate::services::mutation::{Mutant, MutantStatus, MutationOperatorType, SourceLocation};
fn create_test_mutant(source: &str, operator: MutationOperatorType) -> Mutant {
Mutant {
id: "test".to_string(),
original_file: std::path::PathBuf::from("test.rs"),
mutated_source: source.to_string(),
location: SourceLocation {
line: 1,
column: 1,
end_line: 1,
end_column: 10,
},
operator,
hash: "hash".to_string(),
status: MutantStatus::Pending,
}
}
fn create_training_data(count: usize, kill_rate: f64) -> Vec<TrainingData> {
(0..count)
.map(|i| {
let mutant = create_test_mutant(
&format!("fn test{}() {{ x + y }}", i),
MutationOperatorType::ArithmeticReplacement,
);
TrainingData {
mutant,
was_killed: (i as f64 / count as f64) < kill_rate,
test_failures: if (i as f64 / count as f64) < kill_rate {
vec!["test".to_string()]
} else {
vec![]
},
execution_time_ms: 100,
}
})
.collect()
}
#[test]
fn test_predictor_new() {
let predictor = SurvivabilityPredictor::new();
assert!(!predictor.is_trained());
}
#[test]
fn test_predictor_default() {
let predictor = SurvivabilityPredictor::default();
assert!(!predictor.is_trained());
}
#[test]
fn test_predictor_train_empty_fails() {
let mut predictor = SurvivabilityPredictor::new();
let result = predictor.train(&[]);
assert!(result.is_err());
}
#[test]
fn test_predictor_predict_untrained_fails() {
let predictor = SurvivabilityPredictor::new();
let mutant = create_test_mutant("x + y", MutationOperatorType::ArithmeticReplacement);
let result = predictor.predict(&mutant);
assert!(result.is_err());
}
#[test]
fn test_predictor_feature_importance_untrained_fails() {
let predictor = SurvivabilityPredictor::new();
let result = predictor.feature_importance();
assert!(result.is_err());
}
#[test]
fn test_predictor_train_small_sample() {
let mut predictor = SurvivabilityPredictor::new();
let data = create_training_data(5, 0.5);
let result = predictor.train(&data);
assert!(result.is_ok());
assert!(predictor.is_trained());
}
#[test]
fn test_predictor_train_and_predict() {
let mut predictor = SurvivabilityPredictor::new();
let data = create_training_data(30, 0.6);
let result = predictor.train(&data);
assert!(result.is_ok());
let mutant = create_test_mutant("x + y", MutationOperatorType::ArithmeticReplacement);
let prediction = predictor.predict(&mutant);
assert!(prediction.is_ok());
let pred = prediction.unwrap();
assert!(pred.kill_probability >= 0.0 && pred.kill_probability <= 1.0);
assert!(pred.confidence >= 0.0 && pred.confidence <= 1.0);
}
#[test]
fn test_predictor_predict_with_explanation() {
let mut predictor = SurvivabilityPredictor::new();
let data = create_training_data(20, 0.7);
predictor.train(&data).unwrap();
let mutant = create_test_mutant("x + y", MutationOperatorType::ArithmeticReplacement);
let result = predictor.predict_with_explanation(&mutant);
assert!(result.is_ok());
let (pred, explanation) = result.unwrap();
assert!(pred.kill_probability >= 0.0);
assert!(!explanation.is_empty());
assert!(explanation.contains("Kill probability"));
}
#[test]
fn test_predictor_prioritize_mutants() {
let mut predictor = SurvivabilityPredictor::new();
let data = create_training_data(20, 0.6);
predictor.train(&data).unwrap();
let mutants = vec![
create_test_mutant("simple", MutationOperatorType::ArithmeticReplacement),
create_test_mutant(
"if x > 0 { for i in 0..10 { y += i; } }",
MutationOperatorType::ConditionalReplacement,
),
];
let result = predictor.prioritize_mutants(&mutants);
assert!(result.is_ok());
let prioritized = result.unwrap();
assert_eq!(prioritized.len(), 2);
assert!(prioritized[0].1.kill_probability >= prioritized[1].1.kill_probability);
}
#[test]
fn test_predictor_update() {
let mut predictor = SurvivabilityPredictor::new();
let initial_data = create_training_data(20, 0.5);
predictor.train(&initial_data).unwrap();
let new_data = create_training_data(5, 0.8);
let result = predictor.update(&new_data);
assert!(result.is_ok());
}
#[test]
fn test_predictor_update_untrained() {
let mut predictor = SurvivabilityPredictor::new();
let data = create_training_data(20, 0.5);
let result = predictor.update(&data);
assert!(result.is_ok());
assert!(predictor.is_trained());
}
#[test]
fn test_predictor_feature_importance() {
let mut predictor = SurvivabilityPredictor::new();
let data = create_training_data(20, 0.5);
predictor.train(&data).unwrap();
let importance = predictor.feature_importance();
assert!(importance.is_ok());
let imp = importance.unwrap();
assert!(!imp.is_empty());
for (_, value) in &imp {
assert!(*value >= 0.0);
}
}
#[test]
fn test_predictor_cross_validate_empty_fails() {
let predictor = SurvivabilityPredictor::new();
let result = predictor.cross_validate(&[], 5);
assert!(result.is_err());
}
#[test]
fn test_predictor_cross_validate_insufficient_folds() {
let predictor = SurvivabilityPredictor::new();
let data = create_training_data(20, 0.5);
let result = predictor.cross_validate(&data, 1);
assert!(result.is_err());
}
#[test]
fn test_predictor_cross_validate() {
let predictor = SurvivabilityPredictor::new();
let data = create_training_data(100, 0.5);
let result = predictor.cross_validate(&data, 5);
assert!(result.is_ok());
let accuracy = result.unwrap();
assert!(accuracy >= 0.0 && accuracy <= 1.0);
}
#[test]
fn test_predictor_save_and_load() {
use tempfile::tempdir;
let mut predictor = SurvivabilityPredictor::new();
let data = create_training_data(20, 0.5);
predictor.train(&data).unwrap();
let dir = tempdir().unwrap();
let path = dir.path().join("model.bin");
let save_result = predictor.save(&path);
assert!(save_result.is_ok());
let load_result = SurvivabilityPredictor::load(&path);
assert!(load_result.is_ok());
let loaded = load_result.unwrap();
assert!(loaded.is_trained());
}
#[test]
fn test_predictor_different_operators() {
let mut predictor = SurvivabilityPredictor::new();
let data: Vec<TrainingData> = vec![
MutationOperatorType::ArithmeticReplacement,
MutationOperatorType::RelationalReplacement,
MutationOperatorType::ConditionalReplacement,
MutationOperatorType::StatementDeletion,
]
.iter()
.enumerate()
.flat_map(|(i, op)| {
(0..5).map(move |j| TrainingData {
mutant: create_test_mutant(&format!("code_{}{}", i, j), op.clone()),
was_killed: j % 2 == 0,
test_failures: vec![],
execution_time_ms: 100,
})
})
.collect();
predictor.train(&data).unwrap();
for op in [
MutationOperatorType::ArithmeticReplacement,
MutationOperatorType::RelationalReplacement,
] {
let mutant = create_test_mutant("test", op);
let result = predictor.predict(&mutant);
assert!(result.is_ok());
}
}
#[test]
fn test_predictor_unseen_operator() {
let mut predictor = SurvivabilityPredictor::new();
let data: Vec<TrainingData> = (0..20)
.map(|i| TrainingData {
mutant: create_test_mutant(
&format!("code_{}", i),
MutationOperatorType::ArithmeticReplacement,
),
was_killed: i % 2 == 0,
test_failures: vec![],
execution_time_ms: 100,
})
.collect();
predictor.train(&data).unwrap();
let mutant = create_test_mutant("test", MutationOperatorType::BitwiseReplacement);
let result = predictor.predict(&mutant);
assert!(result.is_ok());
let pred = result.unwrap();
assert!(pred.confidence <= 0.8);
}
#[test]
fn test_complex_source_feature_extraction() {
use super::super::types::MutantFeatures;
let complex_source = r#"
fn complex() -> Result<i32, Error> {
let mut result = 0;
for i in 0..100 {
if i % 2 == 0 && i > 10 {
match get_value(i)? {
Some(v) => result += v,
None => continue,
}
}
}
assert!(result > 0);
Ok(result)
}
"#;
let mutant =
create_test_mutant(complex_source, MutationOperatorType::ArithmeticReplacement);
let features = MutantFeatures::from_mutant(&mutant);
assert!(features.has_loops);
assert!(features.has_conditionals);
assert!(features.has_error_handling);
assert!(features.has_assertions);
assert!(features.has_arithmetic);
assert!(features.has_comparisons);
assert!(features.has_logical_ops);
assert!(features.nesting_depth >= 2);
assert!(features.token_count > 10);
}
}