#![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")]
#![allow(clippy::missing_asserts_for_indexing, reason = "test code is direct")]
#![allow(clippy::items_after_statements, reason = "test-local types next to their use")]
#![allow(dead_code, reason = "test-local structs with unused fields")]
#![allow(clippy::panic_in_result_fn, reason = "tests deliberately trigger panics")]
mod common;
use core::cmp::Ordering;
use multitude::Arena;
use multitude::vec::{CollectIn, Vec};
#[test]
fn pop_and_clear() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<u32>();
v.push(1);
v.push(2);
v.push(3);
assert_eq!(v.pop(), Some(3));
assert_eq!(v.len(), 2);
let cap = v.capacity();
v.clear();
assert!(v.is_empty());
assert_eq!(v.capacity(), cap);
assert_eq!(v.pop(), None);
}
#[test]
fn reserve_grows_capacity() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<u32>();
v.reserve(100);
assert!(v.capacity() >= 100);
}
#[test]
fn vec_with_capacity_factory() {
let arena = Arena::new();
let v = arena.alloc_vec_with_capacity::<u32>(50);
assert!(v.capacity() >= 50);
assert!(v.is_empty());
}
#[test]
fn as_mut_slice_modifies_elements() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.push(1_u32);
v.push(2);
v.as_mut_slice()[0] = 10;
assert_eq!(v.as_slice(), &[10, 2]);
}
#[test]
fn extend_from_slice() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.extend_from_slice([1_u32, 2, 3]);
v.extend_from_slice([4, 5]);
assert_eq!(v.as_slice(), &[1, 2, 3, 4, 5]);
}
#[test]
fn extend_iter() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.extend(0_u32..5);
assert_eq!(v.as_slice(), &[0, 1, 2, 3, 4]);
}
#[test]
fn collect_in_works() {
let arena = Arena::new();
let v: Vec<i32, _> = (0..10).collect_in(&arena);
assert_eq!(v.len(), 10);
assert_eq!(v.as_slice(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
}
#[test]
fn traits_compile() {
let arena = Arena::new();
let mut a = arena.alloc_vec();
a.extend([1_u32, 2, 3]);
let mut b = arena.alloc_vec();
b.extend([1_u32, 2, 3]);
let mut c = arena.alloc_vec();
c.extend([4_u32, 5]);
let _: &[u32] = a.as_ref();
let mb: &mut [u32] = a.as_mut();
mb[0] = 1;
let r: &[u32] = core::borrow::Borrow::borrow(&a);
assert_eq!(r, &[1, 2, 3]);
assert_eq!(format!("{a:?}"), "[1, 2, 3]");
assert_eq!(a, b);
assert!(a != c);
assert_eq!(a.cmp(&c), Ordering::Less);
assert_eq!(a.partial_cmp(&c), Some(Ordering::Less));
assert_eq!(common::hash_of(&a), common::hash_of(&b));
}
#[test]
fn try_push_succeeds() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.try_push(1_u32).unwrap();
v.try_push(2_u32).unwrap();
assert_eq!(&*v, &[1, 2]);
}
#[test]
fn try_reserve_succeeds() {
let arena = Arena::new();
let mut v: Vec<u32> = arena.alloc_vec();
v.try_reserve(64).unwrap();
assert!(v.capacity() >= 64);
}
#[test]
fn try_with_capacity_in_succeeds() {
let arena = Arena::new();
let v: Vec<u32> = arena.try_alloc_vec_with_capacity(32).unwrap();
assert!(v.capacity() >= 32);
assert!(v.is_empty());
}
#[test]
fn try_with_capacity_in_zero_does_not_allocate() {
let arena = Arena::new();
let v: Vec<u32> = arena.try_alloc_vec_with_capacity(0).unwrap();
assert_eq!(v.capacity(), 0);
}
#[test]
fn try_push_returns_err_on_alloc_failure() {
let alloc = common::FailingAllocator::new(0);
let arena = Arena::new_in(alloc);
let mut v: Vec<u32, _> = arena.alloc_vec();
let _ = v.try_push(1).unwrap_err();
}
#[test]
fn try_reserve_returns_err_on_alloc_failure() {
let alloc = common::FailingAllocator::new(0);
let arena = Arena::new_in(alloc);
let mut v: Vec<u32, _> = arena.alloc_vec();
let _ = v.try_reserve(16).unwrap_err();
}
#[test]
fn try_with_capacity_in_returns_err_on_alloc_failure() {
let alloc = common::FailingAllocator::new(0);
let arena = Arena::new_in(alloc);
let result: Result<Vec<u32, _>, _> = arena.try_alloc_vec_with_capacity(16);
let _ = result.unwrap_err();
}
#[test]
fn with_capacity_in_pub_succeeds() {
let arena = Arena::new();
let v: Vec<u32> = arena.alloc_vec_with_capacity(8);
assert!(v.capacity() >= 8);
}
#[test]
fn new_in_pub_succeeds() {
let arena = Arena::new();
let v: Vec<u8> = arena.alloc_vec();
assert_eq!(v.len(), 0);
assert_eq!(v.capacity(), 0);
}
#[test]
fn from_iter_in_builds_content() {
let arena = Arena::new();
let v = Vec::<i32>::from_iter_in(0..5, &arena);
assert_eq!(v.as_slice(), &[0, 1, 2, 3, 4]);
}
#[test]
fn as_ptr_and_as_mut_ptr_round_trip() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.extend([10_u32, 20, 30]);
let p = v.as_ptr();
let first = unsafe { *p };
assert_eq!(first, 10);
let mp = v.as_mut_ptr();
unsafe { *mp = 99 };
assert_eq!(v.as_slice(), &[99, 20, 30]);
}
#[test]
fn insert_remove_swap_remove() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.extend([1_u32, 2, 4]);
v.insert(2, 3);
assert_eq!(v.as_slice(), &[1, 2, 3, 4]);
let r = v.remove(0);
assert_eq!(r, 1);
assert_eq!(v.as_slice(), &[2, 3, 4]);
let s = v.swap_remove(0);
assert_eq!(s, 2);
assert_eq!(v.as_slice(), &[4, 3]);
}
#[test]
#[should_panic(expected = "insertion index")]
fn insert_out_of_bounds_panics() {
let arena = Arena::new();
let mut v: Vec<u32> = arena.alloc_vec();
v.insert(99, 1);
}
#[test]
#[should_panic(expected = "removal index")]
fn remove_out_of_bounds_panics() {
let arena = Arena::new();
let mut v: Vec<u32> = arena.alloc_vec();
let _ = v.remove(0);
}
#[test]
#[should_panic(expected = "swap_remove index")]
fn swap_remove_out_of_bounds_panics() {
let arena = Arena::new();
let mut v: Vec<u32> = arena.alloc_vec();
let _ = v.swap_remove(0);
}
#[test]
fn truncate_shortens() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.extend(0_u32..10);
v.truncate(4);
assert_eq!(v.as_slice(), &[0, 1, 2, 3]);
v.truncate(100);
assert_eq!(v.len(), 4);
}
#[test]
fn set_len_unsafe() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<u32>();
v.reserve(4);
let p = v.as_mut_ptr();
for i in 0..4_u32 {
let slot = unsafe { p.add(i as usize) };
unsafe { slot.write(i * 2) };
}
unsafe { v.set_len(4) };
assert_eq!(v.as_slice(), &[0, 2, 4, 6]);
}
#[test]
fn shrink_to_fit_runs() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<u32>();
v.reserve(128);
v.push(1);
v.push(2);
let cap_before = v.capacity();
v.shrink_to_fit();
assert!(v.capacity() <= cap_before);
assert_eq!(v.as_slice(), &[1, 2]);
}
#[test]
fn retain_filters() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.extend(0_u32..10);
v.retain(|x| x % 2 == 0);
assert_eq!(v.as_slice(), &[0, 2, 4, 6, 8]);
}
#[test]
fn retain_mut_filters_and_mutates() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.extend(0_u32..6);
v.retain_mut(|x| {
if *x % 2 == 0 {
*x *= 10;
true
} else {
false
}
});
assert_eq!(v.as_slice(), &[0, 20, 40]);
}
#[test]
fn dedup_basic() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.extend([1_u32, 1, 2, 3, 3, 3, 4]);
v.dedup();
assert_eq!(v.as_slice(), &[1, 2, 3, 4]);
}
#[test]
fn dedup_by_custom() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.extend([1_i32, -1, 2, -2, 3]);
v.dedup_by(|a, b| a.abs() == b.abs());
assert_eq!(v.as_slice(), &[1, 2, 3]);
}
#[test]
fn dedup_by_key_works() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.extend([10_i32, 11, 21, 22, 30]);
v.dedup_by_key(|x| *x / 10);
assert_eq!(v.as_slice(), &[10, 21, 30]);
}
#[test]
fn append_moves_elements() {
let arena = Arena::new();
let mut a = arena.alloc_vec();
a.extend([1_u32, 2]);
let mut b = arena.alloc_vec();
b.extend([3_u32, 4, 5]);
a.append(&mut b);
assert_eq!(a.as_slice(), &[1, 2, 3, 4, 5]);
assert!(b.is_empty());
}
#[test]
fn reserve_exact_grows() {
let arena = Arena::new();
let mut v: Vec<u32> = arena.alloc_vec();
v.reserve_exact(50);
assert!(v.capacity() >= 50);
}
#[test]
fn try_reserve_exact_succeeds() {
let arena = Arena::new();
let mut v: Vec<u32> = arena.alloc_vec();
v.try_reserve_exact(40).unwrap();
assert!(v.capacity() >= 40);
}
#[test]
fn try_reserve_exact_returns_err_on_alloc_failure() {
let alloc = common::FailingAllocator::new(0);
let arena = Arena::new_in(alloc);
let mut v: Vec<u32, _> = arena.alloc_vec();
let _ = v.try_reserve_exact(16).unwrap_err();
}
#[test]
fn resize_grow_and_shrink() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.extend([1_u32, 2, 3]);
v.resize(5, 9);
assert_eq!(v.as_slice(), &[1, 2, 3, 9, 9]);
v.resize(2, 0);
assert_eq!(v.as_slice(), &[1, 2]);
}
#[test]
fn resize_with_closure() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
let mut counter = 0_u32;
v.resize_with(4, || {
counter += 1;
counter
});
assert_eq!(v.as_slice(), &[1, 2, 3, 4]);
}
#[test]
fn split_off_returns_tail() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.extend(0_u32..6);
let tail = v.split_off(4);
assert_eq!(v.as_slice(), &[0, 1, 2, 3]);
assert_eq!(tail.as_slice(), &[4, 5]);
}
#[test]
fn split_off_shares_chunk_without_copying() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<u32>();
v.extend(0_u32..10);
let head_data_before = v.as_ptr();
let head_cap_before = v.capacity();
let tail = v.split_off(4);
assert_eq!(v.as_ptr(), head_data_before);
let expected_tail_ptr = unsafe { head_data_before.add(4) };
assert_eq!(tail.as_ptr(), expected_tail_ptr);
assert_eq!(v.capacity(), 4);
assert_eq!(tail.capacity(), head_cap_before - 4);
drop(tail);
drop(v);
}
#[test]
fn split_off_with_drop_type_drops_each_element_once() {
use core::sync::atomic::{AtomicUsize, Ordering as Ord};
static COUNT: AtomicUsize = AtomicUsize::new(0);
struct D;
impl Drop for D {
fn drop(&mut self) {
COUNT.fetch_add(1, Ord::Relaxed);
}
}
COUNT.store(0, Ord::Relaxed);
{
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.extend((0..6).map(|_| D));
let _tail = v.split_off(4);
assert_eq!(COUNT.load(Ord::Relaxed), 0);
}
assert_eq!(COUNT.load(Ord::Relaxed), 6);
}
#[test]
fn split_off_edge_cases() {
let arena = Arena::new();
let mut v0 = arena.alloc_vec::<u32>();
let t0 = v0.split_off(0);
assert!(t0.is_empty());
let mut v1 = arena.alloc_vec();
v1.extend(0_u32..3);
let t1 = v1.split_off(3);
assert_eq!(v1.as_slice(), &[0, 1, 2]);
assert!(t1.is_empty());
let mut v2 = arena.alloc_vec();
v2.extend(0_u32..3);
let t2 = v2.split_off(0);
assert!(v2.is_empty());
assert_eq!(t2.as_slice(), &[0, 1, 2]);
}
#[test]
#[should_panic(expected = "split index out of bounds")]
fn split_off_out_of_bounds_panics() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<u32>();
v.extend(0_u32..3);
let _ = v.split_off(4);
}
#[test]
fn split_off_then_append_adjacent_round_trips() {
let arena = Arena::new();
let mut head = arena.alloc_vec::<u32>();
head.extend(0_u32..8);
let head_data_before = head.as_ptr();
let mut tail = head.split_off(5);
head.append(&mut tail);
assert_eq!(head.as_slice(), &[0, 1, 2, 3, 4, 5, 6, 7]);
assert_eq!(head.as_ptr(), head_data_before);
assert!(tail.is_empty());
assert_eq!(tail.capacity(), 0);
}
#[test]
fn append_adjacency_fast_path_zero_copy() {
let arena = Arena::new();
let mut a = arena.alloc_vec::<u8>();
a.reserve_exact(4);
a.extend([1_u8, 2, 3, 4]);
let a_end_before = unsafe { a.as_ptr().add(a.capacity()) };
let mut b = arena.alloc_vec::<u8>();
b.reserve_exact(4);
b.extend([5_u8, 6, 7, 8]);
if core::ptr::eq(a_end_before, b.as_ptr()) {
let a_data_before = a.as_ptr();
a.append(&mut b);
assert_eq!(a.as_slice(), &[1, 2, 3, 4, 5, 6, 7, 8]);
assert_eq!(a.as_ptr(), a_data_before);
assert_eq!(a.capacity(), 8);
assert_eq!(a.len(), 8);
assert!(b.is_empty());
assert_eq!(b.capacity(), 0);
}
}
#[test]
fn append_zst_preserves_other_capacity_default_path() {
let arena = Arena::new();
let mut a = arena.alloc_vec::<()>();
for _ in 0..3 {
a.push(());
}
let mut b = arena.alloc_vec::<()>();
for _ in 0..2 {
b.push(());
}
let b_cap_before = b.capacity();
assert!(b_cap_before > 0, "precondition: b must have nonzero capacity");
a.append(&mut b);
assert_eq!(a.len(), 5);
assert!(b.is_empty());
assert_eq!(b.capacity(), b_cap_before);
}
#[test]
fn append_adjacent_other_with_zero_len_does_not_absorb() {
let arena = Arena::new();
let mut a = arena.alloc_vec::<u32>();
a.reserve_exact(4);
a.extend([1_u32, 2, 3, 4]);
let a_end_before = unsafe { a.as_ptr().add(a.capacity()) };
let mut b = arena.alloc_vec::<u32>();
b.reserve_exact(4);
if core::ptr::eq(a_end_before, b.as_ptr()) {
let a_cap_before = a.capacity();
let b_cap_before = b.capacity();
a.append(&mut b);
assert_eq!(a.capacity(), a_cap_before);
assert_eq!(b.capacity(), b_cap_before);
assert_eq!(a.len(), 4);
assert_eq!(b.len(), 0);
}
}
#[test]
fn append_adjacency_fast_path_returns_early() {
let arena = Arena::new();
let mut head = arena.alloc_vec::<u32>();
head.extend(0_u32..6);
let head_ptr_before = head.as_ptr();
let head_cap_before = head.capacity();
let mut tail = head.split_off(4);
let tail_cap_before = tail.capacity();
assert_eq!(head.len(), head.capacity());
assert!(tail_cap_before > 0);
head.append(&mut tail);
assert_eq!(head.as_slice(), &[0, 1, 2, 3, 4, 5]);
assert_eq!(head.as_ptr(), head_ptr_before);
assert_eq!(head.capacity(), head_cap_before);
assert!(tail.is_empty());
assert_eq!(tail.capacity(), 0);
}
#[test]
fn append_outer_gate_true_inner_ptr_eq_false_falls_through() {
let arena = Arena::new();
let mut a = arena.alloc_vec::<u32>();
a.reserve_exact(4);
a.extend(0_u32..4);
assert_eq!(a.len(), a.capacity(), "self.len == self.cap required");
let a_data_before = a.as_ptr();
let a_cap_before = a.capacity();
let a_end_before = unsafe { a_data_before.add(a_cap_before) };
let mut spacer = arena.alloc_vec::<u32>();
spacer.reserve_exact(4);
spacer.extend(100_u32..104);
let mut b = arena.alloc_vec::<u32>();
b.reserve_exact(4);
b.extend([10_u32, 20, 30, 40]);
assert!(!core::ptr::eq(a_end_before, b.as_ptr()), "spacer must have separated a and b");
a.append(&mut b);
assert_eq!(a.as_slice(), &[0, 1, 2, 3, 10, 20, 30, 40]);
assert!(a.capacity() >= 8);
assert!(b.is_empty());
assert_eq!(spacer.as_slice(), &[100, 101, 102, 103]);
}
#[test]
fn split_off_at_len_returns_empty_tail_with_zero_capacity() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<u32>();
v.extend([1_u32, 2, 3]);
let tail = v.split_off(3);
assert_eq!(v.as_slice(), &[1, 2, 3]);
assert!(tail.is_empty());
assert_eq!(tail.capacity(), 0);
}
#[test]
fn shrink_to_fit_at_cursor_reclaims_in_place() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<u32>();
v.reserve_exact(128);
v.extend([1_u32, 2, 3, 4]);
let data_before = v.as_ptr();
let cap_before = v.capacity();
v.shrink_to_fit();
assert_eq!(v.as_ptr(), data_before);
assert_eq!(v.len(), 4);
assert!(v.capacity() <= cap_before);
assert_eq!(v.capacity(), 4);
assert_eq!(v.as_slice(), &[1, 2, 3, 4]);
}
#[test]
fn leak_reclaims_unused_capacity_tail() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<u64>();
v.reserve_exact(16);
v.extend([1_u64, 2, 3]);
let base = v.as_ptr() as usize;
let leaked: &mut [u64] = v.leak();
assert_eq!(leaked, &[1, 2, 3]);
assert_eq!(leaked.as_ptr() as usize, base);
let next = arena.alloc(9_u64);
assert_eq!(core::ptr::from_ref::<u64>(next) as usize, base + 3 * core::mem::size_of::<u64>());
}
#[test]
fn drop_at_cursor_reclaims_storage() {
let arena = Arena::new();
let base = {
let mut v = arena.alloc_vec::<u64>();
v.reserve_exact(16);
v.extend([1_u64, 2, 3]);
v.as_ptr() as usize
}; let next = arena.alloc(9_u64);
assert_eq!(core::ptr::from_ref::<u64>(next) as usize, base);
}
#[test]
fn drop_not_at_cursor_does_not_reclaim() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<u64>();
v.extend([1_u64, 2, 3]);
let v_base = v.as_ptr() as usize;
let mid_addr = core::ptr::from_ref::<u64>(arena.alloc(7_u64)) as usize;
drop(v); let next_addr = core::ptr::from_ref::<u64>(arena.alloc(8_u64)) as usize;
assert!(next_addr > mid_addr);
assert_ne!(next_addr, v_base);
}
#[test]
fn pop_if_removes_when_true() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.extend([1_u32, 2, 3]);
let r = v.pop_if(|x| *x == 3);
assert_eq!(r, Some(3));
assert_eq!(v.as_slice(), &[1, 2]);
}
#[test]
fn pop_if_keeps_when_false() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.extend([1_u32, 2, 3]);
let r = v.pop_if(|x| *x == 99);
assert_eq!(r, None);
assert_eq!(v.as_slice(), &[1, 2, 3]);
}
#[test]
fn pop_if_empty_returns_none() {
let arena = Arena::new();
let mut v: Vec<u32> = arena.alloc_vec();
let r = v.pop_if(|_| true);
assert_eq!(r, None);
}
#[test]
fn drain_removes_and_yields() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.extend(0_u32..6);
let drained: std::vec::Vec<u32> = v.drain(1..4).collect();
assert_eq!(drained, [1, 2, 3]);
assert_eq!(v.as_slice(), &[0, 4, 5]);
}
#[test]
fn clone_produces_equal_independent_vec() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.extend([1_u32, 2, 3]);
let mut c = v.clone();
assert_eq!(c.as_slice(), v.as_slice());
c.push(4);
assert_eq!(v.as_slice(), &[1, 2, 3]);
assert_eq!(c.as_slice(), &[1, 2, 3, 4]);
}
#[test]
fn into_iter_consumes() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.extend([1_u32, 2, 3]);
let collected: std::vec::Vec<u32> = v.into_iter().collect();
assert_eq!(collected, [1, 2, 3]);
}
#[test]
fn into_iter_borrowed() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.extend([1_u32, 2, 3]);
let mut sum = 0_u32;
for x in &v {
sum += *x;
}
assert_eq!(sum, 6);
assert_eq!(v.len(), 3);
}
#[test]
fn into_iter_mut_borrowed() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.extend([1_u32, 2, 3]);
for x in &mut v {
*x *= 10;
}
assert_eq!(v.as_slice(), &[10, 20, 30]);
}
#[test]
fn extend_ref_for_copy_types() {
let arena = Arena::new();
let mut v: Vec<u8> = arena.alloc_vec();
let src = [1_u8, 2, 3];
v.extend(src.iter());
assert_eq!(v.as_slice(), &[1, 2, 3]);
}
#[test]
fn borrow_mut_returns_mut_slice() {
use core::borrow::BorrowMut;
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.extend([1_u32, 2, 3]);
let s: &mut [u32] = v.borrow_mut();
s[0] = 9;
assert_eq!(v.as_slice(), &[9, 2, 3]);
}
#[test]
fn vec_macro_empty() {
let arena = Arena::new();
let v: Vec<i32> = multitude::vec::vec![in &arena];
assert!(v.is_empty());
}
#[test]
fn vec_macro_from_list() {
let arena = Arena::new();
let v = multitude::vec::vec![in &arena; 1, 2, 3];
assert_eq!(&*v, &[1, 2, 3]);
}
#[test]
fn vec_macro_from_list_trailing_comma() {
let arena = Arena::new();
let v = multitude::vec::vec![in &arena; 'a', 'b', 'c',];
assert_eq!(&*v, &['a', 'b', 'c']);
}
#[test]
fn vec_macro_n_copies() {
let arena = Arena::new();
let v = multitude::vec::vec![in &arena; 7_u32; 4];
assert_eq!(&*v, &[7, 7, 7, 7]);
}
#[test]
fn vec_macro_n_copies_zero() {
let arena = Arena::new();
let v: Vec<i32> = multitude::vec::vec![in &arena; 0; 0];
assert!(v.is_empty());
assert_eq!(v.capacity(), 0);
}
#[test]
fn vec_macro_evaluates_each_expr_once() {
use core::cell::Cell;
let arena = Arena::new();
let n = Cell::new(0_u32);
let bump = || {
let v = n.get();
n.set(v + 1);
v
};
let v = multitude::vec::vec![in &arena; bump(), bump(), bump()];
assert_eq!(&*v, &[0, 1, 2]);
assert_eq!(n.get(), 3);
}
#[test]
fn vec_macro_n_copies_evaluates_value_once() {
use core::cell::Cell;
let arena = Arena::new();
let n = Cell::new(0_u32);
let producer = || {
n.set(n.get() + 1);
42_u32
};
let v = multitude::vec::vec![in &arena; producer(); 5];
assert_eq!(&*v, &[42, 42, 42, 42, 42]);
assert_eq!(n.get(), 1);
}
#[test]
fn vec_macro_with_typed_expression() {
let arena = Arena::new();
let v: Vec<u8> = multitude::vec::vec![in &arena; 1, 2, 3];
assert_eq!(v.len(), 3);
}
#[test]
fn vec_macro_can_hold_strings() {
let arena = Arena::new();
let s1 = std::string::String::from("hello");
let s2 = std::string::String::from("world");
let v = multitude::vec::vec![in &arena; s1, s2];
assert_eq!(&v[0], "hello");
assert_eq!(&v[1], "world");
}
#[test]
fn partial_eq_with_slice() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.push(1_u32);
v.push(2);
v.push(3);
let slice: &[u32] = &[1, 2, 3];
assert_eq!(v, *slice);
assert_ne!(v, [1_u32, 2, 4][..]);
}
#[test]
fn partial_eq_with_ref_slice() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.push(10_i32);
v.push(20);
let slice: &[i32] = &[10, 20];
assert_eq!(v, slice);
assert_ne!(v, &[10_i32, 21][..]);
}
#[test]
fn partial_eq_with_array() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.push(1_u8);
v.push(2);
v.push(3);
assert_eq!(v, [1_u8, 2, 3]);
assert_ne!(v, [1_u8, 2, 4]);
}
#[test]
fn partial_eq_with_ref_array() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.push(5_u16);
v.push(6);
assert_eq!(v, &[5_u16, 6]);
assert_ne!(v, &[5_u16, 7]);
}
#[test]
fn partial_eq_empty_vec_vs_empty_slice() {
let arena = Arena::new();
let v = arena.alloc_vec::<i32>();
let empty: &[i32] = &[];
assert_eq!(v, *empty);
assert_eq!(v, empty);
assert_eq!(v, [0_i32; 0]);
assert_eq!(v, &[0_i32; 0]);
}
#[test]
fn resize_grow_with_clone() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.push(1_u32);
v.resize(5, 42);
assert_eq!(v.as_slice(), &[1, 42, 42, 42, 42]);
}
#[test]
fn resize_shrink() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
for i in 0..10_u32 {
v.push(i);
}
v.resize(3, 0);
assert_eq!(v.as_slice(), &[0, 1, 2]);
}
#[test]
fn resize_same_length_is_noop() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.push(7_i32);
v.push(8);
v.resize(2, 0);
assert_eq!(v.as_slice(), &[7, 8]);
}
#[test]
fn resize_from_empty() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<u64>();
v.resize(100, 0xDEAD);
assert_eq!(v.len(), 100);
assert!(v.iter().all(|&x| x == 0xDEAD));
}
#[test]
fn resize_to_zero() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.push(1_u32);
v.push(2);
v.resize(0, 99);
assert!(v.is_empty());
}
#[test]
fn resize_with_grow() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<u32>();
let mut counter = 0_u32;
v.resize_with(5, || {
counter += 1;
counter * 10
});
assert_eq!(v.as_slice(), &[10, 20, 30, 40, 50]);
}
#[test]
fn resize_with_shrink() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
for i in 0..8_u32 {
v.push(i);
}
v.resize_with(3, || panic!("should not be called"));
assert_eq!(v.as_slice(), &[0, 1, 2]);
}
#[test]
fn resize_with_same_length_is_noop() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.push(1_u32);
v.resize_with(1, || panic!("should not be called"));
assert_eq!(v.as_slice(), &[1]);
}
#[test]
fn resize_with_from_empty() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<usize>();
v.resize_with(50, || 7);
assert_eq!(v.len(), 50);
assert!(v.iter().all(|&x| x == 7));
}
#[test]
fn resize_drops_excess_on_shrink() {
use std::sync::atomic::{AtomicUsize, Ordering};
static DROPS: AtomicUsize = AtomicUsize::new(0);
DROPS.store(0, Ordering::Relaxed);
#[derive(Clone)]
struct Tracked(u32);
impl Drop for Tracked {
fn drop(&mut self) {
DROPS.fetch_add(1, Ordering::Relaxed);
}
}
let arena = Arena::new();
let mut v = arena.alloc_vec();
for i in 0..10 {
v.push(Tracked(i));
}
DROPS.store(0, Ordering::Relaxed);
v.resize(3, Tracked(99));
assert_eq!(v.len(), 3);
assert_eq!(DROPS.load(Ordering::Relaxed), 8);
}
#[test]
fn resize_panic_in_clone_drops_already_written() {
use std::sync::atomic::{AtomicUsize, Ordering};
static DROPS: AtomicUsize = AtomicUsize::new(0);
static CLONES: AtomicUsize = AtomicUsize::new(0);
DROPS.store(0, Ordering::Relaxed);
CLONES.store(0, Ordering::Relaxed);
#[derive(Debug)]
struct PanicOnThirdClone(u32);
impl Clone for PanicOnThirdClone {
fn clone(&self) -> Self {
let n = CLONES.fetch_add(1, Ordering::Relaxed);
assert!(n != 2, "deliberate clone panic");
Self(self.0)
}
}
impl Drop for PanicOnThirdClone {
fn drop(&mut self) {
DROPS.fetch_add(1, Ordering::Relaxed);
}
}
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.push(PanicOnThirdClone(1));
DROPS.store(0, Ordering::Relaxed);
CLONES.store(0, Ordering::Relaxed);
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
v.resize(6, PanicOnThirdClone(99));
}));
assert!(result.is_err());
let drops = DROPS.load(Ordering::Relaxed);
assert!(drops >= 2, "at least 2 cloned elements should be dropped; got {drops}");
}
#[test]
fn resize_with_panic_in_f_drops_already_written() {
use std::sync::atomic::{AtomicUsize, Ordering};
static DROPS: AtomicUsize = AtomicUsize::new(0);
DROPS.store(0, Ordering::Relaxed);
struct Tracked(u32);
impl Drop for Tracked {
fn drop(&mut self) {
DROPS.fetch_add(1, Ordering::Relaxed);
}
}
let arena = Arena::new();
let mut v = arena.alloc_vec::<Tracked>();
let mut count = 0_u32;
DROPS.store(0, Ordering::Relaxed);
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
v.resize_with(10, || {
count += 1;
assert!(count != 4, "deliberate panic in f");
Tracked(count)
});
}));
assert!(result.is_err());
let drops = DROPS.load(Ordering::Relaxed);
assert!(drops >= 3, "at least 3 written elements should be dropped; got {drops}");
}
#[test]
fn splice_drop_panic_in_removed_element_restores_tail() {
use std::sync::atomic::{AtomicUsize, Ordering};
static DROPS: AtomicUsize = AtomicUsize::new(0);
DROPS.store(0, Ordering::Relaxed);
struct Bomb(u32);
impl Drop for Bomb {
fn drop(&mut self) {
DROPS.fetch_add(1, Ordering::Relaxed);
assert!(self.0 != 999, "deliberate panic dropping a removed element");
}
}
let arena = Arena::new();
let mut v = arena.alloc_vec::<Bomb>();
v.push(Bomb(0));
v.push(Bomb(999));
v.push(Bomb(2));
v.push(Bomb(3));
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let _ = v.splice(1..3, core::iter::empty::<Bomb>());
}));
assert!(result.is_err());
assert_eq!(v.len(), 2);
assert_eq!(v.as_slice()[0].0, 0);
assert_eq!(v.as_slice()[1].0, 3);
v.push(Bomb(4));
assert_eq!(v.as_slice().iter().map(|b| b.0).collect::<std::vec::Vec<_>>(), [0, 3, 4]);
}
#[test]
fn resize_grow_by_one() {
let arena = Arena::new();
let mut v = arena.alloc_vec();
v.push(1_u32);
v.push(2);
v.resize(3, 99);
assert_eq!(v.as_slice(), &[1, 2, 99]);
}
#[cfg(feature = "std")]
mod io_write {
use std::io::Write as _;
use multitude::Arena;
#[test]
fn write_returns_buf_len() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<u8>();
let n = v.write(b"hello").unwrap();
assert_eq!(n, 5);
assert_eq!(v.as_slice(), b"hello");
}
#[test]
fn write_all_appends_full_buffer() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<u8>();
v.write_all(b"hello, ").unwrap();
v.write_all(b"world").unwrap();
assert_eq!(v.as_slice(), b"hello, world");
}
#[test]
fn flush_is_a_noop() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<u8>();
v.write_all(b"x").unwrap();
v.flush().unwrap();
assert_eq!(v.as_slice(), b"x");
}
#[test]
fn write_zero_length_buffer() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<u8>();
let n = v.write(&[]).unwrap();
assert_eq!(n, 0);
assert!(v.is_empty());
v.write_all(&[]).unwrap();
assert!(v.is_empty());
}
#[test]
fn write_macro_formats_into_vec() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<u8>();
let s = String::from("hi");
write!(&mut v, "x={} y={}", 7, s).unwrap();
assert_eq!(v.as_slice(), b"x=7 y=hi");
}
#[test]
fn write_macro_with_only_args() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<u8>();
let n: u32 = 100;
write!(&mut v, "n={n}").unwrap();
assert_eq!(v.as_slice(), b"n=100");
}
#[test]
fn writeln_macro_appends_newline() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<u8>();
writeln!(&mut v, "line").unwrap();
assert_eq!(v.as_slice(), b"line\n");
}
#[test]
fn std_io_copy_into_arena_vec() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<u8>();
let src = b"copy me through std::io::copy";
let mut reader: &[u8] = src;
let n = std::io::copy(&mut reader, &mut v).unwrap();
assert_eq!(usize::try_from(n).unwrap(), src.len());
assert_eq!(v.as_slice(), src);
}
#[test]
fn many_small_writes_grow_the_buffer() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<u8>();
let n = 32;
for _ in 0..n {
v.write_all(b"abcdefgh").unwrap();
}
assert_eq!(v.len(), 8 * n);
assert_eq!(&v.as_slice()[..8], b"abcdefgh");
assert_eq!(&v.as_slice()[(8 * n - 8)..], b"abcdefgh");
}
#[test]
fn serde_json_writes_into_arena_vec() {
let arena = Arena::new();
let mut v = arena.alloc_vec::<u8>();
let value = serde_json::json!({ "a": 1, "b": [true, false] });
serde_json::to_writer(&mut v, &value).unwrap();
let s = std::str::from_utf8(v.as_slice()).unwrap();
let parsed: serde_json::Value = serde_json::from_str(s).unwrap();
assert_eq!(parsed, value);
}
}
mod mutants_for_vec {
#![allow(clippy::std_instead_of_core, reason = "test code")]
#![allow(clippy::unwrap_used, reason = "test code")]
#![allow(clippy::clone_on_ref_ptr, reason = "explicit .clone() clarifies test intent")]
#![allow(clippy::cast_possible_truncation, reason = "bounded indices fit")]
#![allow(clippy::doc_markdown, reason = "doc comments cite raw identifier names")]
use std::sync::Arc as StdArc;
use std::sync::atomic::{AtomicUsize, Ordering};
use multitude::Arena;
#[expect(unused_imports, reason = "merged test module re-exports common helpers")]
use crate::common;
#[derive(Clone, Debug)]
struct Tracked {
val: u32,
counter: StdArc<AtomicUsize>,
}
impl Drop for Tracked {
fn drop(&mut self) {
self.counter.fetch_add(1, Ordering::Relaxed);
}
}
#[test]
fn shrink_to_fit_full_vec_is_noop() {
let arena = Arena::new();
let mut v = arena.alloc_vec_with_capacity::<u32>(8);
for i in 0..8 {
v.push(i);
}
assert_eq!(v.len(), v.capacity());
let ptr_before = v.as_ptr();
v.shrink_to_fit();
let ptr_after = v.as_ptr();
assert_eq!(ptr_before, ptr_after, "shrink_to_fit at len==cap must not reallocate");
assert_eq!(v.len(), 8);
}
#[test]
fn resize_grows_with_correct_clone_count() {
let arena = Arena::new();
let counter = StdArc::new(AtomicUsize::new(0));
{
let mut v: multitude::vec::Vec<'_, Tracked> = arena.alloc_vec();
for i in 0..3 {
v.push(Tracked {
val: i,
counter: counter.clone(),
});
}
let template = Tracked {
val: 99,
counter: counter.clone(),
};
v.resize(10, template);
assert_eq!(v.len(), 10);
for (i, t) in v.iter().enumerate() {
let expected = if i < 3 { i as u32 } else { 99 };
assert_eq!(t.val, expected, "slot {i} expected {expected}");
}
}
assert_eq!(counter.load(Ordering::Relaxed), 10);
}
#[test]
fn resize_to_same_length_is_noop() {
let arena = Arena::new();
let counter = StdArc::new(AtomicUsize::new(0));
{
let mut v: multitude::vec::Vec<'_, Tracked> = arena.alloc_vec();
for i in 0..5 {
v.push(Tracked {
val: i,
counter: counter.clone(),
});
}
let template = Tracked {
val: 99,
counter: counter.clone(),
};
v.resize(5, template);
assert_eq!(v.len(), 5);
for (i, t) in v.iter().enumerate() {
assert_eq!(t.val, i as u32);
}
}
assert_eq!(counter.load(Ordering::Relaxed), 6);
}
#[cfg(feature = "stats")]
#[test]
fn realloc_growth_and_relocation_counter() {
let arena = Arena::new();
let mut v: multitude::vec::Vec<'_, u32> = arena.alloc_vec_with_capacity(4);
for i in 0..4096_u32 {
v.push(i);
}
let s = arena.stats();
for (i, x) in v.iter().enumerate() {
assert_eq!(*x as usize, i);
}
assert!(v.capacity() >= 4096);
let _ = s.relocations;
}
#[test]
fn into_box_yields_distinct_elements() {
use multitude::vec::Vec as MVec;
let arena = Arena::new();
let mut v: MVec<'_, String> = arena.alloc_vec();
for i in 0..8_u32 {
v.push(format!("item-{i}"));
}
let b = v.into_boxed_slice();
for (i, s) in b.iter().enumerate() {
assert_eq!(s, &format!("item-{i}"));
}
}
}
#[test]
fn split_off_sibling_survives_oversized_growth_of_other_half() {
use std::sync::Arc as StdArc;
use std::sync::atomic::{AtomicUsize, Ordering};
struct Dropper(StdArc<AtomicUsize>);
impl Drop for Dropper {
fn drop(&mut self) {
self.0.fetch_add(1, Ordering::Relaxed);
}
}
let counter = StdArc::new(AtomicUsize::new(0));
let arena = Arena::new();
let mut head = arena.alloc_vec::<Dropper>();
for _ in 0..4096 {
head.push(Dropper(counter.clone()));
}
let tail = head.split_off(2048);
assert_eq!(head.len(), 2048);
assert_eq!(tail.len(), 2048);
for _ in 0..6144 {
head.push(Dropper(counter.clone()));
}
assert_eq!(head.len(), 8192);
let mut sum = 0_usize;
for d in tail.as_slice() {
sum += StdArc::strong_count(&d.0);
}
assert!(sum > 0);
drop(tail);
drop(head);
drop(arena);
assert_eq!(counter.load(Ordering::Relaxed), 4096 + 6144);
}