#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
fn create_test_metrics() -> ProjectMetrics {
ProjectMetrics {
test_coverage: 0.90,
mutation_score: 0.80,
compiler_errors: 0,
clippy_warnings: 0,
test_failures: 0,
tdg_score: 90.0,
rust_project_score: 85,
satd_markers: 0,
dead_code_items: 0,
max_cyclomatic_complexity: 10,
max_cognitive_complexity: 15,
build_time: Duration::from_secs(30),
}
}
fn create_poor_metrics() -> ProjectMetrics {
ProjectMetrics {
test_coverage: 0.50,
mutation_score: 0.40,
compiler_errors: 5,
clippy_warnings: 10,
test_failures: 3,
tdg_score: 50.0,
rust_project_score: 40,
satd_markers: 5,
dead_code_items: 10,
max_cyclomatic_complexity: 30,
max_cognitive_complexity: 50,
build_time: Duration::from_secs(120),
}
}
#[test]
fn test_convergence_tracker_new() {
let tracker = ConvergenceTracker::new();
assert_eq!(tracker.iterations, 0);
assert!(tracker.history.is_empty());
assert!(tracker.best_metrics.is_none());
assert!(tracker.current_status.is_none());
}
#[test]
fn test_convergence_tracker_default() {
let tracker = ConvergenceTracker::default();
assert_eq!(tracker.iterations, 0);
assert!(tracker.history.is_empty());
}
#[test]
fn test_record_first_iteration() {
let mut tracker = ConvergenceTracker::new();
let targets = ConvergenceTargets::default();
let metrics = create_test_metrics();
tracker.record(metrics.clone(), 10, &targets);
assert_eq!(tracker.iterations, 1);
assert_eq!(tracker.history.len(), 1);
assert!(tracker.best_metrics.is_some());
assert!(tracker.current_status.is_some());
}
#[test]
fn test_record_multiple_iterations() {
let mut tracker = ConvergenceTracker::new();
let targets = ConvergenceTargets::default();
tracker.record(create_poor_metrics(), 20, &targets);
tracker.record(create_test_metrics(), 10, &targets);
tracker.record(create_test_metrics(), 5, &targets);
assert_eq!(tracker.iterations, 3);
assert_eq!(tracker.history.len(), 3);
}
#[test]
fn test_record_updates_best_metrics() {
let mut tracker = ConvergenceTracker::new();
let targets = ConvergenceTargets::default();
tracker.record(create_poor_metrics(), 20, &targets);
let first_best = tracker.best_metrics.clone();
tracker.record(create_test_metrics(), 10, &targets);
let second_best = tracker.best_metrics.clone();
assert!(second_best.is_some());
assert_ne!(
first_best.as_ref().map(|m| m.test_coverage),
second_best.as_ref().map(|m| m.test_coverage)
);
}
#[test]
fn test_calculate_score_perfect_metrics() {
let tracker = ConvergenceTracker::new();
let metrics = ProjectMetrics {
test_coverage: 1.0,
mutation_score: 1.0,
compiler_errors: 0,
clippy_warnings: 0,
test_failures: 0,
tdg_score: 100.0,
rust_project_score: 106,
..Default::default()
};
let score = tracker.calculate_score(&metrics);
assert!(score > 0.95);
}
#[test]
fn test_calculate_score_zero_metrics() {
let tracker = ConvergenceTracker::new();
let metrics = ProjectMetrics::default();
let score = tracker.calculate_score(&metrics);
assert!(score >= 0.0);
}
#[test]
fn test_calculate_score_with_errors() {
let tracker = ConvergenceTracker::new();
let metrics = ProjectMetrics {
compiler_errors: 5,
clippy_warnings: 10,
test_failures: 3,
..Default::default()
};
let score = tracker.calculate_score(&metrics);
assert!(score < 0.5);
}
#[test]
fn test_convergence_percentage_no_metrics() {
let tracker = ConvergenceTracker::new();
let targets = ConvergenceTargets::default();
let percentage = tracker.convergence_percentage(&targets);
assert_eq!(percentage, 0.0);
}
#[test]
fn test_convergence_percentage_perfect_metrics() {
let mut tracker = ConvergenceTracker::new();
let targets = ConvergenceTargets::default();
let metrics = ProjectMetrics {
test_coverage: 0.95,
mutation_score: 0.85,
compiler_errors: 0,
clippy_warnings: 0,
test_failures: 0,
tdg_score: 95.0,
rust_project_score: 90,
satd_markers: 0,
dead_code_items: 0,
max_cyclomatic_complexity: 10,
max_cognitive_complexity: 20,
build_time: Duration::from_secs(30),
};
tracker.record(metrics, 0, &targets);
let percentage = tracker.convergence_percentage(&targets);
assert!((percentage - 1.0).abs() < 0.01);
}
#[test]
fn test_convergence_percentage_partial() {
let mut tracker = ConvergenceTracker::new();
let targets = ConvergenceTargets::default();
let metrics = ProjectMetrics {
test_coverage: 0.50, mutation_score: 0.50,
compiler_errors: 0,
clippy_warnings: 0,
test_failures: 0,
tdg_score: 50.0,
rust_project_score: 45,
satd_markers: 0,
dead_code_items: 0,
..Default::default()
};
tracker.record(metrics, 10, &targets);
let percentage = tracker.convergence_percentage(&targets);
assert!(percentage > 0.0);
assert!(percentage < 1.0);
}
#[test]
fn test_is_converged_not_started() {
let tracker = ConvergenceTracker::new();
assert!(!tracker.is_converged());
}
#[test]
fn test_is_converged_false() {
let mut tracker = ConvergenceTracker::new();
let targets = ConvergenceTargets::default();
tracker.record(create_poor_metrics(), 20, &targets);
assert!(!tracker.is_converged());
}
#[test]
fn test_is_converged_true() {
let mut tracker = ConvergenceTracker::new();
let targets = ConvergenceTargets::default();
let metrics = ProjectMetrics {
test_coverage: 0.96,
mutation_score: 0.86,
compiler_errors: 0,
clippy_warnings: 0,
test_failures: 0,
tdg_score: 96.0,
rust_project_score: 91,
satd_markers: 0,
dead_code_items: 0,
max_cyclomatic_complexity: 10,
max_cognitive_complexity: 20,
build_time: Duration::from_secs(30),
};
tracker.record(metrics, 0, &targets);
assert!(tracker.is_converged());
}
#[test]
fn test_remaining_failures_not_started() {
let tracker = ConvergenceTracker::new();
let failures = tracker.remaining_failures();
assert!(failures.is_empty());
}
#[test]
fn test_remaining_failures_converged() {
let mut tracker = ConvergenceTracker::new();
let targets = ConvergenceTargets::default();
let metrics = ProjectMetrics {
test_coverage: 0.96,
mutation_score: 0.86,
compiler_errors: 0,
clippy_warnings: 0,
test_failures: 0,
tdg_score: 96.0,
rust_project_score: 91,
satd_markers: 0,
dead_code_items: 0,
max_cyclomatic_complexity: 10,
max_cognitive_complexity: 20,
build_time: Duration::from_secs(30),
};
tracker.record(metrics, 0, &targets);
let failures = tracker.remaining_failures();
assert!(failures.is_empty());
}
#[test]
fn test_remaining_failures_not_converged() {
let mut tracker = ConvergenceTracker::new();
let targets = ConvergenceTargets::default();
tracker.record(create_poor_metrics(), 20, &targets);
let failures = tracker.remaining_failures();
assert!(!failures.is_empty());
assert!(failures.len() > 1);
}
#[test]
fn test_trend_empty() {
let tracker = ConvergenceTracker::new();
assert_eq!(tracker.trend(), 0.0);
}
#[test]
fn test_trend_single_iteration() {
let mut tracker = ConvergenceTracker::new();
let targets = ConvergenceTargets::default();
tracker.record(create_test_metrics(), 10, &targets);
assert_eq!(tracker.trend(), 0.0);
}
#[test]
fn test_trend_improving() {
let mut tracker = ConvergenceTracker::new();
let targets = ConvergenceTargets::default();
tracker.record(create_poor_metrics(), 20, &targets);
tracker.record(create_test_metrics(), 10, &targets);
let trend = tracker.trend();
assert!(trend > 0.0);
assert!((trend - 0.5).abs() < 0.01);
}
#[test]
fn test_trend_degrading() {
let mut tracker = ConvergenceTracker::new();
let targets = ConvergenceTargets::default();
tracker.record(create_test_metrics(), 10, &targets);
tracker.record(create_poor_metrics(), 20, &targets);
let trend = tracker.trend();
assert!(trend < 0.0);
}
#[test]
fn test_trend_stable() {
let mut tracker = ConvergenceTracker::new();
let targets = ConvergenceTargets::default();
tracker.record(create_test_metrics(), 10, &targets);
tracker.record(create_test_metrics(), 10, &targets);
let trend = tracker.trend();
assert_eq!(trend, 0.0);
}
#[test]
fn test_trend_zero_start() {
let mut tracker = ConvergenceTracker::new();
let targets = ConvergenceTargets::default();
tracker.record(create_test_metrics(), 0, &targets);
tracker.record(create_test_metrics(), 5, &targets);
let trend = tracker.trend();
assert_eq!(trend, 0.0);
}
#[test]
fn test_convergence_snapshot_creation() {
let snapshot = ConvergenceSnapshot {
iteration: 1,
metrics: create_test_metrics(),
defects_remaining: 5,
status: ConvergenceStatus::NotConverged {
remaining: vec!["Coverage too low".to_string()],
},
};
assert_eq!(snapshot.iteration, 1);
assert_eq!(snapshot.defects_remaining, 5);
}
#[test]
fn test_convergence_snapshot_serialization() {
let snapshot = ConvergenceSnapshot {
iteration: 1,
metrics: create_test_metrics(),
defects_remaining: 5,
status: ConvergenceStatus::Converged,
};
let json = serde_json::to_string(&snapshot).unwrap();
let parsed: ConvergenceSnapshot = serde_json::from_str(&json).unwrap();
assert_eq!(snapshot.iteration, parsed.iteration);
assert_eq!(snapshot.defects_remaining, parsed.defects_remaining);
}
#[test]
fn test_full_convergence_workflow() {
let mut tracker = ConvergenceTracker::new();
let targets = ConvergenceTargets::default();
let mut defects = 20;
for i in 0..5 {
let coverage = 0.7 + (i as f32 * 0.05);
let metrics = ProjectMetrics {
test_coverage: coverage,
mutation_score: 0.7 + (i as f32 * 0.03),
compiler_errors: 3_usize.saturating_sub(i),
clippy_warnings: 5_usize.saturating_sub(i),
test_failures: 2_usize.saturating_sub(i),
tdg_score: 70.0 + (i as f32 * 5.0),
rust_project_score: 60 + (i * 5) as u32,
satd_markers: 3_usize.saturating_sub(i),
dead_code_items: 5_usize.saturating_sub(i),
..Default::default()
};
defects = std::cmp::max(0, defects - 4);
tracker.record(metrics, defects, &targets);
}
assert_eq!(tracker.iterations, 5);
assert_eq!(tracker.history.len(), 5);
let trend = tracker.trend();
assert!(trend > 0.0);
let percentage = tracker.convergence_percentage(&targets);
assert!(percentage > 0.5);
}
#[test]
fn test_serialization_roundtrip() {
let mut tracker = ConvergenceTracker::new();
let targets = ConvergenceTargets::default();
tracker.record(create_test_metrics(), 10, &targets);
tracker.record(create_poor_metrics(), 15, &targets);
let json = serde_json::to_string(&tracker).unwrap();
let parsed: ConvergenceTracker = serde_json::from_str(&json).unwrap();
assert_eq!(tracker.iterations, parsed.iterations);
assert_eq!(tracker.history.len(), parsed.history.len());
}
}