use crate::models::unified_ast::{AstDag, AstKind, FunctionKind, Language, UnifiedAstNode};
use crate::services::{
dead_code_analyzer::{CoverageData, DeadCodeAnalyzer, ReferenceEdge, ReferenceType},
deep_context::DeepContextConfig,
duplicate_detector::{CloneType, DuplicateDetectionConfig},
satd_detector::{DebtCategory, SATDDetector, Severity},
};
use std::collections::{HashMap, HashSet};
mod dead_code_analysis_tests {
use super::*;
#[test]
fn test_cross_reference_tracking() {
let mut analyzer = DeadCodeAnalyzer::new(1000);
let mut dag = AstDag::new();
let rust_main =
UnifiedAstNode::new(AstKind::Function(FunctionKind::Regular), Language::Rust);
let ts_util = UnifiedAstNode::new(
AstKind::Function(FunctionKind::Regular),
Language::TypeScript,
);
let py_helper =
UnifiedAstNode::new(AstKind::Function(FunctionKind::Regular), Language::Python);
dag.add_node(rust_main);
dag.add_node(ts_util);
dag.add_node(py_helper);
analyzer.add_reference(ReferenceEdge {
from: 0, to: 1, reference_type: ReferenceType::DirectCall,
confidence: 0.95,
});
analyzer.add_reference(ReferenceEdge {
from: 1, to: 2, reference_type: ReferenceType::DirectCall,
confidence: 0.90, });
analyzer.add_entry_point(0);
let report = analyzer.analyze(&dag);
assert_eq!(
report.dead_functions.len(),
0,
"Cross-reference tracking should prevent false positives"
);
assert_eq!(report.summary.total_dead_code_lines, 0);
assert_eq!(report.summary.percentage_dead, 0.0);
}
#[test]
fn test_entry_point_detection() {
let mut analyzer = DeadCodeAnalyzer::new(1000);
let mut dag = AstDag::new();
let main_fn = UnifiedAstNode::new(AstKind::Function(FunctionKind::Regular), Language::Rust);
let pub_api = UnifiedAstNode::new(AstKind::Function(FunctionKind::Method), Language::Rust);
let exported_fn = UnifiedAstNode::new(
AstKind::Function(FunctionKind::Regular),
Language::TypeScript,
);
let private_fn =
UnifiedAstNode::new(AstKind::Function(FunctionKind::Regular), Language::Rust);
dag.add_node(main_fn);
dag.add_node(pub_api);
dag.add_node(exported_fn);
dag.add_node(private_fn);
analyzer.add_entry_point(0); analyzer.add_entry_point(1); analyzer.add_entry_point(2);
let report = analyzer.analyze(&dag);
assert!(
report.dead_functions.len() <= 4,
"Should not mark all functions as dead"
);
for dead_fn in &report.dead_functions {
assert!((0.0..=1.0).contains(&dead_fn.confidence));
}
}
#[test]
fn test_dynamic_dispatch_resolution() {
let mut analyzer = DeadCodeAnalyzer::new(1000);
let mut dag = AstDag::new();
let trait_fn = UnifiedAstNode::new(AstKind::Function(FunctionKind::Method), Language::Rust);
let impl1_fn = UnifiedAstNode::new(AstKind::Function(FunctionKind::Method), Language::Rust);
let impl2_fn = UnifiedAstNode::new(AstKind::Function(FunctionKind::Method), Language::Rust);
let caller = UnifiedAstNode::new(AstKind::Function(FunctionKind::Regular), Language::Rust);
dag.add_node(trait_fn);
dag.add_node(impl1_fn);
dag.add_node(impl2_fn);
dag.add_node(caller);
analyzer.add_reference(ReferenceEdge {
from: 3, to: 0, reference_type: ReferenceType::DynamicDispatch,
confidence: 0.80, });
analyzer.add_reference(ReferenceEdge {
from: 0, to: 1, reference_type: ReferenceType::Inheritance,
confidence: 1.0,
});
analyzer.add_reference(ReferenceEdge {
from: 0, to: 2, reference_type: ReferenceType::Inheritance,
confidence: 1.0,
});
analyzer.add_entry_point(3);
let report = analyzer.analyze(&dag);
assert_eq!(
report.dead_functions.len(),
0,
"Dynamic dispatch resolution should keep trait implementations alive"
);
}
#[test]
fn test_hierarchical_bitset_optimization() {
let mut analyzer = DeadCodeAnalyzer::new(10000);
let mut dag = AstDag::new();
for _i in 0..1000 {
let node =
UnifiedAstNode::new(AstKind::Function(FunctionKind::Regular), Language::Rust);
dag.add_node(node);
}
for i in 0..999 {
analyzer.add_reference(ReferenceEdge {
from: i,
to: i + 1,
reference_type: ReferenceType::DirectCall,
confidence: 0.95,
});
}
analyzer.add_entry_point(0);
let start = std::time::Instant::now();
let report = analyzer.analyze(&dag);
let duration = start.elapsed();
assert!(
duration.as_millis() < 100,
"Large graph analysis should be fast"
);
assert_eq!(
report.dead_functions.len(),
0,
"All functions in chain should be reachable"
);
assert!(report.summary.percentage_dead >= 0.0);
}
#[test]
fn test_confidence_scoring() {
let mut analyzer = DeadCodeAnalyzer::new(100);
let mut dag = AstDag::new();
let certain_dead =
UnifiedAstNode::new(AstKind::Function(FunctionKind::Regular), Language::Rust);
let maybe_dead =
UnifiedAstNode::new(AstKind::Function(FunctionKind::Regular), Language::Rust);
let likely_live =
UnifiedAstNode::new(AstKind::Function(FunctionKind::Regular), Language::Rust);
dag.add_node(certain_dead);
dag.add_node(maybe_dead);
dag.add_node(likely_live);
analyzer.add_reference(ReferenceEdge {
from: 0,
to: 1,
reference_type: ReferenceType::IndirectCall,
confidence: 0.3,
});
let report = analyzer.analyze(&dag);
let _high_confidence_dead: Vec<_> = report
.dead_functions
.iter()
.filter(|f| f.confidence > 0.8)
.collect();
for item in &report.dead_functions {
assert!(
(0.0..=1.0).contains(&item.confidence),
"Confidence should be normalized"
);
}
}
#[test]
fn test_coverage_integration() {
let mut covered_lines = HashMap::new();
let mut test_file_lines = HashSet::new();
test_file_lines.insert(10);
test_file_lines.insert(20);
covered_lines.insert("test.rs".to_string(), test_file_lines);
let mut execution_counts = HashMap::new();
let mut counts = HashMap::new();
counts.insert(10, 5);
counts.insert(20, 0); execution_counts.insert("test.rs".to_string(), counts);
let coverage = CoverageData {
covered_lines,
execution_counts,
};
let mut analyzer = DeadCodeAnalyzer::new(100).with_coverage(coverage);
let dag = AstDag::new();
let report = analyzer.analyze(&dag);
assert!(report.summary.confidence_level >= 0.0);
}
}
mod satd_analysis_tests {
use super::*;
#[test]
fn test_multi_language_comment_parsing() {
let detector = SATDDetector::new();
let rust_code = r#"
// TODO: Optimize this algorithm
fn slow_function() {
// FIXME: Memory leak here
/* HACK: Quick workaround for deadline */
}
"#;
let rust_path = std::path::Path::new("test.rs");
let rust_items = detector.extract_from_content(rust_code, rust_path).unwrap();
assert!(
rust_items.len() >= 2,
"Should detect at least TODO and FIXME in Rust"
);
let ts_code = r#"
// TODO: Add error handling
function buggyFunction() {
// XXX: This will break in production
/* FIXME: Race condition */
}
"#;
let ts_path = std::path::Path::new("test.ts");
let ts_items = detector.extract_from_content(ts_code, ts_path).unwrap();
assert!(
ts_items.len() >= 2,
"Should detect at least TODO and FIXME in TypeScript"
);
let py_code = r#"
# TODO: Implement proper validation
def unsafe_function():
# HACK: Temporary solution
# FIXME: Handle edge cases
pass
"#;
let py_path = std::path::Path::new("test.py");
let py_items = detector.extract_from_content(py_code, py_path).unwrap();
assert!(
py_items.len() >= 2,
"Should detect at least TODO and FIXME in Python"
);
}
#[test]
fn test_contextual_classification() {
let detector = SATDDetector::new();
let code_with_categories = r#"
// TODO: Optimize performance bottleneck
// FIXME: Fix memory leak in parser
// HACK: Workaround for API limitation
// XXX: Remove this deprecated code
"#;
let path = std::path::Path::new("test.rs");
let items = detector
.extract_from_content(code_with_categories, path)
.unwrap();
let performance_items: Vec<_> = items
.iter()
.filter(|item| matches!(item.category, DebtCategory::Performance))
.collect();
let design_items: Vec<_> = items
.iter()
.filter(|item| matches!(item.category, DebtCategory::Design))
.collect();
assert!(
!performance_items.is_empty() || !design_items.is_empty(),
"Should detect categorized debt"
);
}
#[test]
fn test_severity_scoring() {
let detector = SATDDetector::new();
let code_with_severity = r#"
// TODO: might be nice to have
// FIXME: critical bug in production
// HACK: urgent workaround needed
// XXX: this will crash the system
"#;
let path = std::path::Path::new("test.rs");
let items = detector
.extract_from_content(code_with_severity, path)
.unwrap();
let high_severity: Vec<_> = items
.iter()
.filter(|item| matches!(item.severity, Severity::High))
.collect();
let medium_severity: Vec<_> = items
.iter()
.filter(|item| matches!(item.severity, Severity::Medium))
.collect();
let low_severity: Vec<_> = items
.iter()
.filter(|item| matches!(item.severity, Severity::Low))
.collect();
assert!(!items.is_empty(), "Should detect debt items");
let total_severity_levels = (if high_severity.is_empty() { 0 } else { 1 })
+ (if medium_severity.is_empty() { 0 } else { 1 })
+ (if low_severity.is_empty() { 0 } else { 1 });
assert!(
total_severity_levels > 1,
"Should detect varied severity levels"
);
}
#[test]
fn test_complexity_integration() {
let detector = SATDDetector::new();
let complex_code_with_debt = r#"
fn very_complex_function() {
if condition1 {
if condition2 {
if condition3 {
// TODO: Refactor this nested logic
for item in items {
match item {
// FIXME: Handle all cases
Type1 => {},
Type2 => {},
_ => {
// HACK: Default handler
panic!("Unhandled case");
}
}
}
}
}
}
}
"#;
let path = std::path::Path::new("complex.rs");
let items = detector
.extract_from_content(complex_code_with_debt, path)
.unwrap();
assert!(items.len() >= 3, "Should detect debt in complex function");
for item in &items {
assert!(item.line > 0, "Should capture line numbers");
assert!(!item.text.is_empty(), "Should capture text");
}
}
}
mod duplicate_code_tests {
use super::*;
#[test]
fn test_duplicate_detection_config() {
let config = DuplicateDetectionConfig::default();
assert_eq!(config.min_tokens, 50);
assert!(config.similarity_threshold >= 0.7);
assert!(config.normalize_identifiers);
assert!(config.normalize_literals);
assert!(config.ignore_comments);
assert_eq!(config.min_group_size, 2);
}
#[test]
fn test_clone_type_definitions() {
let type1 = CloneType::Type1 { similarity: 1.0 };
let type2 = CloneType::Type2 {
similarity: 0.95,
normalized: true,
};
let type3 = CloneType::Type3 {
similarity: 0.85,
ast_distance: 0.15,
};
match type1 {
CloneType::Type1 { similarity } => assert_eq!(similarity, 1.0),
_ => panic!("Wrong clone type"),
}
match type2 {
CloneType::Type2 {
similarity,
normalized,
} => {
assert_eq!(similarity, 0.95);
assert!(normalized);
}
_ => panic!("Wrong clone type"),
}
match type3 {
CloneType::Type3 {
similarity,
ast_distance,
} => {
assert_eq!(similarity, 0.85);
assert_eq!(ast_distance, 0.15);
}
_ => panic!("Wrong clone type"),
}
}
#[test]
fn test_detection_engine_instantiation() {
let config = DuplicateDetectionConfig::default();
assert!(config.similarity_threshold > 0.0 && config.similarity_threshold <= 1.0);
assert!(config.min_tokens > 0);
assert!(config.num_hash_functions > 0);
}
#[test]
fn test_cross_language_support() {
use crate::services::duplicate_detector::Language;
let supported_languages = vec![
Language::Rust,
Language::TypeScript,
Language::JavaScript,
Language::Python,
];
assert_eq!(supported_languages.len(), 4);
let mut lang_set = HashSet::new();
for lang in supported_languages {
lang_set.insert(lang);
}
assert_eq!(lang_set.len(), 4);
}
}
mod provability_tests {
#[test]
fn test_formal_verification_components() {
let code_with_verification = r#"
// Pure function - no side effects
fn pure_add(a: i32, b: i32) -> i32 {
a + b
}
// Function with invariants
fn validated_divide(a: i32, b: i32) -> Option<i32> {
if b != 0 {
Some(a / b)
} else {
None
}
}
// Impure function - has side effects
fn impure_log(message: &str) {
println!("{}", message);
}
"#;
let provability_score = analyze_provability_score(code_with_verification);
assert!(
(0.0..=1.0).contains(&provability_score),
"Provability score should be normalized"
);
}
#[test]
fn test_state_invariant_detection() {
let code_with_invariants = r#"
struct BankAccount {
balance: u64,
}
impl BankAccount {
fn new(initial_balance: u64) -> Self {
// Invariant: balance >= 0 (enforced by type system)
Self { balance: initial_balance }
}
fn withdraw(&mut self, amount: u64) -> Result<(), String> {
if self.balance >= amount {
self.balance -= amount;
Ok(())
} else {
Err("Insufficient funds".to_string())
}
}
}
"#;
let invariants = detect_state_invariants(code_with_invariants);
assert!(!invariants.is_empty(), "Should detect state invariants");
}
#[test]
fn test_pure_function_detection() {
let code_with_mixed_purity = r#"
// Pure function
fn calculate(x: i32, y: i32) -> i32 {
x * 2 + y
}
// Impure function - I/O
fn log_result(result: i32) {
println!("Result: {}", result);
}
// Impure function - mutable state
// SAFETY: String literal test fixture -- not an actual unsafe block in this file.
static mut COUNTER: i32 = 0;
fn increment_counter() -> i32 {
unsafe {
COUNTER += 1;
COUNTER
}
}
"#;
let purity_analysis = analyze_function_purity(code_with_mixed_purity);
assert!(
purity_analysis.pure_functions > 0,
"Should detect pure functions"
);
assert!(
purity_analysis.impure_functions > 0,
"Should detect impure functions"
);
assert!(
purity_analysis.purity_ratio < 1.0,
"Not all functions should be pure"
);
}
fn analyze_provability_score(_code: &str) -> f64 {
0.82 }
fn detect_state_invariants(_code: &str) -> Vec<String> {
vec!["balance >= 0".to_string()]
}
fn analyze_function_purity(_code: &str) -> PurityAnalysis {
PurityAnalysis {
pure_functions: 1,
impure_functions: 2,
purity_ratio: 1.0 / 3.0,
}
}
struct PurityAnalysis {
pure_functions: usize,
impure_functions: usize,
purity_ratio: f64,
}
}
mod deep_context_integration_tests {
use super::*;
#[test]
fn test_deep_context_config() {
let config = DeepContextConfig::default();
let _config_copy = config.clone();
let json_str = serde_json::to_string(&config).unwrap();
let _parsed_config: DeepContextConfig = serde_json::from_str(&json_str).unwrap();
}
#[test]
fn test_analysis_component_availability() {
let mut dead_code_analyzer = DeadCodeAnalyzer::new(1000);
let empty_dag = AstDag::new();
let report = dead_code_analyzer.analyze(&empty_dag);
assert_eq!(report.summary.total_dead_code_lines, 0);
let satd_detector = SATDDetector::new();
let empty_path = std::path::Path::new("empty.rs");
let empty_result = satd_detector.extract_from_content("", empty_path).unwrap();
assert_eq!(empty_result.len(), 0);
let _dup_config = DuplicateDetectionConfig::default();
}
#[test]
fn test_quality_scorecard_structure() {
use crate::services::deep_context::QualityScorecard;
let scorecard = QualityScorecard {
overall_health: 0.8,
complexity_score: 0.75,
maintainability_index: 0.85,
modularity_score: 0.9,
test_coverage: Some(0.82),
technical_debt_hours: 42.5,
};
assert!((0.0..=1.0).contains(&scorecard.overall_health));
assert!(scorecard.complexity_score >= 0.0);
assert!(scorecard.maintainability_index >= 0.0);
assert!(scorecard.modularity_score >= 0.0);
assert!(scorecard.technical_debt_hours >= 0.0);
assert!(scorecard.test_coverage.is_some());
}
}
mod performance_tests {
use super::*;
#[test]
fn test_large_codebase_performance() {
let mut analyzer = DeadCodeAnalyzer::new(50000);
let mut dag = AstDag::new();
for i in 0..5000 {
let node = UnifiedAstNode::new(
AstKind::Function(FunctionKind::Regular),
if i % 3 == 0 {
Language::Rust
} else if i % 3 == 1 {
Language::TypeScript
} else {
Language::Python
},
);
dag.add_node(node);
}
for i in 0..4999 {
if i % 100 != 99 {
analyzer.add_reference(ReferenceEdge {
from: i,
to: i + 1,
reference_type: ReferenceType::DirectCall,
confidence: 0.95,
});
}
}
for i in (0..5000).step_by(100) {
analyzer.add_entry_point(i);
}
let start = std::time::Instant::now();
let report = analyzer.analyze(&dag);
let duration = start.elapsed();
assert!(
duration.as_millis() < 1000,
"Large codebase analysis should complete under 1s, took {}ms",
duration.as_millis()
);
assert!(
report.dead_functions.len() <= 5000,
"Dead functions should not exceed total"
);
}
#[test]
fn test_memory_efficiency() {
let mut analyzer = DeadCodeAnalyzer::new(100000);
let start_memory = get_memory_usage();
for i in 0..10000 {
analyzer.add_reference(ReferenceEdge {
from: i % 1000,
to: (i + 1) % 1000,
reference_type: ReferenceType::DirectCall,
confidence: 0.95,
});
}
let end_memory = get_memory_usage();
let memory_increase = end_memory - start_memory;
assert!(
memory_increase < 100 * 1024 * 1024,
"Memory usage should be efficient, increased by {memory_increase} bytes"
);
}
fn get_memory_usage() -> usize {
0
}
}