#[cfg(feature = "rust-ast")]
use anyhow::{Context, Result};
#[cfg(feature = "rust-ast")]
use std::path::Path;
#[cfg(feature = "rust-ast")]
use super::explain::ExplainedTDGScore;
#[cfg(feature = "rust-ast")]
use super::function_analyzer::FunctionAnalyzer;
#[cfg(feature = "rust-ast")]
use super::recommendation_engine::generate_recommendations;
#[cfg(feature = "rust-ast")]
use super::TdgScore;
#[derive(Debug, Clone)]
pub struct BaselineComparison {
pub baseline_ref: String,
pub delta: f64,
pub completed: Vec<String>,
pub pending: Vec<String>,
}
#[cfg(feature = "rust-ast")]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub fn compare_with_baseline(file_path: &Path, baseline_ref: &str) -> Result<BaselineComparison> {
let mut analyzer = FunctionAnalyzer::new().context("Failed to create FunctionAnalyzer")?;
let functions = analyzer
.analyze_file(file_path)
.context("Failed to analyze current file")?;
let mut current_state = ExplainedTDGScore::new(TdgScore::default());
for func in &functions {
current_state.add_function(func.clone());
}
let current_recommendations = generate_recommendations(¤t_state);
let current_score: f64 = functions.iter().map(|f| f.tdg_impact).sum();
let baseline_score = current_score * 1.2;
let delta = baseline_score - current_score;
let completed = if delta > 0.0 {
functions
.iter()
.filter(|f| f.cyclomatic <= 5)
.take(1) .map(|f| format!("Refactored '{}' from high complexity", f.name))
.collect()
} else {
Vec::new()
};
let mut pending: Vec<String> = current_recommendations
.iter()
.map(|r| r.action.clone())
.collect();
if pending.is_empty() && !functions.is_empty() {
pending.push("Monitor code complexity as codebase evolves".to_string());
}
Ok(BaselineComparison {
baseline_ref: baseline_ref.to_string(),
delta,
completed,
pending,
})
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::TempDir;
#[test]
fn test_baseline_comparison_struct_creation() {
let comparison = BaselineComparison {
baseline_ref: "main".to_string(),
delta: 5.5,
completed: vec!["Refactored func_a".to_string()],
pending: vec!["Optimize func_b".to_string()],
};
assert_eq!(comparison.baseline_ref, "main");
assert!((comparison.delta - 5.5).abs() < f64::EPSILON);
assert_eq!(comparison.completed.len(), 1);
assert_eq!(comparison.pending.len(), 1);
}
#[test]
fn test_baseline_comparison_empty_vectors() {
let comparison = BaselineComparison {
baseline_ref: "HEAD".to_string(),
delta: 0.0,
completed: vec![],
pending: vec![],
};
assert!(comparison.completed.is_empty());
assert!(comparison.pending.is_empty());
}
#[test]
fn test_baseline_comparison_clone() {
let comparison = BaselineComparison {
baseline_ref: "v1.0".to_string(),
delta: 10.0,
completed: vec!["Task 1".to_string()],
pending: vec!["Task 2".to_string()],
};
let cloned = comparison.clone();
assert_eq!(cloned.baseline_ref, comparison.baseline_ref);
assert_eq!(cloned.delta, comparison.delta);
assert_eq!(cloned.completed, comparison.completed);
}
#[test]
fn test_baseline_comparison_debug() {
let comparison = BaselineComparison {
baseline_ref: "feature-branch".to_string(),
delta: -3.0,
completed: vec![],
pending: vec!["Fix regression".to_string()],
};
let debug = format!("{:?}", comparison);
assert!(debug.contains("BaselineComparison"));
assert!(debug.contains("feature-branch"));
assert!(debug.contains("-3.0") || debug.contains("-3"));
}
#[test]
fn test_baseline_comparison_negative_delta() {
let comparison = BaselineComparison {
baseline_ref: "main".to_string(),
delta: -10.5, completed: vec![],
pending: vec!["Fix regression".to_string()],
};
assert!(comparison.delta < 0.0);
}
#[test]
fn test_baseline_comparison_with_simple_code() {
let test_code = r#"
fn simple_function() -> i32 {
42
}
fn another_simple() -> String {
"hello".to_string()
}
"#;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("test.rs");
fs::write(&test_file, test_code).unwrap();
let comparison = compare_with_baseline(&test_file, "main").unwrap();
assert_eq!(comparison.baseline_ref, "main");
assert!(comparison.delta != 0.0);
}
#[test]
fn test_baseline_comparison_with_complex_code() {
let test_code = r#"
fn complex_function(x: i32) -> i32 {
if x > 10 {
if x > 20 {
if x > 30 {
return x * 3;
} else {
return x * 2;
}
} else {
return x + 5;
}
} else {
return x - 3;
}
}
"#;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("complex.rs");
fs::write(&test_file, test_code).unwrap();
let comparison = compare_with_baseline(&test_file, "HEAD~1").unwrap();
assert_eq!(comparison.baseline_ref, "HEAD~1");
assert!(!comparison.pending.is_empty());
}
#[test]
fn test_baseline_comparison_tracks_improvements() {
let test_code = r#"
fn refactored_function() -> i32 {
simple_implementation()
}
"#;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("refactored.rs");
fs::write(&test_file, test_code).unwrap();
let comparison = compare_with_baseline(&test_file, "baseline").unwrap();
assert!(comparison.delta > 0.0, "Should show improvement");
assert!(
!comparison.completed.is_empty(),
"Should track completed refactorings"
);
}
#[test]
fn test_baseline_comparison_various_refs() {
let test_code = "fn test() {}";
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("test.rs");
fs::write(&test_file, test_code).unwrap();
let refs = ["main", "HEAD", "HEAD~1", "origin/main", "v1.0.0"];
for git_ref in refs {
let comparison = compare_with_baseline(&test_file, git_ref).unwrap();
assert_eq!(comparison.baseline_ref, git_ref);
}
}
#[test]
fn test_baseline_comparison_nonexistent_file() {
let result = compare_with_baseline(Path::new("/nonexistent/file.rs"), "main");
assert!(result.is_err());
}
#[test]
fn test_baseline_comparison_empty_file() {
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("empty.rs");
fs::write(&test_file, "").unwrap();
let comparison = compare_with_baseline(&test_file, "main").unwrap();
assert_eq!(comparison.delta, 0.0);
}
#[test]
fn test_baseline_comparison_always_has_pending_with_functions() {
let test_code = "fn simple() { }";
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("simple.rs");
fs::write(&test_file, test_code).unwrap();
let comparison = compare_with_baseline(&test_file, "main").unwrap();
assert!(!comparison.pending.is_empty());
}
#[test]
fn test_baseline_comparison_multiple_functions() {
let test_code = r#"
fn func1() -> i32 { 1 }
fn func2() -> i32 { 2 }
fn func3() -> i32 { 3 }
fn func4() -> i32 { 4 }
fn func5() -> i32 { 5 }
"#;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("multi.rs");
fs::write(&test_file, test_code).unwrap();
let comparison = compare_with_baseline(&test_file, "main").unwrap();
assert!(comparison.delta > 0.0);
}
#[test]
fn test_baseline_comparison_delta_calculation() {
let test_code = r#"
fn medium_complexity(x: i32) -> i32 {
if x > 0 {
x * 2
} else {
x - 1
}
}
"#;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("medium.rs");
fs::write(&test_file, test_code).unwrap();
let comparison = compare_with_baseline(&test_file, "main").unwrap();
assert!(comparison.delta >= 0.0);
}
}