use super::*;
use crate::slot::{PayloadAnchor, PayloadKind, PayloadType};
fn payload(id: usize) -> PayloadRecord {
PayloadRecord {
anchor: PayloadAnchor::new(id, 1),
payload_type: PayloadType::of::<usize>(),
source: crate::slot::BRANCH_PATH_ROOT,
kind: PayloadKind::Remember,
value: Box::new(id),
}
}
fn ids(payloads: impl IntoIterator<Item = impl std::borrow::Borrow<PayloadRecord>>) -> Vec<usize> {
payloads
.into_iter()
.map(|payload| payload.borrow().anchor.id())
.collect()
}
fn store_with(count: usize) -> PayloadStore {
let mut store = PayloadStore::default();
for id in 0..count {
store.insert_item(id, payload(id));
}
store
}
#[test]
fn insert_keeps_table_order_and_reads_by_index() {
let mut store = store_with(3);
store.insert_item(0, payload(9));
assert_eq!(store.len(), 4);
assert_eq!(store.item_count(), 4);
assert_eq!(ids(store.iter()), [9, 0, 1, 2]);
assert_eq!(store.get(0).map(|payload| payload.anchor.id()), Some(9));
assert_eq!(store.item(3).map(|payload| payload.anchor.id()), Some(2));
assert!(store.get(4).is_none());
if let Some(payload) = store.get_mut(1) {
payload.kind = PayloadKind::Effect;
}
assert_eq!(
store.get(1).map(|payload| payload.kind),
Some(PayloadKind::Effect)
);
assert!(store.get_mut(4).is_none());
assert_eq!(store.validate_integrity(), Ok(()));
}
#[test]
fn insert_moves_one_order_entry_per_later_payload() {
let mut store = store_with(100);
let before = store.shift_bytes();
store.insert_item(10, payload(100));
assert_eq!(store.shift_bytes() - before, 90 * mem::size_of::<u32>());
}
#[test]
fn removed_slots_are_reused_without_growing_storage() {
let mut store = store_with(4);
let removed = store.remove_items(1..3);
assert_eq!(ids(removed), [1, 2]);
assert_eq!(ids(store.iter()), [0, 3]);
assert_eq!(store.validate_integrity(), Ok(()));
let slot_len = store.slots.len();
store.insert_item(1, payload(5));
store.insert_item(1, payload(6));
assert_eq!(store.slots.len(), slot_len);
assert!(store.free.is_empty());
assert_eq!(ids(store.iter()), [0, 6, 5, 3]);
assert_eq!(store.validate_integrity(), Ok(()));
}
#[test]
fn insert_items_restores_records_in_order() {
let mut store = store_with(3);
let removed = store.remove_items(0..2);
store.insert_items(1, removed);
assert_eq!(ids(store.iter()), [2, 0, 1]);
assert_eq!(store.validate_integrity(), Ok(()));
}
#[test]
fn rotate_items_right_reorders_only_the_range() {
let mut store = store_with(4);
store.rotate_items_right(1..4, 1);
assert_eq!(ids(store.iter()), [0, 3, 1, 2]);
store.rotate_items_right(2..9, 1);
assert_eq!(ids(store.iter()), [0, 3, 1, 2]);
assert_eq!(store.validate_integrity(), Ok(()));
}
#[test]
fn range_reads_table_order_and_ignores_out_of_bounds_ranges() {
let store = store_with(5);
assert_eq!(ids(store.range(1..4)), [1, 2, 3]);
assert_eq!(store.range(4..9).count(), 0);
}
#[test]
fn for_each_mut_visits_in_table_order() {
let mut store = store_with(3);
store.rotate_items_right(0..3, 1);
let mut visited = Vec::new();
store.for_each_mut(|payload| {
visited.push(payload.anchor.id());
payload.kind = PayloadKind::Internal;
});
assert_eq!(visited, [2, 0, 1]);
assert!(
store
.iter()
.all(|payload| payload.kind == PayloadKind::Internal)
);
}
#[test]
fn drain_rev_yields_reverse_table_order_and_empties_the_store() {
let mut store = store_with(4);
drop(store.remove_items(1..2));
let mut drained = Vec::new();
store.drain_rev(|payload| drained.push(payload.anchor.id()));
assert_eq!(drained, [3, 2, 0]);
assert_eq!(store.len(), 0);
assert!(store.slots.is_empty());
assert!(store.free.is_empty());
assert_eq!(store.validate_integrity(), Ok(()));
}
#[test]
fn compact_renumbers_slots_densely_in_table_order() {
let mut store = store_with(6);
drop(store.remove_items(0..3));
store.rotate_items_right(0..3, 1);
store.compact();
assert_eq!(ids(store.iter()), [5, 3, 4]);
assert_eq!(store.order, [0, 1, 2]);
assert_eq!(store.slots.len(), 3);
assert_eq!(store.capacity(), 3);
assert!(store.free.is_empty());
assert_eq!(store.validate_integrity(), Ok(()));
}
#[test]
fn heap_bytes_counts_order_slots_and_free_list() {
let mut store = store_with(8);
drop(store.remove_items(0..2));
assert_eq!(
store.heap_bytes(),
(store.order.capacity() + store.free.capacity()) * mem::size_of::<u32>()
+ store.slots.capacity() * mem::size_of::<Option<PayloadRecord>>()
);
}
#[test]
fn validate_integrity_reports_vacant_order_entry() {
let mut store = store_with(2);
store.slots[1] = None;
assert_eq!(
store.validate_integrity(),
Err(SlotInvariantError::PayloadStoreMismatch {
detail: "payload order entry must name an occupied slot",
expected: 1,
actual: 1,
})
);
}
#[test]
fn validate_integrity_reports_duplicate_order_entry() {
let mut store = store_with(2);
store.order[1] = 0;
assert_eq!(
store.validate_integrity(),
Err(SlotInvariantError::PayloadStoreMismatch {
detail: "payload slot must appear once in the order",
expected: 1,
actual: 0,
})
);
}
#[test]
fn validate_integrity_reports_occupied_free_slot() {
let mut store = store_with(2);
store.free.push(0);
assert_eq!(
store.validate_integrity(),
Err(SlotInvariantError::PayloadStoreMismatch {
detail: "free payload slot must be vacant",
expected: 0,
actual: 0,
})
);
}
#[test]
fn validate_integrity_reports_duplicate_free_slot() {
let mut store = store_with(2);
drop(store.remove_items(1..2));
store.free.push(1);
assert_eq!(
store.validate_integrity(),
Err(SlotInvariantError::PayloadStoreMismatch {
detail: "free payload slot must appear once",
expected: 1,
actual: 1,
})
);
}
#[test]
fn validate_integrity_reports_unordered_occupied_slot() {
let mut store = store_with(2);
store.order.pop();
assert_eq!(
store.validate_integrity(),
Err(SlotInvariantError::PayloadStoreMismatch {
detail: "every payload slot must be ordered or free",
expected: 2,
actual: 1,
})
);
}
#[test]
fn validate_integrity_reports_leaked_vacant_slot() {
let mut store = store_with(2);
drop(store.remove_items(1..2));
store.free.clear();
assert_eq!(
store.validate_integrity(),
Err(SlotInvariantError::PayloadStoreMismatch {
detail: "every payload slot must be ordered or free",
expected: 2,
actual: 1,
})
);
}
#[test]
fn vec_and_store_segment_items_agree() {
let mut vec = (0..6).map(payload).collect::<Vec<_>>();
let mut store = store_with(6);
for items in [
&mut vec as &mut dyn SegmentItems<Item = PayloadRecord>,
&mut store,
] {
let removed = items.remove_items(1..3);
items.insert_item(0, payload(7));
items.rotate_items_right(2..5, 2);
items.insert_items(items.item_count(), removed);
}
let vec_ids = ids(&vec);
assert_eq!(vec_ids, ids(store.iter()));
assert_eq!(vec.item_count(), store.item_count());
assert_eq!(
vec.item(2).map(|payload| payload.anchor.id()),
store.item(2).map(|payload| payload.anchor.id())
);
}