use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;
use std::time::Duration;
use zipora::memory::{SecureMemoryPool, SecurePoolConfig};
#[test]
fn test_basic_allocation() {
let config = SecurePoolConfig::small_secure();
let pool = SecureMemoryPool::new(config).unwrap();
let ptr = pool.allocate().unwrap();
assert!(!ptr.as_ptr().is_null());
assert_eq!(ptr.size(), 1024);
assert!(ptr.generation() > 0);
assert!(ptr.validate().is_ok());
let slice = ptr.as_slice();
assert_eq!(slice.len(), 1024);
drop(ptr);
let stats = pool.stats();
assert_eq!(stats.alloc_count, 1);
assert_eq!(stats.dealloc_count, 1);
assert_eq!(stats.corruption_detected, 0);
assert_eq!(stats.double_free_detected, 0);
}
#[test]
fn test_multiple_allocations() {
let config = SecurePoolConfig::small_secure();
let pool = SecureMemoryPool::new(config).unwrap();
let mut ptrs = Vec::new();
for _ in 0..10 {
let ptr = pool.allocate().unwrap();
assert!(!ptr.as_ptr().is_null());
assert!(ptr.validate().is_ok());
ptrs.push(ptr);
}
for i in 0..ptrs.len() {
for j in i + 1..ptrs.len() {
assert_ne!(ptrs[i].as_ptr(), ptrs[j].as_ptr());
}
}
drop(ptrs);
let stats = pool.stats();
assert_eq!(stats.alloc_count, 10);
assert_eq!(stats.dealloc_count, 10);
assert_eq!(stats.corruption_detected, 0);
assert_eq!(stats.double_free_detected, 0);
}
#[test]
fn test_memory_reuse() {
let config = SecurePoolConfig::small_secure();
let pool = SecureMemoryPool::new(config).unwrap();
for _ in 0..5 {
let ptr = pool.allocate().unwrap();
drop(ptr);
}
let ptr = pool.allocate().unwrap();
assert!(!ptr.as_ptr().is_null());
drop(ptr);
let stats = pool.stats();
assert_eq!(stats.alloc_count, 6);
assert_eq!(stats.dealloc_count, 6);
assert!(stats.pool_hits > 0 || stats.local_cache_hits > 0);
}
#[test]
fn test_concurrent_allocation() {
let config = SecurePoolConfig::small_secure();
let pool = SecureMemoryPool::new(config).unwrap();
let allocated_count = Arc::new(AtomicUsize::new(0));
let error_count = Arc::new(AtomicUsize::new(0));
let handles: Vec<_> = (0..8)
.map(|_| {
let pool = pool.clone();
let count = allocated_count.clone();
let errors = error_count.clone();
thread::spawn(move || {
for _ in 0..125 {
match pool.allocate() {
Ok(ptr) => {
count.fetch_add(1, Ordering::Relaxed);
if ptr.validate().is_err() {
errors.fetch_add(1, Ordering::Relaxed);
}
let mut_slice =
unsafe { std::slice::from_raw_parts_mut(ptr.as_ptr(), ptr.size()) };
mut_slice[0] = 42;
mut_slice[ptr.size() - 1] = 84;
thread::sleep(Duration::from_micros(1));
}
Err(_) => {
errors.fetch_add(1, Ordering::Relaxed);
}
}
}
})
})
.collect();
for handle in handles {
handle.join().unwrap();
}
assert_eq!(allocated_count.load(Ordering::Relaxed), 1000);
assert_eq!(error_count.load(Ordering::Relaxed), 0);
let stats = pool.stats();
assert_eq!(stats.alloc_count, 1000);
assert_eq!(stats.dealloc_count, 1000);
assert_eq!(stats.corruption_detected, 0);
assert_eq!(stats.double_free_detected, 0);
}
#[test]
fn test_double_free_prevention() {
let config = SecurePoolConfig::small_secure();
let pool = SecureMemoryPool::new(config).unwrap();
let ptr = pool.allocate().unwrap();
let _raw_ptr = ptr.as_ptr();
drop(ptr);
let stats = pool.stats();
assert_eq!(stats.double_free_detected, 0);
}
#[test]
fn test_corruption_detection() {
let config = SecurePoolConfig::small_secure();
let pool = SecureMemoryPool::new(config).unwrap();
let ptr = pool.allocate().unwrap();
assert!(ptr.validate().is_ok());
assert!(ptr.validate().is_ok());
drop(ptr);
let stats = pool.stats();
assert_eq!(stats.corruption_detected, 0);
}
#[test]
fn test_thread_local_caching() {
let config = SecurePoolConfig::small_secure();
let pool = SecureMemoryPool::new(config).unwrap();
for _ in 0..20 {
let ptr = pool.allocate().unwrap();
drop(ptr);
}
for _ in 0..10 {
let ptr = pool.allocate().unwrap();
drop(ptr);
}
let stats = pool.stats();
assert!(stats.local_cache_hits > 0);
assert_eq!(stats.corruption_detected, 0);
assert_eq!(stats.double_free_detected, 0);
}
#[test]
fn test_cross_thread_stealing() {
let config = SecurePoolConfig::small_secure();
let pool = SecureMemoryPool::new(config).unwrap();
let pool_clone = pool.clone();
let handle = thread::spawn(move || {
let mut ptrs = Vec::new();
for _ in 0..10 {
ptrs.push(pool_clone.allocate().unwrap());
}
drop(ptrs);
});
handle.join().unwrap();
let ptr = pool.allocate().unwrap();
assert!(!ptr.as_ptr().is_null());
drop(ptr);
let stats = pool.stats();
println!(
"Cross-thread stats: steals={}, pool_hits={}, cache_hits={}",
stats.cross_thread_steals, stats.pool_hits, stats.local_cache_hits
);
}
#[test]
fn test_memory_zeroing() {
let mut config = SecurePoolConfig::small_secure();
config.zero_on_free = true;
let pool = SecureMemoryPool::new(config).unwrap();
let ptr = pool.allocate().unwrap();
unsafe {
let slice = std::slice::from_raw_parts_mut(ptr.as_ptr(), ptr.size());
slice.fill(0xFF);
}
drop(ptr);
let stats = pool.stats();
assert_eq!(stats.dealloc_count, 1);
}
#[test]
fn test_different_configurations() {
let small_pool = SecureMemoryPool::new(SecurePoolConfig::small_secure()).unwrap();
let small_ptr = small_pool.allocate().unwrap();
assert_eq!(small_ptr.size(), 1024);
drop(small_ptr);
let medium_pool = SecureMemoryPool::new(SecurePoolConfig::medium_secure()).unwrap();
let medium_ptr = medium_pool.allocate().unwrap();
assert_eq!(medium_ptr.size(), 64 * 1024);
drop(medium_ptr);
let large_pool = SecureMemoryPool::new(SecurePoolConfig::large_secure()).unwrap();
let large_ptr = large_pool.allocate().unwrap();
assert_eq!(large_ptr.size(), 1024 * 1024);
drop(large_ptr);
}
#[test]
fn test_pool_validation() {
let config = SecurePoolConfig::small_secure();
let pool = SecureMemoryPool::new(config).unwrap();
let _ptr1 = pool.allocate().unwrap();
let _ptr2 = pool.allocate().unwrap();
assert!(pool.validate().is_ok());
drop(_ptr1);
drop(_ptr2);
assert!(pool.validate().is_ok());
}
#[test]
fn test_global_pools() {
use zipora::memory::{get_global_pool_for_size, get_global_secure_pool_stats};
let small_pool = get_global_pool_for_size(100);
let medium_pool = get_global_pool_for_size(10000);
let large_pool = get_global_pool_for_size(100000);
assert_eq!(small_pool.config().chunk_size, 1024);
assert_eq!(medium_pool.config().chunk_size, 64 * 1024);
assert_eq!(large_pool.config().chunk_size, 1024 * 1024);
let ptr1 = small_pool.allocate().unwrap();
let ptr2 = medium_pool.allocate().unwrap();
let ptr3 = large_pool.allocate().unwrap();
assert!(!ptr1.as_ptr().is_null());
assert!(!ptr2.as_ptr().is_null());
assert!(!ptr3.as_ptr().is_null());
let _stats = get_global_secure_pool_stats();
drop(ptr1);
drop(ptr2);
drop(ptr3);
}
#[test]
fn test_pool_clearing() {
let config = SecurePoolConfig::small_secure();
let pool = SecureMemoryPool::new(config).unwrap();
for _ in 0..5 {
let ptr = pool.allocate().unwrap();
drop(ptr);
}
assert!(pool.clear().is_ok());
let ptr = pool.allocate().unwrap();
assert!(!ptr.as_ptr().is_null());
drop(ptr);
}
#[test]
fn test_memory_access_patterns() {
let config = SecurePoolConfig::small_secure();
let pool = SecureMemoryPool::new(config).unwrap();
let mut ptr = pool.allocate().unwrap();
{
let slice = ptr.as_mut_slice();
assert_eq!(slice.len(), 1024);
for (i, byte) in slice.iter_mut().enumerate() {
*byte = (i % 256) as u8;
}
}
{
let slice = ptr.as_slice();
for (i, &byte) in slice.iter().enumerate() {
assert_eq!(byte, (i % 256) as u8);
}
}
drop(ptr);
}
#[test]
fn test_high_contention_stress() {
let config = SecurePoolConfig::small_secure();
let pool = SecureMemoryPool::new(config).unwrap();
let success_count = Arc::new(AtomicUsize::new(0));
let error_count = Arc::new(AtomicUsize::new(0));
let handles: Vec<_> = (0..16)
.map(|_| {
let pool = pool.clone();
let success = success_count.clone();
let errors = error_count.clone();
thread::spawn(move || {
for _ in 0..500 {
match pool.allocate() {
Ok(ptr) => {
success.fetch_add(1, Ordering::Relaxed);
if ptr.validate().is_err() {
errors.fetch_add(1, Ordering::Relaxed);
}
drop(ptr);
}
Err(_) => {
errors.fetch_add(1, Ordering::Relaxed);
}
}
}
})
})
.collect();
for handle in handles {
handle.join().unwrap();
}
assert_eq!(success_count.load(Ordering::Relaxed), 8000);
assert_eq!(error_count.load(Ordering::Relaxed), 0);
let stats = pool.stats();
assert_eq!(stats.alloc_count, 8000);
assert_eq!(stats.dealloc_count, 8000);
assert_eq!(stats.corruption_detected, 0);
assert_eq!(stats.double_free_detected, 0);
}
#[test]
fn test_send_sync_traits() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<SecureMemoryPool>();
assert_send_sync::<Arc<SecureMemoryPool>>();
assert_send_sync::<zipora::memory::SecurePooledPtr>();
}
#[test]
fn test_performance_throughput() {
let config = SecurePoolConfig::small_secure();
let pool = SecureMemoryPool::new(config).unwrap();
let start = std::time::Instant::now();
for _ in 0..10_000 {
let ptr = pool.allocate().unwrap();
drop(ptr);
}
let duration = start.elapsed();
println!(
"Secure pool throughput: {} allocs/sec",
10_000 as f64 / duration.as_secs_f64()
);
let stats = pool.stats();
assert_eq!(stats.alloc_count, 10_000);
assert_eq!(stats.dealloc_count, 10_000);
assert_eq!(stats.corruption_detected, 0);
assert_eq!(stats.double_free_detected, 0);
assert!(
duration.as_millis() < 1000,
"Performance regression detected"
);
}