use xpct::{
be_ok, contain_element, equal, expect, fields, have_len, match_elements, match_fields, not,
};
use crate::{
block::{
adapter::ChunkingBlockStoreAdapter,
store::{BlockStore, DataStore},
types::{Block, FileId, FileOffset},
},
hash::BlockHasher,
testing::MemoryBlockStore,
};
use super::{BlockSignature, BlockStoreTestExt, DataBlock, TEST_BLOCK_SIZE};
#[test]
fn truncate_to_len_larger_than_file_is_noop() {
let mut block_store = MemoryBlockStore::new();
let file_id = FileId::from(1);
let DataBlock {
id: block_id,
signature,
..
} = expect!(block_store.insert_data(file_id, 0))
.to(be_ok())
.into_inner();
let mut data_store = ChunkingBlockStoreAdapter::without_chunking(block_store);
let block_list = expect!(data_store.list_blocks(file_id))
.to(be_ok())
.into_inner();
let block_list = expect!(data_store.truncate(file_id, block_list, 16))
.to(be_ok())
.into_inner()
.into_iter()
.collect::<Vec<_>>();
expect!(&block_list).to(have_len(1));
expect!(&block_list).to(contain_element(Block {
id: block_id,
signature: signature.clone(),
}));
let actual_block_list = expect!(data_store.list_blocks(file_id))
.to(be_ok())
.into_inner()
.into_iter()
.collect::<Vec<_>>();
expect!(block_list).to(equal(actual_block_list));
}
#[test]
fn truncate_to_zero_removes_all_blocks() {
let mut block_store = MemoryBlockStore::new();
let file_id = FileId::from(1);
expect!(block_store.insert_data(file_id, 0)).to(be_ok());
let mut data_store = ChunkingBlockStoreAdapter::without_chunking(block_store);
let block_list = expect!(data_store.list_blocks(file_id))
.to(be_ok())
.into_inner();
let block_list = expect!(data_store.truncate(file_id, block_list, 0))
.to(be_ok())
.into_inner()
.into_iter()
.collect::<Vec<_>>();
expect!(&block_list).to(have_len(0));
let actual_block_list = expect!(data_store.list_blocks(file_id))
.to(be_ok())
.into_inner()
.into_iter()
.collect::<Vec<_>>();
expect!(&actual_block_list).to(have_len(0));
}
#[test]
fn truncate_splits_data_blocks() {
let mut block_store = MemoryBlockStore::new();
let file_id = FileId::from(1);
let DataBlock {
id: block_id,
bytes,
..
} = expect!(block_store.insert_data(file_id, 0))
.to(be_ok())
.into_inner();
let mut data_store = ChunkingBlockStoreAdapter::without_chunking(block_store);
let truncate_len = TEST_BLOCK_SIZE / 2;
let block_list = expect!(data_store.list_blocks(file_id))
.to(be_ok())
.into_inner();
let block_list = expect!(data_store.truncate(file_id, block_list, truncate_len as FileOffset))
.to(be_ok())
.into_inner()
.into_iter()
.collect::<Vec<_>>();
expect!(&block_list).to(have_len(1));
let expected_hash = BlockHasher::hash(bytes.get(0..truncate_len).unwrap());
expect!(block_list.clone()).to(match_elements([match_fields(fields!(Block {
id: not(equal(block_id)),
signature: equal(BlockSignature::Data {
hash: expected_hash.clone(),
len: truncate_len,
}),
}))]));
let actual_block_list = expect!(data_store.list_blocks(file_id))
.to(be_ok())
.into_inner()
.into_iter()
.collect::<Vec<_>>();
expect!(block_list).to(equal(actual_block_list));
}
#[test]
fn truncate_at_exact_block_boundary() {
let mut block_store = MemoryBlockStore::new();
let file_id = FileId::from(1);
let DataBlock {
id: first_block_id,
signature: first_signature,
..
} = expect!(block_store.insert_data(file_id, 0))
.to(be_ok())
.into_inner();
expect!(block_store.insert_data(file_id, 1)).to(be_ok());
let mut data_store = ChunkingBlockStoreAdapter::without_chunking(block_store);
let block_list = expect!(data_store.list_blocks(file_id))
.to(be_ok())
.into_inner();
expect!(&block_list).to(have_len(2));
let block_list =
expect!(data_store.truncate(file_id, block_list, TEST_BLOCK_SIZE as FileOffset))
.to(be_ok())
.into_inner()
.into_iter()
.collect::<Vec<_>>();
expect!(&block_list).to(have_len(1));
expect!(&block_list).to(contain_element(Block {
id: first_block_id,
signature: first_signature,
}));
let actual_block_list = expect!(data_store.list_blocks(file_id))
.to(be_ok())
.into_inner()
.into_iter()
.collect::<Vec<_>>();
expect!(block_list).to(equal(actual_block_list));
}
#[test]
fn truncate_empty_file_to_zero_is_noop() {
let mut block_store = MemoryBlockStore::new();
let file_id = FileId::from(1);
expect!(block_store.insert_data(file_id, 0)).to(be_ok());
expect!(block_store.remove_blocks(file_id, 0..1)).to(be_ok());
let mut data_store = ChunkingBlockStoreAdapter::without_chunking(block_store);
let block_list = expect!(data_store.list_blocks(file_id))
.to(be_ok())
.into_inner();
expect!(&block_list).to(have_len(0));
let block_list = expect!(data_store.truncate(file_id, block_list, 0))
.to(be_ok())
.into_inner()
.into_iter()
.collect::<Vec<_>>();
expect!(&block_list).to(have_len(0));
let actual_block_list = expect!(data_store.list_blocks(file_id))
.to(be_ok())
.into_inner()
.into_iter()
.collect::<Vec<_>>();
expect!(&actual_block_list).to(have_len(0));
}
#[test]
fn truncate_to_exact_current_length_preserves_blocks() {
let mut block_store = MemoryBlockStore::new();
let file_id = FileId::from(1);
let DataBlock {
id: block_id,
signature,
..
} = expect!(block_store.insert_data(file_id, 0))
.to(be_ok())
.into_inner();
let mut data_store = ChunkingBlockStoreAdapter::without_chunking(block_store);
let block_list = expect!(data_store.list_blocks(file_id))
.to(be_ok())
.into_inner();
let block_list =
expect!(data_store.truncate(file_id, block_list, TEST_BLOCK_SIZE as FileOffset))
.to(be_ok())
.into_inner()
.into_iter()
.collect::<Vec<_>>();
expect!(&block_list).to(have_len(1));
expect!(&block_list).to(contain_element(Block {
id: block_id,
signature: signature.clone(),
}));
let actual_block_list = expect!(data_store.list_blocks(file_id))
.to(be_ok())
.into_inner()
.into_iter()
.collect::<Vec<_>>();
expect!(block_list).to(equal(actual_block_list));
}