use super::{
StableSlot, StableSlotInsertError, StableSlotRemoveOutcome, StableSlotResolveOutcome,
StableSlotRowRelocation, StableSlots, StableSlotsInconsistency,
};
use crate::storage::warp_table::secondary_index::SlotSet;
#[test]
fn stable_slots_follow_dense_relocation_and_reuse() -> Result<(), &'static str> {
let mut slots = StableSlots::try_new(3).map_err(|_| "slot storage unavailable")?;
let first = slots
.try_prepare_insert()
.map_err(|_| "first slot unavailable")?
.commit();
let second = slots
.try_prepare_insert()
.map_err(|_| "second slot unavailable")?
.commit();
let third = slots
.try_prepare_insert()
.map_err(|_| "third slot unavailable")?
.commit();
assert_eq!(
(first, second, third),
(StableSlot::new(0), StableSlot::new(1), StableSlot::new(2))
);
{
let prepared = slots
.try_prepare_remove(1)
.map_err(|_| "second slot removal unavailable")?;
assert_eq!(prepared.slot(), second);
}
assert_eq!(
slots.resolve(second),
Ok(StableSlotResolveOutcome::Resolved {
slot: second,
dense_row: 1,
})
);
assert_eq!(slots.slot(1), Ok(second));
assert_eq!(
slots
.try_prepare_remove(1)
.map_err(|_| "second slot removal unavailable")?
.commit(),
StableSlotRemoveOutcome::Removed {
slot: second,
relocation: StableSlotRowRelocation::Relocated {
slot: third,
from: 2,
to: 1,
},
}
);
assert_eq!(
slots.resolve(third),
Ok(StableSlotResolveOutcome::Resolved {
slot: third,
dense_row: 1,
})
);
assert_eq!(slots.slot(1), Ok(third));
assert_eq!(
slots.resolve_slots(&SlotSet::one(second)).next(),
Some(Err(StableSlotsInconsistency::SlotVacant { slot: second }))
);
let retained = SlotSet::one(first).union(&SlotSet::one(third));
let mut retained = slots.resolve_slots(&retained);
assert_eq!(retained.size_hint(), (2, Some(2)));
#[cfg(target_pointer_width = "64")]
assert_eq!(retained.len(), 2);
assert_eq!(
retained.next_back(),
Some(Ok(StableSlotResolveOutcome::Resolved {
slot: third,
dense_row: 1,
}))
);
assert_eq!(
retained.next(),
Some(Ok(StableSlotResolveOutcome::Resolved {
slot: first,
dense_row: 0,
}))
);
assert_eq!(retained.size_hint(), (0, Some(0)));
#[cfg(target_pointer_width = "64")]
assert_eq!(retained.len(), 0);
assert_eq!(retained.next(), None);
assert_eq!(retained.next_back(), None);
let reused = slots
.try_prepare_insert()
.map_err(|_| "reused slot unavailable")?
.commit();
assert_eq!(reused, second);
assert_eq!(
slots.resolve(reused),
Ok(StableSlotResolveOutcome::Resolved {
slot: reused,
dense_row: 2,
})
);
assert_eq!(
slots.try_prepare_insert().map(|prepared| prepared.slot()),
Err(StableSlotInsertError::Full { maximum_rows: 3 })
);
assert_eq!(
slots.remove(2),
Ok(StableSlotRemoveOutcome::Removed {
slot: reused,
relocation: StableSlotRowRelocation::Stable,
})
);
assert_eq!(
slots.resolve(reused),
Err(StableSlotsInconsistency::SlotVacant { slot: reused })
);
Ok(())
}
#[test]
fn abandoned_preparation_publishes_nothing() -> Result<(), &'static str> {
let mut slots = StableSlots::try_new(1).map_err(|_| "slot storage unavailable")?;
let reserved = {
let prepared = slots
.try_prepare_insert()
.map_err(|_| "prepared slot unavailable")?;
prepared.slot()
};
let committed = slots
.try_prepare_insert()
.map_err(|_| "slot changed after abandonment")?
.commit();
assert_eq!(committed, reserved);
assert_eq!(
slots.resolve(committed),
Ok(StableSlotResolveOutcome::Resolved {
slot: committed,
dense_row: 0,
})
);
Ok(())
}