use crate::import::{
analyze_fix_strategy, smart_import_filter, FixStrategy, ImportDecision, ImportStats,
SmartImportConfig, SourceLanguage,
};
use std::collections::HashMap;
#[test]
fn strategy_clone_via_dot_clone() {
assert_eq!(
analyze_fix_strategy("let x = value.clone();"),
FixStrategy::AddClone
);
}
#[test]
fn strategy_clone_via_to_owned() {
assert_eq!(
analyze_fix_strategy("let s = slice.to_owned();"),
FixStrategy::AddClone
);
}
#[test]
fn strategy_lifetime_via_angle_bracket_a() {
assert_eq!(
analyze_fix_strategy("fn foo<'a>(x: &'a str) -> &'a str"),
FixStrategy::AddLifetime
);
}
#[test]
fn strategy_lifetime_via_static() {
assert_eq!(
analyze_fix_strategy("let s: &'static str = \"hello\";"),
FixStrategy::AddLifetime
);
}
#[test]
fn strategy_lifetime_via_underscore() {
assert_eq!(
analyze_fix_strategy("fn bar(x: &'_ str)"),
FixStrategy::AddLifetime
);
}
#[test]
fn strategy_lifetime_via_tick_a_in_fn() {
assert_eq!(
analyze_fix_strategy("fn process<'a>(data: &'a [u8])"),
FixStrategy::AddLifetime
);
}
#[test]
fn strategy_borrow_via_ref_type() {
assert_eq!(
analyze_fix_strategy("fn foo(x: &String)"),
FixStrategy::AddBorrow
);
}
#[test]
fn strategy_borrow_via_mut_ref() {
assert_eq!(
analyze_fix_strategy("fn foo(x: &mut Vec<i32>)"),
FixStrategy::AddBorrow
);
}
#[test]
fn strategy_borrow_via_self_ref() {
assert_eq!(
analyze_fix_strategy("fn method(&self) -> i32"),
FixStrategy::AddBorrow
);
}
#[test]
fn strategy_borrow_via_mut_self() {
assert_eq!(
analyze_fix_strategy("fn method(&mut self) { }"),
FixStrategy::AddBorrow
);
}
#[test]
fn strategy_borrow_via_param_x_ref() {
assert_eq!(
analyze_fix_strategy("fn foo(x: &i32)"),
FixStrategy::AddBorrow
);
}
#[test]
fn strategy_borrow_via_param_y_ref() {
assert_eq!(
analyze_fix_strategy("fn bar(y: &str)"),
FixStrategy::AddBorrow
);
}
#[test]
fn strategy_borrow_via_param_z_ref() {
assert_eq!(
analyze_fix_strategy("fn baz(z: &[u8])"),
FixStrategy::AddBorrow
);
}
#[test]
fn strategy_borrow_via_ampersand_plus_fn() {
assert_eq!(
analyze_fix_strategy("+ fn handle(data: &[u8]) {}\n& used"),
FixStrategy::AddBorrow
);
}
#[test]
fn strategy_option_via_option_type() {
assert_eq!(
analyze_fix_strategy("let x: Option<i32> = None;"),
FixStrategy::WrapInOption
);
}
#[test]
fn strategy_option_via_some() {
assert_eq!(
analyze_fix_strategy("let x = Some(42);"),
FixStrategy::WrapInOption
);
}
#[test]
fn strategy_option_via_unwrap() {
assert_eq!(
analyze_fix_strategy("let v = opt.unwrap();"),
FixStrategy::WrapInOption
);
}
#[test]
fn strategy_option_via_is_none() {
assert_eq!(
analyze_fix_strategy("if x.is_none() { return; }"),
FixStrategy::WrapInOption
);
}
#[test]
fn strategy_option_via_is_some() {
assert_eq!(
analyze_fix_strategy("if x.is_some() { process(x); }"),
FixStrategy::WrapInOption
);
}
#[test]
fn strategy_result_via_result_type() {
assert_eq!(
analyze_fix_strategy("fn foo() -> Result<i32, Error>"),
FixStrategy::WrapInResult
);
}
#[test]
fn strategy_result_via_ok() {
assert_eq!(
analyze_fix_strategy("return Ok(42);"),
FixStrategy::WrapInResult
);
}
#[test]
fn strategy_result_via_err() {
assert_eq!(
analyze_fix_strategy("return Err(\"failed\");"),
FixStrategy::WrapInResult
);
}
#[test]
fn strategy_type_annotation_via_i32() {
assert_eq!(
analyze_fix_strategy("let x: i32 = 5;"),
FixStrategy::AddTypeAnnotation
);
}
#[test]
fn strategy_type_annotation_via_string() {
assert_eq!(
analyze_fix_strategy("let s: String = String::new();"),
FixStrategy::AddTypeAnnotation
);
}
#[test]
fn strategy_type_annotation_generic_colon_space() {
assert_eq!(
analyze_fix_strategy("let v: Vec<u8> = vec![];"),
FixStrategy::AddTypeAnnotation
);
}
#[test]
fn strategy_unknown_for_gibberish() {
assert_eq!(
analyze_fix_strategy("random text without patterns"),
FixStrategy::Unknown
);
}
#[test]
fn strategy_unknown_empty_string() {
assert_eq!(analyze_fix_strategy(""), FixStrategy::Unknown);
}
fn python_config() -> SmartImportConfig {
SmartImportConfig {
source_language: SourceLanguage::Python,
min_confidence: 0.5,
allow_warnings: true,
}
}
fn c_config() -> SmartImportConfig {
SmartImportConfig {
source_language: SourceLanguage::C,
min_confidence: 0.5,
allow_warnings: true,
}
}
fn cpp_config() -> SmartImportConfig {
SmartImportConfig {
source_language: SourceLanguage::Cpp,
min_confidence: 0.5,
allow_warnings: true,
}
}
fn other_config() -> SmartImportConfig {
SmartImportConfig {
source_language: SourceLanguage::Other,
min_confidence: 0.5,
allow_warnings: true,
}
}
#[test]
fn filter_clone_python_with_list_construct_rejected() {
let diff = "let x = lst.clone();";
let mut meta = HashMap::new();
meta.insert("source_construct".to_string(), "list_copy".to_string());
let decision = smart_import_filter(diff, &meta, &python_config());
assert!(matches!(decision, ImportDecision::Reject(_)));
assert!(!decision.allows_import());
}
#[test]
fn filter_clone_python_with_dict_construct_rejected() {
let diff = "let x = d.clone();";
let mut meta = HashMap::new();
meta.insert("source_construct".to_string(), "dict_merge".to_string());
let decision = smart_import_filter(diff, &meta, &python_config());
assert!(matches!(decision, ImportDecision::Reject(_)));
}
#[test]
fn filter_clone_python_no_construct_accepted() {
let diff = "let x = val.clone();";
let meta = HashMap::new();
let decision = smart_import_filter(diff, &meta, &python_config());
assert_eq!(decision, ImportDecision::Accept);
}
#[test]
fn filter_clone_python_non_collection_construct_accepted() {
let diff = "let x = val.clone();";
let mut meta = HashMap::new();
meta.insert("source_construct".to_string(), "integer_copy".to_string());
let decision = smart_import_filter(diff, &meta, &python_config());
assert_eq!(decision, ImportDecision::Accept);
}
#[test]
fn filter_clone_c_source_always_accepted() {
let diff = "let x = val.clone();";
let mut meta = HashMap::new();
meta.insert("source_construct".to_string(), "list_copy".to_string());
let decision = smart_import_filter(diff, &meta, &c_config());
assert_eq!(decision, ImportDecision::Accept);
}
#[test]
fn filter_borrow_python_accepted() {
let diff = "fn foo(x: &String)";
let decision = smart_import_filter(diff, &HashMap::new(), &python_config());
assert_eq!(decision, ImportDecision::Accept);
}
#[test]
fn filter_borrow_c_accepted() {
let diff = "fn foo(x: &mut Vec<i32>)";
let decision = smart_import_filter(diff, &HashMap::new(), &c_config());
assert_eq!(decision, ImportDecision::Accept);
}
#[test]
fn filter_borrow_cpp_accepted() {
let diff = "fn foo(x: &i32)";
let decision = smart_import_filter(diff, &HashMap::new(), &cpp_config());
assert_eq!(decision, ImportDecision::Accept);
}
#[test]
fn filter_lifetime_python_accepted() {
let diff = "fn foo<'a>(x: &'a str)";
let decision = smart_import_filter(diff, &HashMap::new(), &python_config());
assert_eq!(decision, ImportDecision::Accept);
}
#[test]
fn filter_lifetime_c_accepted() {
let diff = "fn foo<'a>(x: &'a str)";
let decision = smart_import_filter(diff, &HashMap::new(), &c_config());
assert_eq!(decision, ImportDecision::Accept);
}
#[test]
fn filter_lifetime_other_accepted() {
let diff = "fn foo<'a>(x: &'a str)";
let decision = smart_import_filter(diff, &HashMap::new(), &other_config());
assert_eq!(decision, ImportDecision::Accept);
}
#[test]
fn filter_option_python_without_null_warns() {
let diff = "let x = Some(value);";
let decision = smart_import_filter(diff, &HashMap::new(), &python_config());
match decision {
ImportDecision::AcceptWithWarning(msg) => {
assert!(msg.contains("NULL"));
}
_ => panic!("Expected AcceptWithWarning, got {:?}", decision),
}
}
#[test]
fn filter_option_python_with_null_accepts() {
let diff = "if ptr == NULL { None } else { Some(v) }";
let decision = smart_import_filter(diff, &HashMap::new(), &python_config());
assert_eq!(decision, ImportDecision::Accept);
}
#[test]
fn filter_option_python_with_nullptr_accepts() {
let diff = "let x: Option<i32> = if ptr == nullptr { None } else { Some(v) };";
let decision = smart_import_filter(diff, &HashMap::new(), &python_config());
assert_eq!(decision, ImportDecision::Accept);
}
#[test]
fn filter_option_python_with_lowercase_null_accepts() {
let diff = "let x = if p == null { None } else { Some(p) };";
let decision = smart_import_filter(diff, &HashMap::new(), &python_config());
assert_eq!(decision, ImportDecision::Accept);
}
#[test]
fn filter_option_python_with_is_none_accepts() {
let diff = "if x.is_none() { handle_null(); }";
let decision = smart_import_filter(diff, &HashMap::new(), &python_config());
assert_eq!(decision, ImportDecision::Accept);
}
#[test]
fn filter_option_python_with_is_some_accepts() {
let diff = "if x.is_some() { process(); }";
let decision = smart_import_filter(diff, &HashMap::new(), &python_config());
assert_eq!(decision, ImportDecision::Accept);
}
#[test]
fn filter_option_python_with_unwrap_or_accepts() {
let diff = "let v: Option<i32> = x; let r = v.unwrap_or(0);";
let decision = smart_import_filter(diff, &HashMap::new(), &python_config());
assert_eq!(decision, ImportDecision::Accept);
}
#[test]
fn filter_option_c_source_always_accepted() {
let diff = "let x = Some(value);";
let decision = smart_import_filter(diff, &HashMap::new(), &c_config());
assert_eq!(decision, ImportDecision::Accept);
}
#[test]
fn filter_option_cpp_source_always_accepted() {
let diff = "let x = Some(value);";
let decision = smart_import_filter(diff, &HashMap::new(), &cpp_config());
assert_eq!(decision, ImportDecision::Accept);
}
#[test]
fn filter_result_python_accepted() {
let diff = "fn process() -> Result<i32, Error>";
let decision = smart_import_filter(diff, &HashMap::new(), &python_config());
assert_eq!(decision, ImportDecision::Accept);
}
#[test]
fn filter_result_c_accepted() {
let diff = "return Ok(value);";
let decision = smart_import_filter(diff, &HashMap::new(), &c_config());
assert_eq!(decision, ImportDecision::Accept);
}
#[test]
fn filter_result_err_variant() {
let diff = "return Err(e);";
let decision = smart_import_filter(diff, &HashMap::new(), &python_config());
assert_eq!(decision, ImportDecision::Accept);
}
#[test]
fn filter_type_annotation_python_warns() {
let diff = "let x: i32 = 5;";
let decision = smart_import_filter(diff, &HashMap::new(), &python_config());
match decision {
ImportDecision::AcceptWithWarning(msg) => {
assert!(msg.contains("type mapping"));
}
_ => panic!(
"Expected AcceptWithWarning for Python type annotation, got {:?}",
decision
),
}
}
#[test]
fn filter_type_annotation_c_accepted() {
let diff = "let x: i32 = 5;";
let decision = smart_import_filter(diff, &HashMap::new(), &c_config());
assert_eq!(decision, ImportDecision::Accept);
}
#[test]
fn filter_type_annotation_cpp_accepted() {
let diff = "let x: i32 = 5;";
let decision = smart_import_filter(diff, &HashMap::new(), &cpp_config());
assert_eq!(decision, ImportDecision::Accept);
}
#[test]
fn filter_type_annotation_other_accepted() {
let diff = "let x: i32 = 5;";
let decision = smart_import_filter(diff, &HashMap::new(), &other_config());
assert_eq!(decision, ImportDecision::Accept);
}
#[test]
fn filter_unknown_rejected() {
let diff = "random junk no patterns";
let decision = smart_import_filter(diff, &HashMap::new(), &python_config());
match decision {
ImportDecision::Reject(reason) => {
assert!(reason.contains("Unknown"));
}
_ => panic!("Expected Reject for unknown strategy"),
}
}
#[test]
fn filter_unknown_rejected_c() {
let diff = "no fix patterns here";
let decision = smart_import_filter(diff, &HashMap::new(), &c_config());
assert!(matches!(decision, ImportDecision::Reject(_)));
}
#[test]
fn decision_accept_allows_import() {
assert!(ImportDecision::Accept.allows_import());
}
#[test]
fn decision_accept_with_warning_allows_import() {
assert!(ImportDecision::AcceptWithWarning("warn".to_string()).allows_import());
}
#[test]
fn decision_reject_does_not_allow_import() {
assert!(!ImportDecision::Reject("reason".to_string()).allows_import());
}
#[test]
fn stats_new_is_empty() {
let stats = ImportStats::new();
assert_eq!(stats.total_evaluated, 0);
assert_eq!(stats.warnings, 0);
assert!(stats.accepted_by_strategy.is_empty());
assert!(stats.rejected_by_strategy.is_empty());
}
#[test]
fn stats_record_accept_increments() {
let mut stats = ImportStats::new();
stats.record(FixStrategy::AddBorrow, &ImportDecision::Accept);
assert_eq!(stats.total_evaluated, 1);
assert_eq!(stats.warnings, 0);
assert_eq!(
stats.accepted_by_strategy.get(&FixStrategy::AddBorrow),
Some(&1)
);
}
#[test]
fn stats_record_warning_increments_both() {
let mut stats = ImportStats::new();
stats.record(
FixStrategy::WrapInOption,
&ImportDecision::AcceptWithWarning("check NULL".to_string()),
);
assert_eq!(stats.total_evaluated, 1);
assert_eq!(stats.warnings, 1);
assert_eq!(
stats.accepted_by_strategy.get(&FixStrategy::WrapInOption),
Some(&1)
);
}
#[test]
fn stats_record_reject_increments() {
let mut stats = ImportStats::new();
stats.record(
FixStrategy::AddClone,
&ImportDecision::Reject("python collection".to_string()),
);
assert_eq!(stats.total_evaluated, 1);
assert_eq!(stats.warnings, 0);
assert_eq!(
stats.rejected_by_strategy.get(&FixStrategy::AddClone),
Some(&1)
);
}
#[test]
fn stats_acceptance_rate_for_strategy() {
let mut stats = ImportStats::new();
stats.record(FixStrategy::AddBorrow, &ImportDecision::Accept);
stats.record(FixStrategy::AddBorrow, &ImportDecision::Accept);
stats.record(FixStrategy::AddBorrow, &ImportDecision::Accept);
stats.record(
FixStrategy::AddBorrow,
&ImportDecision::Reject("r".to_string()),
);
let rate = stats.acceptance_rate(FixStrategy::AddBorrow);
assert!((rate - 0.75).abs() < 0.01);
}
#[test]
fn stats_acceptance_rate_zero_for_unseen_strategy() {
let stats = ImportStats::new();
assert_eq!(stats.acceptance_rate(FixStrategy::AddLifetime), 0.0);
}
#[test]
fn stats_overall_acceptance_rate_mixed() {
let mut stats = ImportStats::new();
stats.record(FixStrategy::AddBorrow, &ImportDecision::Accept);
stats.record(FixStrategy::AddClone, &ImportDecision::Accept);
stats.record(
FixStrategy::Unknown,
&ImportDecision::Reject("unknown".to_string()),
);
stats.record(
FixStrategy::WrapInOption,
&ImportDecision::AcceptWithWarning("w".to_string()),
);
let rate = stats.overall_acceptance_rate();
assert!((rate - 0.75).abs() < 0.01);
}
#[test]
fn stats_overall_acceptance_rate_empty() {
let stats = ImportStats::new();
assert_eq!(stats.overall_acceptance_rate(), 0.0);
}
#[test]
fn stats_overall_acceptance_rate_all_accepted() {
let mut stats = ImportStats::new();
for _ in 0..10 {
stats.record(FixStrategy::AddBorrow, &ImportDecision::Accept);
}
assert!((stats.overall_acceptance_rate() - 1.0).abs() < 0.001);
}
#[test]
fn stats_overall_acceptance_rate_all_rejected() {
let mut stats = ImportStats::new();
for _ in 0..5 {
stats.record(
FixStrategy::Unknown,
&ImportDecision::Reject("bad".to_string()),
);
}
assert_eq!(stats.overall_acceptance_rate(), 0.0);
}
#[test]
fn config_default_values() {
let config = SmartImportConfig::default();
assert_eq!(config.source_language, SourceLanguage::Python);
assert!((config.min_confidence - 0.5).abs() < 0.01);
assert!(config.allow_warnings);
}
#[test]
fn config_custom_values() {
let config = SmartImportConfig {
source_language: SourceLanguage::C,
min_confidence: 0.8,
allow_warnings: false,
};
assert_eq!(config.source_language, SourceLanguage::C);
assert!((config.min_confidence - 0.8).abs() < 0.01);
assert!(!config.allow_warnings);
}
#[test]
fn source_language_equality() {
assert_eq!(SourceLanguage::Python, SourceLanguage::Python);
assert_eq!(SourceLanguage::C, SourceLanguage::C);
assert_eq!(SourceLanguage::Cpp, SourceLanguage::Cpp);
assert_eq!(SourceLanguage::Other, SourceLanguage::Other);
assert_ne!(SourceLanguage::Python, SourceLanguage::C);
}
#[test]
fn fix_strategy_hash_and_eq() {
let mut map: HashMap<FixStrategy, i32> = HashMap::new();
map.insert(FixStrategy::AddClone, 1);
map.insert(FixStrategy::AddBorrow, 2);
map.insert(FixStrategy::AddLifetime, 3);
map.insert(FixStrategy::WrapInOption, 4);
map.insert(FixStrategy::WrapInResult, 5);
map.insert(FixStrategy::AddTypeAnnotation, 6);
map.insert(FixStrategy::Unknown, 7);
assert_eq!(map.len(), 7);
assert_eq!(map[&FixStrategy::AddClone], 1);
assert_eq!(map[&FixStrategy::Unknown], 7);
}
#[test]
fn fix_strategy_debug() {
let strategies = [
FixStrategy::AddClone,
FixStrategy::AddBorrow,
FixStrategy::AddLifetime,
FixStrategy::WrapInOption,
FixStrategy::WrapInResult,
FixStrategy::AddTypeAnnotation,
FixStrategy::Unknown,
];
for s in &strategies {
let debug = format!("{:?}", s);
assert!(!debug.is_empty());
}
}
#[test]
fn fix_strategy_clone() {
let original = FixStrategy::AddBorrow;
let cloned = original;
assert_eq!(original, cloned);
}
#[test]
fn import_decision_debug() {
let decisions = [
ImportDecision::Accept,
ImportDecision::AcceptWithWarning("test warning".to_string()),
ImportDecision::Reject("test reason".to_string()),
];
for d in &decisions {
let debug = format!("{:?}", d);
assert!(!debug.is_empty());
}
}
#[test]
fn import_decision_eq() {
assert_eq!(ImportDecision::Accept, ImportDecision::Accept);
assert_eq!(
ImportDecision::AcceptWithWarning("a".to_string()),
ImportDecision::AcceptWithWarning("a".to_string())
);
assert_ne!(
ImportDecision::AcceptWithWarning("a".to_string()),
ImportDecision::AcceptWithWarning("b".to_string())
);
assert_eq!(
ImportDecision::Reject("x".to_string()),
ImportDecision::Reject("x".to_string())
);
assert_ne!(ImportDecision::Accept, ImportDecision::Reject("r".to_string()));
}