use super::*;
use alloc::boxed::Box;
use core::mem::size_of;
use pretty_assertions::{assert_eq, assert_ne};
#[test]
fn stack_without_chunks() {
let stack = Stack::<u8>::new();
unsafe {
assert!(wrap::<u8>(stack.current_footer.get()).is_dead());
assert!(core::ptr::eq(
stack.current_footer.get().as_ptr(),
DEAD_CHUNK.footer().as_ptr(),
));
assert!(core::ptr::eq(
stack.first_footer.get().as_ptr(),
DEAD_CHUNK.footer().as_ptr(),
));
}
assert!(stack.is_empty());
}
#[test]
fn stack_one_chunk() {
type Stk = super::Stack<usize>;
let mut stack = Stk::new();
assert_eq!(stack.capacity(), 0);
assert_eq!(stack.len(), 0);
unsafe {
let current_chunk = wrap::<usize>(stack.current_footer.get());
assert!(current_chunk.is_dead());
assert!(current_chunk.is_empty());
assert!(wrap::<usize>(stack.first_footer.get()).is_dead());
}
stack.push(0);
let capacity = stack.capacity();
debug_assert!(capacity > 1);
for i in 1..capacity {
unsafe {
let current_chunk = wrap::<usize>(stack.current_footer.get());
assert!(!current_chunk.is_dead());
assert!(!current_chunk.is_full());
assert_eq!(stack.first_footer, stack.current_footer);
}
stack.push(i);
assert_eq!(stack.capacity(), capacity);
assert_eq!(stack.len(), i + 1);
}
unsafe {
let current_chunk = wrap::<usize>(stack.current_footer.get());
assert!(!current_chunk.is_dead());
assert!(current_chunk.is_full());
assert!(wrap::<usize>(current_chunk.prev()).is_dead());
assert!(wrap::<usize>(current_chunk.next()).is_dead());
assert_eq!(stack.first_footer, stack.current_footer);
}
for i in (0..capacity).rev() {
unsafe {
assert!(!wrap::<usize>(stack.current_footer.get()).is_dead());
}
assert_eq!(stack.pop(), Some(i));
assert_eq!(stack.len(), i);
}
assert!(stack.is_empty());
unsafe {
let current_chunk = wrap::<usize>(stack.current_footer.get());
assert!(!current_chunk.is_dead());
assert!(wrap::<usize>(current_chunk.prev()).is_dead());
assert!(wrap::<usize>(current_chunk.next()).is_dead());
assert_eq!(stack.first_footer, stack.current_footer);
}
}
#[derive(Debug, PartialEq, Eq)]
struct Element {
_value: [usize; 6],
}
fn elem(i: usize) -> Element {
Element { _value: [i; 6] }
}
#[test]
fn stack_two_chunks() {
let mut stack = Stack::<Element>::new();
stack.push(elem(0));
let capacity_1 = stack.capacity();
assert_eq!(stack.first_footer, stack.current_footer);
for i in 1..capacity_1 + 1 {
stack.push(elem(i));
}
let capacity_12 = stack.capacity();
assert!(capacity_1 < capacity_12);
unsafe {
let current_chunk = wrap::<Element>(stack.current_footer.get());
assert!(!current_chunk.is_dead());
assert!(!wrap::<Element>(current_chunk.prev()).is_dead());
assert!(wrap::<Element>(current_chunk.next()).is_dead());
assert_eq!(stack.first_footer.get(), current_chunk.prev());
}
for i in capacity_1 + 1..capacity_12 {
stack.push(elem(i));
}
assert_eq!(stack.capacity(), capacity_12);
assert_eq!(stack.len(), capacity_12);
for i in (capacity_1..capacity_12).rev() {
assert_eq!(stack.pop(), Some(elem(i)));
}
assert_eq!(stack.capacity(), capacity_12);
assert_eq!(stack.len(), capacity_1);
unsafe {
let current_chunk = wrap::<Element>(stack.current_footer.get());
assert!(!current_chunk.is_dead());
assert!(!wrap::<Element>(current_chunk.prev()).is_dead());
assert!(wrap::<Element>(current_chunk.next()).is_dead());
assert_eq!(stack.first_footer.get(), current_chunk.prev());
}
for i in (0..capacity_1).rev() {
assert_eq!(stack.pop(), Some(elem(i)));
}
assert_eq!(stack.capacity(), capacity_12);
assert_eq!(stack.len(), 0);
stack.pop();
assert_eq!(stack.capacity(), capacity_12 - capacity_1);
assert_eq!(stack.len(), 0);
unsafe {
let current_chunk = wrap::<Element>(stack.current_footer.get());
assert!(!current_chunk.is_dead());
assert!(wrap::<Element>(current_chunk.prev()).is_dead());
assert!(wrap::<Element>(current_chunk.next()).is_dead());
assert_eq!(stack.first_footer, stack.current_footer);
}
}
#[test]
fn stack_three_chunks() {
type Stk = Stack<Element>;
let mut stack = Stk::new();
stack.push(elem(0));
let capacity_1 = stack.capacity();
assert_eq!(stack.first_footer, stack.current_footer);
for i in 1..capacity_1 + 1 {
stack.push(elem(i));
}
let capacity_12 = stack.capacity();
for i in capacity_1 + 1..capacity_12 + 1 {
stack.push(elem(i));
}
let capacity_123 = stack.capacity();
assert!(capacity_12 < capacity_123);
for i in capacity_12 + 1..capacity_123 {
assert_eq!(stack.len(), i);
stack.push(elem(i));
assert_eq!(stack.capacity(), capacity_123);
}
assert_eq!(stack.len(), stack.capacity());
assert_eq!(stack.capacity(), capacity_123);
unsafe {
let current_chunk = wrap::<Element>(stack.current_footer.get());
assert!(!current_chunk.is_dead());
assert!(!wrap::<Element>(current_chunk.prev()).is_dead());
assert!(wrap::<Element>(current_chunk.next()).is_dead());
assert_eq!(
stack.first_footer.get(),
wrap::<Element>(current_chunk.prev()).prev()
);
}
for i in (capacity_12..capacity_123).rev() {
assert_eq!(stack.pop(), Some(elem(i)));
}
assert_eq!(stack.len(), capacity_12);
assert_eq!(stack.capacity(), capacity_123);
unsafe {
let current_chunk = wrap::<Element>(stack.current_footer.get());
assert!(!current_chunk.is_dead());
assert!(current_chunk.is_empty());
assert!(!wrap::<Element>(current_chunk.prev()).is_dead());
assert!(wrap::<Element>(current_chunk.next()).is_dead());
assert_eq!(
stack.first_footer.get(),
wrap::<Element>(current_chunk.prev()).prev()
);
}
assert_eq!(stack.pop(), Some(elem(capacity_12 - 1)));
stack.push(elem(capacity_12 - 1));
stack.push(elem(capacity_12));
unsafe {
let current_chunk = wrap::<Element>(stack.current_footer.get());
assert!(!current_chunk.is_dead());
assert!(!wrap::<Element>(current_chunk.prev()).is_dead());
assert!(wrap::<Element>(current_chunk.next()).is_dead());
}
assert_eq!(stack.pop(), Some(elem(capacity_12)));
for i in (capacity_1..capacity_12).rev() {
assert_eq!(stack.pop(), Some(elem(i)));
}
assert_eq!(stack.len(), capacity_1);
assert_eq!(stack.capacity(), capacity_123);
unsafe {
let current_chunk = wrap::<Element>(stack.current_footer.get());
assert!(!current_chunk.is_dead());
assert!(current_chunk.is_empty());
assert!(!wrap::<Element>(current_chunk.prev()).is_dead());
assert!(!wrap::<Element>(current_chunk.next()).is_dead());
assert!(wrap::<Element>(current_chunk.next()).is_empty());
assert_eq!(stack.first_footer.get(), current_chunk.prev());
}
for i in (0..capacity_1).rev() {
assert_eq!(stack.pop(), Some(elem(i)));
}
let capacity_13 = capacity_123 - capacity_12 + capacity_1;
assert_eq!(stack.capacity(), capacity_13);
assert_eq!(stack.len(), 0);
unsafe {
let current_chunk = wrap::<Element>(stack.current_footer.get());
assert!(!current_chunk.is_dead());
assert!(current_chunk.is_empty());
assert!(wrap::<Element>(current_chunk.prev()).is_dead());
let next_chunk = wrap::<Element>(current_chunk.next());
assert!(!next_chunk.is_dead());
assert!(next_chunk.is_empty());
assert_eq!(next_chunk.prev(), stack.current_footer.get());
assert!(wrap::<Element>(next_chunk.next()).is_dead());
assert_eq!(stack.first_footer, stack.current_footer);
}
}
#[test]
fn zero_sized_element() {
let mut stack = Stack::<()>::default();
assert_eq!(stack.capacity(), usize::MAX);
assert_eq!(stack.len(), 0);
const COUNT: usize = 200;
for i in 0..COUNT {
assert_eq!(stack.len(), i);
stack.push(());
assert_eq!(stack.capacity(), usize::MAX);
}
unsafe {
let current_chunk = wrap::<Element>(stack.current_footer.get());
assert!(current_chunk.is_dead());
assert!(wrap::<Element>(current_chunk.prev()).is_dead());
assert!(wrap::<Element>(current_chunk.next()).is_dead());
assert_eq!(stack.first_footer, stack.current_footer);
}
for i in (0..COUNT).rev() {
assert_eq!(stack.pop(), Some(()));
assert_eq!(stack.len(), i);
assert_eq!(stack.capacity(), usize::MAX);
}
assert_eq!(stack.pop(), None);
assert_eq!(stack.len(), 0);
assert_eq!(stack.capacity(), usize::MAX);
unsafe {
let current_chunk = wrap::<Element>(stack.current_footer.get());
assert!(current_chunk.is_dead());
assert!(wrap::<Element>(current_chunk.prev()).is_dead());
assert!(wrap::<Element>(current_chunk.next()).is_dead());
assert_eq!(stack.first_footer, stack.current_footer);
}
}
#[test]
fn stack_with_capacity() {
let stack = Stack::<i32>::with_capacity(0);
unsafe {
assert!(wrap::<i32>(stack.current_footer.get()).is_dead());
}
let stack = Stack::<i32>::with_capacity(1);
unsafe {
let current_chunk = wrap::<i32>(stack.current_footer.get());
assert!(!current_chunk.is_dead());
assert!(wrap::<i32>(current_chunk.prev()).is_dead());
assert!(wrap::<i32>(current_chunk.next()).is_dead());
}
let stack = Stack::<usize>::with_capacity(1 << 20);
assert!(stack.capacity() >= 1 << 20);
unsafe {
let current_chunk = wrap::<usize>(stack.current_footer.get());
assert!(!current_chunk.is_dead());
assert!(wrap::<usize>(current_chunk.prev()).is_dead());
assert!(wrap::<usize>(current_chunk.next()).is_dead());
}
}
#[test]
fn check_alignments() {
#[repr(C)]
#[repr(packed(1))]
struct T5Packed {
_m0: u32,
_m1: u8,
}
assert_eq!(size_of::<T5Packed>(), 5);
let stk = Stack::<T5Packed>::new();
stk.push(T5Packed { _m0: 0, _m1: 0 });
const { assert!(!Chunk::<T5Packed>::FOOTER_IS_END) };
assert_eq!(stk.capacity(), 89);
unsafe {
let current_chunk = wrap::<T5Packed>(stk.current_footer.get());
let end = current_chunk
.start()
.as_ptr()
.byte_add(stk.capacity() * size_of::<T5Packed>());
assert_ne!(end, current_chunk.footer().cast().as_ptr());
}
#[repr(align(16))]
struct T5 {
_m0: u32,
_m1: u8,
}
assert_eq!(size_of::<T5>(), 16);
let stk = Stack::<T5>::new();
stk.push(T5 { _m0: 0, _m1: 0 });
const { assert!(Chunk::<T5>::FOOTER_IS_END) };
assert_eq!(stk.capacity(), 28);
unsafe {
let current_chunk = wrap::<T5>(stk.current_footer.get());
let end = current_chunk
.start()
.as_ptr()
.byte_add(stk.capacity() * size_of::<T5>());
assert_eq!(end, current_chunk.footer().cast().as_ptr());
}
let stk = Stack::<usize>::new();
stk.push(0);
const { assert!(Chunk::<usize>::FOOTER_IS_END) };
assert_eq!(stk.capacity(), 56);
unsafe {
let current_chunk = wrap::<usize>(stk.current_footer.get());
let end = current_chunk
.start()
.as_ptr()
.byte_add(stk.capacity() * size_of::<usize>());
assert_eq!(end, current_chunk.footer().cast().as_ptr());
}
let stk = Stack::<u8>::new();
stk.push(0);
const { assert!(Chunk::<u8>::FOOTER_IS_END) };
assert_eq!(stk.capacity(), 448);
unsafe {
let current_chunk = wrap::<u8>(stk.current_footer.get());
let end = current_chunk
.start()
.as_ptr()
.byte_add(stk.capacity() * size_of::<u8>());
assert_eq!(end, current_chunk.footer().cast().as_ptr());
}
}
#[test]
fn check_packed_drop() {
use alloc::boxed::Box;
#[repr(C)]
#[repr(packed(1))]
struct Packed {
m0: u8,
m1: Box<u32>,
}
let mut stk = Stack::<Packed>::new();
let elem_ref = stk.push(Packed {
m0: 0,
m1: Box::new(0),
});
assert_eq!(elem_ref.m0, 0);
assert_eq!(*elem_ref.m1, 0);
let elem_ref = stk.push(Packed {
m0: 1,
m1: Box::new(1),
});
assert_eq!(elem_ref.m0, 1);
assert_eq!(*elem_ref.m1, 1);
stk.clear();
}
#[repr(C)]
#[repr(packed(1))]
struct TPacked {
b: bool,
value: Box<i32>,
}
impl TPacked {
fn new() -> Self {
Self {
b: false,
value: Box::new(42),
}
}
}
impl Clone for TPacked {
fn clone(&self) -> Self {
Self {
b: self.b,
value: Box::new(*self.value),
}
}
}
#[test]
fn stack_push_pop_packed() {
const { assert!(!Chunk::<TPacked>::FOOTER_IS_END) };
let mut stack = Stack::default();
const MAX: usize = 1 << 10;
for i in 0..MAX {
assert_eq!(stack.len(), i);
stack.push(TPacked::new());
}
for _ in (0..MAX).rev() {
stack.pop();
}
assert_eq!(stack.len(), 0);
assert!(stack.len() < stack.capacity());
}
#[test]
fn stack_iter_next() {
let stack = Stack::from(&[TPacked::new(), TPacked::new(), TPacked::new()]);
for elem in stack.iter() {
assert_eq!(*elem.value, 42);
}
for elem in stack.iter().rev() {
assert_eq!(*elem.value, 42);
}
}
#[test]
fn stack_send_check() {
let stk = Stack::from([1, 2, 3]);
let stk = std::thread::spawn(move || {
stk.push(4);
stk.push(5);
stk.push(6);
stk
})
.join()
.expect("thread failed");
assert_eq!(stk.len(), 6);
}