use crate::runtime::region_heap::{HeapIndex, RegionHeap, global_alloc_count};
#[derive(Debug, Clone, PartialEq, Eq)]
struct TestValue {
id: u32,
data: Vec<u8>,
}
impl TestValue {
fn new(id: u32, size: usize) -> Self {
Self {
id,
data: vec![id as u8; size],
}
}
}
#[derive(Debug, Clone)]
enum HeapOperation {
Alloc { id: u32, size: usize },
Dealloc { target_id: u32 },
AllocMany { count: u32, base_id: u32 },
DeallocMany { target_ids: Vec<u32> },
ReclaimAll,
}
fn execute_operations(heap: &mut RegionHeap, operations: &[HeapOperation]) -> OperationResults {
let mut allocated_indices: std::collections::HashMap<u32, HeapIndex> =
std::collections::HashMap::new();
let mut allocation_count = 0;
let mut deallocation_count = 0;
let mut reclaim_count = 0;
for op in operations {
match op {
HeapOperation::Alloc { id, size } => {
let value = TestValue::new(*id, *size);
let index = heap.alloc(value);
allocated_indices.insert(*id, index);
allocation_count += 1;
}
HeapOperation::Dealloc { target_id } => {
if let Some(index) = allocated_indices.remove(target_id) {
if heap.dealloc(index) {
deallocation_count += 1;
}
}
}
HeapOperation::AllocMany { count, base_id } => {
for i in 0..*count {
let id = base_id + i;
let value = TestValue::new(id, 64); let index = heap.alloc(value);
allocated_indices.insert(id, index);
allocation_count += 1;
}
}
HeapOperation::DeallocMany { target_ids } => {
for target_id in target_ids {
if let Some(index) = allocated_indices.remove(target_id) {
if heap.dealloc(index) {
deallocation_count += 1;
}
}
}
}
HeapOperation::ReclaimAll => {
let live_before = heap.len();
heap.reclaim_all();
allocated_indices.clear();
reclaim_count += live_before;
}
}
}
OperationResults {
allocation_count,
deallocation_count,
reclaim_count,
remaining_allocations: allocated_indices,
}
}
#[derive(Debug)]
struct OperationResults {
allocation_count: usize,
deallocation_count: usize,
reclaim_count: usize,
remaining_allocations: std::collections::HashMap<u32, HeapIndex>,
}
#[cfg(test)]
mod metamorphic_tests {
use super::*;
#[test]
fn mr_statistics_conservation() {
crate::test_utils::init_test_logging();
crate::test_phase!("mr_statistics_conservation");
let mut heap = RegionHeap::new();
let operation_sequences = vec![
vec![
HeapOperation::Alloc { id: 1, size: 64 },
HeapOperation::Alloc { id: 2, size: 128 },
HeapOperation::Dealloc { target_id: 1 },
],
vec![
HeapOperation::AllocMany {
count: 5,
base_id: 10,
},
HeapOperation::DeallocMany {
target_ids: vec![10, 12, 14],
},
],
vec![
HeapOperation::Alloc { id: 100, size: 32 },
HeapOperation::Alloc { id: 101, size: 64 },
HeapOperation::ReclaimAll,
],
];
for (i, ops) in operation_sequences.iter().enumerate() {
let _results = execute_operations(&mut heap, ops);
let final_stats = heap.stats();
assert_eq!(
final_stats.allocations - final_stats.reclaimed,
final_stats.live,
"Statistics conservation violated in sequence {}: allocations={}, reclaimed={}, live={}",
i,
final_stats.allocations,
final_stats.reclaimed,
final_stats.live
);
assert_eq!(
heap.len() as u64,
final_stats.live,
"Length inconsistent with live count in sequence {}: len={}, live={}",
i,
heap.len(),
final_stats.live
);
}
crate::test_complete!("mr_statistics_conservation");
}
#[test]
fn mr_global_count_consistency() {
crate::test_utils::init_test_logging();
crate::test_phase!("mr_global_count_consistency");
let initial_global_count = global_alloc_count();
let mut heap1 = RegionHeap::new();
let mut heap2 = RegionHeap::new();
let idx1 = heap1.alloc(TestValue::new(1, 64));
let _idx2 = heap1.alloc(TestValue::new(2, 128));
let after_heap1_allocs = global_alloc_count();
assert_eq!(
after_heap1_allocs,
initial_global_count + 2,
"Global count not updated after heap1 allocations"
);
let _idx3 = heap2.alloc(TestValue::new(3, 32));
let after_heap2_alloc = global_alloc_count();
assert_eq!(
after_heap2_alloc,
initial_global_count + 3,
"Global count inconsistent with multiple heaps"
);
heap1.dealloc(idx1);
let after_dealloc = global_alloc_count();
assert_eq!(
after_dealloc,
initial_global_count + 2,
"Global count not decremented after dealloc"
);
heap2.reclaim_all();
let after_reclaim = global_alloc_count();
assert_eq!(
after_reclaim,
initial_global_count + 1, "Global count inconsistent after reclaim_all"
);
heap1.reclaim_all();
let final_global_count = global_alloc_count();
assert_eq!(
final_global_count, initial_global_count,
"Global count not restored to baseline after cleanup"
);
crate::test_complete!("mr_global_count_consistency");
}
#[test]
fn mr_generation_monotonicity() {
crate::test_utils::init_test_logging();
crate::test_phase!("mr_generation_monotonicity");
let mut heap = RegionHeap::new();
let idx1 = heap.alloc(TestValue::new(1, 64));
let original_gen = idx1.generation();
assert!(heap.dealloc(idx1), "Failed to deallocate idx1");
let idx2 = heap.alloc(TestValue::new(2, 128));
if idx2.index() == idx1.index() {
assert!(
idx2.generation() > original_gen,
"Generation did not increase on slot reuse: original={}, reused={}",
original_gen,
idx2.generation()
);
}
assert!(heap.dealloc(idx2), "Failed to deallocate idx2");
let idx3 = heap.alloc(TestValue::new(3, 64));
if idx3.index() == idx1.index() {
assert!(
idx3.generation() > idx2.generation(),
"Generation not monotonic across multiple reuses"
);
}
crate::test_complete!("mr_generation_monotonicity");
}
#[test]
fn mr_access_stability() {
crate::test_utils::init_test_logging();
crate::test_phase!("mr_access_stability");
let mut heap = RegionHeap::new();
let test_value = TestValue::new(42, 256);
let idx = heap.alloc(test_value.clone());
for i in 0..10 {
let retrieved = heap.get::<TestValue>(idx);
assert!(retrieved.is_some(), "Access failed on iteration {}", i);
assert_eq!(
*retrieved.unwrap(),
test_value,
"Value changed between accesses on iteration {}",
i
);
}
let other_idx = heap.alloc(TestValue::new(99, 64));
let retrieved = heap.get::<TestValue>(idx);
assert!(retrieved.is_some(), "Access failed after other allocation");
assert_eq!(
*retrieved.unwrap(),
test_value,
"Value corrupted by other allocation"
);
heap.dealloc(other_idx);
let retrieved = heap.get::<TestValue>(idx);
assert!(
retrieved.is_some(),
"Access failed after other deallocation"
);
assert_eq!(
*retrieved.unwrap(),
test_value,
"Value corrupted by other deallocation"
);
crate::test_complete!("mr_access_stability");
}
#[test]
fn mr_free_list_integrity() {
crate::test_utils::init_test_logging();
crate::test_phase!("mr_free_list_integrity");
let mut heap = RegionHeap::new();
let mut indices = Vec::new();
for i in 0..10 {
let idx = heap.alloc(TestValue::new(i, 64));
indices.push(idx);
}
let dealloc_indices = [0, 2, 4, 6, 8];
for &i in &dealloc_indices {
heap.dealloc(indices[i]);
}
let new_idx = heap.alloc(TestValue::new(100, 64));
assert!(heap.contains(new_idx), "New allocation not accessible");
let remaining_indices = [1, 3, 5, 7, 9];
for &i in &remaining_indices {
assert!(
heap.contains(indices[i]),
"Remaining allocation {} not accessible",
i
);
let value = heap.get::<TestValue>(indices[i]);
assert!(value.is_some(), "Cannot access remaining value {}", i);
assert_eq!(
value.unwrap().id,
i as u32,
"Value corrupted for index {}",
i
);
}
crate::test_complete!("mr_free_list_integrity");
}
#[test]
fn mr_allocation_deallocation_symmetry() {
crate::test_utils::init_test_logging();
crate::test_phase!("mr_allocation_deallocation_symmetry");
let mut heap = RegionHeap::new();
let initial_stats = heap.stats();
let initial_len = heap.len();
let initial_global = global_alloc_count();
let test_sizes = [32, 64, 128, 256, 512];
for (i, &size) in test_sizes.iter().enumerate() {
let value = TestValue::new(i as u32, size);
let idx = heap.alloc(value);
assert!(heap.contains(idx), "Allocation {} not present", i);
assert!(heap.dealloc(idx), "Failed to deallocate {}", i);
assert!(
!heap.contains(idx),
"Index {} still accessible after dealloc",
i
);
}
let final_stats = heap.stats();
let final_len = heap.len();
let final_global = global_alloc_count();
assert_eq!(
final_len, initial_len,
"Heap length not symmetric after alloc/dealloc cycle"
);
assert_eq!(
final_stats.live, initial_stats.live,
"Live count not symmetric: initial={}, final={}",
initial_stats.live, final_stats.live
);
assert_eq!(
final_global, initial_global,
"Global count not symmetric: initial={}, final={}",
initial_global, final_global
);
crate::test_complete!("mr_allocation_deallocation_symmetry");
}
#[test]
fn mr_composite_operation_invariants() {
crate::test_utils::init_test_logging();
crate::test_phase!("mr_composite_operation_invariants");
let initial_global = global_alloc_count();
let mut heap = RegionHeap::new();
let operations = vec![
HeapOperation::AllocMany {
count: 5,
base_id: 1000,
},
HeapOperation::Alloc {
id: 2000,
size: 256,
},
HeapOperation::DeallocMany {
target_ids: vec![1001, 1003],
},
HeapOperation::AllocMany {
count: 3,
base_id: 3000,
},
HeapOperation::Dealloc { target_id: 2000 },
HeapOperation::Alloc {
id: 4000,
size: 512,
},
];
let results = execute_operations(&mut heap, &operations);
let final_stats = heap.stats();
assert_eq!(
results.allocation_count,
results.deallocation_count
+ results.reclaim_count
+ results.remaining_allocations.len(),
"operation accounting must explain every allocation"
);
assert_eq!(
final_stats.allocations - final_stats.reclaimed,
final_stats.live,
"Statistics conservation violated in composite operations"
);
assert_eq!(
heap.len() as u64,
final_stats.live,
"Length inconsistent with live count in composite operations"
);
for (&id, &index) in &results.remaining_allocations {
assert!(
heap.contains(index),
"Remaining allocation {} not accessible",
id
);
let value = heap.get::<TestValue>(index);
assert!(value.is_some(), "Cannot retrieve remaining value {}", id);
assert_eq!(value.unwrap().id, id, "Value corrupted for id {}", id);
}
let expected_global = initial_global + (results.remaining_allocations.len() as u64);
let actual_global = global_alloc_count();
assert_eq!(
actual_global, expected_global,
"Global count inconsistent after composite operations: expected={}, actual={}",
expected_global, actual_global
);
heap.reclaim_all();
let final_global = global_alloc_count();
assert_eq!(
final_global, initial_global,
"Global count not restored after reclaim_all: initial={}, final={}",
initial_global, final_global
);
crate::test_complete!("mr_composite_operation_invariants");
}
#[test]
fn mr_type_safety_invariant() {
crate::test_utils::init_test_logging();
crate::test_phase!("mr_type_safety_invariant");
let mut heap = RegionHeap::new();
let string_idx = heap.alloc("hello".to_string());
let int_idx = heap.alloc(42u32);
let vec_idx = heap.alloc(vec![1, 2, 3]);
assert!(heap.get::<String>(string_idx).is_some());
assert!(heap.get::<u32>(int_idx).is_some());
assert!(heap.get::<Vec<i32>>(vec_idx).is_some());
assert!(
heap.get::<u32>(string_idx).is_none(),
"Type mismatch allowed for string->u32"
);
assert!(
heap.get::<String>(int_idx).is_none(),
"Type mismatch allowed for u32->String"
);
assert!(
heap.get::<Vec<i32>>(string_idx).is_none(),
"Type mismatch allowed for string->Vec"
);
assert!(
heap.get::<String>(vec_idx).is_none(),
"Type mismatch allowed for Vec->String"
);
let _another_idx = heap.alloc(TestValue::new(99, 64));
assert!(
heap.get::<u32>(string_idx).is_none(),
"Type safety changed after other alloc"
);
assert!(
heap.get::<String>(int_idx).is_none(),
"Type safety changed after other alloc"
);
crate::test_complete!("mr_type_safety_invariant");
}
}