use borrowscope_macro::trace_borrow;
use borrowscope_runtime::{get_events, reset, Event};
use serial_test::serial;
use std::sync::{Mutex, RwLock};
fn has_event_type(events: &[Event], type_name: &str) -> bool {
events.iter().any(|e| format!("{:?}", e).contains(type_name))
}
fn count_event_type(events: &[Event], type_name: &str) -> usize {
events.iter().filter(|e| format!("{:?}", e).contains(type_name)).count()
}
#[test]
#[serial]
fn test_mutex_lock_guard_acquire() {
reset();
#[trace_borrow]
fn lock_mutex() {
let mutex = Mutex::new(42);
let _guard = mutex.lock().unwrap();
}
lock_mutex();
let events = get_events();
assert!(has_event_type(&events, "LockGuardAcquire"), "Should track lock guard acquisition");
assert!(has_event_type(&events, "Lock"), "Should track lock operation");
}
#[test]
#[serial]
fn test_rwlock_read_guard_acquire() {
reset();
#[trace_borrow]
fn read_rwlock() {
let rwlock = RwLock::new(100);
let _guard = rwlock.read().unwrap();
}
read_rwlock();
let events = get_events();
assert!(has_event_type(&events, "LockGuardAcquire"), "Should track read guard acquisition");
let guard_event = events.iter()
.find(|e| format!("{:?}", e).contains("LockGuardAcquire"))
.map(|e| format!("{:?}", e));
assert!(guard_event.unwrap().contains("rwlock_read"), "Should be rwlock_read type");
}
#[test]
#[serial]
fn test_rwlock_write_guard_acquire() {
reset();
#[trace_borrow]
fn write_rwlock() {
let rwlock = RwLock::new(100);
let _guard = rwlock.write().unwrap();
}
write_rwlock();
let events = get_events();
assert!(has_event_type(&events, "LockGuardAcquire"), "Should track write guard acquisition");
let guard_event = events.iter()
.find(|e| format!("{:?}", e).contains("LockGuardAcquire"))
.map(|e| format!("{:?}", e));
assert!(guard_event.unwrap().contains("rwlock_write"), "Should be rwlock_write type");
}
#[test]
#[serial]
fn test_closure_create_ref() {
reset();
#[trace_borrow]
fn create_ref_closure() {
let x = 10;
let _closure = |a: i32| a + x;
}
create_ref_closure();
let events = get_events();
assert!(has_event_type(&events, "ClosureCreate"), "Should track closure creation");
let create_event = events.iter()
.find(|e| format!("{:?}", e).contains("ClosureCreate"))
.map(|e| format!("{:?}", e));
assert!(create_event.unwrap().contains("\"ref\""), "Should be ref capture mode");
}
#[test]
#[serial]
fn test_closure_create_move() {
reset();
#[trace_borrow]
fn create_move_closure() {
let data = vec![1, 2, 3];
let _closure = move || data.len();
}
create_move_closure();
let events = get_events();
assert!(has_event_type(&events, "ClosureCreate"), "Should track closure creation");
let create_event = events.iter()
.find(|e| format!("{:?}", e).contains("ClosureCreate"))
.map(|e| format!("{:?}", e));
assert!(create_event.unwrap().contains("\"move\""), "Should be move capture mode");
}
#[test]
#[serial]
fn test_closure_capture_tracking() {
reset();
#[trace_borrow]
fn closure_with_captures() {
let x = 10;
let y = 20;
let _closure = |a: i32| a + x + y;
}
closure_with_captures();
let events = get_events();
assert!(has_event_type(&events, "ClosureCapture"), "Should track closure captures");
assert!(count_event_type(&events, "ClosureCapture") >= 2, "Should capture at least 2 variables");
}
#[test]
#[serial]
fn test_move_closure_capture_mode() {
reset();
#[trace_borrow]
fn move_closure_captures() {
let data = String::from("hello");
let _closure = move || data.len();
}
move_closure_captures();
let events = get_events();
let capture_event = events.iter()
.find(|e| format!("{:?}", e).contains("ClosureCapture"))
.map(|e| format!("{:?}", e));
assert!(capture_event.is_some(), "Should have capture event");
assert!(capture_event.unwrap().contains("\"move\""), "Capture mode should be move");
}
#[test]
#[serial]
fn test_box_new_tracking() {
reset();
#[trace_borrow]
fn create_box() {
let _boxed = Box::new(42);
}
create_box();
let events = get_events();
assert!(has_event_type(&events, "BoxNew"), "Should track Box::new");
}
#[test]
#[serial]
fn test_box_into_raw_tracking() {
reset();
#[trace_borrow]
fn box_to_raw() {
let boxed = Box::new(42);
let _ptr = Box::into_raw(boxed);
}
box_to_raw();
let events = get_events();
assert!(has_event_type(&events, "BoxNew"), "Should track Box::new");
assert!(has_event_type(&events, "BoxIntoRaw"), "Should track Box::into_raw");
}
#[test]
#[serial]
fn test_box_from_raw_tracking() {
reset();
#[trace_borrow]
fn box_from_raw() {
let boxed = Box::new(42);
let ptr = Box::into_raw(boxed);
let _recovered = unsafe { Box::from_raw(ptr) };
}
box_from_raw();
let events = get_events();
assert!(has_event_type(&events, "BoxNew"), "Should track Box::new");
assert!(has_event_type(&events, "BoxIntoRaw"), "Should track Box::into_raw");
assert!(has_event_type(&events, "BoxFromRaw"), "Should track Box::from_raw");
}
#[test]
#[serial]
fn test_box_raw_roundtrip() {
reset();
#[trace_borrow]
fn box_roundtrip() {
let original = Box::new(String::from("test"));
let ptr = Box::into_raw(original);
let recovered = unsafe { Box::from_raw(ptr) };
assert_eq!(*recovered, "test");
}
box_roundtrip();
let events = get_events();
assert!(has_event_type(&events, "BoxNew"), "Should track Box::new");
assert!(has_event_type(&events, "BoxIntoRaw"), "Should track Box::into_raw");
assert!(has_event_type(&events, "BoxFromRaw"), "Should track Box::from_raw");
}
#[test]
#[serial]
fn test_combined_medium_priority_features() {
reset();
#[trace_borrow]
fn combined_test() {
let mutex = Mutex::new(42);
{
let _guard = mutex.lock().unwrap();
}
let x = 10;
let _closure = |a: i32| a + x;
let boxed = Box::new(100);
let _ptr = Box::into_raw(boxed);
}
combined_test();
let events = get_events();
assert!(has_event_type(&events, "LockGuardAcquire"), "Should track lock guard");
assert!(has_event_type(&events, "ClosureCreate"), "Should track closure");
assert!(has_event_type(&events, "BoxIntoRaw"), "Should track Box::into_raw");
}