use debtmap::builders::parallel_unified_analysis::{
ParallelUnifiedAnalysisBuilder, ParallelUnifiedAnalysisOptions,
};
use debtmap::core::FunctionMetrics;
use debtmap::priority::call_graph::CallGraph;
use debtmap::priority::UnifiedAnalysisQueries;
use debtmap::risk::lcov::{FunctionCoverage, LcovData};
use std::path::PathBuf;
#[test]
fn test_well_tested_simple_functions_excluded_from_top_10() {
let test_dir = PathBuf::from("test_project");
let mut functions = vec![];
let mut coverage_data = LcovData::default();
for i in 0..5 {
let func = FunctionMetrics {
file: test_dir.join("simple.rs"),
name: format!("simple_well_tested_{}", i),
line: 10 + i * 10,
length: 8,
cyclomatic: 3, cognitive: 5,
nesting: 1,
is_test: false,
visibility: None,
is_trait_method: false,
in_test_module: false,
entropy_score: None,
is_pure: None,
purity_confidence: None,
detected_patterns: None,
upstream_callers: None,
downstream_callees: None,
mapping_pattern_result: None,
adjusted_complexity: None,
composition_metrics: None,
language_specific: None,
purity_reason: None,
call_dependencies: None,
purity_level: None,
error_swallowing_count: None,
error_swallowing_patterns: None,
entropy_analysis: None,
};
coverage_data
.functions
.entry(func.file.clone())
.or_insert_with(Vec::new)
.push(FunctionCoverage {
name: func.name.clone(),
start_line: func.line,
execution_count: 20,
coverage_percentage: 100.0,
uncovered_lines: vec![],
normalized: debtmap::risk::lcov::NormalizedFunctionName::simple(&func.name),
});
functions.push(func);
}
for i in 0..10 {
let func = FunctionMetrics {
file: test_dir.join("complex.rs"),
name: format!("complex_untested_{}", i),
line: 10 + i * 20,
length: 100,
cyclomatic: 15, cognitive: 25,
nesting: 4,
is_test: false,
visibility: None,
is_trait_method: false,
in_test_module: false,
entropy_score: None,
is_pure: None,
purity_confidence: None,
detected_patterns: None,
upstream_callers: None,
downstream_callees: None,
mapping_pattern_result: None,
adjusted_complexity: None,
composition_metrics: None,
language_specific: None,
purity_reason: None,
call_dependencies: None,
purity_level: None,
error_swallowing_count: None,
error_swallowing_patterns: None,
entropy_analysis: None,
};
functions.push(func);
}
let call_graph = CallGraph::new();
let options = ParallelUnifiedAnalysisOptions {
parallel: true,
jobs: Some(4),
batch_size: 10,
progress: false,
reference_time: chrono::Utc::now(),
};
let mut builder = ParallelUnifiedAnalysisBuilder::new(call_graph, options);
let (data_flow, purity, test_funcs, debt_agg) =
builder.execute_phase1_parallel(&functions, None);
let items = builder.execute_phase2_parallel(
&functions,
&test_funcs,
&debt_agg,
&data_flow,
Some(&coverage_data),
&Default::default(),
None,
);
let file_items = builder.execute_phase3_parallel(&functions, Some(&coverage_data), false);
let (analysis, _timings) =
builder.build(data_flow, purity, items, file_items, Some(&coverage_data));
let recommendations = analysis.get_top_priorities(10);
let simple_in_top_10: Vec<_> = recommendations
.iter()
.filter(|item| item.location.function.starts_with("simple_well_tested_"))
.collect();
assert!(
simple_in_top_10.is_empty(),
"Well-tested simple functions should not appear in top 10, but found: {:?}",
simple_in_top_10
.iter()
.map(|item| &item.location.function)
.collect::<Vec<_>>()
);
let complex_in_top_10: Vec<_> = recommendations
.iter()
.filter(|item| item.location.function.starts_with("complex_untested_"))
.collect();
assert!(
complex_in_top_10.len() == 10,
"Top 10 should be dominated by complex/untested functions, but only found {} of them",
complex_in_top_10.len()
);
}
#[test]
fn test_well_tested_simple_function_has_low_score() {
let func = FunctionMetrics {
file: PathBuf::from("test.rs"),
name: "simple_tested".to_string(),
line: 10,
length: 8,
cyclomatic: 5,
cognitive: 15,
nesting: 1,
is_test: false,
visibility: None,
is_trait_method: false,
in_test_module: false,
entropy_score: None,
is_pure: None,
purity_confidence: None,
detected_patterns: None,
upstream_callers: None,
downstream_callees: None,
mapping_pattern_result: None,
adjusted_complexity: None,
composition_metrics: None,
language_specific: None,
purity_reason: None,
call_dependencies: None,
purity_level: None,
error_swallowing_count: None,
error_swallowing_patterns: None,
entropy_analysis: None,
};
let mut coverage_data = LcovData::default();
coverage_data.functions.insert(
func.file.clone(),
vec![FunctionCoverage {
name: func.name.clone(),
start_line: func.line,
execution_count: 15,
coverage_percentage: 100.0,
uncovered_lines: vec![],
normalized: debtmap::risk::lcov::NormalizedFunctionName::simple(&func.name),
}],
);
let call_graph = CallGraph::new();
let options = ParallelUnifiedAnalysisOptions {
parallel: true,
jobs: Some(4),
batch_size: 10,
progress: false,
reference_time: chrono::Utc::now(),
};
let mut builder = ParallelUnifiedAnalysisBuilder::new(call_graph, options);
let functions = vec![func.clone()];
let (data_flow, purity, test_funcs, debt_agg) =
builder.execute_phase1_parallel(&functions, None);
let items = builder.execute_phase2_parallel(
&functions,
&test_funcs,
&debt_agg,
&data_flow,
Some(&coverage_data),
&Default::default(),
None,
);
let file_items = builder.execute_phase3_parallel(&functions, Some(&coverage_data), false);
let (analysis, _timings) =
builder.build(data_flow, purity, items, file_items, Some(&coverage_data));
let recommendations = analysis.get_top_priorities(10);
let simple_func = recommendations
.iter()
.find(|item| item.location.function == "simple_tested");
if let Some(item) = simple_func {
assert!(
item.unified_score.final_score < 50.0,
"Well-tested simple function should score < 50.0, got {}",
item.unified_score.final_score
);
}
}