#![allow(clippy::clone_on_ref_ptr, reason = "tests prefer concise method-call form")]
#![allow(clippy::std_instead_of_core, reason = "tests use std")]
#![allow(clippy::unwrap_used, reason = "test code")]
use core::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
use multitude::Arena;
#[test]
fn arena_box_into_rc_basic() {
let arena = Arena::new();
let b = arena.alloc_box(42_u32);
let r = b.into_rc();
assert_eq!(*r, 42);
}
#[test]
fn arena_box_into_rc_preserves_chunk_refcount() {
let arena = Arena::new();
let b = arena.alloc_box(123_u32);
let r = b.into_rc();
let r2 = r.clone();
drop(r);
drop(r2);
let v = arena.alloc_rc(456_u32);
assert_eq!(*v, 456);
}
#[test]
fn arena_box_into_rc_drops_value_at_chunk_teardown() {
static COUNT: AtomicUsize = AtomicUsize::new(0);
struct Tracked(u32);
impl Drop for Tracked {
fn drop(&mut self) {
let _ = COUNT.fetch_add(1, AtomicOrdering::SeqCst);
}
}
COUNT.store(0, AtomicOrdering::SeqCst);
let arena = Arena::new();
let b = arena.alloc_box(Tracked(7));
let r = b.into_rc();
assert_eq!(COUNT.load(AtomicOrdering::SeqCst), 0);
assert_eq!(r.0, 7);
drop(r);
drop(arena);
assert_eq!(COUNT.load(AtomicOrdering::SeqCst), 1);
}
#[test]
fn arena_box_into_rc_outlives_arena() {
let r = {
let arena = Arena::new();
let b = arena.alloc_box("outlives".to_string());
b.into_rc()
};
assert_eq!(*r, "outlives");
}
#[test]
fn arena_box_into_rc_no_double_drop() {
static COUNT: AtomicUsize = AtomicUsize::new(0);
struct Tracked;
impl Drop for Tracked {
fn drop(&mut self) {
let _ = COUNT.fetch_add(1, AtomicOrdering::SeqCst);
}
}
COUNT.store(0, AtomicOrdering::SeqCst);
{
let arena = Arena::new();
let b = arena.alloc_box(Tracked);
let _r = b.into_rc();
}
assert_eq!(COUNT.load(AtomicOrdering::SeqCst), 1, "value must drop exactly once");
}
#[test]
fn arena_box_into_rc_for_copy_type_no_drop() {
let arena = Arena::new();
let b = arena.alloc_box(42_u32);
let r = b.into_rc();
let r2 = r.clone();
drop(r);
drop(r2);
let v = arena.alloc_rc(99_u32);
assert_eq!(*v, 99);
}
#[test]
fn arena_box_str_into_rc_str_basic() {
let arena = Arena::new();
let b = arena.alloc_str_box("hello");
let s = b.into_rc_str();
assert_eq!(&*s, "hello");
}
#[test]
fn arena_box_str_into_rc_str_after_mutation() {
let arena = Arena::new();
let mut b = arena.alloc_str_box("hello");
b.make_ascii_uppercase();
let s = b.into_rc_str();
assert_eq!(&*s, "HELLO");
}
#[test]
fn arena_box_str_into_rc_str_clone_works() {
let arena = Arena::new();
let b = arena.alloc_str_box("shareable");
let s = b.into_rc_str();
let s2 = s.clone();
let s3 = s.clone();
drop(s);
drop(s2);
assert_eq!(&*s3, "shareable");
}
#[test]
fn arena_box_str_into_rc_str_outlives_arena() {
let s = {
let arena = Arena::new();
let b = arena.alloc_str_box("survives");
b.into_rc_str()
};
assert_eq!(&*s, "survives");
}
#[test]
fn arena_box_str_into_rc_str_empty_string() {
let arena = Arena::new();
let b = arena.alloc_str_box("");
let s = b.into_rc_str();
assert_eq!(&*s, "");
assert_eq!(s.len(), 0);
}
#[test]
fn arena_box_str_into_rc_str_preserves_size_invariant() {
use multitude::strings::RcStr;
assert_eq!(size_of::<RcStr>(), size_of::<usize>());
}
#[test]
fn arena_box_into_rc_zst() {
#[derive(Debug)]
struct Zst;
let arena = Arena::new();
let b = arena.alloc_box(Zst);
let r = b.into_rc();
let r2 = r.clone();
drop(r);
drop(r2);
let v = arena.alloc_rc(99_u32);
assert_eq!(*v, 99);
}
#[test]
fn arena_box_into_rc_zst_with_drop() {
static COUNT: AtomicUsize = AtomicUsize::new(0);
struct ZstDrop;
impl Drop for ZstDrop {
fn drop(&mut self) {
let _ = COUNT.fetch_add(1, AtomicOrdering::SeqCst);
}
}
COUNT.store(0, AtomicOrdering::SeqCst);
{
let arena = Arena::new();
let b = arena.alloc_box(ZstDrop);
let _r = b.into_rc();
}
assert_eq!(COUNT.load(AtomicOrdering::SeqCst), 1);
}
#[test]
fn arena_box_into_rc_high_alignment_value() {
static COUNT: AtomicUsize = AtomicUsize::new(0);
#[repr(align(64))]
#[derive(Debug)]
struct AlignedDrop {
_data: [u8; 16],
}
impl Drop for AlignedDrop {
fn drop(&mut self) {
let _ = COUNT.fetch_add(1, AtomicOrdering::SeqCst);
}
}
COUNT.store(0, AtomicOrdering::SeqCst);
{
let arena = Arena::new();
let _decoy = arena.alloc(0_u8);
let b = arena.alloc_box(AlignedDrop { _data: [0xAB; 16] });
let r = b.into_rc();
drop(r);
}
assert_eq!(COUNT.load(AtomicOrdering::SeqCst), 1);
}
#[test]
fn arena_box_str_into_rc_str_round_trip_many_strings() {
let arena = Arena::new();
let mut handles = std::vec::Vec::new();
for i in 0..200 {
let b = arena.alloc_str_box(format!("string-{i}"));
handles.push(b.into_rc_str());
}
assert_eq!(&*handles[0], "string-0");
assert_eq!(&*handles[199], "string-199");
handles.reverse();
for h in handles {
let _ = h.len();
}
}
#[test]
fn arena_box_into_rc_then_back_to_arena_works() {
let arena = Arena::new();
{
let b = arena.alloc_box(42_u32);
let r = b.into_rc();
drop(r);
}
{
let b = arena.alloc_box(99_u32);
let r = b.into_rc();
assert_eq!(*r, 99);
}
}
#[test]
fn arena_box_into_rc_does_not_corrupt_drop_list() {
use core::sync::atomic::{AtomicUsize, Ordering};
static DROPS: [AtomicUsize; 4] = [AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0)];
struct DropProbe<const ID: usize>;
impl<const ID: usize> Drop for DropProbe<ID> {
fn drop(&mut self) {
DROPS[ID].fetch_add(1, Ordering::SeqCst);
}
}
for d in &DROPS {
d.store(0, Ordering::SeqCst);
}
{
let arena = Arena::new();
let b: multitude::Box<DropProbe<0>> = arena.alloc_box(DropProbe::<0>);
let _r1: multitude::Rc<DropProbe<1>> = arena.alloc_rc(DropProbe::<1>);
let _rc0: multitude::Rc<DropProbe<0>> = b.into_rc();
let _r2: multitude::Rc<DropProbe<2>> = arena.alloc_rc(DropProbe::<2>);
}
assert_eq!(DROPS[0].load(Ordering::SeqCst), 1, "DropProbe<0> dropped wrong number of times");
assert_eq!(DROPS[1].load(Ordering::SeqCst), 1, "DropProbe<1> dropped wrong number of times");
assert_eq!(DROPS[2].load(Ordering::SeqCst), 1, "DropProbe<2> dropped wrong number of times");
assert_eq!(DROPS[3].load(Ordering::SeqCst), 0, "DropProbe<3> should not have been allocated");
}