use crate::blocks::{BlockError, BlockMetadata, CompleteBlock, ImmutableBlock, MutableBlock};
use crate::manager::BlockManager;
use crate::{BlockId, SequenceHash};
use dynamo_tokens::TokenBlock;
use super::super::store::BlockStore;
#[derive(Debug, thiserror::Error)]
pub enum LogicalBlockAssignmentError<T: BlockMetadata> {
#[error("duplicate block_id {block_id} already present")]
DuplicateBlockId {
block_id: BlockId,
blocks: Vec<MutableBlock<T>>,
},
#[error("duplicate block_id {block_id} already present")]
DuplicateAssignedBlockId {
block_id: BlockId,
blocks: Vec<ImmutableBlock<T>>,
},
#[error("sequence hash mismatch at position {position}: expected {expected}, got {actual}")]
SequenceHashMismatch {
position: usize,
expected: SequenceHash,
actual: SequenceHash,
blocks: Vec<ImmutableBlock<T>>,
},
}
pub struct LogicalBlockAssignments<T: BlockMetadata> {
store: BlockStore<MutableBlock<T>, CompleteBlock<T>, ImmutableBlock<T>>,
}
impl<T: BlockMetadata> LogicalBlockAssignments<T> {
pub fn new() -> Self {
Self {
store: BlockStore::new(),
}
}
pub fn assigned_count(&self) -> usize {
self.store.assigned_count()
}
pub fn staged_count(&self) -> usize {
self.store.staged_count()
}
pub fn unassigned_count(&self) -> usize {
self.store.unassigned_count()
}
pub fn is_empty(&self) -> bool {
self.store.is_empty()
}
pub fn contains(&self, block_id: &BlockId) -> bool {
self.store.contains(block_id)
}
pub fn get_assigned(&self, index: usize) -> Option<(&BlockId, &ImmutableBlock<T>)> {
self.store.get_assigned(index)
}
pub fn get_staged(&self, index: usize) -> Option<(&BlockId, &CompleteBlock<T>)> {
self.store.get_staged(index)
}
pub fn get_unassigned(&self, index: usize) -> Option<(&BlockId, &MutableBlock<T>)> {
self.store.get_unassigned(index)
}
pub fn assigned_iter(&self) -> impl Iterator<Item = (&BlockId, &ImmutableBlock<T>)> {
self.store.assigned_iter()
}
pub fn staged_iter(&self) -> impl Iterator<Item = (&BlockId, &CompleteBlock<T>)> {
self.store.staged_iter()
}
pub fn unassigned_iter(&self) -> impl Iterator<Item = (&BlockId, &MutableBlock<T>)> {
self.store.unassigned_iter()
}
pub fn all_block_ids(&self) -> impl Iterator<Item = &BlockId> {
self.store.all_block_ids()
}
pub fn extend_blocks(
&mut self,
blocks: impl IntoIterator<Item = MutableBlock<T>>,
) -> Result<usize, LogicalBlockAssignmentError<T>> {
let blocks: Vec<MutableBlock<T>> = blocks.into_iter().collect();
if let Err(block_id) = self
.store
.validate_no_duplicates(blocks.iter().map(|b| b.block_id()), blocks.len())
{
return Err(LogicalBlockAssignmentError::DuplicateBlockId { block_id, blocks });
}
let count = blocks.len();
for block in blocks {
let id = block.block_id();
self.store.insert_unassigned(id, block);
}
Ok(count)
}
pub fn extend_assigned(
&mut self,
blocks: impl IntoIterator<Item = ImmutableBlock<T>>,
) -> Result<usize, LogicalBlockAssignmentError<T>> {
let blocks: Vec<ImmutableBlock<T>> = blocks.into_iter().collect();
if let Err(block_id) = self
.store
.validate_no_duplicates(blocks.iter().map(|b| b.block_id()), blocks.len())
{
return Err(LogicalBlockAssignmentError::DuplicateAssignedBlockId { block_id, blocks });
}
let count = blocks.len();
for block in blocks {
let id = block.block_id();
self.store.insert_assigned(id, block);
}
Ok(count)
}
pub fn stage(
&mut self,
sequence_blocks: &[TokenBlock],
) -> Result<usize, BlockError<MutableBlock<T>>> {
let to_stage = sequence_blocks.len().min(self.store.unassigned_count());
#[allow(clippy::needless_range_loop)]
for i in 0..to_stage {
let (block_id, mutable) = self.store.shift_unassigned().unwrap();
match mutable.complete(&sequence_blocks[i]) {
Ok(complete) => {
self.store.insert_staged(block_id, complete);
}
Err(err) => {
return Err(err);
}
}
}
Ok(to_stage)
}
pub fn register(&mut self, manager: &BlockManager<T>) -> usize {
let count = self.store.staged_count();
while let Some((block_id, complete)) = self.store.shift_staged() {
let immutable = manager.register_block(complete);
self.store.insert_assigned(block_id, immutable);
}
count
}
pub fn pop_last_unassigned(&mut self) -> Option<(BlockId, MutableBlock<T>)> {
self.store.pop_unassigned()
}
pub fn clear(&mut self) {
self.store.clear();
}
pub fn take_assigned(&mut self) -> Vec<(BlockId, ImmutableBlock<T>)> {
self.store.take_assigned()
}
pub fn take_staged(&mut self) -> Vec<(BlockId, CompleteBlock<T>)> {
self.store.take_staged()
}
pub fn take_unassigned(&mut self) -> Vec<(BlockId, MutableBlock<T>)> {
self.store.take_unassigned()
}
}
impl<T: BlockMetadata> Default for LogicalBlockAssignments<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: BlockMetadata> std::fmt::Debug for LogicalBlockAssignments<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LogicalBlockAssignments")
.field("assigned_count", &self.store.assigned_count())
.field("staged_count", &self.store.staged_count())
.field("unassigned_count", &self.store.unassigned_count())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sequence::BlockSequence;
use crate::testing::{TestMeta, create_test_manager};
const BLOCK_SIZE: u32 = 4;
fn create_sequence(num_blocks: usize) -> BlockSequence {
let total_tokens = num_blocks * BLOCK_SIZE as usize;
let tokens: Vec<u32> = (0..total_tokens as u32).collect();
BlockSequence::new(tokens, BLOCK_SIZE, None)
}
#[test]
fn test_empty_construction() {
let la = LogicalBlockAssignments::<TestMeta>::new();
assert!(la.is_empty());
assert_eq!(la.assigned_count(), 0);
assert_eq!(la.staged_count(), 0);
assert_eq!(la.unassigned_count(), 0);
assert!(!la.contains(&0));
}
#[test]
fn test_default_is_empty() {
let la = LogicalBlockAssignments::<TestMeta>::default();
assert!(la.is_empty());
}
#[test]
fn test_extend_blocks_basic() {
let manager = create_test_manager::<TestMeta>(10);
let blocks = manager.allocate_blocks(5).unwrap();
let ids: Vec<BlockId> = blocks.iter().map(|b| b.block_id()).collect();
let mut la = LogicalBlockAssignments::new();
let count = la.extend_blocks(blocks).unwrap();
assert_eq!(count, 5);
assert_eq!(la.unassigned_count(), 5);
assert!(!la.is_empty());
for (i, expected_id) in ids.iter().enumerate() {
let (id, _) = la.get_unassigned(i).unwrap();
assert_eq!(id, expected_id);
}
}
fn register_blocks_directly(
manager: &BlockManager<TestMeta>,
seq: &BlockSequence,
count: usize,
) -> Vec<ImmutableBlock<TestMeta>> {
let mutables = manager.allocate_blocks(count).unwrap();
mutables
.into_iter()
.zip(seq.blocks().iter())
.map(|(m, tb)| manager.register_block(m.complete(tb).unwrap()))
.collect()
}
#[test]
fn test_extend_assigned_basic() {
let manager = create_test_manager::<TestMeta>(10);
let seq = create_sequence(3);
let immutables = register_blocks_directly(&manager, &seq, 3);
let ids: Vec<BlockId> = immutables.iter().map(|b| b.block_id()).collect();
let hashes = seq.all_sequence_hashes();
let mut la = LogicalBlockAssignments::new();
let count = la.extend_assigned(immutables).unwrap();
assert_eq!(count, 3);
assert_eq!(la.assigned_count(), 3);
assert_eq!(la.staged_count(), 0);
assert_eq!(la.unassigned_count(), 0);
for (i, expected_id) in ids.iter().enumerate() {
let (id, imm) = la.get_assigned(i).unwrap();
assert_eq!(id, expected_id);
assert_eq!(imm.block_id(), *expected_id);
assert_eq!(imm.sequence_hash(), hashes[i]);
}
}
#[test]
fn test_extend_assigned_ordering_preserved() {
let manager = create_test_manager::<TestMeta>(10);
let seq = create_sequence(5);
let immutables = register_blocks_directly(&manager, &seq, 5);
let ids: Vec<BlockId> = immutables.iter().map(|b| b.block_id()).collect();
let mut la = LogicalBlockAssignments::new();
la.extend_assigned(immutables).unwrap();
for (i, expected_id) in ids.iter().enumerate() {
let (id, _) = la.get_assigned(i).unwrap();
assert_eq!(id, expected_id);
}
}
#[test]
fn test_extend_assigned_contains() {
let manager = create_test_manager::<TestMeta>(10);
let seq = create_sequence(3);
let immutables = register_blocks_directly(&manager, &seq, 3);
let ids: Vec<BlockId> = immutables.iter().map(|b| b.block_id()).collect();
let mut la = LogicalBlockAssignments::new();
la.extend_assigned(immutables).unwrap();
for id in &ids {
assert!(la.contains(id));
}
assert!(!la.contains(&999));
}
#[test]
fn test_extend_assigned_then_extend_stage_register() {
let manager = create_test_manager::<TestMeta>(10);
let seq = create_sequence(5);
let matched = register_blocks_directly(&manager, &seq, 3);
let matched_ids: Vec<BlockId> = matched.iter().map(|b| b.block_id()).collect();
let mut la = LogicalBlockAssignments::new();
la.extend_assigned(matched).unwrap();
assert_eq!(la.assigned_count(), 3);
let new_blocks = manager.allocate_blocks(2).unwrap();
let new_ids: Vec<BlockId> = new_blocks.iter().map(|b| b.block_id()).collect();
la.extend_blocks(new_blocks).unwrap();
la.stage(&seq.blocks()[3..5]).unwrap();
la.register(&manager);
assert_eq!(la.assigned_count(), 5);
assert_eq!(la.staged_count(), 0);
assert_eq!(la.unassigned_count(), 0);
for (i, expected_id) in matched_ids.iter().enumerate() {
let (id, _) = la.get_assigned(i).unwrap();
assert_eq!(id, expected_id);
}
for (i, expected_id) in new_ids.iter().enumerate() {
let (id, _) = la.get_assigned(3 + i).unwrap();
assert_eq!(id, expected_id);
}
}
#[test]
fn test_extend_assigned_with_match_blocks() {
let manager = create_test_manager::<TestMeta>(10);
let seq = create_sequence(3);
let hashes = seq.all_sequence_hashes();
let mutables = manager.allocate_blocks(3).unwrap();
let registered: Vec<ImmutableBlock<TestMeta>> = mutables
.into_iter()
.zip(seq.blocks().iter())
.map(|(m, tb)| manager.register_block(m.complete(tb).unwrap()))
.collect();
drop(registered);
let matched = manager.match_blocks(&hashes);
assert_eq!(matched.len(), 3);
let mut la = LogicalBlockAssignments::new();
la.extend_assigned(matched).unwrap();
assert_eq!(la.assigned_count(), 3);
for (i, expected_hash) in hashes.iter().enumerate() {
let (_, imm) = la.get_assigned(i).unwrap();
assert_eq!(imm.sequence_hash(), *expected_hash);
}
}
#[test]
fn test_extend_assigned_empty() {
let mut la = LogicalBlockAssignments::<TestMeta>::new();
let count = la.extend_assigned(Vec::new()).unwrap();
assert_eq!(count, 0);
assert!(la.is_empty());
}
#[test]
fn test_stage_basic() {
let manager = create_test_manager::<TestMeta>(10);
let blocks = manager.allocate_blocks(3).unwrap();
let ids: Vec<BlockId> = blocks.iter().map(|b| b.block_id()).collect();
let seq = create_sequence(3);
let mut la = LogicalBlockAssignments::new();
la.extend_blocks(blocks).unwrap();
let staged = la.stage(seq.blocks()).unwrap();
assert_eq!(staged, 3);
assert_eq!(la.staged_count(), 3);
assert_eq!(la.unassigned_count(), 0);
for (i, expected_id) in ids.iter().enumerate() {
let (id, _) = la.get_staged(i).unwrap();
assert_eq!(id, expected_id);
}
}
#[test]
fn test_stage_fifo_drain() {
let manager = create_test_manager::<TestMeta>(10);
let blocks = manager.allocate_blocks(5).unwrap();
let ids: Vec<BlockId> = blocks.iter().map(|b| b.block_id()).collect();
let seq = create_sequence(3);
let mut la = LogicalBlockAssignments::new();
la.extend_blocks(blocks).unwrap();
la.stage(seq.blocks()).unwrap();
assert_eq!(la.unassigned_count(), 2);
let (remaining_0, _) = la.get_unassigned(0).unwrap();
let (remaining_1, _) = la.get_unassigned(1).unwrap();
assert_eq!(*remaining_0, ids[3]);
assert_eq!(*remaining_1, ids[4]);
}
#[test]
fn test_stage_partial_fewer_blocks_than_unassigned() {
let manager = create_test_manager::<TestMeta>(10);
let blocks = manager.allocate_blocks(5).unwrap();
let seq = create_sequence(3);
let mut la = LogicalBlockAssignments::new();
la.extend_blocks(blocks).unwrap();
let staged = la.stage(seq.blocks()).unwrap();
assert_eq!(staged, 3);
assert_eq!(la.staged_count(), 3);
assert_eq!(la.unassigned_count(), 2);
}
#[test]
fn test_stage_partial_fewer_unassigned_than_blocks() {
let manager = create_test_manager::<TestMeta>(10);
let blocks = manager.allocate_blocks(2).unwrap();
let seq = create_sequence(5);
let mut la = LogicalBlockAssignments::new();
la.extend_blocks(blocks).unwrap();
let staged = la.stage(seq.blocks()).unwrap();
assert_eq!(staged, 2);
assert_eq!(la.staged_count(), 2);
assert_eq!(la.unassigned_count(), 0);
}
#[test]
fn test_stage_block_size_mismatch_recovery() {
let manager = create_test_manager::<TestMeta>(10);
let blocks = manager.allocate_blocks(3).unwrap();
let tokens: Vec<u32> = (0..24).collect();
let bad_seq = BlockSequence::new(tokens, 8, None);
let mut la = LogicalBlockAssignments::new();
la.extend_blocks(blocks).unwrap();
let result = la.stage(bad_seq.blocks());
assert!(result.is_err());
match result.unwrap_err() {
BlockError::BlockSizeMismatch {
expected,
actual,
block,
} => {
assert_eq!(expected, 4);
assert_eq!(actual, 8);
drop(block);
}
}
assert_eq!(la.staged_count(), 0);
assert_eq!(la.unassigned_count(), 2);
}
#[test]
fn test_stage_partial_then_mismatch() {
let manager = create_test_manager::<TestMeta>(10);
let blocks = manager.allocate_blocks(3).unwrap();
let good_seq = create_sequence(2);
let bad_tokens: Vec<u32> = (0..8).collect();
let bad_seq = BlockSequence::new(bad_tokens, 8, None);
let mut la = LogicalBlockAssignments::new();
la.extend_blocks(blocks).unwrap();
let staged = la.stage(good_seq.blocks()).unwrap();
assert_eq!(staged, 2);
assert_eq!(la.staged_count(), 2);
assert_eq!(la.unassigned_count(), 1);
let result = la.stage(bad_seq.blocks());
assert!(result.is_err());
assert_eq!(la.staged_count(), 2);
assert_eq!(la.unassigned_count(), 0);
}
#[test]
fn test_register_basic() {
let manager = create_test_manager::<TestMeta>(10);
let blocks = manager.allocate_blocks(3).unwrap();
let ids: Vec<BlockId> = blocks.iter().map(|b| b.block_id()).collect();
let seq = create_sequence(3);
let mut la = LogicalBlockAssignments::new();
la.extend_blocks(blocks).unwrap();
la.stage(seq.blocks()).unwrap();
let registered = la.register(&manager);
assert_eq!(registered, 3);
assert_eq!(la.assigned_count(), 3);
assert_eq!(la.staged_count(), 0);
for (i, expected_id) in ids.iter().enumerate() {
let (id, imm) = la.get_assigned(i).unwrap();
assert_eq!(id, expected_id);
assert_eq!(imm.block_id(), *expected_id);
}
}
#[test]
fn test_register_empty_staged() {
let manager = create_test_manager::<TestMeta>(10);
let mut la = LogicalBlockAssignments::<TestMeta>::new();
let registered = la.register(&manager);
assert_eq!(registered, 0);
}
#[test]
fn test_full_pipeline_extend_stage_register() {
let manager = create_test_manager::<TestMeta>(10);
let blocks = manager.allocate_blocks(5).unwrap();
let ids: Vec<BlockId> = blocks.iter().map(|b| b.block_id()).collect();
let seq = create_sequence(5);
let expected_hashes = seq.all_sequence_hashes();
let mut la = LogicalBlockAssignments::new();
la.extend_blocks(blocks).unwrap();
assert_eq!(la.unassigned_count(), 5);
la.stage(seq.blocks()).unwrap();
assert_eq!(la.staged_count(), 5);
assert_eq!(la.unassigned_count(), 0);
la.register(&manager);
assert_eq!(la.assigned_count(), 5);
assert_eq!(la.staged_count(), 0);
for (i, expected_id) in ids.iter().enumerate() {
let (id, immutable) = la.get_assigned(i).unwrap();
assert_eq!(id, expected_id);
assert_eq!(immutable.block_id(), *expected_id);
assert_eq!(immutable.sequence_hash(), expected_hashes[i]);
}
}
#[test]
fn test_full_pipeline_incremental() {
let manager = create_test_manager::<TestMeta>(10);
let blocks = manager.allocate_blocks(6).unwrap();
let ids: Vec<BlockId> = blocks.iter().map(|b| b.block_id()).collect();
let seq = create_sequence(6);
let mut la = LogicalBlockAssignments::new();
la.extend_blocks(blocks).unwrap();
la.stage(&seq.blocks()[..3]).unwrap();
la.register(&manager);
assert_eq!(la.assigned_count(), 3);
assert_eq!(la.unassigned_count(), 3);
la.stage(&seq.blocks()[..3]).unwrap();
la.register(&manager);
assert_eq!(la.assigned_count(), 6);
assert_eq!(la.unassigned_count(), 0);
for (i, expected_id) in ids.iter().enumerate() {
let (id, _) = la.get_assigned(i).unwrap();
assert_eq!(id, expected_id);
}
}
#[test]
fn test_contains_across_collections() {
let manager = create_test_manager::<TestMeta>(10);
let blocks = manager.allocate_blocks(6).unwrap();
let ids: Vec<BlockId> = blocks.iter().map(|b| b.block_id()).collect();
let seq = create_sequence(4);
let mut la = LogicalBlockAssignments::new();
la.extend_blocks(blocks).unwrap();
la.stage(&seq.blocks()[..2]).unwrap();
la.register(&manager);
la.stage(&seq.blocks()[..2]).unwrap();
assert_eq!(la.assigned_count(), 2);
assert_eq!(la.staged_count(), 2);
assert_eq!(la.unassigned_count(), 2);
for id in &ids {
assert!(la.contains(id), "block_id {id} should be contained");
}
assert!(!la.contains(&999));
}
#[test]
fn test_clear() {
let manager = create_test_manager::<TestMeta>(10);
let blocks = manager.allocate_blocks(3).unwrap();
let seq = create_sequence(3);
let mut la = LogicalBlockAssignments::new();
la.extend_blocks(blocks).unwrap();
la.stage(seq.blocks()).unwrap();
la.register(&manager);
assert_eq!(la.assigned_count(), 3);
la.clear();
assert!(la.is_empty());
}
#[test]
fn test_take_assigned() {
let manager = create_test_manager::<TestMeta>(10);
let blocks = manager.allocate_blocks(3).unwrap();
let ids: Vec<BlockId> = blocks.iter().map(|b| b.block_id()).collect();
let seq = create_sequence(3);
let mut la = LogicalBlockAssignments::new();
la.extend_blocks(blocks).unwrap();
la.stage(seq.blocks()).unwrap();
la.register(&manager);
let drained = la.take_assigned();
assert_eq!(drained.len(), 3);
assert_eq!(la.assigned_count(), 0);
for (i, (id, _)) in drained.iter().enumerate() {
assert_eq!(*id, ids[i]);
}
}
#[test]
fn test_take_staged() {
let manager = create_test_manager::<TestMeta>(10);
let blocks = manager.allocate_blocks(3).unwrap();
let ids: Vec<BlockId> = blocks.iter().map(|b| b.block_id()).collect();
let seq = create_sequence(3);
let mut la = LogicalBlockAssignments::new();
la.extend_blocks(blocks).unwrap();
la.stage(seq.blocks()).unwrap();
let drained = la.take_staged();
assert_eq!(drained.len(), 3);
assert_eq!(la.staged_count(), 0);
for (i, (id, _)) in drained.iter().enumerate() {
assert_eq!(*id, ids[i]);
}
}
#[test]
fn test_take_unassigned() {
let manager = create_test_manager::<TestMeta>(10);
let blocks = manager.allocate_blocks(3).unwrap();
let ids: Vec<BlockId> = blocks.iter().map(|b| b.block_id()).collect();
let mut la = LogicalBlockAssignments::new();
la.extend_blocks(blocks).unwrap();
let drained = la.take_unassigned();
assert_eq!(drained.len(), 3);
assert_eq!(la.unassigned_count(), 0);
for (i, (id, _)) in drained.iter().enumerate() {
assert_eq!(*id, ids[i]);
}
}
#[test]
fn test_extend_assigned_duplicate_already_in_assigned() {
let manager = create_test_manager::<TestMeta>(10);
let seq = create_sequence(3);
let immutables = register_blocks_directly(&manager, &seq, 3);
let clones: Vec<ImmutableBlock<TestMeta>> = immutables.to_vec();
let dup_id = clones[0].block_id();
let mut la = LogicalBlockAssignments::new();
la.extend_assigned(immutables).unwrap();
let result = la.extend_assigned(clones);
assert!(result.is_err());
match result.unwrap_err() {
LogicalBlockAssignmentError::DuplicateAssignedBlockId { block_id, blocks } => {
assert_eq!(block_id, dup_id);
assert_eq!(blocks.len(), 3);
}
other => panic!("expected DuplicateAssignedBlockId, got: {other:?}"),
}
assert_eq!(la.assigned_count(), 3);
}
#[test]
fn test_extend_assigned_duplicate_within_input_batch() {
let manager = create_test_manager::<TestMeta>(10);
let seq = create_sequence(1);
let immutables = register_blocks_directly(&manager, &seq, 1);
let dup = immutables[0].clone();
let dup_id = dup.block_id();
let batch = vec![immutables.into_iter().next().unwrap(), dup];
let mut la = LogicalBlockAssignments::new();
let result = la.extend_assigned(batch);
assert!(result.is_err());
match result.unwrap_err() {
LogicalBlockAssignmentError::DuplicateAssignedBlockId { block_id, blocks } => {
assert_eq!(block_id, dup_id);
assert_eq!(blocks.len(), 2);
}
other => panic!("expected DuplicateAssignedBlockId, got: {other:?}"),
}
assert!(la.is_empty());
}
#[test]
fn test_extend_assigned_disjoint_from_staged() {
let manager = create_test_manager::<TestMeta>(10);
let blocks = manager.allocate_blocks(3).unwrap();
let ids: Vec<BlockId> = blocks.iter().map(|b| b.block_id()).collect();
let seq = create_sequence(3);
let mut la = LogicalBlockAssignments::new();
la.extend_blocks(blocks).unwrap();
la.stage(seq.blocks()).unwrap();
let other_blocks = manager.allocate_blocks(3).unwrap();
let immutables: Vec<ImmutableBlock<TestMeta>> = other_blocks
.into_iter()
.zip(seq.blocks().iter())
.map(|(m, tb)| manager.register_block(m.complete(tb).unwrap()))
.collect();
let other_ids: Vec<BlockId> = immutables.iter().map(|b| b.block_id()).collect();
assert!(ids.iter().all(|id| !other_ids.contains(id)));
la.extend_assigned(immutables).unwrap();
assert_eq!(la.assigned_count(), 3);
assert_eq!(la.staged_count(), 3);
}
#[test]
fn test_extend_assigned_disjoint_from_unassigned() {
let manager = create_test_manager::<TestMeta>(10);
let seq = create_sequence(3);
let blocks = manager.allocate_blocks(3).unwrap();
let unassigned_ids: Vec<BlockId> = blocks.iter().map(|b| b.block_id()).collect();
let mut la = LogicalBlockAssignments::new();
la.extend_blocks(blocks).unwrap();
let other_blocks = manager.allocate_blocks(3).unwrap();
let immutables: Vec<ImmutableBlock<TestMeta>> = other_blocks
.into_iter()
.zip(seq.blocks().iter())
.map(|(m, tb)| manager.register_block(m.complete(tb).unwrap()))
.collect();
let imm_ids: Vec<BlockId> = immutables.iter().map(|b| b.block_id()).collect();
assert!(unassigned_ids.iter().all(|id| !imm_ids.contains(id)));
la.extend_assigned(immutables).unwrap();
assert_eq!(la.assigned_count(), 3);
assert_eq!(la.unassigned_count(), 3);
}
#[test]
fn test_extend_blocks_id_already_in_assigned() {
let manager = create_test_manager::<TestMeta>(10);
let seq = create_sequence(3);
let blocks = manager.allocate_blocks(3).unwrap();
let mut la = LogicalBlockAssignments::new();
la.extend_blocks(blocks).unwrap();
la.stage(seq.blocks()).unwrap();
la.register(&manager);
assert_eq!(la.assigned_count(), 3);
assert_eq!(la.assigned_count(), 3);
}
#[test]
fn test_stage_empty_unassigned() {
let mut la = LogicalBlockAssignments::<TestMeta>::new();
let seq = create_sequence(3);
let staged = la.stage(seq.blocks()).unwrap();
assert_eq!(staged, 0);
assert_eq!(la.staged_count(), 0);
}
#[test]
fn test_stage_empty_sequence_blocks() {
let manager = create_test_manager::<TestMeta>(10);
let blocks = manager.allocate_blocks(3).unwrap();
let mut la = LogicalBlockAssignments::new();
la.extend_blocks(blocks).unwrap();
let staged = la.stage(&[]).unwrap();
assert_eq!(staged, 0);
assert_eq!(la.staged_count(), 0);
assert_eq!(la.unassigned_count(), 3);
}
#[test]
fn test_stage_both_empty() {
let mut la = LogicalBlockAssignments::<TestMeta>::new();
let staged = la.stage(&[]).unwrap();
assert_eq!(staged, 0);
assert!(la.is_empty());
}
#[test]
fn test_get_assigned_out_of_bounds() {
let la = LogicalBlockAssignments::<TestMeta>::new();
assert!(la.get_assigned(0).is_none());
assert!(la.get_assigned(100).is_none());
}
#[test]
fn test_get_staged_out_of_bounds() {
let la = LogicalBlockAssignments::<TestMeta>::new();
assert!(la.get_staged(0).is_none());
assert!(la.get_staged(100).is_none());
}
#[test]
fn test_get_unassigned_out_of_bounds() {
let la = LogicalBlockAssignments::<TestMeta>::new();
assert!(la.get_unassigned(0).is_none());
assert!(la.get_unassigned(100).is_none());
}
#[test]
fn test_get_out_of_bounds_with_populated_collections() {
let manager = create_test_manager::<TestMeta>(10);
let blocks = manager.allocate_blocks(2).unwrap();
let seq = create_sequence(2);
let mut la = LogicalBlockAssignments::new();
la.extend_blocks(blocks).unwrap();
la.stage(seq.blocks()).unwrap();
la.register(&manager);
assert!(la.get_assigned(0).is_some());
assert!(la.get_assigned(1).is_some());
assert!(la.get_assigned(2).is_none());
assert!(la.get_staged(0).is_none());
assert!(la.get_unassigned(0).is_none());
}
#[test]
fn test_debug_impl() {
let la = LogicalBlockAssignments::<TestMeta>::new();
let debug_str = format!("{la:?}");
assert!(debug_str.contains("LogicalBlockAssignments"));
}
}