#![allow(clippy::std_instead_of_core, reason = "test code uses std")]
#![allow(clippy::unwrap_used, reason = "test code")]
#![allow(clippy::missing_panics_doc, reason = "test code")]
#![allow(clippy::clone_on_ref_ptr, reason = "tests prefer concise method-call form")]
#![allow(clippy::items_after_statements, reason = "test layout")]
#![allow(dead_code, reason = "test scaffolding may be conditionally used")]
#![allow(clippy::large_stack_arrays, reason = "test allocations are intentional")]
#![allow(clippy::collection_is_never_read, reason = "tests retain handles to keep chunks alive")]
#![allow(clippy::cast_possible_truncation, reason = "test code: bounded test indices")]
#![allow(clippy::cast_lossless, reason = "test code")]
#![allow(clippy::cast_sign_loss, reason = "test code")]
#![allow(clippy::range_plus_one, reason = "test code")]
#![allow(clippy::assertions_on_result_states, reason = "test code")]
#![allow(clippy::ptr_as_ptr, reason = "test code")]
#![allow(clippy::as_pointer_underscore, reason = "test code")]
#![allow(clippy::multiple_unsafe_ops_per_block, reason = "test code")]
#![allow(clippy::empty_drop, reason = "test code: probe types use empty Drop on purpose")]
#![allow(clippy::deref_by_slicing, reason = "tests prefer explicit slicing")]
#![allow(clippy::needless_borrow, reason = "tests prefer explicit borrows")]
#![allow(clippy::needless_borrows_for_generic_args, reason = "tests prefer explicit borrows")]
#![allow(clippy::redundant_slicing, reason = "tests prefer explicit slicing")]
mod common;
mod sync_failing {
use core::alloc::Layout;
use core::ptr::NonNull;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use allocator_api2::alloc::{AllocError, Allocator, Global};
#[derive(Clone)]
pub(crate) struct SyncFailingAllocator {
remaining: Arc<AtomicUsize>,
}
impl SyncFailingAllocator {
pub(crate) fn new(allow_n_allocs: usize) -> Self {
Self {
remaining: Arc::new(AtomicUsize::new(allow_n_allocs)),
}
}
}
unsafe impl Allocator for SyncFailingAllocator {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
let mut cur = self.remaining.load(Ordering::Relaxed);
loop {
if cur == 0 {
return Err(AllocError);
}
match self
.remaining
.compare_exchange_weak(cur, cur - 1, Ordering::Relaxed, Ordering::Relaxed)
{
Ok(_) => break,
Err(actual) => cur = actual,
}
}
Global.allocate(layout)
}
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
unsafe { Global.deallocate(ptr, layout) };
}
}
}
#[cfg(feature = "utf16")]
mod box_utf16_str_traits {
use core::borrow::{Borrow, BorrowMut};
use core::cmp::Ordering;
use core::hash::{Hash, Hasher};
use std::collections::hash_map::DefaultHasher;
use multitude::Arena;
use widestring::{Utf16Str, utf16str};
#[test]
fn methods_len_is_empty_and_as_mut_utf16_str() {
let arena = Arena::new();
let mut b = arena.alloc_utf16_str_box(utf16str!("hello"));
assert_eq!(b.len(), 5);
assert!(!b.is_empty());
let _: &mut Utf16Str = b.as_mut_utf16_str();
let empty = arena.alloc_utf16_str_box(utf16str!(""));
assert!(empty.is_empty());
assert_eq!(empty.len(), 0);
}
#[test]
fn deref_and_deref_mut() {
let arena = Arena::new();
let mut b = arena.alloc_utf16_str_box(utf16str!("hi"));
let _: &Utf16Str = &b;
let _: &mut Utf16Str = &mut b;
}
#[test]
fn as_ref_and_as_mut_via_trait() {
let arena = Arena::new();
let mut b = arena.alloc_utf16_str_box(utf16str!("abc"));
let _: &Utf16Str = AsRef::as_ref(&b);
let _: &mut Utf16Str = AsMut::as_mut(&mut b);
}
#[test]
fn borrow_and_borrow_mut_via_trait() {
let arena = Arena::new();
let mut b = arena.alloc_utf16_str_box(utf16str!("xyz"));
let _: &Utf16Str = Borrow::borrow(&b);
let _: &mut Utf16Str = BorrowMut::borrow_mut(&mut b);
}
#[test]
fn debug_and_display_format() {
let arena = Arena::new();
let b = arena.alloc_utf16_str_box(utf16str!("dbg"));
let _ = format!("{b:?}");
let _ = format!("{b}");
}
#[test]
fn eq_ord_partialord() {
let arena = Arena::new();
let a = arena.alloc_utf16_str_box(utf16str!("alpha"));
let b = arena.alloc_utf16_str_box(utf16str!("alpha"));
let c = arena.alloc_utf16_str_box(utf16str!("beta"));
assert!(a == b);
assert!(a != c);
assert_eq!(a.cmp(&b), Ordering::Equal);
assert_eq!(a.cmp(&c), Ordering::Less);
assert_eq!(a.partial_cmp(&c), Some(Ordering::Less));
}
#[test]
fn hash_and_pointer_format() {
let arena = Arena::new();
let a = arena.alloc_utf16_str_box(utf16str!("hh"));
let mut h = DefaultHasher::new();
a.hash(&mut h);
let _ = h.finish();
let _ = format!("{a:p}");
}
#[test]
fn unpin_impl_compiles() {
fn assert_unpin<T: Unpin>() {}
assert_unpin::<multitude::strings::BoxUtf16Str>();
}
}
mod alloc_panics_on_failing_allocator {
use std::panic::{AssertUnwindSafe, catch_unwind};
use multitude::Arena;
#[cfg(feature = "utf16")]
use widestring::utf16str;
use crate::common::FailingAllocator;
use crate::sync_failing::SyncFailingAllocator;
fn fa() -> Arena<FailingAllocator> {
Arena::new_in(FailingAllocator::new(0))
}
fn sfa() -> Arena<SyncFailingAllocator> {
Arena::new_in(SyncFailingAllocator::new(0))
}
#[test]
fn alloc_value_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc(0_u32);
}));
assert!(r.is_err());
}
#[test]
fn alloc_with_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_with(|| 0_u32);
}));
assert!(r.is_err());
}
#[test]
fn alloc_arc_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_arc(0_u32);
}));
assert!(r.is_err());
}
#[test]
fn alloc_arc_with_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_arc_with(|| 0_u32);
}));
assert!(r.is_err());
}
#[test]
fn alloc_box_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_box(0_u32);
}));
assert!(r.is_err());
}
#[test]
fn alloc_box_with_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_box_with(|| 0_u32);
}));
assert!(r.is_err());
}
#[test]
fn alloc_slice_copy_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_slice_copy(&[1_u8, 2, 3][..]);
}));
assert!(r.is_err());
}
#[test]
fn alloc_slice_clone_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let v: std::vec::Vec<u32> = std::vec![1, 2, 3];
let _ = a.alloc_slice_clone(&v[..]);
}));
assert!(r.is_err());
}
#[test]
fn alloc_slice_fill_with_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_slice_fill_with::<u32, _>(3, |i| i as u32);
}));
assert!(r.is_err());
}
#[test]
fn alloc_slice_fill_iter_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_slice_fill_iter::<u32, _>([1_u32, 2, 3]);
}));
assert!(r.is_err());
}
#[test]
fn alloc_slice_clone_arc_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let v: std::vec::Vec<u32> = std::vec![1, 2, 3];
let _ = a.alloc_slice_clone_arc(&v[..]);
}));
assert!(r.is_err());
}
#[test]
fn alloc_slice_copy_arc_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_slice_copy_arc(&[1_u8, 2, 3][..]);
}));
assert!(r.is_err());
}
#[test]
fn alloc_slice_fill_with_arc_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_slice_fill_with_arc::<u32, _>(3, |i| i as u32);
}));
assert!(r.is_err());
}
#[test]
fn alloc_slice_fill_iter_arc_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_slice_fill_iter_arc::<u32, _>([1_u32, 2, 3]);
}));
assert!(r.is_err());
}
#[test]
fn alloc_slice_clone_box_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let v: std::vec::Vec<u32> = std::vec![1, 2, 3];
let _ = a.alloc_slice_clone_box(&v[..]);
}));
assert!(r.is_err());
}
#[test]
fn alloc_slice_copy_box_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_slice_copy_box(&[1_u8, 2, 3][..]);
}));
assert!(r.is_err());
}
#[test]
fn alloc_slice_fill_with_box_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_slice_fill_with_box::<u32, _>(3, |i| i as u32);
}));
assert!(r.is_err());
}
#[test]
fn alloc_slice_fill_iter_box_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_slice_fill_iter_box::<u32, _>([1_u32, 2, 3]);
}));
assert!(r.is_err());
}
#[test]
fn alloc_str_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_str("abc");
}));
assert!(r.is_err());
}
#[test]
fn alloc_str_arc_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_str_arc("abc");
}));
assert!(r.is_err());
}
#[test]
fn alloc_str_box_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_str_box("abc");
}));
assert!(r.is_err());
}
#[cfg(feature = "utf16")]
#[test]
fn alloc_utf16_str_arc_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_utf16_str_arc(utf16str!("abc"));
}));
assert!(r.is_err());
}
#[cfg(feature = "utf16")]
#[test]
fn alloc_utf16_str_box_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_utf16_str_box(utf16str!("abc"));
}));
assert!(r.is_err());
}
#[cfg(feature = "utf16")]
#[test]
fn alloc_utf16_str_arc_from_str_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_utf16_str_arc_from_str("abc");
}));
assert!(r.is_err());
}
#[cfg(feature = "utf16")]
#[test]
fn alloc_utf16_str_box_from_str_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_utf16_str_box_from_str("abc");
}));
assert!(r.is_err());
}
#[test]
fn alloc_uninit_arc_drop_type_panics() {
struct D(u32);
impl Drop for D {
fn drop(&mut self) {}
}
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_uninit_arc::<D>();
}));
assert!(r.is_err());
}
#[test]
fn alloc_zeroed_arc_drop_type_panics() {
struct D(u32);
impl Drop for D {
fn drop(&mut self) {}
}
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_zeroed_arc::<D>();
}));
assert!(r.is_err());
}
#[test]
fn alloc_uninit_slice_arc_drop_type_panics() {
struct D(u32);
impl Drop for D {
fn drop(&mut self) {}
}
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_uninit_slice_arc::<D>(2);
}));
assert!(r.is_err());
}
#[test]
fn alloc_zeroed_slice_arc_drop_type_panics() {
struct D(u32);
impl Drop for D {
fn drop(&mut self) {}
}
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_zeroed_slice_arc::<D>(2);
}));
assert!(r.is_err());
}
#[test]
fn alloc_uninit_box_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_uninit_box::<u32>();
}));
assert!(r.is_err());
}
#[test]
fn alloc_zeroed_box_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_zeroed_box::<u32>();
}));
assert!(r.is_err());
}
#[test]
fn alloc_uninit_slice_box_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_uninit_slice_box::<u32>(2);
}));
assert!(r.is_err());
}
#[test]
fn alloc_zeroed_slice_box_panics() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = sfa();
let _ = a.alloc_zeroed_slice_box::<u32>(2);
}));
assert!(r.is_err());
}
}
mod try_alloc_returns_err_on_failing_allocator {
use multitude::Arena;
#[cfg(feature = "utf16")]
use widestring::utf16str;
use crate::sync_failing::SyncFailingAllocator;
fn fa() -> Arena<SyncFailingAllocator> {
Arena::new_in(SyncFailingAllocator::new(0))
}
#[test]
fn try_alloc_value_err() {
let a = fa();
assert!(a.try_alloc(0_u32).is_err());
}
#[test]
fn try_alloc_with_err() {
let a = fa();
assert!(a.try_alloc_with(|| 0_u32).is_err());
}
#[test]
fn try_alloc_arc_err() {
let a = fa();
assert!(a.try_alloc_arc(0_u32).is_err());
}
#[test]
fn try_alloc_arc_with_err() {
let a = fa();
assert!(a.try_alloc_arc_with(|| 0_u32).is_err());
}
#[test]
fn try_alloc_box_err() {
let a = fa();
assert!(a.try_alloc_box(0_u32).is_err());
}
#[test]
fn try_alloc_box_with_err() {
let a = fa();
assert!(a.try_alloc_box_with(|| 0_u32).is_err());
}
#[test]
fn try_alloc_slice_copy_err() {
let a = fa();
assert!(a.try_alloc_slice_copy(&[1_u8, 2, 3][..]).is_err());
}
#[test]
fn try_alloc_slice_clone_err() {
let a = fa();
let v: std::vec::Vec<u32> = std::vec![1, 2, 3];
assert!(a.try_alloc_slice_clone(&v[..]).is_err());
}
#[test]
fn try_alloc_slice_fill_with_err() {
let a = fa();
assert!(a.try_alloc_slice_fill_with::<u32, _>(3, |i| i as u32).is_err());
}
#[test]
fn try_alloc_slice_fill_iter_err() {
let a = fa();
assert!(a.try_alloc_slice_fill_iter::<u32, _>([1_u32, 2, 3]).is_err());
}
#[test]
fn try_alloc_slice_clone_arc_err() {
let a = fa();
let v: std::vec::Vec<u32> = std::vec![1, 2, 3];
assert!(a.try_alloc_slice_clone_arc(&v[..]).is_err());
}
#[test]
fn try_alloc_slice_copy_arc_err() {
let a = fa();
assert!(a.try_alloc_slice_copy_arc(&[1_u8, 2, 3][..]).is_err());
}
#[test]
fn try_alloc_slice_fill_with_arc_err() {
let a = fa();
assert!(a.try_alloc_slice_fill_with_arc::<u32, _>(3, |i| i as u32).is_err());
}
#[test]
fn try_alloc_slice_fill_iter_arc_err() {
let a = fa();
assert!(a.try_alloc_slice_fill_iter_arc::<u32, _>([1_u32, 2, 3]).is_err());
}
#[test]
fn try_alloc_slice_clone_box_err() {
let a = fa();
let v: std::vec::Vec<u32> = std::vec![1, 2, 3];
assert!(a.try_alloc_slice_clone_box(&v[..]).is_err());
}
#[test]
fn try_alloc_slice_copy_box_err() {
let a = fa();
assert!(a.try_alloc_slice_copy_box(&[1_u8, 2, 3][..]).is_err());
}
#[test]
fn try_alloc_slice_fill_with_box_err() {
let a = fa();
assert!(a.try_alloc_slice_fill_with_box::<u32, _>(3, |i| i as u32).is_err());
}
#[test]
fn try_alloc_slice_fill_iter_box_err() {
let a = fa();
assert!(a.try_alloc_slice_fill_iter_box::<u32, _>([1_u32, 2, 3]).is_err());
}
#[test]
fn try_alloc_str_err() {
let a = fa();
assert!(a.try_alloc_str("abc").is_err());
}
#[test]
fn try_alloc_str_arc_err() {
let a = fa();
assert!(a.try_alloc_str_arc("abc").is_err());
}
#[test]
fn try_alloc_str_box_err() {
let a = fa();
assert!(a.try_alloc_str_box("abc").is_err());
}
#[cfg(feature = "utf16")]
#[test]
fn try_alloc_utf16_str_arc_err() {
let a = fa();
assert!(a.try_alloc_utf16_str_arc(utf16str!("abc")).is_err());
}
#[cfg(feature = "utf16")]
#[test]
fn try_alloc_utf16_str_box_err() {
let a = fa();
assert!(a.try_alloc_utf16_str_box(utf16str!("abc")).is_err());
}
#[cfg(feature = "utf16")]
#[test]
fn try_alloc_utf16_str_arc_from_str_err() {
let a = fa();
assert!(a.try_alloc_utf16_str_arc_from_str("abc").is_err());
}
#[cfg(feature = "utf16")]
#[test]
fn try_alloc_utf16_str_box_from_str_err() {
let a = fa();
assert!(a.try_alloc_utf16_str_box_from_str("abc").is_err());
}
#[test]
fn try_alloc_uninit_arc_drop_err() {
struct D(u32);
impl Drop for D {
fn drop(&mut self) {}
}
let a = fa();
assert!(a.try_alloc_uninit_arc::<D>().is_err());
}
#[test]
fn try_alloc_zeroed_arc_drop_err() {
struct D(u32);
impl Drop for D {
fn drop(&mut self) {}
}
let a = fa();
assert!(a.try_alloc_zeroed_arc::<D>().is_err());
}
#[test]
fn try_alloc_uninit_slice_arc_drop_err() {
struct D(u32);
impl Drop for D {
fn drop(&mut self) {}
}
let a = fa();
assert!(a.try_alloc_uninit_slice_arc::<D>(2).is_err());
}
#[test]
fn try_alloc_zeroed_slice_arc_drop_err() {
struct D(u32);
impl Drop for D {
fn drop(&mut self) {}
}
let a = fa();
assert!(a.try_alloc_zeroed_slice_arc::<D>(2).is_err());
}
#[test]
fn try_alloc_uninit_box_err() {
let a = fa();
assert!(a.try_alloc_uninit_box::<u32>().is_err());
}
#[test]
fn try_alloc_zeroed_box_err() {
let a = fa();
assert!(a.try_alloc_zeroed_box::<u32>().is_err());
}
#[test]
fn try_alloc_uninit_slice_box_err() {
let a = fa();
assert!(a.try_alloc_uninit_slice_box::<u32>(2).is_err());
}
#[test]
fn try_alloc_zeroed_slice_box_err() {
let a = fa();
assert!(a.try_alloc_zeroed_slice_box::<u32>(2).is_err());
}
}
mod drop_slice_over_u16_max_returns_err {
use multitude::Arena;
#[derive(Clone)]
struct D(#[expect(dead_code, reason = "field gives the type a non-zero size")] u8);
impl Drop for D {
fn drop(&mut self) {}
}
const TOO_LONG: usize = (u16::MAX as usize) + 1;
#[test]
fn try_alloc_slice_clone_drop_over_u16_err() {
let a = Arena::new();
let v: std::vec::Vec<D> = (0..TOO_LONG).map(|i| D(i as u8)).collect();
assert!(a.try_alloc_slice_clone(&v[..]).is_err());
}
#[test]
fn try_alloc_slice_fill_with_drop_over_u16_err() {
let a = Arena::new();
assert!(a.try_alloc_slice_fill_with::<D, _>(TOO_LONG, |i| D(i as u8)).is_err());
}
#[test]
fn try_alloc_slice_fill_iter_drop_over_u16_err() {
let a = Arena::new();
assert!(a.try_alloc_slice_fill_iter::<D, _>((0..TOO_LONG).map(|i| D(i as u8))).is_err());
}
#[test]
fn try_alloc_uninit_slice_arc_over_u16_err() {
struct D(u32);
impl Drop for D {
fn drop(&mut self) {}
}
let a = Arena::new();
assert!(a.try_alloc_uninit_slice_arc::<D>(TOO_LONG).is_err());
}
#[test]
fn try_alloc_zeroed_slice_arc_over_u16_err() {
struct D(u32);
impl Drop for D {
fn drop(&mut self) {}
}
let a = Arena::new();
assert!(a.try_alloc_zeroed_slice_arc::<D>(TOO_LONG).is_err());
}
}
mod vec_freeze_try_into_arena_arc {
use multitude::Arena;
use crate::sync_failing::SyncFailingAllocator;
#[test]
fn try_into_arena_arc_ok() {
let a = Arena::new();
let mut v = a.alloc_vec::<u32>();
v.push(1);
v.push(2);
v.push(3);
let arc = v.try_into_arena_arc().unwrap();
assert_eq!(&*arc, &[1, 2, 3][..]);
}
#[test]
fn try_into_arena_arc_err_on_failing_allocator() {
let a = Arena::new_in(SyncFailingAllocator::new(1));
let mut v = a.alloc_vec::<u32>();
v.push(1);
let r = v.try_into_arena_arc();
assert!(r.is_err());
}
}
mod vec_into_iter_traits {
use multitude::Arena;
#[test]
fn double_ended_next_back() {
let a = Arena::new();
let mut v = a.alloc_vec::<u32>();
v.push(1);
v.push(2);
v.push(3);
let mut it = v.into_iter();
assert_eq!(it.next_back(), Some(3));
assert_eq!(it.next(), Some(1));
assert_eq!(it.next_back(), Some(2));
assert_eq!(it.next_back(), None);
}
#[test]
fn debug_formats_remaining() {
let a = Arena::new();
let mut v = a.alloc_vec::<u32>();
v.push(10);
v.push(20);
let it = v.into_iter();
let s = format!("{it:?}");
assert!(s.contains("IntoIter"));
assert!(s.contains("remaining"));
}
}
mod vec_try_grow_to_noop {
use multitude::Arena;
#[test]
fn reserve_within_capacity_is_noop() {
let a = Arena::new();
let mut v = a.alloc_vec_with_capacity::<u32>(16);
let cap_before = v.capacity();
v.reserve(0);
assert_eq!(v.capacity(), cap_before);
}
}
mod vec_mutate_extras {
use std::panic::{AssertUnwindSafe, catch_unwind};
use multitude::Arena;
use crate::common::FailingAllocator;
#[test]
fn insert_panics_on_failing_allocator() {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = Arena::new_in(FailingAllocator::new(0));
let mut v = a.alloc_vec::<u32>();
v.insert(0, 7);
}));
assert!(r.is_err());
}
#[test]
fn dedup_by_empty_is_noop() {
let a = Arena::new();
let mut v = a.alloc_vec::<u32>();
v.dedup_by(|_, _| true);
assert!(v.is_empty());
}
#[test]
fn dedup_by_singleton_is_noop() {
let a = Arena::new();
let mut v = a.alloc_vec::<u32>();
v.push(7);
v.dedup_by(|_, _| true);
assert_eq!(v.len(), 1);
}
}
mod uninit_drop_init_from_iter {
use std::sync::Arc as StdArc;
use std::sync::atomic::{AtomicUsize, Ordering};
use multitude::Arena;
#[derive(Clone)]
struct Counted(StdArc<AtomicUsize>);
impl Drop for Counted {
fn drop(&mut self) {
self.0.fetch_add(1, Ordering::Relaxed);
}
}
#[test]
fn alloc_slice_fill_iter_drop_type_runs_drop_at_arena_teardown() {
let counter = StdArc::new(AtomicUsize::new(0));
{
let a = Arena::new();
let _: &mut [Counted] = a.alloc_slice_fill_iter((0..4).map(|_| Counted(counter.clone())));
assert_eq!(counter.load(Ordering::Relaxed), 0);
}
assert_eq!(counter.load(Ordering::Relaxed), 4);
}
}
mod uninit_into_uninit_slice_placeholder_zeroed {
use core::mem::MaybeUninit;
use multitude::Arena;
#[test]
fn zeroed_slice_arc_drop_type_zeroes_payload() {
struct D(u32);
impl Drop for D {
fn drop(&mut self) {}
}
let a = Arena::new();
let slice = a.alloc_zeroed_slice_arc::<D>(3);
unsafe {
let bytes = core::slice::from_raw_parts(slice.as_ptr() as *const MaybeUninit<u8>, core::mem::size_of::<D>() * 3);
for b in bytes {
assert_eq!(b.assume_init(), 0);
}
}
}
}
mod vec_intoiterator_impl {
use multitude::Arena;
#[test]
fn into_iter_yields_in_order() {
let a = Arena::new();
let mut v = a.alloc_vec::<u32>();
for i in 0..5_u32 {
v.push(i);
}
let collected: std::vec::Vec<u32> = v.into_iter().collect();
assert_eq!(collected, std::vec![0, 1, 2, 3, 4]);
}
}
mod arc_str_traits {
use multitude::Arena;
#[cfg(feature = "utf16")]
use widestring::utf16str;
#[test]
fn arc_str_partial_eq_str_and_ref_str() {
let a = Arena::new();
let s = a.alloc_str_arc("hello");
assert!(s == *"hello");
assert!(s == "hello");
let bad = "world";
assert!(s != *bad);
}
#[test]
fn arc_str_pointer_fmt() {
let a = Arena::new();
let s = a.alloc_str_arc("p");
let _ = format!("{s:p}");
}
#[cfg(feature = "utf16")]
#[test]
fn arc_utf16_str_is_empty_and_eq_and_display() {
let a = Arena::new();
let s = a.alloc_utf16_str_arc(utf16str!(""));
assert!(s.is_empty());
let one = a.alloc_utf16_str_arc(utf16str!("x"));
let two = a.alloc_utf16_str_arc(utf16str!("x"));
assert!(one == two);
let _ = format!("{one}");
}
}
mod box_str_into_box_u8_slice {
use multitude::{Arena, Box as ArenaBox};
#[test]
fn from_box_str_to_box_u8_slice() {
let a = Arena::new();
let s = a.alloc_str_box("hello");
let bytes: ArenaBox<[u8]> = ArenaBox::from(s);
assert_eq!(&*bytes, b"hello");
}
}
mod arena_builder_default {
use multitude::ArenaBuilder;
#[test]
fn arena_builder_default_matches_new() {
let b1 = ArenaBuilder::default();
let b2 = ArenaBuilder::new();
let _a1 = b1.build();
let _a2 = b2.build();
}
}
mod allocator_impl_paths {
use core::alloc::Layout;
use allocator_api2::alloc::{Allocator, Global};
use multitude::Arena;
#[test]
fn arena_as_allocator_zst_allocate_returns_dangling() {
let arena: Arena<Global> = Arena::new();
let alloc = &arena;
let layout = Layout::from_size_align(0, 8).unwrap();
let nn = alloc.allocate(layout).unwrap();
assert_eq!(nn.len(), 0);
}
#[test]
fn arena_as_allocator_zst_dealloc_is_noop() {
let arena: Arena<Global> = Arena::new();
let alloc = &arena;
let layout = Layout::from_size_align(0, 8).unwrap();
let nn = alloc.allocate(layout).unwrap();
unsafe { alloc.deallocate(nn.cast::<u8>(), layout) };
}
#[test]
fn arena_as_allocator_grow_copies_overlap() {
let arena: Arena<Global> = Arena::new();
let alloc = &arena;
let old = Layout::from_size_align(8, 1).unwrap();
let nn = alloc.allocate(old).unwrap();
unsafe {
for i in 0..8 {
nn.cast::<u8>().as_ptr().add(i).write(i as u8);
}
}
let new = Layout::from_size_align(32, 1).unwrap();
let grown = unsafe { alloc.grow(nn.cast::<u8>(), old, new).unwrap() };
unsafe {
for i in 0..8_u8 {
assert_eq!(*grown.cast::<u8>().as_ptr().add(usize::from(i)), i);
}
alloc.deallocate(grown.cast::<u8>(), new);
}
}
}
mod arc_borrow {
use core::borrow::Borrow;
use multitude::Arena;
#[test]
fn arc_borrow_returns_inner() {
let a = Arena::new();
let arc = a.alloc_arc(42_u32);
let b: &u32 = Borrow::borrow(&arc);
assert_eq!(*b, 42);
}
}
mod arc_assume_init_slice_panics_when_drop_entry_missing {
use core::mem::MaybeUninit;
use std::panic::{AssertUnwindSafe, catch_unwind};
use multitude::Arena;
#[test]
fn slice_assume_init_for_drop_type_without_placeholder_panics() {
#[derive(Clone)]
struct D(#[expect(dead_code, reason = "field gives the type a non-zero size")] u32);
impl Drop for D {
fn drop(&mut self) {}
}
let arena = Arena::new();
let r = catch_unwind(AssertUnwindSafe(|| {
let arc: multitude::Arc<[MaybeUninit<D>]> = arena.alloc_slice_fill_with_arc(2, |_| MaybeUninit::new(D(0)));
let _: multitude::Arc<[D]> = unsafe { arc.assume_init() };
}));
assert!(r.is_err());
}
}
mod in_chunk_clone_is_copy_proxy {
use multitude::Arena;
#[test]
fn arc_clone_exercises_inchunk_clone() {
let arena = Arena::new();
let a = arena.alloc_arc(7_u32);
let b = a.clone();
assert_eq!(*a, 7);
assert_eq!(*b, 7);
}
}
mod vec_try_grow_to_within_cap {
use multitude::Arena;
#[test]
fn try_grow_to_no_op_when_within_cap() {
let a = Arena::new();
let mut v = a.alloc_vec_with_capacity::<u32>(8);
let cap = v.capacity();
v.try_reserve(0).unwrap();
assert_eq!(v.capacity(), cap);
}
#[test]
fn try_grow_to_zst_early_return() {
let a = Arena::new();
let mut v = a.alloc_vec::<()>();
for _ in 0..1024_u32 {
v.push(());
}
assert_eq!(v.len(), 1024);
}
}
mod chunk_ops_destroy_branch {
use multitude::Arena;
#[test]
fn box_outlives_arena_takes_destroy_branch_on_release() {
let arena = Arena::new();
let b = arena.alloc_box(42_u32);
drop(arena);
assert_eq!(*b, 42);
drop(b);
}
}
mod arena_constructors {
use allocator_api2::alloc::Global;
use multitude::Arena;
use crate::sync_failing::SyncFailingAllocator;
#[test]
fn arena_try_new_ok() {
let a: Arena<Global> = Arena::try_new().unwrap();
let _ = a.alloc(0_u32);
}
#[test]
fn arena_default_constructs_global() {
let a: Arena<Global> = Arena::default();
let _ = a.alloc(0_u32);
}
#[test]
fn arena_try_new_in_ok() {
let a = Arena::try_new_in(SyncFailingAllocator::new(usize::MAX)).unwrap();
let _ = a.alloc(0_u32);
}
}
mod alloc_slice_overflow_paths {
use std::panic::{AssertUnwindSafe, catch_unwind};
use multitude::Arena;
const HUGE: usize = usize::MAX / 2;
#[test]
fn try_alloc_slice_fill_with_u32_huge_len_returns_err() {
let a = Arena::new();
let r = a.try_alloc_slice_fill_with::<u32, _>(HUGE, |_| 0);
assert!(r.is_err());
}
#[test]
fn try_alloc_slice_fill_iter_u32_huge_len_returns_err() {
let a = Arena::new();
let r = a.try_alloc_slice_fill_iter::<u32, _>((0..HUGE).map(|_| 0_u32));
assert!(r.is_err());
}
#[test]
fn try_alloc_slice_fill_with_box_u32_huge_len_returns_err() {
let a = Arena::new();
let r = a.try_alloc_slice_fill_with_box::<u32, _>(HUGE, |_| 0);
assert!(r.is_err());
}
#[test]
fn try_alloc_slice_fill_iter_box_u32_huge_len_returns_err() {
let a = Arena::new();
let r = a.try_alloc_slice_fill_iter_box::<u32, _>((0..HUGE).map(|_| 0_u32));
assert!(r.is_err());
}
#[test]
fn try_alloc_slice_fill_with_arc_u32_huge_len_returns_err() {
let a = Arena::new();
let r = a.try_alloc_slice_fill_with_arc::<u32, _>(HUGE, |_| 0);
assert!(r.is_err());
}
#[test]
fn try_alloc_slice_fill_iter_arc_u32_huge_len_returns_err() {
let a = Arena::new();
let r = a.try_alloc_slice_fill_iter_arc::<u32, _>((0..HUGE).map(|_| 0_u32));
assert!(r.is_err());
}
fn p<F: FnOnce()>(f: F) -> bool {
catch_unwind(AssertUnwindSafe(f)).is_err()
}
#[test]
fn alloc_slice_fill_with_u32_huge_len_panics() {
assert!(p(|| {
let a = Arena::new();
let _ = a.alloc_slice_fill_with::<u32, _>(HUGE, |_| 0);
}));
}
#[test]
fn alloc_slice_fill_iter_u32_huge_len_panics() {
assert!(p(|| {
let a = Arena::new();
let _ = a.alloc_slice_fill_iter::<u32, _>((0..HUGE).map(|_| 0_u32));
}));
}
#[test]
fn alloc_slice_fill_with_box_u32_huge_len_panics() {
assert!(p(|| {
let a = Arena::new();
let _ = a.alloc_slice_fill_with_box::<u32, _>(HUGE, |_| 0);
}));
}
#[test]
fn alloc_slice_fill_iter_box_u32_huge_len_panics() {
assert!(p(|| {
let a = Arena::new();
let _ = a.alloc_slice_fill_iter_box::<u32, _>((0..HUGE).map(|_| 0_u32));
}));
}
#[test]
fn alloc_slice_fill_with_arc_u32_huge_len_panics() {
assert!(p(|| {
let a = Arena::new();
let _ = a.alloc_slice_fill_with_arc::<u32, _>(HUGE, |_| 0);
}));
}
#[test]
fn alloc_slice_fill_iter_arc_u32_huge_len_panics() {
assert!(p(|| {
let a = Arena::new();
let _ = a.alloc_slice_fill_iter_arc::<u32, _>((0..HUGE).map(|_| 0_u32));
}));
}
#[derive(Clone)]
struct D(#[expect(dead_code, reason = "field gives the type a non-zero size")] u8);
impl Drop for D {
fn drop(&mut self) {}
}
#[test]
fn alloc_slice_clone_drop_over_u16_panics() {
let v: std::vec::Vec<D> = std::vec![D(0); u16::MAX as usize + 1];
let arena = Arena::new();
let r = catch_unwind(AssertUnwindSafe(|| {
let _ = arena.alloc_slice_clone(&v[..]);
}));
assert!(r.is_err());
}
#[test]
fn alloc_slice_fill_with_drop_over_u16_panics() {
let arena = Arena::new();
let r = catch_unwind(AssertUnwindSafe(|| {
let _ = arena.alloc_slice_fill_with::<D, _>(u16::MAX as usize + 1, |i| D(i as u8));
}));
assert!(r.is_err());
}
#[test]
fn alloc_slice_fill_iter_drop_over_u16_panics() {
let arena = Arena::new();
let r = catch_unwind(AssertUnwindSafe(|| {
let _ = arena.alloc_slice_fill_iter::<D, _>((0..(u16::MAX as usize + 1)).map(|i| D(i as u8)));
}));
assert!(r.is_err());
}
}
#[cfg(feature = "utf16")]
mod alloc_utf16_try_variants_ok {
use multitude::Arena;
use widestring::utf16str;
#[test]
fn try_alloc_utf16_str_arc_ok() {
let a = Arena::new();
let r = a.try_alloc_utf16_str_arc(utf16str!("ok")).unwrap();
assert_eq!(r.len(), 2);
}
#[test]
fn try_alloc_utf16_str_box_ok() {
let a = Arena::new();
let r = a.try_alloc_utf16_str_box(utf16str!("ok")).unwrap();
assert_eq!(r.len(), 2);
}
}
mod arena_buf_zst_split {
use multitude::Arena;
#[test]
fn vec_drain_zst_does_not_panic() {
let a = Arena::new();
let mut v = a.alloc_vec::<()>();
for _ in 0..32_u32 {
v.push(());
}
let drained: usize = v.drain(..16).count();
assert_eq!(drained, 16);
assert_eq!(v.len(), 16);
}
}
mod allocator_impl_grow_to_zero_overlap {
use core::alloc::Layout;
use allocator_api2::alloc::{Allocator, Global};
use multitude::Arena;
#[test]
fn grow_from_zero_old_does_not_copy() {
let arena: Arena<Global> = Arena::new();
let alloc = &arena;
let zero = Layout::from_size_align(0, 1).unwrap();
let nn = alloc.allocate(zero).unwrap();
let one = Layout::from_size_align(8, 1).unwrap();
let grown = unsafe { alloc.grow(nn.cast::<u8>(), zero, one).unwrap() };
assert_eq!(grown.len(), 8);
unsafe { alloc.deallocate(grown.cast::<u8>(), one) };
}
}
#[cfg(feature = "dst")]
mod alloc_unsized_extras {
use core::alloc::Layout;
use multitude::Arena;
use crate::sync_failing::SyncFailingAllocator;
#[derive(Default)]
struct D(#[expect(dead_code, reason = "field gives the type a non-zero size")] u32);
impl Drop for D {
fn drop(&mut self) {}
}
#[test]
fn try_alloc_dst_arc_slice_drop_metadata_too_large_returns_err() {
let arena = Arena::new();
let len = (u16::MAX as usize) + 1;
let layout = Layout::array::<D>(len).unwrap();
let r = unsafe { arena.try_alloc_dst_arc::<[D]>(layout, len, |_p: *mut [D]| {}) };
assert!(r.is_err());
}
#[test]
fn try_alloc_dst_box_refill_failure_returns_err() {
let arena = Arena::new_in(SyncFailingAllocator::new(0));
let layout = Layout::new::<u32>();
let r = unsafe {
arena.try_alloc_dst_box::<u32>(layout, (), |p: *mut u32| {
p.write(0);
})
};
assert!(r.is_err());
}
}
mod oversized_paths {
use multitude::Arena;
#[derive(Clone)]
struct DropU64(u64);
impl Drop for DropU64 {
fn drop(&mut self) {}
}
#[derive(Clone)]
struct BigDrop([u64; 3000]);
impl Drop for BigDrop {
fn drop(&mut self) {}
}
struct CountedDrop<'a>(&'a core::sync::atomic::AtomicUsize);
impl Drop for CountedDrop<'_> {
fn drop(&mut self) {
self.0.fetch_add(1, core::sync::atomic::Ordering::SeqCst);
}
}
#[test]
fn alloc_slice_fill_with_oversized_drop_replays_destructors() {
use core::sync::atomic::{AtomicUsize, Ordering};
let counter = AtomicUsize::new(0);
{
let arena = Arena::new();
let out = arena.alloc_slice_fill_with(3000, |_| CountedDrop(&counter));
assert_eq!(out.len(), 3000);
assert_eq!(counter.load(Ordering::SeqCst), 0, "no drops before teardown");
}
assert_eq!(counter.load(Ordering::SeqCst), 3000, "every element dropped at arena teardown");
}
#[test]
fn alloc_slice_clone_oversized_drop() {
let arena = Arena::new();
let src: Vec<DropU64> = (0..3000).map(DropU64).collect();
let out = arena.alloc_slice_clone(&src);
assert_eq!(out.len(), 3000);
assert_eq!(out[2999].0, 2999);
}
#[test]
fn alloc_slice_fill_with_oversized_drop() {
let arena = Arena::new();
let out = arena.alloc_slice_fill_with(3000, |i| DropU64(i as u64));
assert_eq!(out.len(), 3000);
assert_eq!(out[2999].0, 2999);
}
#[test]
fn alloc_slice_fill_iter_oversized_drop() {
let arena = Arena::new();
let out = arena.alloc_slice_fill_iter((0_u32..3000).map(|i| DropU64(u64::from(i))));
assert_eq!(out.len(), 3000);
assert_eq!(out[0].0, 0);
}
#[test]
fn alloc_with_oversized_drop_value() {
let arena = Arena::new();
let v = arena.alloc_with(|| BigDrop([7_u64; 3000]));
assert_eq!(v.0[0], 7);
assert_eq!(v.0[2999], 7);
}
#[test]
fn alloc_uninit_arc_oversized() {
use core::mem::MaybeUninit;
use multitude::Arc;
let arena = Arena::new();
let a = arena.alloc_uninit_arc::<BigDrop>();
unsafe {
let p = Arc::as_ptr(&a).cast::<MaybeUninit<BigDrop>>().cast_mut();
(*p).write(BigDrop([9_u64; 3000]));
}
let typed = unsafe { a.assume_init() };
assert_eq!(typed.0[0], 9);
assert_eq!(typed.0[2999], 9);
}
#[test]
fn alloc_uninit_slice_arc_oversized() {
use core::mem::MaybeUninit;
use multitude::Arc;
let arena = Arena::new();
let len = 3000_usize;
let s = arena.alloc_uninit_slice_arc::<DropU64>(len);
unsafe {
let base = Arc::as_ptr(&s).cast::<MaybeUninit<DropU64>>().cast_mut();
for i in 0..len {
(*base.add(i)).write(DropU64(i as u64));
}
}
let typed = unsafe { s.assume_init() };
assert_eq!(typed.len(), len);
assert_eq!(typed[123].0, 123);
}
#[cfg(feature = "utf16")]
#[test]
fn alloc_utf16_str_arc_oversized() {
let arena = Arena::new();
let s = "a".repeat(10_000);
let u = arena.alloc_utf16_str_arc_from_str(&s);
assert_eq!(u.len(), 10_000);
}
#[cfg(feature = "utf16")]
#[test]
fn alloc_utf16_str_box_oversized() {
let arena = Arena::new();
let s = "b".repeat(10_000);
let u = arena.alloc_utf16_str_box_from_str(&s);
assert_eq!(u.len(), 10_000);
}
#[test]
fn alloc_zeroed_arc_oversized() {
use core::mem::MaybeUninit;
use multitude::Arc;
let arena = Arena::new();
let _a: Arc<MaybeUninit<BigDrop>> = arena.alloc_zeroed_arc::<BigDrop>();
}
#[test]
fn box_str_as_mut_str() {
let arena = Arena::new();
let mut b = arena.alloc_str_box("hello");
let m: &mut str = b.as_mut_str();
m.make_ascii_uppercase();
assert_eq!(b.as_str(), "HELLO");
}
}