use crate::config::{DebtmapConfig, ThresholdsConfig};
use crate::io::traits::{CoverageData, FileCoverage};
use crate::testkit::DebtmapTestEnv;
use std::path::PathBuf;
pub fn parse_test_code(code: &str) -> syn::File {
syn::parse_str(code).unwrap_or_else(|e| {
panic!(
"Failed to parse test code:\n{}\n\nError: {}",
code.lines()
.enumerate()
.map(|(i, line)| format!("{:3} | {}", i + 1, line))
.collect::<Vec<_>>()
.join("\n"),
e
)
})
}
pub fn create_test_ast(if_count: u32) -> syn::File {
let mut code = "fn test_function() {\n".to_string();
for i in 0..if_count {
code.push_str(&format!(" if x{} {{ }}\n", i));
}
code.push_str("}\n");
parse_test_code(&code)
}
pub fn create_nested_ast(nesting_depth: u32) -> syn::File {
let mut code = "fn test_function() {\n".to_string();
for i in 0..nesting_depth {
code.push_str(&" ".repeat(i as usize + 1));
code.push_str(&format!("if x{} {{\n", i));
}
code.push_str(&" ".repeat(nesting_depth as usize + 1));
code.push_str("println!(\"deep\");\n");
for i in (0..nesting_depth).rev() {
code.push_str(&" ".repeat(i as usize + 1));
code.push_str("}\n");
}
code.push_str("}\n");
parse_test_code(&code)
}
pub fn create_multi_function_ast(function_count: u32) -> syn::File {
let mut code = String::new();
for i in 0..function_count {
code.push_str(&format!(
"fn func_{}() {{ println!(\"func {}\"); }}\n",
i, i
));
}
parse_test_code(&code)
}
#[derive(Debug, Clone, Default)]
pub struct ConfigBuilder {
config: DebtmapConfig,
}
impl ConfigBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn complexity_threshold(mut self, threshold: u32) -> Self {
let thresholds = self
.config
.thresholds
.get_or_insert_with(ThresholdsConfig::default);
thresholds.complexity = Some(threshold);
self
}
pub fn max_function_length(mut self, length: usize) -> Self {
let thresholds = self
.config
.thresholds
.get_or_insert_with(ThresholdsConfig::default);
thresholds.max_function_length = Some(length);
self
}
pub fn max_file_length(mut self, length: usize) -> Self {
let thresholds = self
.config
.thresholds
.get_or_insert_with(ThresholdsConfig::default);
thresholds.max_file_length = Some(length);
self
}
pub fn minimum_debt_score(mut self, score: f64) -> Self {
let thresholds = self
.config
.thresholds
.get_or_insert_with(ThresholdsConfig::default);
thresholds.minimum_debt_score = Some(score);
self
}
pub fn minimum_risk_score(mut self, score: f64) -> Self {
let thresholds = self
.config
.thresholds
.get_or_insert_with(ThresholdsConfig::default);
thresholds.minimum_risk_score = Some(score);
self
}
pub fn ignore_patterns(mut self, patterns: Vec<String>) -> Self {
self.config.ignore = Some(crate::config::IgnoreConfig { patterns });
self
}
pub fn build(self) -> DebtmapConfig {
self.config
}
}
pub fn create_test_coverage(total_lines: usize, hit_lines: usize) -> FileCoverage {
let mut fc = FileCoverage::new();
for i in 1..=total_lines {
fc.add_line(i, if i <= hit_lines { 1 } else { 0 });
}
fc
}
pub fn create_coverage_data(path: impl Into<PathBuf>, percentage: f64) -> CoverageData {
let mut data = CoverageData::new();
let fc = create_test_coverage(100, percentage as usize);
data.add_file_coverage(path.into(), fc);
data
}
pub fn create_test_project() -> DebtmapTestEnv {
DebtmapTestEnv::new()
.with_files(vec![
("src/main.rs", "fn main() { println!(\"Hello\"); }"),
("src/lib.rs", "pub fn add(a: i32, b: i32) -> i32 { a + b }"),
("src/utils.rs", "pub fn helper() { /* ... */ }"),
(
"tests/integration_test.rs",
"#[test] fn test_main() { assert!(true); }",
),
])
.with_coverage_percentage("src/main.rs", 80.0)
.with_coverage_percentage("src/lib.rs", 100.0)
.with_coverage_percentage("src/utils.rs", 50.0)
.with_config(DebtmapConfig::default())
}
pub fn create_complex_project() -> DebtmapTestEnv {
DebtmapTestEnv::new()
.with_file(
"src/simple.rs",
r#"
fn simple_function() {
println!("Hello");
}
fn another_simple() -> i32 {
42
}
"#,
)
.with_file(
"src/complex.rs",
r#"
fn complex_function(x: i32, y: i32, z: bool) -> i32 {
if x > 0 {
if y > 0 {
if z {
return x + y;
} else {
return x - y;
}
} else {
return x;
}
} else if y > 0 {
return y;
} else {
return 0;
}
}
fn with_loop(items: &[i32]) -> i32 {
let mut sum = 0;
for item in items {
if *item > 0 {
sum += item;
}
}
sum
}
"#,
)
.with_file(
"src/lib.rs",
r#"
pub mod simple;
pub mod complex;
"#,
)
.with_coverage_percentage("src/simple.rs", 100.0)
.with_coverage_percentage("src/complex.rs", 60.0)
.with_config(ConfigBuilder::new().complexity_threshold(10).build())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_test_code() {
let ast = parse_test_code("fn foo() {}");
assert_eq!(ast.items.len(), 1);
}
#[test]
fn test_create_test_ast() {
let ast = create_test_ast(3);
assert_eq!(ast.items.len(), 1);
let code = quote::quote!(#ast).to_string();
assert!(code.contains("if x0"));
assert!(code.contains("if x1"));
assert!(code.contains("if x2"));
}
#[test]
fn test_create_nested_ast() {
let ast = create_nested_ast(3);
assert_eq!(ast.items.len(), 1);
let code = quote::quote!(#ast).to_string();
assert!(code.contains("if x0"));
assert!(code.contains("if x1"));
assert!(code.contains("if x2"));
}
#[test]
fn test_create_multi_function_ast() {
let ast = create_multi_function_ast(5);
assert_eq!(ast.items.len(), 5);
}
#[test]
fn test_config_builder() {
let config = ConfigBuilder::new()
.complexity_threshold(15)
.max_function_length(200)
.minimum_debt_score(2.0)
.build();
let thresholds = config.thresholds.unwrap();
assert_eq!(thresholds.complexity, Some(15));
assert_eq!(thresholds.max_function_length, Some(200));
assert_eq!(thresholds.minimum_debt_score, Some(2.0));
}
#[test]
fn test_config_builder_ignore_patterns() {
let config = ConfigBuilder::new()
.ignore_patterns(vec!["tests/**/*".to_string()])
.build();
let ignore = config.ignore.unwrap();
assert_eq!(ignore.patterns, vec!["tests/**/*"]);
}
#[test]
fn test_create_test_coverage() {
let fc = create_test_coverage(100, 75);
assert_eq!(fc.total_lines, 100);
assert_eq!(fc.hit_lines, 75);
}
#[test]
fn test_create_coverage_data() {
let data = create_coverage_data("test.rs", 80.0);
let pct = data
.get_file_coverage(std::path::Path::new("test.rs"))
.unwrap();
assert!((pct - 80.0).abs() < 1.0);
}
#[test]
fn test_create_test_project() {
let env = create_test_project();
assert!(env.has_file("src/main.rs"));
assert!(env.has_file("src/lib.rs"));
assert!(env.has_file("src/utils.rs"));
assert!(env.has_file("tests/integration_test.rs"));
}
#[test]
fn test_create_complex_project() {
use crate::env::AnalysisEnv;
let env = create_complex_project();
assert!(env.has_file("src/simple.rs"));
assert!(env.has_file("src/complex.rs"));
assert!(env.has_file("src/lib.rs"));
let config = env.config();
assert!(config.thresholds.is_some());
}
}