use super::*;
use crate::core::types::AllocationInfo;
fn create_test_allocation(
ptr: usize,
size: usize,
var_name: Option<String>,
type_name: Option<String>,
) -> AllocationInfo {
AllocationInfo {
ptr,
size,
var_name,
type_name,
scope_name: Some("test_scope".to_string()),
timestamp_alloc: 1000000,
timestamp_dealloc: Some(2000000),
thread_id: "test_thread".to_string(),
borrow_count: 0,
stack_trace: Some(vec!["test_function".to_string()]),
is_leaked: false,
lifetime_ms: Some(1000),
borrow_info: None,
clone_info: None,
ownership_history_available: false,
smart_pointer_info: None,
memory_layout: None,
generic_info: None,
dynamic_type_info: None,
runtime_state: None,
stack_allocation: None,
temporary_object: None,
fragmentation_analysis: None,
generic_instantiation: None,
type_relationships: None,
type_usage: None,
function_call_tracking: None,
lifecycle_tracking: None,
access_tracking: None,
drop_chain_analysis: None,
}
}
#[test]
fn test_variable_registry_register_and_get() {
let _ = VariableRegistry::clear_registry();
let address = 0x10000; let var_name = "test_var".to_string();
let type_name = "String".to_string();
let size = 24;
let result =
VariableRegistry::register_variable(address, var_name.clone(), type_name.clone(), size);
assert!(result.is_ok());
let var_info = VariableRegistry::get_variable_info(address);
assert!(var_info.is_some());
let info = var_info.unwrap();
assert_eq!(info.var_name, var_name);
assert_eq!(info.type_name, type_name);
assert_eq!(info.size, size);
assert!(info.timestamp > 0);
assert!(info.thread_id > 0);
assert_eq!(info.memory_usage, size as u64);
}
#[test]
fn test_variable_registry_mark_destroyed() {
let address = 0x2000;
let destruction_time = 5000000;
let result = VariableRegistry::mark_variable_destroyed(address, destruction_time);
assert!(result.is_ok());
}
#[test]
fn test_get_all_variables() {
let _ = VariableRegistry::clear_registry();
let address1 = 0x30000; let address2 = 0x40000;
let result1 =
VariableRegistry::register_variable(address1, "var1".to_string(), "i32".to_string(), 4);
let result2 =
VariableRegistry::register_variable(address2, "var2".to_string(), "String".to_string(), 24);
assert!(result1.is_ok());
assert!(result2.is_ok());
let all_vars = VariableRegistry::get_all_variables();
assert!(
all_vars.contains_key(&address1) || all_vars.contains_key(&address2) || !all_vars.is_empty(),
"Registry should contain at least one variable or the ones we just added. Registry size: {}",
all_vars.len()
);
}
#[test]
fn test_enhance_allocations_with_registry() {
let explicit_alloc = create_test_allocation(
0x60000,
50,
Some("explicit_var".to_string()),
Some("i64".to_string()),
);
let system_alloc = create_test_allocation(0x70000, 200, None, None);
let allocations = vec![explicit_alloc, system_alloc];
let enhanced = VariableRegistry::enhance_allocations_with_registry(&allocations);
assert_eq!(enhanced.len(), 2);
let user_count = enhanced
.iter()
.filter(|a| a["allocation_source"] == "user")
.count();
let system_count = enhanced
.iter()
.filter(|a| a["allocation_source"] == "system")
.count();
assert_eq!(user_count, 1, "Should have exactly one user allocation");
assert_eq!(system_count, 1, "Should have exactly one system allocation");
let explicit_result = enhanced
.iter()
.find(|a| a["ptr"].as_u64().unwrap() as usize == 0x60000)
.expect("Should find explicit allocation");
assert_eq!(explicit_result["allocation_source"], "user");
assert_eq!(explicit_result["variable_name"], "explicit_var");
assert_eq!(explicit_result["type_name"], "i64");
assert_eq!(explicit_result["tracking_method"], "explicit_tracking");
let system_result = enhanced
.iter()
.find(|a| a["ptr"].as_u64().unwrap() as usize == 0x70000)
.expect("Should find system allocation");
assert_eq!(system_result["allocation_source"], "system");
assert_eq!(system_result["tracking_method"], "automatic_inference");
assert!(!system_result["variable_name"].as_str().unwrap().is_empty());
assert!(!system_result["type_name"].as_str().unwrap().is_empty());
let test_addr = 0x50000;
if VariableRegistry::clear_registry().is_ok()
&& VariableRegistry::register_variable(
test_addr,
"tracked_var".to_string(),
"Vec<u8>".to_string(),
100,
)
.is_ok()
{
let registry_alloc = create_test_allocation(test_addr, 100, None, None);
let enhanced_with_registry =
VariableRegistry::enhance_allocations_with_registry(&[registry_alloc]);
if enhanced_with_registry.len() == 1 {
let result = &enhanced_with_registry[0];
if result["allocation_source"] == "user" {
assert_eq!(result["variable_name"], "tracked_var");
assert_eq!(result["type_name"], "Vec<u8>");
assert_eq!(result["tracking_method"], "track_var_macro");
}
}
}
}
#[test]
fn test_extract_scope_from_var_name() {
let _ = VariableRegistry::clear_registry();
let result1 = VariableRegistry::extract_scope_from_var_name("scope::variable");
assert!(!result1.is_empty(), "Scope name should not be empty");
assert!(
result1 == "scope"
|| result1 == "user_code_scope"
|| result1 == "user_scope"
|| result1.starts_with("function_")
|| result1 == "main_function"
|| result1 == "test_function",
"Expected a valid scope name, but got: '{result1}'"
);
let result2 = VariableRegistry::extract_scope_from_var_name("my_vec");
assert!(!result2.is_empty(), "Scope name should not be empty");
assert!(
result2 == "user_scope"
|| result2 == "user_code_scope"
|| result2.starts_with("function_")
|| result2 == "main_function"
|| result2 == "test_function",
"Expected a valid scope name, but got: '{result2}'"
);
let result3 = VariableRegistry::extract_scope_from_var_name("main_variable");
assert!(!result3.is_empty(), "Scope name should not be empty");
assert!(
result3 == "main_function"
|| result3 == "user_code_scope"
|| result3 == "user_scope"
|| result3.starts_with("function_")
|| result3 == "test_function",
"Expected a valid scope name, but got: '{result3}'"
);
let result4 = VariableRegistry::extract_scope_from_var_name("test_variable");
assert!(!result4.is_empty(), "Scope name should not be empty");
assert!(
result4 == "test_function"
|| result4 == "user_code_scope"
|| result4 == "user_scope"
|| result4.starts_with("function_")
|| result4 == "main_function",
"Expected a valid scope name, but got: '{result4}'"
);
}
#[test]
fn test_calculate_lifetime_stats() {
let lifetimes = vec![0, 1, 5, 15, 50, 150, 500, 1500];
let stats = VariableRegistry::calculate_lifetime_stats(&lifetimes);
assert_eq!(stats["count"], 8);
assert_eq!(stats["categories"]["very_short"], 2); assert_eq!(stats["categories"]["short"], 1); assert_eq!(stats["categories"]["medium"], 2); assert_eq!(stats["categories"]["long"], 2); assert_eq!(stats["categories"]["very_long"], 1);
let empty_lifetimes: Vec<u64> = vec![];
let empty_stats = VariableRegistry::calculate_lifetime_stats(&empty_lifetimes);
assert_eq!(empty_stats["count"], 0);
}
#[test]
fn test_group_by_scope() {
let active_allocations = vec![
serde_json::json!({
"scope_name": "main_function",
"size": 100,
"ptr": 0x1000
}),
serde_json::json!({
"scope_name": "test_function",
"size": 200,
"ptr": 0x2000
}),
];
let history_allocations = vec![serde_json::json!({
"scope_name": "main_function",
"size": 150,
"ptr": 0x3000
})];
let grouped = VariableRegistry::group_by_scope(&active_allocations, &history_allocations);
assert!(
grouped["main_function"]["allocation_count"]
.as_u64()
.unwrap()
>= 2
);
assert!(
grouped["test_function"]["allocation_count"]
.as_u64()
.unwrap()
>= 1
);
assert!(
grouped["main_function"]["total_size_bytes"]
.as_u64()
.unwrap()
>= 250
);
}
#[test]
fn test_get_scope_summary() {
let mut registry = HashMap::new();
registry.insert(
0x1000,
VariableInfo {
var_name: "main_var".to_string(),
type_name: "i32".to_string(),
timestamp: 1000,
size: 4,
thread_id: 1,
memory_usage: 4,
},
);
registry.insert(
0x2000,
VariableInfo {
var_name: "test_var".to_string(),
type_name: "String".to_string(),
timestamp: 2000,
size: 24,
thread_id: 2,
memory_usage: 24,
},
);
let summary = VariableRegistry::get_scope_summary(®istry);
if let Some(obj) = summary.as_object() {
let total_count: u64 = obj.values().map(|v| v.as_u64().unwrap_or(0)).sum();
assert!(total_count >= 2);
let has_main = obj
.get("main_function")
.and_then(|v| v.as_u64())
.unwrap_or(0)
>= 1;
let has_test = obj
.get("test_function")
.and_then(|v| v.as_u64())
.unwrap_or(0)
>= 1;
let has_user_code = obj
.get("user_code_scope")
.and_then(|v| v.as_u64())
.unwrap_or(0)
>= 1;
assert!(has_main || has_test || has_user_code);
} else {
panic!("Expected summary to be a JSON object");
}
}
#[test]
fn test_variable_info_creation() {
let info = VariableInfo {
var_name: "test_var".to_string(),
type_name: "Vec<u8>".to_string(),
timestamp: 1234567890,
size: 1024,
thread_id: 1,
memory_usage: 1024,
};
assert_eq!(info.var_name, "test_var");
assert_eq!(info.type_name, "Vec<u8>");
assert_eq!(info.timestamp, 1234567890);
assert_eq!(info.size, 1024);
assert_eq!(info.thread_id, 1);
assert_eq!(info.memory_usage, 1024);
}
#[test]
fn test_variable_info_debug() {
let info = VariableInfo {
var_name: "x".to_string(),
type_name: "i32".to_string(),
timestamp: 0,
size: 4,
thread_id: 0,
memory_usage: 4,
};
let debug_str = format!("{:?}", info);
assert!(debug_str.contains("VariableInfo"));
assert!(debug_str.contains("var_name"));
}
#[test]
fn test_variable_info_clone() {
let info = VariableInfo {
var_name: "original".to_string(),
type_name: "String".to_string(),
timestamp: 100,
size: 24,
thread_id: 1,
memory_usage: 24,
};
let cloned = info.clone();
assert_eq!(cloned.var_name, info.var_name);
assert_eq!(cloned.type_name, info.type_name);
}
#[test]
fn test_get_all_variables_empty() {
let _ = VariableRegistry::clear_registry();
let vars = VariableRegistry::get_all_variables();
let _ = vars;
}
#[test]
fn test_register_multiple_variables() {
let _ = VariableRegistry::clear_registry();
let result1 =
VariableRegistry::register_variable(0x10001, "a".to_string(), "i32".to_string(), 4);
let result2 =
VariableRegistry::register_variable(0x10002, "b".to_string(), "i64".to_string(), 8);
let result3 =
VariableRegistry::register_variable(0x10003, "c".to_string(), "String".to_string(), 24);
assert!(result1.is_ok());
assert!(result2.is_ok());
assert!(result3.is_ok());
}
#[test]
fn test_get_variable_info_not_found() {
let _ = VariableRegistry::clear_registry();
let info = VariableRegistry::get_variable_info(0xDEADBEEF);
assert!(info.is_none());
}
#[test]
fn test_lifetime_stats_categories() {
let lifetimes = vec![0, 1, 2, 10, 20, 100, 200, 1000, 2000];
let stats = VariableRegistry::calculate_lifetime_stats(&lifetimes);
assert_eq!(stats["count"], 9);
let very_short = stats["categories"]["very_short"].as_u64().unwrap();
let short = stats["categories"]["short"].as_u64().unwrap();
let medium = stats["categories"]["medium"].as_u64().unwrap();
let long = stats["categories"]["long"].as_u64().unwrap();
let very_long = stats["categories"]["very_long"].as_u64().unwrap();
let total = very_short + short + medium + long + very_long;
assert_eq!(total, 9);
}
#[test]
fn test_lifetime_stats_single_value() {
let lifetimes = vec![50];
let stats = VariableRegistry::calculate_lifetime_stats(&lifetimes);
assert_eq!(stats["count"], 1);
assert_eq!(stats["categories"]["medium"], 1);
}
#[test]
fn test_enhance_allocations_mixed() {
let _ = VariableRegistry::clear_registry();
let alloc1 = create_test_allocation(
0x80001,
100,
Some("user_var".to_string()),
Some("Vec<u8>".to_string()),
);
let alloc2 = create_test_allocation(0x80002, 200, None, None);
let alloc3 = create_test_allocation(
0x80003,
300,
Some("another".to_string()),
Some("String".to_string()),
);
let allocations = vec![alloc1, alloc2, alloc3];
let enhanced = VariableRegistry::enhance_allocations_with_registry(&allocations);
assert_eq!(enhanced.len(), 3);
let user_count = enhanced
.iter()
.filter(|a| a["allocation_source"] == "user")
.count();
assert_eq!(user_count, 2);
}
#[test]
fn test_calculate_percentile_empty() {
let values: Vec<u64> = vec![];
let result = VariableRegistry::calculate_percentile(&values, 50.0);
assert_eq!(result, 0);
}
#[test]
fn test_calculate_percentile_single() {
let values = vec![100];
let result = VariableRegistry::calculate_percentile(&values, 50.0);
assert_eq!(result, 100);
}
#[test]
fn test_calculate_percentile_multiple() {
let values = vec![10, 20, 30, 40, 50];
let p50 = VariableRegistry::calculate_percentile(&values, 50.0);
let p90 = VariableRegistry::calculate_percentile(&values, 90.0);
assert!((20..=40).contains(&p50));
assert!((40..=50).contains(&p90));
}
#[test]
fn test_get_stats_basic() {
let _ = VariableRegistry::clear_registry();
let (total, recent) = VariableRegistry::get_stats();
let _ = (total, recent);
}
#[test]
fn test_get_stats_with_variables() {
let _ = VariableRegistry::clear_registry();
let _ =
VariableRegistry::register_variable(0x99901, "stat_var".to_string(), "i32".to_string(), 4);
let (total, _recent) = VariableRegistry::get_stats();
assert!(total <= 1);
}
#[test]
fn test_analyze_lifecycle_statistics() {
let user_active = vec![serde_json::json!({
"lifetime_ms": 50,
"is_active": true,
"timestamp_dealloc": null
})];
let user_history = vec![serde_json::json!({
"lifetime_ms": 100,
"is_active": false,
"timestamp_dealloc": 2000
})];
let system_active = vec![serde_json::json!({
"lifetime_ms": 25,
"is_active": true,
"timestamp_dealloc": null
})];
let system_history = vec![];
let result = VariableRegistry::analyze_lifecycle_statistics(
&user_active,
&user_history,
&system_active,
&system_history,
);
assert_eq!(result["user_allocations"]["total_count"], 2);
assert_eq!(result["system_allocations"]["total_count"], 1);
assert_eq!(result["user_allocations"]["active_count"], 1);
}
#[test]
fn test_analyze_deallocation_patterns() {
let user_active = vec![serde_json::json!({
"timestamp_dealloc": null
})];
let user_history = vec![serde_json::json!({
"timestamp_dealloc": 2000
})];
let system_active = vec![];
let system_history = vec![];
let result = VariableRegistry::analyze_deallocation_patterns(
&user_active,
&user_history,
&system_active,
&system_history,
);
assert_eq!(result["user_deallocations"]["total_deallocated"], 1);
assert_eq!(result["user_deallocations"]["still_active"], 1);
}
#[test]
fn test_clear_registry() {
let _ = VariableRegistry::clear_registry();
let result = VariableRegistry::register_variable(
0xABC01,
"clear_test".to_string(),
"i32".to_string(),
4,
);
assert!(result.is_ok());
let clear_result = VariableRegistry::clear_registry();
assert!(clear_result.is_ok());
}
#[test]
fn test_mark_variable_destroyed() {
let result = VariableRegistry::mark_variable_destroyed(0x12345, 999999);
assert!(result.is_ok());
}
#[test]
fn test_extract_function_name_from_backtrace() {
let line = "test::module::function_name::h12345678";
let result = VariableRegistry::extract_function_name_from_backtrace(line);
let _ = result;
}
#[test]
fn test_extract_function_name_system_filter() {
let line = "std::alloc::alloc::h12345678";
let result = VariableRegistry::extract_function_name_from_backtrace(line);
assert!(result.is_none() || !result.unwrap().contains("std"));
}
#[test]
fn test_extract_scope_from_var_name_main() {
let scope = VariableRegistry::extract_scope_from_var_name("main_buffer");
assert!(!scope.is_empty());
}
#[test]
fn test_extract_scope_from_var_name_test() {
let scope = VariableRegistry::extract_scope_from_var_name("test_data");
assert!(!scope.is_empty());
}
#[test]
fn test_extract_scope_from_var_name_fn() {
let scope = VariableRegistry::extract_scope_from_var_name("fn_result");
assert!(!scope.is_empty());
}
#[test]
fn test_extract_scope_from_var_name_vec() {
let scope = VariableRegistry::extract_scope_from_var_name("my_vec_data");
assert!(!scope.is_empty());
}
#[test]
fn test_extract_scope_from_var_name_string() {
let scope = VariableRegistry::extract_scope_from_var_name("my_string_buffer");
assert!(!scope.is_empty());
}
#[test]
fn test_extract_scope_from_var_name_boxed() {
let scope = VariableRegistry::extract_scope_from_var_name("boxed_value");
assert!(!scope.is_empty());
}
#[test]
fn test_extract_scope_from_var_name_rc() {
let scope = VariableRegistry::extract_scope_from_var_name("rc_pointer");
assert!(!scope.is_empty());
}
#[test]
fn test_extract_scope_from_var_name_arc() {
let scope = VariableRegistry::extract_scope_from_var_name("arc_shared");
assert!(!scope.is_empty());
}
#[test]
fn test_extract_scope_from_var_name_module() {
let scope = VariableRegistry::extract_scope_from_var_name("mymodule::myvar");
assert!(!scope.is_empty());
}
#[test]
fn test_extract_scope_from_var_name_default() {
let scope = VariableRegistry::extract_scope_from_var_name("simple_var");
assert!(!scope.is_empty());
}
#[test]
fn test_get_current_scope_name() {
let scope = VariableRegistry::get_current_scope_name();
let _ = scope;
}
#[test]
fn test_get_scope_from_tracker() {
let scope = VariableRegistry::get_scope_from_tracker();
let _ = scope;
}
#[test]
fn live_allocation_is_not_counted_as_leaked() {
let live = serde_json::json!({
"ptr": 0x1000,
"size": 64,
"is_active": true,
"timestamp_dealloc": null,
"is_leaked": false,
"lifetime_ms": null
});
let leaked = serde_json::json!({
"ptr": 0x2000,
"size": 32,
"is_active": true,
"timestamp_dealloc": null,
"is_leaked": true,
"lifetime_ms": null
});
let report = VariableRegistry::analyze_lifecycle_statistics(&[live, leaked], &[], &[], &[]);
assert_eq!(
report["user_allocations"]["active_count"], 2,
"both blocks are still live"
);
assert_eq!(
report["user_allocations"]["leaked_count"], 1,
"only the explicitly flagged allocation may count as a leak"
);
}