use std::sync::atomic::{AtomicUsize, Ordering};
use typescript::gc::{GC, GCEventHandler, GCPhase, GCStats, ObjectHeader};
use typescript_types::TsValue;
#[test]
fn test_gc_basic() {
let mut gc = GC::new();
let value = TsValue::Number(42.0);
let obj = gc.allocate(value, 8, 1);
gc.add_root(obj);
gc.collect();
assert_eq!(gc.heap_size(), 1);
}
#[test]
fn test_gc_collect_unreachable() {
let mut gc = GC::new();
let value = TsValue::Number(42.0);
let _obj = gc.allocate(value, 8, 1);
gc.collect();
assert_eq!(gc.heap_size(), 0);
}
#[test]
fn test_gc_young_gen() {
let mut gc = GC::new();
gc.set_young_gen_threshold(10);
for i in 0..15 {
let value = TsValue::Number(i as f64);
gc.allocate(value, 8, 1);
}
assert!(gc.young_gen_size() < 15);
}
#[test]
fn test_object_header() {
let mut header = ObjectHeader::new(64, 1);
assert_eq!(header.ref_count, 0);
header.increment_ref_count();
assert_eq!(header.ref_count, 1);
header.decrement_ref_count();
assert_eq!(header.ref_count, 0);
assert!(!header.is_tenured());
header.increment_age();
header.increment_age();
header.increment_age();
assert!(header.is_tenured());
}
#[test]
fn test_gc_stats() {
let mut stats = GCStats::default();
stats.record_collection(100, 10, 5, 500);
assert_eq!(stats.collection_count, 1);
assert_eq!(stats.marked_objects, 10);
assert_eq!(stats.collected_objects, 5);
assert_eq!(stats.collected_bytes, 500);
stats.record_collection(200, 20, 10, 1000);
assert_eq!(stats.collection_count, 2);
assert_eq!(stats.average_collection_time_us(), 150);
}
struct TestEventHandler {
gc_start_count: AtomicUsize,
gc_end_count: AtomicUsize,
}
impl TestEventHandler {
fn new() -> Self {
Self { gc_start_count: AtomicUsize::new(0), gc_end_count: AtomicUsize::new(0) }
}
}
impl GCEventHandler for TestEventHandler {
fn on_gc_start(&self, _phase: GCPhase) {
self.gc_start_count.fetch_add(1, Ordering::Relaxed);
}
fn on_gc_end(&self, _phase: GCPhase, _stats: &GCStats) {
self.gc_end_count.fetch_add(1, Ordering::Relaxed);
}
fn on_object_collected(&self, _size: usize) {}
}
#[test]
fn test_gc_events() {
let mut gc = GC::new();
let handler = Box::new(TestEventHandler::new());
let handler_ref = unsafe { &*(handler.as_ref() as *const TestEventHandler) };
gc.add_event_handler(handler);
let value = TsValue::Number(42.0);
let _obj = gc.allocate(value, 8, 1);
gc.collect();
assert!(handler_ref.gc_start_count.load(Ordering::Relaxed) >= 2);
assert!(handler_ref.gc_end_count.load(Ordering::Relaxed) >= 2);
}
#[test]
fn test_memory_manager() {
let mut mm = typescript::gc::MemoryManager::new(100);
let ptr1 = mm.allocate(8).unwrap();
let ptr2 = mm.allocate(16).unwrap();
mm.deallocate(ptr1, 8);
mm.deallocate(ptr2, 16);
let ptr3 = mm.allocate(8).unwrap();
assert_ne!(ptr1, ptr3);
}
#[test]
fn test_parallel_gc_performance() {
use std::time::Instant;
let mut gc = typescript::gc::GC::new();
for i in 0..10000 {
let value = typescript_types::TsValue::Number(i as f64);
let obj = gc.allocate(value, 8, 1);
gc.add_root(obj);
}
let start = Instant::now();
gc.collect();
let parallel_time = start.elapsed().as_micros();
println!("Parallel GC time: {}us", parallel_time);
assert!(gc.heap_size() > 0); }
#[test]
fn test_memory_allocation_strategies() {
use std::time::Instant;
let mut gc_default = typescript::gc::GC::new();
let start_default = Instant::now();
for i in 0..5000 {
let value = typescript_types::TsValue::Number(i as f64);
gc_default.allocate(value, 8, 1);
}
gc_default.collect();
let default_time = start_default.elapsed().as_micros();
let mut gc_buddy = typescript::gc::GC::with_buddy_allocator();
let start_buddy = Instant::now();
for i in 0..5000 {
let value = typescript_types::TsValue::Number(i as f64);
gc_buddy.allocate(value, 8, 1);
}
gc_buddy.collect();
let buddy_time = start_buddy.elapsed().as_micros();
println!("Default allocator time: {}us", default_time);
println!("Buddy allocator time: {}us", buddy_time);
}
#[test]
fn test_generational_collection() {
let mut gc = typescript::gc::GC::new();
let mut objects = Vec::new();
for i in 0..200 {
let value = typescript_types::TsValue::Number(i as f64);
let obj = gc.allocate(value, 8, 1);
objects.push(obj);
}
for i in 0..10 {
gc.add_root(objects[i]);
}
for _ in 0..5 {
gc.collect_young_gen();
}
assert!(gc.old_gen_size() > 0);
assert!(gc.young_gen_size() > 0);
}
#[test]
fn test_concurrent_gc() {
let mut gc = typescript::gc::GC::new();
gc.enable_concurrent(true);
for i in 0..1000 {
let value = typescript_types::TsValue::Number(i as f64);
let obj = gc.allocate(value, 8, 1);
if i < 100 {
gc.add_root(obj);
}
}
gc.collect();
assert!(gc.heap_size() > 0);
assert!(gc.is_concurrent_enabled());
}
#[test]
fn test_gc_stats() {
let mut gc = typescript::gc::GC::new();
for i in 0..1000 {
let value = typescript_types::TsValue::Number(i as f64);
let obj = gc.allocate(value, 8, 1);
if i < 100 {
gc.add_root(obj);
}
}
gc.collect();
let stats = gc.stats();
assert!(stats.collection_count > 0);
assert!(stats.collected_objects > 0);
assert!(stats.allocation_count > 0);
assert!(stats.total_collection_time_us > 0);
println!("GC Stats:");
println!(" Collection count: {}", stats.collection_count);
println!(" Collected objects: {}", stats.collected_objects);
println!(" Collected bytes: {}", stats.collected_bytes);
println!(" Average collection time: {}us", stats.average_collection_time_us());
println!(" Memory fragmentation: {:.2}%", stats.fragmentation_ratio * 100.0);
}
#[test]
fn test_allocation_strategy_switch() {
use typescript::memory::AllocationStrategy;
let mut gc = typescript::gc::GC::new();
for i in 0..100 {
let value = typescript_types::TsValue::Number(i as f64);
gc.allocate(value, 8, 1);
}
gc.set_allocation_strategy(AllocationStrategy::Buddy);
for i in 100..200 {
let value = typescript_types::TsValue::Number(i as f64);
gc.allocate(value, 8, 1);
}
gc.collect();
assert!(gc.heap_size() > 0);
}
#[test]
fn test_logging_event_handler() {
use typescript::gc::LoggingEventHandler;
let handler = LoggingEventHandler::new();
handler.on_gc_start(GCPhase::Marking);
let stats = GCStats::default();
handler.on_gc_end(GCPhase::Marking, &stats);
handler.on_object_collected(100);
}
#[test]
fn test_verbose_logging_event_handler() {
use typescript::gc::LoggingEventHandler;
let handler = LoggingEventHandler::verbose();
handler.on_gc_start(GCPhase::Sweeping);
let mut stats = GCStats::default();
stats.marked_objects = 10;
stats.collected_objects = 5;
stats.collected_bytes = 500;
handler.on_gc_end(GCPhase::Sweeping, &stats);
handler.on_object_collected(100);
}
#[test]
fn test_work_stealing_queue_basic() {
use typescript::gc::WorkStealingQueue;
use std::ptr::NonNull;
let queue = WorkStealingQueue::new();
assert!(queue.is_empty());
assert_eq!(queue.len(), 0);
let obj = Box::new(typescript::gc::GcObject::new(typescript_types::TsValue::Null, 0, 0));
let ptr = NonNull::from(Box::leak(obj));
queue.push(ptr);
assert!(!queue.is_empty());
assert_eq!(queue.len(), 1);
let popped = queue.pop();
assert!(popped.is_some());
assert!(queue.is_empty());
unsafe {
let _ = Box::from_raw(ptr.as_ptr());
}
}
#[test]
fn test_work_stealing_queue_steal() {
use typescript::gc::WorkStealingQueue;
use std::ptr::NonNull;
let queue = WorkStealingQueue::new();
let obj1 = Box::new(typescript::gc::GcObject::new(typescript_types::TsValue::Null, 0, 0));
let ptr1 = NonNull::from(Box::leak(obj1));
let obj2 = Box::new(typescript::gc::GcObject::new(typescript_types::TsValue::Null, 0, 0));
let ptr2 = NonNull::from(Box::leak(obj2));
queue.push(ptr1);
queue.push(ptr2);
let stolen = queue.steal();
assert!(stolen.is_some());
let popped = queue.pop();
assert!(popped.is_some());
assert!(queue.is_empty());
unsafe {
let _ = Box::from_raw(ptr1.as_ptr());
let _ = Box::from_raw(ptr2.as_ptr());
}
}