use decy_llm::{AnalysisContext, ContextBuilder};
#[test]
fn test_create_context_builder() {
let builder = ContextBuilder::new();
let context = builder.build();
assert!(
context.functions.is_empty(),
"Empty builder should have no functions"
);
}
#[test]
fn test_add_function_to_context() {
let mut builder = ContextBuilder::new();
builder.add_function("process", "void process(int* data, size_t len)");
let context = builder.build();
assert_eq!(context.functions.len(), 1);
assert_eq!(context.functions[0].name, "process");
assert_eq!(
context.functions[0].c_signature,
"void process(int* data, size_t len)"
);
}
#[test]
fn test_add_ownership_inference() {
let mut builder = ContextBuilder::new();
builder
.add_function("transfer", "void transfer(int* dest, int* src)")
.add_ownership(
"transfer",
"dest",
"mutable_borrow",
0.95,
"Mutated via assignment",
)
.add_ownership(
"transfer",
"src",
"immutable_borrow",
0.9,
"Read-only access",
);
let context = builder.build();
let func = &context.functions[0];
assert!(func.ownership.contains_key("dest"));
assert!(func.ownership.contains_key("src"));
let dest_ownership = &func.ownership["dest"];
assert_eq!(dest_ownership.kind, "mutable_borrow");
assert!((dest_ownership.confidence - 0.95).abs() < 0.01);
let src_ownership = &func.ownership["src"];
assert_eq!(src_ownership.kind, "immutable_borrow");
}
#[test]
fn test_add_lifetime_info() {
let mut builder = ContextBuilder::new();
builder
.add_function("create", "int* create()")
.add_lifetime("create", "result", 0, true);
let context = builder.build();
let func = &context.functions[0];
assert_eq!(func.lifetimes.len(), 1);
assert_eq!(func.lifetimes[0].variable, "result");
assert_eq!(func.lifetimes[0].scope_depth, 0);
assert!(func.lifetimes[0].escapes);
}
#[test]
fn test_add_lock_mapping() {
let mut builder = ContextBuilder::new();
builder
.add_function("sync_update", "void sync_update()")
.add_lock_mapping(
"sync_update",
"counter_mutex",
vec!["counter".to_string(), "total".to_string()],
);
let context = builder.build();
let func = &context.functions[0];
assert!(func.lock_mappings.contains_key("counter_mutex"));
let protected = &func.lock_mappings["counter_mutex"];
assert!(protected.contains(&"counter".to_string()));
assert!(protected.contains(&"total".to_string()));
}
#[test]
fn test_serialize_to_json() {
let mut builder = ContextBuilder::new();
builder
.add_function("example", "int example(int* ptr)")
.add_ownership("example", "ptr", "owning", 0.85, "Allocated via malloc");
let json = builder.to_json().expect("JSON serialization failed");
let parsed: serde_json::Value = serde_json::from_str(&json).expect("Invalid JSON");
assert!(parsed["functions"].is_array());
assert_eq!(parsed["functions"][0]["name"], "example");
assert_eq!(parsed["functions"][0]["ownership"]["ptr"]["kind"], "owning");
}
#[test]
fn test_multiple_functions() {
let mut builder = ContextBuilder::new();
builder
.add_function("init", "void init()")
.add_function("cleanup", "void cleanup()")
.add_function("process", "int process(int* data)");
let context = builder.build();
assert_eq!(context.functions.len(), 3);
let names: Vec<&str> = context.functions.iter().map(|f| f.name.as_str()).collect();
assert!(names.contains(&"init"));
assert!(names.contains(&"cleanup"));
assert!(names.contains(&"process"));
}
#[test]
fn test_complex_function_context() {
let mut builder = ContextBuilder::new();
builder
.add_function("thread_safe_update", "void thread_safe_update(Counter* c)")
.add_ownership(
"thread_safe_update",
"c",
"mutable_borrow",
0.9,
"Modified under lock",
)
.add_lifetime("thread_safe_update", "c", 0, false)
.add_lock_mapping("thread_safe_update", "mutex", vec!["c".to_string()]);
let context = builder.build();
let func = &context.functions[0];
assert!(!func.ownership.is_empty());
assert!(!func.lifetimes.is_empty());
assert!(!func.lock_mappings.is_empty());
}
#[test]
fn test_json_schema_structure() {
let mut builder = ContextBuilder::new();
builder
.add_function("test_fn", "void test_fn(int* ptr)")
.add_ownership("test_fn", "ptr", "immutable_borrow", 0.8, "Read only")
.add_lifetime("test_fn", "ptr", 1, false)
.add_lock_mapping("test_fn", "lock", vec!["data".to_string()]);
let json = builder.to_json().expect("Serialization failed");
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
let func = &parsed["functions"][0];
assert!(func["name"].is_string());
assert!(func["c_signature"].is_string());
assert!(func["ownership"].is_object());
assert!(func["lifetimes"].is_array());
assert!(func["lock_mappings"].is_object());
let ownership = &func["ownership"]["ptr"];
assert!(ownership["kind"].is_string());
assert!(ownership["confidence"].is_number());
assert!(ownership["reason"].is_string());
let lifetime = &func["lifetimes"][0];
assert!(lifetime["variable"].is_string());
assert!(lifetime["scope_depth"].is_number());
assert!(lifetime["escapes"].is_boolean());
}
#[test]
fn test_json_roundtrip() {
let mut builder = ContextBuilder::new();
builder
.add_function("roundtrip", "int roundtrip(int* x, int* y)")
.add_ownership("roundtrip", "x", "mutable_borrow", 0.95, "Modified")
.add_ownership("roundtrip", "y", "immutable_borrow", 0.88, "Read only")
.add_lifetime("roundtrip", "x", 0, false)
.add_lifetime("roundtrip", "y", 0, false);
let json = builder.to_json().expect("Serialization failed");
let deserialized: AnalysisContext =
serde_json::from_str(&json).expect("Deserialization failed");
assert_eq!(deserialized.functions.len(), 1);
assert_eq!(deserialized.functions[0].name, "roundtrip");
assert_eq!(deserialized.functions[0].ownership.len(), 2);
assert_eq!(deserialized.functions[0].lifetimes.len(), 2);
}