use arena_lib::{Arena, Bump, DropArena, Interner};
use proptest::prelude::*;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
proptest! {
#![proptest_config(ProptestConfig {
cases: 256,
..ProptestConfig::default()
})]
#[test]
fn arena_insert_then_get(values in prop::collection::vec(any::<u32>(), 0..200)) {
let mut arena: Arena<u32> = Arena::with_capacity(values.len());
let handles: Vec<_> = values.iter().map(|v| (arena.insert(*v), *v)).collect();
for (idx, expected) in &handles {
prop_assert_eq!(arena.get(*idx), Some(expected));
}
prop_assert_eq!(arena.len(), values.len());
}
#[test]
fn arena_remove_invalidates_handle(values in prop::collection::vec(any::<u32>(), 1..50)) {
let mut arena: Arena<u32> = Arena::new();
let handles: Vec<_> = values.iter().map(|v| arena.insert(*v)).collect();
for h in &handles {
let _ = arena.remove(*h);
}
for h in &handles {
prop_assert!(arena.get(*h).is_none(), "removed handle must not resolve");
}
let new_handles: Vec<_> = values.iter().map(|v| arena.insert(*v)).collect();
for old in &handles {
prop_assert!(arena.get(*old).is_none(), "old handle must remain stale after slot reuse");
}
for n in new_handles {
prop_assert!(arena.get(n).is_some());
}
}
#[test]
fn arena_len_matches_iter(values in prop::collection::vec(any::<u8>(), 0..100)) {
let mut arena: Arena<u8> = Arena::new();
let handles: Vec<_> = values.iter().map(|v| arena.insert(*v)).collect();
let mut removed = 0;
for (i, h) in handles.iter().enumerate() {
if i % 3 == 0 && arena.remove(*h).is_some() {
removed += 1;
}
}
prop_assert_eq!(arena.len(), values.len() - removed);
prop_assert_eq!(arena.iter().count(), arena.len());
}
#[test]
fn interner_idempotent_round_trip(inputs in prop::collection::vec("[a-z]{1,16}", 0..50)) {
let mut interner = Interner::new();
let mut symbols = Vec::new();
for s in &inputs {
let sym = interner.intern(s);
symbols.push((sym, s.clone()));
}
for (expected_sym, s) in &symbols {
prop_assert_eq!(interner.intern(s), *expected_sym);
}
for (sym, s) in &symbols {
prop_assert_eq!(interner.resolve(*sym), Some(s.as_str()));
}
let distinct: std::collections::BTreeSet<&String> = inputs.iter().collect();
prop_assert_eq!(interner.len(), distinct.len());
}
#[test]
fn interner_lookup_does_not_mutate(queries in prop::collection::vec("[a-z]{1,8}", 0..30)) {
let interner = Interner::new();
let before = interner.len();
for s in &queries {
let result = interner.lookup(s);
prop_assert!(result.is_none());
}
prop_assert_eq!(interner.len(), before);
}
#[test]
fn bump_alloc_round_trips(values in prop::collection::vec(any::<u64>(), 0..200)) {
let bump = Bump::with_capacity(8);
let refs: Vec<_> = values.iter().map(|v| {
let ptr: *const u64 = bump.alloc(*v);
(ptr, *v)
}).collect();
for (ptr, expected) in refs {
let observed = unsafe { *ptr };
prop_assert_eq!(observed, expected);
}
}
#[test]
fn bump_allocated_bytes_monotonic(values in prop::collection::vec(any::<u32>(), 0..100)) {
let bump = Bump::with_capacity(64);
let mut prev = bump.allocated_bytes();
for v in &values {
let _ = bump.alloc(*v);
let current = bump.allocated_bytes();
prop_assert!(current >= prev, "allocated_bytes must be monotonic");
prev = current;
}
}
#[test]
fn drop_arena_alloc_round_trips(
chunk_cap in 1_usize..32,
values in prop::collection::vec(any::<u64>(), 0..200),
) {
let arena = DropArena::<u64>::with_chunk_capacity(chunk_cap);
let mut refs: Vec<(*const u64, u64)> = Vec::with_capacity(values.len());
for v in &values {
let r: *const u64 = arena.alloc(*v);
refs.push((r, *v));
}
for (ptr, expected) in refs {
let observed = unsafe { *ptr };
prop_assert_eq!(observed, expected);
}
prop_assert_eq!(arena.len(), values.len());
}
#[test]
fn drop_arena_runs_destructors(count in 0_usize..50) {
let counter = Arc::new(AtomicUsize::new(0));
struct Tracked(Arc<AtomicUsize>);
impl Drop for Tracked {
fn drop(&mut self) {
let _ = self.0.fetch_add(1, Ordering::SeqCst);
}
}
{
let arena = DropArena::<Tracked>::new();
for _ in 0..count {
let _ = arena.alloc(Tracked(Arc::clone(&counter)));
}
prop_assert_eq!(counter.load(Ordering::SeqCst), 0);
}
prop_assert_eq!(counter.load(Ordering::SeqCst), count);
}
}