use runmat_builtins::Value;
use runmat_gc::*;
use std::sync::atomic::Ordering;
#[test]
fn test_minor_collection() {
gc_test_context(|| {
let initial_stats = gc_stats();
let initial_collections = initial_stats.minor_collections.load(Ordering::Relaxed);
let _collected = gc_collect_minor().expect("minor collection should succeed");
let final_stats = gc_stats();
let final_collections = final_stats.minor_collections.load(Ordering::Relaxed);
assert_eq!(final_collections - initial_collections, 1);
});
}
#[test]
fn test_major_collection() {
gc_test_context(|| {
let initial_stats = gc_stats();
let initial_collections = initial_stats.major_collections.load(Ordering::Relaxed);
let _collected = gc_collect_major().expect("major collection should succeed");
let final_stats = gc_stats();
let final_collections = final_stats.major_collections.load(Ordering::Relaxed);
assert_eq!(final_collections - initial_collections, 1);
});
}
#[test]
fn test_collection_with_live_objects() {
gc_test_context(|| {
let config = GcConfig::default();
gc_configure(config).expect("configuration should succeed");
let mut live_objects = Vec::new();
for i in 0..10 {
let ptr = gc_allocate(Value::Num(i as f64)).expect("allocation should succeed");
gc_add_root(ptr.clone()).expect("root registration should succeed"); live_objects.push(ptr);
}
for i in 0..10 {
let _ptr =
gc_allocate(Value::String(format!("temp_{i}"))).expect("allocation should succeed");
}
let before_collection = gc_stats();
let before_allocations = before_collection.total_allocations.load(Ordering::Relaxed);
let _collected = gc_collect_minor().expect("collection should succeed");
for (i, ptr) in live_objects.iter().enumerate() {
assert_eq!(**ptr, Value::Num(i as f64));
}
let after_collection = gc_stats();
let after_allocations = after_collection.total_allocations.load(Ordering::Relaxed);
assert_eq!(before_allocations, after_allocations);
for ptr in &live_objects {
gc_remove_root(ptr.clone()).expect("root removal should succeed");
}
});
}
#[test]
fn test_collection_frequency() {
gc_test_context(|| {
let config = GcConfig {
minor_gc_threshold: 0.5, young_generation_size: 64 * 1024, ..GcConfig::default()
};
gc_configure(config).expect("configuration should succeed");
let initial_stats = gc_stats();
let initial_collections = initial_stats.minor_collections.load(Ordering::Relaxed);
for i in 0..50 {
let _ptr = gc_allocate(Value::Num(i as f64)).expect("allocation should succeed");
if i % 10 == 0 {
let _ = gc_collect_minor().expect("collection should succeed");
}
}
let final_stats = gc_stats();
let final_collections = final_stats.minor_collections.load(Ordering::Relaxed);
assert!(final_collections > initial_collections);
assert!(final_collections - initial_collections >= 5); });
}
#[test]
fn test_collection_timing() {
gc_test_context(|| {
use runmat_time::Instant;
let config = GcConfig::default();
gc_configure(config).expect("configuration should succeed");
for i in 0..20 {
let _ptr = gc_allocate(Value::String(format!("object_{i}")))
.expect("allocation should succeed");
}
let start = Instant::now();
let collected = gc_collect_minor().expect("collection should succeed");
let duration = start.elapsed();
assert!(duration.as_millis() < 1000);
println!("Collected {collected} objects in {duration:?}");
});
}
#[test]
fn test_collection_with_different_generations() {
gc_test_context(|| {
let config = GcConfig::default();
gc_configure(config).expect("configuration should succeed");
let mut young_objects = Vec::new();
for i in 0..5 {
let ptr = gc_allocate(Value::Num(i as f64)).expect("allocation should succeed");
gc_add_root(ptr.clone()).expect("root registration should succeed"); young_objects.push(ptr);
}
let _collected_minor = gc_collect_minor().expect("minor collection should succeed");
for i in 10..15 {
let _ptr = gc_allocate(Value::Num(i as f64)).expect("allocation should succeed");
}
let _collected_major = gc_collect_major().expect("major collection should succeed");
for (i, ptr) in young_objects.iter().enumerate() {
assert_eq!(**ptr, Value::Num(i as f64));
}
for ptr in &young_objects {
gc_remove_root(ptr.clone()).expect("root removal should succeed");
}
});
}
#[test]
fn test_collection_stats_accuracy() {
gc_test_context(|| {
let initial_stats = gc_stats();
let initial_minor = initial_stats.minor_collections.load(Ordering::Relaxed);
let initial_major = initial_stats.major_collections.load(Ordering::Relaxed);
for _ in 0..3 {
let _ = gc_collect_minor().expect("minor collection should succeed");
}
for _ in 0..2 {
let _ = gc_collect_major().expect("major collection should succeed");
}
let final_stats = gc_stats();
let final_minor = final_stats.minor_collections.load(Ordering::Relaxed);
let final_major = final_stats.major_collections.load(Ordering::Relaxed);
assert_eq!(final_minor - initial_minor, 3);
assert_eq!(final_major - initial_major, 2);
});
}
#[test]
fn test_collection_with_roots() {
gc_test_context(|| {
use runmat_gc::{gc_register_root, gc_unregister_root, GlobalRoot};
let config = GcConfig::default();
gc_configure(config).expect("configuration should succeed");
let root_values = vec![Value::Num(100.0), Value::String("persistent".to_string())];
let root = Box::new(GlobalRoot::new(root_values, "test root".to_string()));
let root_id = gc_register_root(root).expect("root registration should succeed");
for i in 0..10 {
let _ptr = gc_allocate(Value::Num(i as f64)).expect("allocation should succeed");
}
let _collected = gc_collect_major().expect("collection should succeed");
gc_unregister_root(root_id).expect("root unregistration should succeed");
});
}
#[test]
fn test_allocation_after_collection() {
gc_test_context(|| {
for i in 0..10 {
let _ptr = gc_allocate(Value::Num(i as f64)).expect("allocation should succeed");
}
let _collected = gc_collect_minor().expect("collection should succeed");
let new_ptr = gc_allocate(Value::String("post-collection".to_string()))
.expect("allocation after collection should succeed");
assert_eq!(*new_ptr, Value::String("post-collection".to_string()));
});
}
#[cfg(feature = "debug-gc")]
#[test]
fn test_force_collection() {
use runmat_gc::gc_force_collect;
gc_test_context(|| {
for i in 0..5 {
let _ptr = gc_allocate(Value::Num(i as f64)).expect("allocation should succeed");
}
let _collected = gc_force_collect().expect("forced collection should succeed");
});
}
#[test]
fn test_collection_performance() {
gc_test_context(|| {
use runmat_time::Instant;
let config = GcConfig::default();
gc_configure(config).expect("configuration should succeed");
let num_objects = 100;
let mut objects = Vec::new();
let alloc_start = Instant::now();
for i in 0..num_objects {
let ptr = gc_allocate(Value::Num(i as f64)).expect("allocation should succeed");
gc_add_root(ptr.clone()).expect("root registration should succeed"); objects.push(ptr);
}
let alloc_duration = alloc_start.elapsed();
let collect_start = Instant::now();
let collected = gc_collect_minor().expect("collection should succeed");
let collect_duration = collect_start.elapsed();
println!("Allocated {num_objects} objects in {alloc_duration:?}");
println!("Collected {collected} objects in {collect_duration:?}");
assert!(alloc_duration.as_millis() < 100);
assert!(collect_duration.as_millis() < 50);
for (i, ptr) in objects.iter().enumerate() {
assert_eq!(**ptr, Value::Num(i as f64));
}
for ptr in &objects {
gc_remove_root(ptr.clone()).expect("root removal should succeed");
}
});
}