use debtmap::analyzers::{rust::RustAnalyzer, Analyzer};
use debtmap::complexity::entropy::{apply_entropy_dampening, EntropyAnalyzer};
use std::path::PathBuf;
struct PatternExample {
name: &'static str,
code: &'static str,
expected_high_repetition: bool,
expected_low_entropy: bool,
expected_dampening: bool,
}
fn get_pattern_corpus() -> Vec<PatternExample> {
vec![
PatternExample {
name: "validation_chain",
code: r#"
fn validate_config(config: &Config) -> Result<(), String> {
if config.port < 1024 {
return Err("Port must be >= 1024".to_string());
}
if config.port > 65535 {
return Err("Port must be <= 65535".to_string());
}
if config.timeout < 0 {
return Err("Timeout must be positive".to_string());
}
if config.retries < 0 {
return Err("Retries must be positive".to_string());
}
if config.buffer_size < 1024 {
return Err("Buffer size must be >= 1024".to_string());
}
Ok(())
}
"#,
expected_high_repetition: true,
expected_low_entropy: true,
expected_dampening: true,
},
PatternExample {
name: "dispatcher_pattern",
code: r#"
fn handle_event(event: Event) -> Response {
match event {
Event::Click(x, y) => handle_click(x, y),
Event::KeyPress(key) => handle_key(key),
Event::MouseMove(x, y) => handle_mouse(x, y),
Event::Scroll(delta) => handle_scroll(delta),
Event::Resize(w, h) => handle_resize(w, h),
Event::Focus => handle_focus(),
Event::Blur => handle_blur(),
_ => handle_unknown(),
}
}
"#,
expected_high_repetition: true,
expected_low_entropy: false, expected_dampening: true,
},
PatternExample {
name: "configuration_parser",
code: r#"
fn parse_settings(input: &str) -> Settings {
let mut settings = Settings::default();
if let Some(value) = get_field(input, "theme") {
settings.theme = value;
}
if let Some(value) = get_field(input, "language") {
settings.language = value;
}
if let Some(value) = get_field(input, "font_size") {
settings.font_size = value.parse().unwrap_or(12);
}
if let Some(value) = get_field(input, "auto_save") {
settings.auto_save = value == "true";
}
if let Some(value) = get_field(input, "tab_width") {
settings.tab_width = value.parse().unwrap_or(4);
}
settings
}
"#,
expected_high_repetition: true,
expected_low_entropy: true,
expected_dampening: true,
},
PatternExample {
name: "complex_algorithm",
code: r#"
fn calculate_score(data: &Data, config: &Config) -> f64 {
let base_score = data.value * config.multiplier;
let time_factor = (data.timestamp - config.baseline) as f64 / 86400.0;
let adjusted = base_score * (1.0 + time_factor * 0.1);
let penalty = if data.errors > 0 {
data.errors as f64 * config.error_weight
} else {
0.0
};
let bonus = match data.category {
Category::Premium => adjusted * 0.2,
Category::Standard => adjusted * 0.1,
Category::Basic => 0.0,
};
let final_score = adjusted - penalty + bonus;
final_score.max(0.0).min(100.0)
}
"#,
expected_high_repetition: false,
expected_low_entropy: false,
expected_dampening: false,
},
PatternExample {
name: "error_handler_chain",
code: r#"
fn handle_errors(result: Result<Data, Error>) -> Response {
match result {
Ok(data) => Response::Success(data),
Err(Error::NotFound) => Response::NotFound,
Err(Error::Unauthorized) => Response::Unauthorized,
Err(Error::BadRequest(msg)) => Response::BadRequest(msg),
Err(Error::Internal(msg)) => Response::Internal(msg),
Err(Error::Timeout) => Response::Timeout,
Err(Error::RateLimit) => Response::RateLimit,
Err(_) => Response::Unknown,
}
}
"#,
expected_high_repetition: true,
expected_low_entropy: true, expected_dampening: true,
},
PatternExample {
name: "data_transformation",
code: r#"
fn transform_record(record: &Record) -> TransformedRecord {
let name = normalize_string(&record.name);
let email = record.email.to_lowercase();
let phone = format_phone(&record.phone);
let address = Address {
street: capitalize(&record.street),
city: capitalize(&record.city),
state: record.state.to_uppercase(),
zip: validate_zip(&record.zip),
};
let created_at = parse_date(&record.created).unwrap_or_else(|| Utc::now());
let tags = record.tags.iter().map(|t| t.trim().to_lowercase()).collect();
TransformedRecord {
id: record.id,
name,
email,
phone,
address,
created_at,
tags,
status: Status::Active,
}
}
"#,
expected_high_repetition: true, expected_low_entropy: true, expected_dampening: true, },
PatternExample {
name: "state_machine",
code: r#"
fn next_state(state: State, event: Event) -> State {
match (state, event) {
(State::Idle, Event::Start) => State::Running,
(State::Running, Event::Pause) => State::Paused,
(State::Paused, Event::Resume) => State::Running,
(State::Running, Event::Stop) => State::Stopped,
(State::Paused, Event::Stop) => State::Stopped,
(State::Stopped, Event::Reset) => State::Idle,
(State::Error(_), Event::Reset) => State::Idle,
(_, Event::Error(e)) => State::Error(e),
(state, _) => state,
}
}
"#,
expected_high_repetition: true,
expected_low_entropy: true,
expected_dampening: true,
},
]
}
#[test]
fn test_pattern_corpus_detection() {
let analyzer = RustAnalyzer::new();
let mut entropy_analyzer = EntropyAnalyzer::new();
for example in get_pattern_corpus() {
println!("Testing pattern: {}", example.name);
let ast = analyzer
.parse(example.code, PathBuf::from("test.rs"))
.unwrap();
let metrics = analyzer.analyze(&ast);
if let Some(_func) = metrics.complexity.functions.first() {
if let Ok(file) = syn::parse_str::<syn::File>(example.code) {
if let Some(syn::Item::Fn(item_fn)) = file.items.first() {
let score = entropy_analyzer.calculate_entropy(&item_fn.block);
if example.expected_high_repetition {
assert!(
score.pattern_repetition > 0.4,
"{}: Expected high repetition, got {}",
example.name,
score.pattern_repetition
);
} else {
assert!(
score.pattern_repetition < 0.8,
"{}: Expected lower repetition, got {}",
example.name,
score.pattern_repetition
);
}
if example.expected_low_entropy {
assert!(
score.token_entropy < 0.65, "{}: Expected low entropy, got {}",
example.name,
score.token_entropy
);
} else {
assert!(
score.token_entropy > 0.3,
"{}: Expected high entropy, got {}",
example.name,
score.token_entropy
);
}
if example.expected_dampening {
assert!(
score.effective_complexity < 0.8, "{}: Expected dampening, got effective complexity {}",
example.name,
score.effective_complexity
);
} else {
assert!(
score.effective_complexity > 0.7, "{}: Expected no dampening, got effective complexity {}",
example.name,
score.effective_complexity
);
}
}
}
}
}
}
#[test]
fn test_false_positive_reduction() {
let analyzer = RustAnalyzer::new();
let validation_code = r#"
fn validate_request(req: &Request) -> Result<(), ValidationError> {
if req.method != "POST" && req.method != "PUT" {
return Err(ValidationError::InvalidMethod);
}
if req.content_length > MAX_SIZE {
return Err(ValidationError::TooLarge);
}
if req.content_type != "application/json" {
return Err(ValidationError::InvalidContentType);
}
if !req.headers.contains_key("Authorization") {
return Err(ValidationError::Unauthorized);
}
if req.body.is_empty() {
return Err(ValidationError::EmptyBody);
}
Ok(())
}
"#;
let ast = analyzer
.parse(validation_code, PathBuf::from("test.rs"))
.unwrap();
let metrics = analyzer.analyze(&ast);
if let Some(func) = metrics.complexity.functions.first() {
assert!(func.cyclomatic >= 5, "Expected high cyclomatic complexity");
if let Ok(file) = syn::parse_str::<syn::File>(validation_code) {
if let Some(syn::Item::Fn(item_fn)) = file.items.first() {
let mut entropy_analyzer = EntropyAnalyzer::new();
let score = entropy_analyzer.calculate_entropy(&item_fn.block);
assert!(score.pattern_repetition > 0.4); assert!(score.effective_complexity < 0.85);
let _dampened = apply_entropy_dampening(func.cyclomatic, &score);
assert!(score.effective_complexity < 1.0);
}
}
}
}
#[test]
fn test_entropy_cache_performance() {
use std::time::Instant;
let mut analyzer = EntropyAnalyzer::with_cache_size(100);
let block: syn::Block = syn::parse_quote! {{
let mut result = 0;
for i in 0..100 {
if i % 2 == 0 {
result += i;
} else if i % 3 == 0 {
result -= i;
} else {
result *= 2;
}
}
result
}};
let start = Instant::now();
let score1 = analyzer.calculate_entropy_cached(&block, "test_hash");
let first_duration = start.elapsed();
let start = Instant::now();
let score2 = analyzer.calculate_entropy_cached(&block, "test_hash");
let cached_duration = start.elapsed();
assert_eq!(score1, score2);
let stats = analyzer.get_cache_stats();
assert_eq!(stats.hit_rate, 0.5);
println!("First calculation: {:?}", first_duration);
println!("Cached calculation: {:?}", cached_duration);
assert_eq!(stats.entries, 1);
assert_eq!(stats.evictions, 0);
}
#[test]
fn test_entropy_score_ranges() {
let mut entropy_analyzer = EntropyAnalyzer::new();
let empty_block: syn::Block = syn::parse_quote! {{}};
let empty_score = entropy_analyzer.calculate_entropy(&empty_block);
assert_eq!(empty_score.token_entropy, 0.0);
assert_eq!(empty_score.pattern_repetition, 0.0);
let single_block: syn::Block = syn::parse_quote! {{
x + 1
}};
let single_score = entropy_analyzer.calculate_entropy(&single_block);
assert!(single_score.token_entropy >= 0.0);
assert!(single_score.token_entropy <= 1.0);
let complex_block: syn::Block = syn::parse_quote! {{
let mut sum = 0;
for i in 0..n {
if i % 2 == 0 {
sum += i * 2;
} else {
sum -= i;
}
if sum > 1000 {
break;
}
}
sum
}};
let complex_score = entropy_analyzer.calculate_entropy(&complex_block);
assert!(complex_score.token_entropy >= 0.0 && complex_score.token_entropy <= 1.0);
assert!(complex_score.pattern_repetition >= 0.0 && complex_score.pattern_repetition <= 1.0);
assert!(complex_score.branch_similarity >= 0.0 && complex_score.branch_similarity <= 1.0);
assert!(complex_score.effective_complexity >= 0.1 && complex_score.effective_complexity <= 1.0);
}
#[test]
fn test_entropy_benchmark_suite() {
let corpus = get_pattern_corpus();
let analyzer = RustAnalyzer::new();
let mut entropy_analyzer = EntropyAnalyzer::with_cache_size(50);
let mut total_reduction = 0.0;
let mut count = 0;
for example in corpus {
let ast = analyzer
.parse(example.code, PathBuf::from("test.rs"))
.unwrap();
let metrics = analyzer.analyze(&ast);
if let Some(func) = metrics.complexity.functions.first() {
if let Ok(file) = syn::parse_str::<syn::File>(example.code) {
if let Some(syn::Item::Fn(item_fn)) = file.items.first() {
let hash = format!("{}_hash", example.name);
let score = entropy_analyzer.calculate_entropy_cached(&item_fn.block, &hash);
let reduction = 1.0 - score.effective_complexity;
total_reduction += reduction;
count += 1;
println!(
"{}: Complexity {} -> {:.1} ({}% reduction)",
example.name,
func.cyclomatic,
func.cyclomatic as f64 * score.effective_complexity,
(reduction * 100.0) as i32
);
}
}
}
}
let avg_reduction = total_reduction / count as f64;
println!(
"Average complexity reduction: {:.1}%",
avg_reduction * 100.0
);
let stats = entropy_analyzer.get_cache_stats();
println!(
"Cache stats: {} entries, {:.1}% hit rate",
stats.entries,
stats.hit_rate * 100.0
);
}