#[cfg(test)]
mod tests {
use debtmap::complexity::entropy::EntropyAnalyzer;
use debtmap::complexity::token_classifier::*;
use syn::parse_str;
fn create_test_classifier(enabled: bool) -> TokenClassifier {
let config = ClassificationConfig {
enabled,
..Default::default()
};
TokenClassifier::new(config)
}
#[test]
fn test_method_call_classification() {
let mut classifier = create_test_classifier(true);
let context = TokenContext {
is_method_call: true,
is_field_access: false,
is_external: false,
scope_depth: 0,
parent_node_type: NodeType::Expression,
};
let class = classifier.classify("get_value", &context);
assert!(matches!(class, TokenClass::MethodCall(CallType::Getter)));
let class = classifier.classify("set_value", &context);
assert!(matches!(class, TokenClass::MethodCall(CallType::Setter)));
let class = classifier.classify("is_valid", &context);
assert!(matches!(class, TokenClass::MethodCall(CallType::Validator)));
let class = classifier.classify("read", &context);
assert!(matches!(class, TokenClass::MethodCall(CallType::IO)));
let class = classifier.classify("unwrap", &context);
assert!(matches!(
class,
TokenClass::MethodCall(CallType::ErrorHandle)
));
}
#[test]
fn test_local_var_classification() {
let mut classifier = create_test_classifier(true);
let context = TokenContext {
is_method_call: false,
is_field_access: false,
is_external: false,
scope_depth: 1,
parent_node_type: NodeType::Expression,
};
let class = classifier.classify("i", &context);
assert!(matches!(class, TokenClass::LocalVar(VarType::Iterator)));
let class = classifier.classify("count", &context);
assert!(matches!(class, TokenClass::LocalVar(VarType::Counter)));
let class = classifier.classify("tmp", &context);
assert!(matches!(class, TokenClass::LocalVar(VarType::Temporary)));
let class = classifier.classify("config", &context);
assert!(matches!(
class,
TokenClass::LocalVar(VarType::Configuration)
));
let class = classifier.classify("file_handle", &context);
assert!(matches!(class, TokenClass::LocalVar(VarType::Resource)));
}
#[test]
fn test_control_flow_classification() {
let mut classifier = create_test_classifier(true);
let context = TokenContext {
is_method_call: false,
is_field_access: false,
is_external: false,
scope_depth: 0,
parent_node_type: NodeType::Statement,
};
let class = classifier.classify("if", &context);
assert!(matches!(class, TokenClass::ControlFlow(FlowType::If)));
let class = classifier.classify("match", &context);
assert!(matches!(class, TokenClass::ControlFlow(FlowType::Match)));
let class = classifier.classify("loop", &context);
assert!(matches!(class, TokenClass::ControlFlow(FlowType::Loop)));
let class = classifier.classify("return", &context);
assert!(matches!(class, TokenClass::ControlFlow(FlowType::Return)));
}
#[test]
fn test_weight_assignment() {
let classifier = create_test_classifier(true);
let weight = classifier.get_weight(&TokenClass::LocalVar(VarType::Iterator));
assert_eq!(weight, 0.1);
let weight = classifier.get_weight(&TokenClass::MethodCall(CallType::IO));
assert_eq!(weight, 0.9);
let weight = classifier.get_weight(&TokenClass::MethodCall(CallType::External));
assert_eq!(weight, 1.0);
let weight = classifier.get_weight(&TokenClass::Literal(LiteralCategory::Numeric));
assert_eq!(weight, 0.1);
}
#[test]
fn test_disabled_classification() {
let mut classifier = create_test_classifier(false);
let context = TokenContext {
is_method_call: true,
is_field_access: false,
is_external: false,
scope_depth: 0,
parent_node_type: NodeType::Expression,
};
let class = classifier.classify("get_value", &context);
assert!(matches!(class, TokenClass::Unknown(_)));
}
#[test]
fn test_classification_caching() {
let mut classifier = create_test_classifier(true);
let context = TokenContext {
is_method_call: true,
is_field_access: false,
is_external: false,
scope_depth: 0,
parent_node_type: NodeType::Expression,
};
let class1 = classifier.classify("get_value", &context);
let class2 = classifier.classify("get_value", &context);
assert_eq!(format!("{:?}", class1), format!("{:?}", class2));
}
#[test]
fn test_entropy_with_classification() {
let code = r#"
fn validate_input(input: &str) -> bool {
if input.is_empty() {
return false;
}
if input.len() > 100 {
return false;
}
if !input.chars().all(|c| c.is_alphanumeric()) {
return false;
}
true
}
"#;
let file = parse_str::<syn::File>(code).expect("Failed to parse");
let item_fn = match &file.items[0] {
syn::Item::Fn(f) => f,
_ => panic!("Expected function"),
};
let config = ClassificationConfig {
enabled: true,
..Default::default()
};
let mut analyzer = EntropyAnalyzer::new_with_config(1000, config);
let score = analyzer.calculate_entropy(&item_fn.block);
assert!(score.token_entropy >= 0.0 && score.token_entropy <= 1.0);
assert!(score.pattern_repetition >= 0.0 && score.pattern_repetition <= 1.0);
assert!(score.effective_complexity >= 0.0);
}
#[test]
fn test_different_token_classes_produce_different_weights() {
let code1 = r#"
fn process() {
let i = 0;
let j = 1;
let k = 2;
for idx in 0..10 {
println!("{}", idx);
}
}
"#;
let code2 = r#"
fn process() {
let file = File::open("data.txt").unwrap();
let conn = connect().expect("connection failed");
let result = read_data(&file);
write_output(result);
}
"#;
let file1 = parse_str::<syn::File>(code1).expect("Failed to parse");
let file2 = parse_str::<syn::File>(code2).expect("Failed to parse");
let item_fn1 = match &file1.items[0] {
syn::Item::Fn(f) => f,
_ => panic!("Expected function"),
};
let item_fn2 = match &file2.items[0] {
syn::Item::Fn(f) => f,
_ => panic!("Expected function"),
};
let config = ClassificationConfig {
enabled: true,
..Default::default()
};
let mut analyzer = EntropyAnalyzer::new_with_config(1000, config);
let score1 = analyzer.calculate_entropy(&item_fn1.block);
let score2 = analyzer.calculate_entropy(&item_fn2.block);
assert!(score1.token_entropy >= 0.0);
assert!(score2.token_entropy >= 0.0);
println!("Score1: {:?}", score1);
println!("Score2: {:?}", score2);
}
#[test]
fn test_field_access_classification() {
let mut classifier = create_test_classifier(true);
let context = TokenContext {
is_method_call: false,
is_field_access: true,
is_external: false,
scope_depth: 1,
parent_node_type: NodeType::Expression,
};
let class = classifier.classify("field_name", &context);
assert!(matches!(class, TokenClass::FieldAccess(AccessType::Getter)));
let class = classifier.classify("_private_field", &context);
assert!(matches!(class, TokenClass::FieldAccess(AccessType::Getter)));
let class = classifier.classify("field1", &context);
assert!(matches!(class, TokenClass::FieldAccess(AccessType::Getter)));
}
#[test]
fn test_literal_classification() {
let mut classifier = create_test_classifier(true);
let context = TokenContext {
is_method_call: false,
is_field_access: false,
is_external: false,
scope_depth: 1,
parent_node_type: NodeType::Expression,
};
let class = classifier.classify("42", &context);
assert!(
matches!(class, TokenClass::LocalVar(_))
|| matches!(class, TokenClass::Literal(LiteralCategory::Numeric)),
"42 should be classified as LocalVar or Numeric, got {:?}",
class
);
let class = classifier.classify("3.14", &context);
assert!(
matches!(class, TokenClass::Literal(LiteralCategory::Numeric)),
"3.14 should be classified as Numeric, got {:?}",
class
);
let class = classifier.classify("true", &context);
assert!(
matches!(class, TokenClass::Literal(LiteralCategory::Boolean))
|| matches!(class, TokenClass::LocalVar(_)),
"true should be classified as Boolean or LocalVar, got {:?}",
class
);
let class = classifier.classify("false", &context);
assert!(
matches!(class, TokenClass::Literal(LiteralCategory::Boolean))
|| matches!(class, TokenClass::LocalVar(_)),
"false should be classified as Boolean or LocalVar, got {:?}",
class
);
let class = classifier.classify("\"hello\"", &context);
assert!(matches!(
class,
TokenClass::Literal(LiteralCategory::String)
));
let class = classifier.classify("'a'", &context);
assert!(matches!(class, TokenClass::Literal(LiteralCategory::Char)));
let class = classifier.classify("null", &context);
assert!(
matches!(class, TokenClass::Literal(LiteralCategory::Null))
|| matches!(class, TokenClass::LocalVar(_)),
"null should be classified as Null or LocalVar, got {:?}",
class
);
let class = classifier.classify("None", &context);
assert!(
matches!(class, TokenClass::Literal(LiteralCategory::Null))
|| matches!(class, TokenClass::LocalVar(_)),
"None should be classified as Null or LocalVar, got {:?}",
class
);
let class = classifier.classify("nil", &context);
assert!(
matches!(class, TokenClass::Literal(LiteralCategory::Null))
|| matches!(class, TokenClass::LocalVar(_)),
"nil should be classified as Null or LocalVar, got {:?}",
class
);
}
#[test]
fn test_keyword_classification() {
let mut classifier = create_test_classifier(true);
let context = TokenContext {
is_method_call: false,
is_field_access: false,
is_external: false,
scope_depth: 0,
parent_node_type: NodeType::Statement,
};
let keywords = vec![
"fn", "let", "const", "mut", "pub", "struct", "enum", "trait", "impl", "mod", "use",
"async", "await", "self", "Self",
];
for keyword in keywords {
let class = classifier.classify(keyword, &context);
assert!(
matches!(class, TokenClass::Keyword(_)) || matches!(class, TokenClass::LocalVar(_)),
"Failed for keyword: {} (got {:?})",
keyword,
class
);
}
}
#[test]
fn test_operator_classification() {
let mut classifier = create_test_classifier(true);
let context = TokenContext {
is_method_call: false,
is_field_access: false,
is_external: false,
scope_depth: 1,
parent_node_type: NodeType::Expression,
};
let operators = vec![
"+", "-", "*", "/", "%", "=", "==", "!=", "<", ">", "<=", ">=", "&&", "||", "!", "&",
"|", "^", "~", "?", ".",
];
for op in operators {
let class = classifier.classify(op, &context);
assert!(
matches!(class, TokenClass::Operator(_)),
"Failed for operator: {}",
op
);
}
}
#[test]
fn test_collection_method_classification() {
let mut classifier = create_test_classifier(true);
let context = TokenContext {
is_method_call: true,
is_field_access: false,
is_external: false,
scope_depth: 1,
parent_node_type: NodeType::Expression,
};
let collection_methods = vec![
"push", "pop", "insert", "remove", "clear", "len", "contains", "get", "iter", "map",
"filter", "fold", "collect", "sort",
];
for method in collection_methods {
let class = classifier.classify(method, &context);
assert!(
matches!(class, TokenClass::MethodCall(CallType::Collection)),
"Failed for collection method: {} (got {:?})",
method,
class
);
}
let class = classifier.classify("is_empty", &context);
assert!(
matches!(class, TokenClass::MethodCall(CallType::Validator)),
"is_empty should be classified as Validator"
);
}
#[test]
fn test_converter_method_classification() {
let mut classifier = create_test_classifier(true);
let context = TokenContext {
is_method_call: true,
is_field_access: false,
is_external: false,
scope_depth: 1,
parent_node_type: NodeType::Expression,
};
let class = classifier.classify("to_string", &context);
assert!(matches!(class, TokenClass::MethodCall(CallType::Converter)));
let class = classifier.classify("into_iter", &context);
assert!(matches!(class, TokenClass::MethodCall(CallType::Converter)));
let class = classifier.classify("from_str", &context);
assert!(matches!(class, TokenClass::MethodCall(CallType::Converter)));
let class = classifier.classify("parse", &context);
assert!(matches!(class, TokenClass::MethodCall(CallType::Converter)));
}
#[test]
fn test_external_method_classification() {
let mut classifier = create_test_classifier(true);
let context = TokenContext {
is_method_call: true,
is_field_access: false,
is_external: true, scope_depth: 1,
parent_node_type: NodeType::Expression,
};
let class = classifier.classify("some_external_method", &context);
assert!(matches!(class, TokenClass::MethodCall(CallType::External)));
}
#[test]
fn test_cache_clearing() {
let mut classifier = create_test_classifier(true);
let context = TokenContext {
is_method_call: true,
is_field_access: false,
is_external: false,
scope_depth: 0,
parent_node_type: NodeType::Expression,
};
classifier.classify("get_value", &context);
classifier.classify("set_value", &context);
classifier.clear_cache();
let class = classifier.classify("get_value", &context);
assert!(matches!(class, TokenClass::MethodCall(CallType::Getter)));
}
#[test]
fn test_update_weights() {
let mut classifier = create_test_classifier(true);
let original_weight = classifier.get_weight(&TokenClass::LocalVar(VarType::Iterator));
assert_eq!(original_weight, 0.1);
let mut new_weights = std::collections::HashMap::new();
new_weights.insert(TokenClass::LocalVar(VarType::Iterator), 0.5);
classifier.update_weights(new_weights);
let updated_weight = classifier.get_weight(&TokenClass::LocalVar(VarType::Iterator));
assert_eq!(updated_weight, 0.5);
}
}