use borrowscope_runtime::*;
lazy_static::lazy_static! {
static ref TEST_LOCK: parking_lot::Mutex<()> = parking_lot::Mutex::new(());
}
#[test]
fn test_id_based_tracking_concept() {
let _lock = TEST_LOCK.lock();
reset();
let x = track_new("x", 42);
let _y = track_borrow("y", &x);
let _z = track_move("x", "z", x);
let events = get_events();
assert!(events.len() >= 3);
assert!(events[0].is_new());
assert!(events[1].is_borrow());
assert!(events[2].is_move());
}
#[test]
fn test_location_tracking_concept() {
let _lock = TEST_LOCK.lock();
reset();
let _x = track_new("x @ src/main.rs:10:5", 42);
let events = get_events();
assert_eq!(events.len(), 1);
if let Some(name) = events[0].var_name() {
assert!(name.contains("@"));
}
}
#[test]
fn test_rc_with_id_tracking_concept() {
use std::rc::Rc;
let _lock = TEST_LOCK.lock();
reset();
let x = track_rc_new("x", Rc::new(42));
let _y = track_rc_clone("y", "x", Rc::clone(&x));
let _z = track_rc_clone("z", "x", Rc::clone(&x));
let events = get_events();
assert_eq!(events.len(), 3);
assert_eq!(events[0].strong_count(), Some(1));
assert_eq!(events[1].strong_count(), Some(2));
assert_eq!(events[2].strong_count(), Some(3));
match &events[1] {
Event::RcClone { source_id, .. } => {
assert_eq!(source_id, "x");
}
_ => panic!("Expected RcClone"),
}
}
#[test]
fn test_arc_with_id_tracking_concept() {
use std::sync::Arc;
let _lock = TEST_LOCK.lock();
reset();
let x = track_arc_new("x", Arc::new(100));
let _y = track_arc_clone("y", "x", Arc::clone(&x));
let events = get_events();
assert_eq!(events.len(), 2);
assert!(events[0].is_arc());
assert!(events[1].is_arc());
assert_eq!(events[1].strong_count(), Some(2));
}
#[test]
fn test_complex_ownership_graph_concept() {
use std::rc::Rc;
let _lock = TEST_LOCK.lock();
reset();
let data = track_rc_new("data", Rc::new(vec![1, 2, 3]));
let _reader1 = track_rc_clone("reader1", "data", Rc::clone(&data));
let _reader2 = track_rc_clone("reader2", "data", Rc::clone(&data));
let _processor = track_rc_clone("processor", "data", Rc::clone(&data));
let events = get_events();
assert_eq!(events.len(), 4);
let rc_events: Vec<_> = events.iter().filter(|e| e.is_rc()).collect();
assert_eq!(rc_events.len(), 4);
assert_eq!(events[3].strong_count(), Some(4));
}
#[test]
fn test_borrow_chain_tracking_concept() {
let _lock = TEST_LOCK.lock();
reset();
let x = track_new("x", String::from("hello"));
let r1 = track_borrow("r1", &x);
let _r2 = track_borrow("r2", &r1);
let events = get_events();
assert_eq!(events.len(), 3);
assert!(events[0].is_new());
assert!(events[1].is_borrow());
assert!(events[2].is_borrow());
}
#[test]
fn test_type_information_tracking() {
let _lock = TEST_LOCK.lock();
reset();
let _x = track_new("x", vec![1, 2, 3, 4, 5]);
let events = get_events();
assert_eq!(events.len(), 1);
match &events[0] {
Event::New { type_name, .. } => {
assert!(type_name.contains("Vec") || type_name.contains("alloc"));
}
_ => panic!("Expected New event"),
}
}
#[test]
fn test_mutable_vs_immutable_tracking() {
let _lock = TEST_LOCK.lock();
reset();
let mut x = track_new("x", vec![1, 2, 3]);
let _r1 = track_borrow("r1", &x);
let _r2 = track_borrow_mut("r2", &mut x);
let events = get_events();
assert_eq!(events.len(), 3);
match &events[1] {
Event::Borrow { mutable, .. } => assert!(!mutable),
_ => panic!("Expected Borrow"),
}
match &events[2] {
Event::Borrow { mutable, .. } => assert!(*mutable),
_ => panic!("Expected Borrow"),
}
}
#[test]
fn test_drop_tracking_concept() {
let _lock = TEST_LOCK.lock();
reset();
{
let _x = track_new("x", 42);
track_drop("x");
}
let events = get_events();
assert_eq!(events.len(), 2);
assert!(events[0].is_new());
assert!(events[1].is_drop());
}
#[test]
fn test_concurrent_tracking_concept() {
use std::sync::Arc;
use std::thread;
let _lock = TEST_LOCK.lock();
reset();
let data = track_arc_new("data", Arc::new(42));
let data_clone = track_arc_clone("data_clone", "data", Arc::clone(&data));
let handle = thread::spawn(move || {
assert_eq!(*data_clone, 42);
});
handle.join().unwrap();
let events = get_events();
assert_eq!(events.len(), 2);
assert!(events[0].is_arc());
assert!(events[1].is_arc());
}
#[test]
fn test_weak_reference_tracking_concept() {
use std::rc::Rc;
let _lock = TEST_LOCK.lock();
reset();
let _x = track_rc_new("x", Rc::new(42));
let events = get_events();
assert_eq!(events.len(), 1);
assert_eq!(events[0].weak_count(), Some(0));
}
#[test]
fn test_event_query_api() {
use std::rc::Rc;
let _lock = TEST_LOCK.lock();
reset();
let _x = track_new("x", 42);
let _rc = track_rc_new("rc", Rc::new(100));
let _y = track_borrow("y", &_x);
let events = get_events();
let new_events: Vec<_> = events.iter().filter(|e| e.is_new()).collect();
let rc_events: Vec<_> = events.iter().filter(|e| e.is_rc()).collect();
let borrow_events: Vec<_> = events.iter().filter(|e| e.is_borrow()).collect();
assert_eq!(new_events.len(), 1);
assert_eq!(rc_events.len(), 1);
assert_eq!(borrow_events.len(), 1);
}
#[test]
fn test_timestamp_ordering() {
let _lock = TEST_LOCK.lock();
reset();
let _x = track_new("x", 1);
let _y = track_new("y", 2);
let _z = track_new("z", 3);
let events = get_events();
assert_eq!(events.len(), 3);
assert!(events[0].timestamp() < events[1].timestamp());
assert!(events[1].timestamp() < events[2].timestamp());
}
#[test]
fn test_graph_building_from_events() {
let _lock = TEST_LOCK.lock();
reset();
let x = track_new("x", 42);
let _y = track_borrow("y", &x);
track_drop("x");
let graph = get_graph();
assert!(!graph.nodes.is_empty());
let stats = graph.stats();
assert_eq!(stats.total_variables, 1);
}