use super::*;
use crate::config::DataFlowScoringConfig;
use crate::core::FunctionMetrics;
use crate::priority::call_graph::CallGraph;
use crate::risk::lcov::{FunctionCoverage, LcovData};
use std::path::PathBuf;
fn create_test_metrics() -> FunctionMetrics {
FunctionMetrics {
file: PathBuf::from("test.rs"),
name: "test_function".to_string(),
line: 10,
length: 50,
cyclomatic: 5,
cognitive: 8,
nesting: 0,
is_test: false,
visibility: None,
is_trait_method: false,
in_test_module: false,
entropy_score: None,
is_pure: None,
purity_confidence: None,
purity_reason: None,
call_dependencies: None,
detected_patterns: None,
upstream_callers: None,
downstream_callees: None,
mapping_pattern_result: None,
adjusted_complexity: None,
composition_metrics: None,
language_specific: None,
purity_level: None,
error_swallowing_count: None,
error_swallowing_patterns: None,
entropy_analysis: None,
}
}
#[test]
fn test_unified_scoring() {
let func = create_test_metrics();
let graph = CallGraph::new();
let score = calculate_unified_priority(&func, &graph, None, None);
assert!(score.complexity_factor > 0.0);
assert!(score.coverage_factor > 0.0);
assert!(score.final_score > 0.0);
assert!(score.final_score <= 100.0); }
fn create_simple_io_wrapper() -> FunctionMetrics {
let mut func = create_test_metrics();
func.name = "extract_module_from_import".to_string();
func.cyclomatic = 1;
func.cognitive = 1;
func.length = 3;
func.nesting = 1;
func
}
fn create_full_coverage_data(func: &FunctionMetrics) -> LcovData {
let mut lcov = LcovData::default();
lcov.functions.insert(
func.file.clone(),
vec![FunctionCoverage {
name: func.name.clone(),
start_line: func.line,
execution_count: 18,
coverage_percentage: 100.0,
uncovered_lines: vec![],
normalized: crate::risk::lcov::NormalizedFunctionName::simple(&func.name),
}],
);
lcov.build_index(); lcov
}
fn assert_zero_debt_score(score: &UnifiedScore) {
assert_eq!(score.final_score, 0.0);
assert_eq!(score.complexity_factor, 0.0);
assert_eq!(score.coverage_factor, 0.0);
}
#[test]
fn test_simple_io_wrapper_with_coverage_zero_score() {
let func = create_simple_io_wrapper();
let call_graph = CallGraph::new();
let lcov = create_full_coverage_data(&func);
let score = calculate_unified_priority(&func, &call_graph, Some(&lcov), None);
assert_zero_debt_score(&score);
}
#[test]
fn test_simple_io_wrapper_without_coverage_has_score() {
let mut func = create_test_metrics();
func.name = "print_risk_function".to_string();
func.cyclomatic = 1;
func.cognitive = 0;
func.length = 4;
func.nesting = 1;
let call_graph = CallGraph::new();
let score = calculate_unified_priority(&func, &call_graph, None, None);
assert!(
score.final_score > 0.0,
"Untested I/O wrapper should have non-zero score"
);
}
fn assert_zero_coverage_boost(score: &UnifiedScore) {
assert!(
score.final_score > 0.0,
"Zero coverage functions should have non-zero score, got {}",
score.final_score
);
}
#[test]
fn test_zero_coverage_prioritization() {
let func = create_test_function_for_coverage();
let call_graph = CallGraph::new();
let lcov = create_coverage_function(&func, 0, 0.0);
let score = calculate_unified_priority(&func, &call_graph, Some(&lcov), None);
assert_zero_coverage_boost(&score);
}
fn create_coverage_function(
func: &FunctionMetrics,
execution_count: u64,
coverage_percentage: f64,
) -> LcovData {
let mut lcov = LcovData::default();
lcov.functions.insert(
func.file.clone(),
vec![FunctionCoverage {
name: func.name.clone(),
start_line: func.line,
execution_count,
coverage_percentage,
uncovered_lines: vec![],
normalized: crate::risk::lcov::NormalizedFunctionName::simple(&func.name),
}],
);
lcov.build_index(); lcov
}
fn create_test_function_for_coverage() -> FunctionMetrics {
let mut func = create_test_metrics();
func.cyclomatic = 5;
func.cognitive = 8;
func.is_test = false;
func
}
fn assert_low_coverage_boost(score_low: &UnifiedScore, score_mid: &UnifiedScore) {
assert!(
score_low.final_score > score_mid.final_score,
"10% coverage ({}) should score higher than 50% coverage ({})",
score_low.final_score,
score_mid.final_score
);
}
#[test]
fn test_low_coverage_prioritization() {
let func = create_test_function_for_coverage();
let call_graph = CallGraph::new();
let lcov_low = create_coverage_function(&func, 1, 10.0);
let lcov_mid = create_coverage_function(&func, 5, 50.0);
let score_low = calculate_unified_priority(&func, &call_graph, Some(&lcov_low), None);
let score_mid = calculate_unified_priority(&func, &call_graph, Some(&lcov_mid), None);
assert_low_coverage_boost(&score_low, &score_mid);
}
#[test]
fn test_test_code_not_boosted() {
let mut func = create_test_metrics();
func.cyclomatic = 5;
func.cognitive = 8;
func.is_test = true; func.name = "test_something".to_string();
let call_graph = CallGraph::new();
let score = calculate_unified_priority(&func, &call_graph, None, None);
assert!(
score.final_score < 50.0,
"Test code should not get zero coverage boost, got {}",
score.final_score
);
}
#[test]
fn test_complex_function_has_score() {
let mut func = create_test_metrics();
func.name = "complex_logic".to_string();
func.cyclomatic = 8;
func.cognitive = 12;
func.length = 50;
let call_graph = CallGraph::new();
let score = calculate_unified_priority(&func, &call_graph, None, None);
assert!(score.final_score > 0.0);
assert!(score.complexity_factor > 0.0);
}
#[test]
fn test_complexity_factor_stores_calculated_factor_not_raw_complexity() {
let mut func = create_test_metrics();
func.cyclomatic = 5;
func.cognitive = 15;
let call_graph = CallGraph::new();
let score = calculate_unified_priority(&func, &call_graph, None, None);
assert!(
(score.complexity_factor - 5.5).abs() < 0.1,
"complexity_factor should be ~5.5 with cognitive weighting, got {}",
score.complexity_factor
);
}
#[test]
fn test_well_tested_simple_function_scores_below_20() {
let mut func = create_test_metrics();
func.cyclomatic = 5;
func.cognitive = 15;
func.is_test = false;
let call_graph = CallGraph::new();
let lcov = create_full_coverage_data(&func);
let score = calculate_unified_priority(&func, &call_graph, Some(&lcov), None);
assert!(
score.final_score < 20.0,
"Well-tested simple function (100% coverage, cyclomatic=5) should score < 20.0, got {}",
score.final_score
);
}
fn create_entry_point_function(cyclomatic: u32, cognitive: u32) -> FunctionMetrics {
let mut func = create_test_metrics();
func.name = "handle_analyze".to_string(); func.cyclomatic = cyclomatic;
func.cognitive = cognitive;
func
}
fn create_pure_logic_function(cyclomatic: u32, cognitive: u32) -> FunctionMetrics {
let mut func = create_test_metrics();
func.name = "calculate_score".to_string(); func.cyclomatic = cyclomatic;
func.cognitive = cognitive;
func
}
fn create_zero_coverage_data(func: &FunctionMetrics) -> LcovData {
let mut lcov = LcovData::default();
lcov.functions.insert(
func.file.clone(),
vec![FunctionCoverage {
name: func.name.clone(),
start_line: func.line,
execution_count: 0,
coverage_percentage: 0.0,
uncovered_lines: vec![func.line],
normalized: crate::risk::lcov::NormalizedFunctionName::simple(&func.name),
}],
);
lcov.build_index();
lcov
}
#[test]
fn test_entry_point_coverage_adjustment() {
let entry_point = create_entry_point_function(17, 17);
let pure_logic = create_pure_logic_function(17, 17);
let call_graph = CallGraph::new();
let entry_lcov = create_zero_coverage_data(&entry_point);
let logic_lcov = create_zero_coverage_data(&pure_logic);
let entry_score =
calculate_unified_priority(&entry_point, &call_graph, Some(&entry_lcov), None);
let logic_score = calculate_unified_priority(&pure_logic, &call_graph, Some(&logic_lcov), None);
assert!(
entry_score.final_score > logic_score.final_score,
"Entry point (score: {}) should score higher than pure logic (score: {}) - entry points need testing",
entry_score.final_score,
logic_score.final_score
);
}
#[test]
fn test_orchestrator_coverage_adjustment() {
let mut orchestrator = create_test_metrics();
orchestrator.name = "orchestrate_analysis".to_string();
orchestrator.cyclomatic = 12;
orchestrator.cognitive = 15;
let call_graph = CallGraph::new();
let lcov = create_zero_coverage_data(&orchestrator);
let score = calculate_unified_priority(&orchestrator, &call_graph, Some(&lcov), None);
assert!(
score.final_score > 0.0,
"Orchestrator should still have a score, but reduced due to coverage adjustment"
);
}
#[test]
fn test_entry_point_with_coverage_not_overly_penalized() {
let entry_point = create_entry_point_function(12, 12);
let mut partial_lcov = LcovData::default();
partial_lcov.functions.insert(
entry_point.file.clone(),
vec![FunctionCoverage {
name: entry_point.name.clone(),
start_line: entry_point.line,
execution_count: 5,
coverage_percentage: 50.0,
uncovered_lines: vec![],
normalized: crate::risk::lcov::NormalizedFunctionName::simple(&entry_point.name),
}],
);
partial_lcov.build_index();
let call_graph = CallGraph::new();
let score = calculate_unified_priority(&entry_point, &call_graph, Some(&partial_lcov), None);
assert!(
score.final_score < 20.0,
"Entry point with 50% coverage and moderate complexity should not be critical (score: {})",
score.final_score
);
}
#[test]
fn test_complex_entry_point_still_flagged() {
let complex_entry = create_entry_point_function(25, 30);
let call_graph = CallGraph::new();
let lcov = create_zero_coverage_data(&complex_entry);
let score = calculate_unified_priority(&complex_entry, &call_graph, Some(&lcov), None);
assert!(
score.final_score > 4.0,
"Complex entry point should still be flagged (score: {})",
score.final_score
);
}
#[test]
fn test_io_wrapper_role_multiplier_not_clamped() {
let mut io_wrapper = create_test_metrics();
io_wrapper.name = "write_output".to_string();
io_wrapper.cyclomatic = 16;
io_wrapper.cognitive = 18;
io_wrapper.length = 50;
io_wrapper.nesting = 3;
let call_graph = CallGraph::new();
let lcov = create_zero_coverage_data(&io_wrapper);
let score = calculate_unified_priority(&io_wrapper, &call_graph, Some(&lcov), None);
assert!(
score.final_score > 10.0,
"IOWrapper should have elevated score due to I/O testing difficulty. Score: {}",
score.final_score
);
assert!(
score.role_multiplier >= 1.0,
"Role multiplier for IOWrapper should be >=1.0 (I/O is hard to test), got: {}",
score.role_multiplier
);
}
#[test]
fn test_entry_point_role_multiplier_not_clamped() {
let entry_point = create_entry_point_function(15, 18);
let call_graph = CallGraph::new();
let lcov = create_zero_coverage_data(&entry_point);
let score = calculate_unified_priority(&entry_point, &call_graph, Some(&lcov), None);
assert!(
(score.role_multiplier - 1.3).abs() < 0.01,
"Role multiplier should be 1.3 for EntryPoint, got: {}",
score.role_multiplier
);
assert!(
score.final_score > 2.0,
"EntryPoint with 1.3x multiplier should have elevated score. Score: {}",
score.final_score
);
}
#[test]
fn test_io_wrapper_coverage_weight_reduced() {
let mut io_wrapper = create_test_metrics();
io_wrapper.name = "format_output".to_string();
io_wrapper.file = PathBuf::from("src/io/formatter.rs");
io_wrapper.cyclomatic = 12;
io_wrapper.cognitive = 14;
io_wrapper.length = 40;
io_wrapper.nesting = 2;
let call_graph = CallGraph::new();
let lcov = create_zero_coverage_data(&io_wrapper);
let score = calculate_unified_priority(&io_wrapper, &call_graph, Some(&lcov), None);
assert!(
score.final_score > 5.0,
"IOWrapper should have a meaningful score. Score: {}",
score.final_score
);
}
#[test]
fn test_pure_logic_coverage_weight_unchanged() {
let pure_logic = create_pure_logic_function(12, 14);
let call_graph = CallGraph::new();
let lcov = create_zero_coverage_data(&pure_logic);
let score = calculate_unified_priority(&pure_logic, &call_graph, Some(&lcov), None);
assert!(
score.final_score > 2.0,
"PureLogic with 0% coverage should score reasonably. Score: {}",
score.final_score
);
assert!(
score.role_multiplier < 1.0,
"Role multiplier for PureLogic should be < 1.0 (pure is easy to test), got: {}",
score.role_multiplier
);
}
#[test]
fn test_purity_adjustment_strictly_pure_high_confidence() {
let mut func = create_test_metrics();
func.purity_level = Some(crate::core::PurityLevel::StrictlyPure);
func.purity_confidence = Some(0.9);
let adjustment = calculate_purity_adjustment(&func);
assert_eq!(
adjustment, 0.70,
"StrictlyPure with high confidence should get 0.70"
);
}
#[test]
fn test_purity_adjustment_strictly_pure_medium_confidence() {
let mut func = create_test_metrics();
func.purity_level = Some(crate::core::PurityLevel::StrictlyPure);
func.purity_confidence = Some(0.7);
let adjustment = calculate_purity_adjustment(&func);
assert_eq!(
adjustment, 0.80,
"StrictlyPure with medium confidence should get 0.80"
);
}
#[test]
fn test_purity_adjustment_locally_pure_high_confidence() {
let mut func = create_test_metrics();
func.purity_level = Some(crate::core::PurityLevel::LocallyPure);
func.purity_confidence = Some(0.9);
let adjustment = calculate_purity_adjustment(&func);
assert_eq!(
adjustment, 0.75,
"LocallyPure with high confidence should get 0.75"
);
}
#[test]
fn test_purity_adjustment_locally_pure_medium_confidence() {
let mut func = create_test_metrics();
func.purity_level = Some(crate::core::PurityLevel::LocallyPure);
func.purity_confidence = Some(0.7);
let adjustment = calculate_purity_adjustment(&func);
assert_eq!(
adjustment, 0.85,
"LocallyPure with medium confidence should get 0.85"
);
}
#[test]
fn test_purity_adjustment_read_only() {
let mut func = create_test_metrics();
func.purity_level = Some(crate::core::PurityLevel::ReadOnly);
func.purity_confidence = Some(0.9);
let adjustment = calculate_purity_adjustment(&func);
assert_eq!(adjustment, 0.90, "ReadOnly should get 0.90");
}
#[test]
fn test_purity_adjustment_impure() {
let mut func = create_test_metrics();
func.purity_level = Some(crate::core::PurityLevel::Impure);
func.purity_confidence = Some(0.9);
let adjustment = calculate_purity_adjustment(&func);
assert_eq!(adjustment, 1.0, "Impure should get 1.0");
}
#[test]
fn test_backward_compatibility_with_is_pure() {
let mut func = create_test_metrics();
func.purity_level = None; func.is_pure = Some(true);
func.purity_confidence = Some(0.9);
let adjustment = calculate_purity_adjustment(&func);
assert_eq!(
adjustment, 0.70,
"Old is_pure field with high confidence should still work"
);
}
#[test]
fn test_backward_compatibility_with_is_pure_medium_confidence() {
let mut func = create_test_metrics();
func.purity_level = None; func.is_pure = Some(true);
func.purity_confidence = Some(0.7);
let adjustment = calculate_purity_adjustment(&func);
assert_eq!(
adjustment, 0.85,
"Old is_pure field with medium confidence should still work"
);
}
#[test]
fn test_locally_pure_scores_lower_than_impure() {
let mut func_locally_pure = create_test_metrics();
func_locally_pure.purity_level = Some(crate::core::PurityLevel::LocallyPure);
func_locally_pure.purity_confidence = Some(0.9);
func_locally_pure.cyclomatic = 10;
func_locally_pure.cognitive = 15;
let mut func_impure = create_test_metrics();
func_impure.purity_level = Some(crate::core::PurityLevel::Impure);
func_impure.purity_confidence = Some(0.9);
func_impure.cyclomatic = 10;
func_impure.cognitive = 15;
let call_graph = CallGraph::new();
let score_locally_pure =
calculate_unified_priority(&func_locally_pure, &call_graph, None, None);
let score_impure = calculate_unified_priority(&func_impure, &call_graph, None, None);
assert!(
score_locally_pure.final_score < score_impure.final_score,
"LocallyPure (score: {}) should score lower than Impure (score: {})",
score_locally_pure.final_score,
score_impure.final_score
);
}
#[test]
fn test_locally_pure_scores_higher_than_strictly_pure() {
let mut func_locally_pure = create_test_metrics();
func_locally_pure.purity_level = Some(crate::core::PurityLevel::LocallyPure);
func_locally_pure.purity_confidence = Some(0.9);
func_locally_pure.cyclomatic = 10;
func_locally_pure.cognitive = 15;
let mut func_strictly_pure = create_test_metrics();
func_strictly_pure.purity_level = Some(crate::core::PurityLevel::StrictlyPure);
func_strictly_pure.purity_confidence = Some(0.9);
func_strictly_pure.cyclomatic = 10;
func_strictly_pure.cognitive = 15;
let call_graph = CallGraph::new();
let score_locally_pure =
calculate_unified_priority(&func_locally_pure, &call_graph, None, None);
let score_strictly_pure =
calculate_unified_priority(&func_strictly_pure, &call_graph, None, None);
assert!(
score_locally_pure.final_score > score_strictly_pure.final_score,
"LocallyPure (score: {}) should score slightly higher than StrictlyPure (score: {})",
score_locally_pure.final_score,
score_strictly_pure.final_score
);
}
#[test]
fn test_entropy_dampening_reduces_complexity_score() {
use crate::complexity::entropy_core::EntropyScore;
let mut func_with_patterns = create_test_metrics();
func_with_patterns.cyclomatic = 10;
func_with_patterns.cognitive = 15;
func_with_patterns.entropy_score = Some(EntropyScore {
token_entropy: 0.3, pattern_repetition: 0.8, branch_similarity: 0.6,
effective_complexity: 0.3,
unique_variables: 5,
max_nesting: 2,
dampening_applied: 1.0,
});
let mut func_without_patterns = create_test_metrics();
func_without_patterns.cyclomatic = 10;
func_without_patterns.cognitive = 15;
func_without_patterns.entropy_score = None;
let call_graph = CallGraph::new();
let score_with_patterns =
calculate_unified_priority(&func_with_patterns, &call_graph, None, None);
let score_without_patterns =
calculate_unified_priority(&func_without_patterns, &call_graph, None, None);
assert!(
score_with_patterns.final_score < score_without_patterns.final_score,
"Repetitive code (score: {}) should score lower than code without patterns (score: {})",
score_with_patterns.final_score,
score_without_patterns.final_score
);
}
#[test]
fn test_entropy_dampening_never_increases_complexity() {
use crate::complexity::entropy_core::EntropyScore;
let mut func = create_test_metrics();
func.cyclomatic = 20;
func.cognitive = 30;
func.entropy_score = Some(EntropyScore {
token_entropy: 0.3, pattern_repetition: 0.5,
branch_similarity: 0.4,
effective_complexity: 0.6,
unique_variables: 10,
max_nesting: 3,
dampening_applied: 1.0,
});
let entropy_analysis = crate::priority::scoring::computation::calculate_entropy_analysis(&func);
assert!(entropy_analysis.is_some());
let analysis = entropy_analysis.unwrap();
assert!(
analysis.adjusted_complexity <= func.cognitive,
"Adjusted complexity ({}) should not exceed raw cognitive ({})",
analysis.adjusted_complexity,
func.cognitive
);
assert!(
analysis.dampening_factor <= 1.0,
"Dampening factor ({}) should not exceed 1.0",
analysis.dampening_factor
);
}
#[test]
fn test_high_entropy_no_dampening() {
use crate::complexity::entropy_core::EntropyScore;
let mut func = create_test_metrics();
func.cyclomatic = 20;
func.cognitive = 30;
func.entropy_score = Some(EntropyScore {
token_entropy: 0.6, pattern_repetition: 0.5,
branch_similarity: 0.4,
effective_complexity: 0.6,
unique_variables: 10,
max_nesting: 3,
dampening_applied: 1.0,
});
let entropy_analysis = crate::priority::scoring::computation::calculate_entropy_analysis(&func);
assert!(entropy_analysis.is_some());
let analysis = entropy_analysis.unwrap();
assert_eq!(
analysis.dampening_factor, 1.0,
"High entropy code should have dampening_factor=1.0, got {}",
analysis.dampening_factor
);
assert_eq!(
analysis.adjusted_complexity, func.cognitive,
"High entropy code cognitive should not be reduced"
);
}
#[test]
fn test_entropy_details_populated_in_debt_item() {
use crate::complexity::entropy_core::EntropyScore;
use crate::priority::scoring::construction::create_unified_debt_item_enhanced;
let mut func = create_test_metrics();
func.cyclomatic = 15;
func.cognitive = 20;
func.nesting = 3;
func.entropy_score = Some(EntropyScore {
token_entropy: 0.6,
pattern_repetition: 0.7,
branch_similarity: 0.5,
effective_complexity: 0.4,
unique_variables: 8,
max_nesting: 3,
dampening_applied: 1.0,
});
let call_graph = CallGraph::new();
let debt_item = create_unified_debt_item_enhanced(&func, &call_graph, None, None);
assert!(debt_item.is_some());
let item = debt_item.unwrap();
assert!(item.entropy_analysis.is_some());
let entropy = item.entropy_analysis.as_ref().unwrap();
assert!(entropy.adjusted_complexity <= func.cognitive);
}
#[test]
fn test_normalize_complexity_with_entropy_dampening() {
use crate::complexity::entropy_core::EntropyScore;
let mut func = create_test_metrics();
func.cyclomatic = 20;
func.cognitive = 30;
func.entropy_score = Some(EntropyScore {
token_entropy: 0.25, pattern_repetition: 0.8, branch_similarity: 0.6,
effective_complexity: 0.3,
unique_variables: 5,
max_nesting: 2,
dampening_applied: 1.0,
});
let call_graph = CallGraph::new();
let score_with_entropy = calculate_unified_priority(&func, &call_graph, None, None);
func.entropy_score = None;
let score_without_entropy = calculate_unified_priority(&func, &call_graph, None, None);
assert!(
score_with_entropy.complexity_factor < score_without_entropy.complexity_factor,
"Complexity factor with entropy ({}) should be lower than without entropy ({})",
score_with_entropy.complexity_factor,
score_without_entropy.complexity_factor
);
}
#[test]
fn test_purity_spectrum_score_multipliers() {
assert_eq!(PuritySpectrum::StrictlyPure.score_multiplier(), 0.0);
assert_eq!(PuritySpectrum::LocallyPure.score_multiplier(), 0.3);
assert_eq!(PuritySpectrum::IOIsolated.score_multiplier(), 0.6);
assert_eq!(PuritySpectrum::IOMixed.score_multiplier(), 0.9);
assert_eq!(PuritySpectrum::Impure.score_multiplier(), 1.0);
}
#[test]
fn test_calculate_purity_factor_strictly_pure() {
use crate::data_flow::{DataFlowGraph, MutationInfo, PurityInfo};
let mut data_flow = DataFlowGraph::new();
let func_id = FunctionId::new(PathBuf::from("test.rs"), "pure_func".to_string(), 10);
data_flow.set_purity_info(
func_id.clone(),
PurityInfo {
is_pure: true,
confidence: 0.9,
impurity_reasons: vec![],
},
);
data_flow.set_mutation_info(
func_id.clone(),
MutationInfo {
has_mutations: false,
detected_mutations: vec![],
},
);
let purity_factor = calculate_purity_factor(&func_id, &data_flow);
assert_eq!(purity_factor, 0.0);
}
#[test]
fn test_calculate_purity_factor_locally_pure() {
use crate::data_flow::{DataFlowGraph, MutationInfo, PurityInfo};
let mut data_flow = DataFlowGraph::new();
let func_id = FunctionId::new(PathBuf::from("test.rs"), "locally_pure".to_string(), 20);
data_flow.set_purity_info(
func_id.clone(),
PurityInfo {
is_pure: true,
confidence: 0.9,
impurity_reasons: vec![],
},
);
data_flow.set_mutation_info(
func_id.clone(),
MutationInfo {
has_mutations: true,
detected_mutations: vec!["local_var".to_string()],
},
);
let purity_factor = calculate_purity_factor(&func_id, &data_flow);
assert_eq!(purity_factor, 0.3);
}
#[test]
fn test_calculate_refactorability_factor_returns_neutral() {
use crate::config::DataFlowScoringConfig;
use crate::data_flow::{DataFlowGraph, MutationInfo};
let mut data_flow = DataFlowGraph::new();
let func_id = FunctionId::new(PathBuf::from("test.rs"), "refactorable".to_string(), 30);
data_flow.set_mutation_info(
func_id.clone(),
MutationInfo {
has_mutations: true,
detected_mutations: vec!["live1".to_string()],
},
);
let config = DataFlowScoringConfig::default();
let refactor_factor = calculate_refactorability_factor(&func_id, &data_flow, &config);
assert_eq!(refactor_factor, 1.0);
}
#[test]
fn test_calculate_pattern_factor_data_flow_pipeline() {
use crate::data_flow::{DataFlowGraph, DataTransformation};
use crate::priority::call_graph::{CallType, FunctionCall};
let mut call_graph = CallGraph::new();
let func_id = FunctionId::new(PathBuf::from("test.rs"), "pipeline".to_string(), 50);
let callee1 = FunctionId::new(PathBuf::from("test.rs"), "map_fn".to_string(), 60);
let callee2 = FunctionId::new(PathBuf::from("test.rs"), "filter_fn".to_string(), 70);
call_graph.add_call(FunctionCall {
caller: func_id.clone(),
callee: callee1.clone(),
call_type: CallType::Direct,
});
call_graph.add_call(FunctionCall {
caller: func_id.clone(),
callee: callee2.clone(),
call_type: CallType::Direct,
});
let mut data_flow = DataFlowGraph::from_call_graph(call_graph);
let mut vars = std::collections::HashSet::new();
vars.insert("input".to_string());
vars.insert("output".to_string());
data_flow.add_variable_dependencies(func_id.clone(), vars);
data_flow.add_data_transformation(
func_id.clone(),
callee1,
DataTransformation {
input_vars: vec!["input".to_string()],
output_vars: vec!["mapped".to_string()],
transformation_type: "map".to_string(),
},
);
data_flow.add_data_transformation(
func_id.clone(),
callee2,
DataTransformation {
input_vars: vec!["mapped".to_string()],
output_vars: vec!["output".to_string()],
transformation_type: "filter".to_string(),
},
);
let pattern_factor = calculate_pattern_factor(&func_id, &data_flow);
assert_eq!(pattern_factor, 0.7);
}
#[test]
fn test_calculate_unified_priority_with_data_flow_disabled() {
let func = create_test_metrics();
let call_graph = CallGraph::new();
let data_flow = DataFlowGraph::from_call_graph(call_graph.clone());
let config = DataFlowScoringConfig {
enabled: false,
..Default::default()
};
let score = calculate_unified_priority_with_data_flow(
&func,
&call_graph,
&data_flow,
None,
None,
None,
&config,
);
assert!(score.purity_factor.is_none());
assert!(score.refactorability_factor.is_none());
assert!(score.pattern_factor.is_none());
}
#[test]
fn test_calculate_unified_priority_with_data_flow_enabled() {
use crate::data_flow::{DataFlowGraph, PurityInfo};
let func = create_test_metrics();
let call_graph = CallGraph::new();
let mut data_flow = DataFlowGraph::from_call_graph(call_graph.clone());
let func_id = FunctionId::new(func.file.clone(), func.name.clone(), func.line);
data_flow.set_purity_info(
func_id,
PurityInfo {
is_pure: false,
confidence: 0.5,
impurity_reasons: vec!["mutation detected".to_string()],
},
);
let config = DataFlowScoringConfig::default();
let score = calculate_unified_priority_with_data_flow(
&func,
&call_graph,
&data_flow,
None,
None,
None,
&config,
);
assert!(score.purity_factor.is_some());
assert!(score.refactorability_factor.is_some());
assert!(score.pattern_factor.is_some());
assert_eq!(score.purity_factor.unwrap(), 1.0);
}