#pragma once #ifndef MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_TESTUTILS_BLOCKSTORETEST_H_ #define MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_TESTUTILS_BLOCKSTORETEST_H_ #include #include #include #include "blockstore/interface/BlockStore.h" class MockForEachBlockCallback final { public: std::function callback() { return [this] (const blockstore::Key &key) { called_with.push_back(key); }; } std::vector called_with; }; class BlockStoreTestFixture { public: virtual ~BlockStoreTestFixture() {} virtual cpputils::unique_ref createBlockStore() = 0; }; template class BlockStoreTest: public ::testing::Test { public: BlockStoreTest() :fixture() {} BOOST_STATIC_ASSERT_MSG( (std::is_base_of::value), "Given test fixture for instantiating the (type parameterized) BlockStoreTest must inherit from BlockStoreTestFixture" ); ConcreteBlockStoreTestFixture fixture; void TestBlockIsUsable(cpputils::unique_ref block, blockstore::BlockStore *blockStore) { // Write full block space and check it was correctly written cpputils::Data fixture = cpputils::DataFixture::generate(block->size()); block->write(fixture.data(), 0, fixture.size()); EXPECT_EQ(0, std::memcmp(fixture.data(), block->data(), fixture.size())); // Store and reload block and check data is still correct auto key = block->key(); cpputils::destruct(std::move(block)); block = blockStore->load(key).value(); EXPECT_EQ(0, std::memcmp(fixture.data(), block->data(), fixture.size())); } template void EXPECT_UNORDERED_EQ(const std::vector &expected, std::vector actual) { EXPECT_EQ(expected.size(), actual.size()); for (const Entry &expectedEntry : expected) { removeOne(&actual, expectedEntry); } } template void removeOne(std::vector *entries, const Entry &toRemove) { auto found = std::find(entries->begin(), entries->end(), toRemove); if (found != entries->end()) { entries->erase(found); return; } EXPECT_TRUE(false); } }; TYPED_TEST_CASE_P(BlockStoreTest); TYPED_TEST_P(BlockStoreTest, TwoCreatedBlocksHaveDifferentKeys) { auto blockStore = this->fixture.createBlockStore(); auto block1 = blockStore->create(cpputils::Data(1024)); auto block2 = blockStore->create(cpputils::Data(1024)); EXPECT_NE(block1->key(), block2->key()); } TYPED_TEST_P(BlockStoreTest, BlockIsNotLoadableAfterDeleting_DeleteByBlock) { auto blockStore = this->fixture.createBlockStore(); auto blockkey = blockStore->create(cpputils::Data(1024))->key(); auto block = blockStore->load(blockkey); EXPECT_NE(boost::none, block); blockStore->remove(std::move(*block)); EXPECT_EQ(boost::none, blockStore->load(blockkey)); } TYPED_TEST_P(BlockStoreTest, BlockIsNotLoadableAfterDeleting_DeleteByKey) { auto blockStore = this->fixture.createBlockStore(); auto blockkey = blockStore->create(cpputils::Data(1024))->key(); blockStore->remove(blockkey); EXPECT_EQ(boost::none, blockStore->load(blockkey)); } TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectOnEmptyBlockstore) { auto blockStore = this->fixture.createBlockStore(); EXPECT_EQ(0u, blockStore->numBlocks()); } TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingOneBlock) { auto blockStore = this->fixture.createBlockStore(); auto block = blockStore->create(cpputils::Data(1)); EXPECT_EQ(1u, blockStore->numBlocks()); } TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingOneBlock_AfterClosingBlock) { auto blockStore = this->fixture.createBlockStore(); blockStore->create(cpputils::Data(1)); EXPECT_EQ(1u, blockStore->numBlocks()); } TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterRemovingTheLastBlock_DeleteByBlock) { auto blockStore = this->fixture.createBlockStore(); auto block = blockStore->create(cpputils::Data(1)); blockStore->remove(std::move(block)); EXPECT_EQ(0u, blockStore->numBlocks()); } TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterRemovingTheLastBlock_DeleteByKey) { auto blockStore = this->fixture.createBlockStore(); auto key = blockStore->create(cpputils::Data(1))->key(); blockStore->remove(key); EXPECT_EQ(0u, blockStore->numBlocks()); } TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks) { auto blockStore = this->fixture.createBlockStore(); auto block1 = blockStore->create(cpputils::Data(1)); auto block2 = blockStore->create(cpputils::Data(0)); EXPECT_EQ(2u, blockStore->numBlocks()); } TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingFirstBlock) { auto blockStore = this->fixture.createBlockStore(); blockStore->create(cpputils::Data(1)); auto block2 = blockStore->create(cpputils::Data(0)); EXPECT_EQ(2u, blockStore->numBlocks()); } TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingSecondBlock) { auto blockStore = this->fixture.createBlockStore(); auto block1 = blockStore->create(cpputils::Data(1)); blockStore->create(cpputils::Data(0)); EXPECT_EQ(2u, blockStore->numBlocks()); } TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingBothBlocks) { auto blockStore = this->fixture.createBlockStore(); blockStore->create(cpputils::Data(1)); blockStore->create(cpputils::Data(0)); EXPECT_EQ(2u, blockStore->numBlocks()); } TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterRemovingABlock_DeleteByBlock) { auto blockStore = this->fixture.createBlockStore(); auto block = blockStore->create(cpputils::Data(1)); blockStore->create(cpputils::Data(1)); blockStore->remove(std::move(block)); EXPECT_EQ(1u, blockStore->numBlocks()); } TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterRemovingABlock_DeleteByKey) { auto blockStore = this->fixture.createBlockStore(); auto key = blockStore->create(cpputils::Data(1))->key(); blockStore->create(cpputils::Data(1)); blockStore->remove(key); EXPECT_EQ(1u, blockStore->numBlocks()); } TYPED_TEST_P(BlockStoreTest, CanRemoveModifiedBlock) { auto blockStore = this->fixture.createBlockStore(); auto block = blockStore->create(cpputils::Data(5)); block->write("data", 0, 4); blockStore->remove(std::move(block)); EXPECT_EQ(0u, blockStore->numBlocks()); } TYPED_TEST_P(BlockStoreTest, ForEachBlock_zeroblocks) { auto blockStore = this->fixture.createBlockStore(); MockForEachBlockCallback mockForEachBlockCallback; blockStore->forEachBlock(mockForEachBlockCallback.callback()); this->EXPECT_UNORDERED_EQ({}, mockForEachBlockCallback.called_with); } TYPED_TEST_P(BlockStoreTest, ForEachBlock_oneblock) { auto blockStore = this->fixture.createBlockStore(); auto block = blockStore->create(cpputils::Data(1)); MockForEachBlockCallback mockForEachBlockCallback; blockStore->forEachBlock(mockForEachBlockCallback.callback()); this->EXPECT_UNORDERED_EQ({block->key()}, mockForEachBlockCallback.called_with); } TYPED_TEST_P(BlockStoreTest, ForEachBlock_twoblocks) { auto blockStore = this->fixture.createBlockStore(); auto block1 = blockStore->create(cpputils::Data(1)); auto block2 = blockStore->create(cpputils::Data(1)); MockForEachBlockCallback mockForEachBlockCallback; blockStore->forEachBlock(mockForEachBlockCallback.callback()); this->EXPECT_UNORDERED_EQ({block1->key(), block2->key()}, mockForEachBlockCallback.called_with); } TYPED_TEST_P(BlockStoreTest, ForEachBlock_threeblocks) { auto blockStore = this->fixture.createBlockStore(); auto block1 = blockStore->create(cpputils::Data(1)); auto block2 = blockStore->create(cpputils::Data(1)); auto block3 = blockStore->create(cpputils::Data(1)); MockForEachBlockCallback mockForEachBlockCallback; blockStore->forEachBlock(mockForEachBlockCallback.callback()); this->EXPECT_UNORDERED_EQ({block1->key(), block2->key(), block3->key()}, mockForEachBlockCallback.called_with); } TYPED_TEST_P(BlockStoreTest, ForEachBlock_doesntListRemovedBlocks_oneblock) { auto blockStore = this->fixture.createBlockStore(); auto block1 = blockStore->create(cpputils::Data(1)); blockStore->remove(std::move(block1)); MockForEachBlockCallback mockForEachBlockCallback; blockStore->forEachBlock(mockForEachBlockCallback.callback()); this->EXPECT_UNORDERED_EQ({}, mockForEachBlockCallback.called_with); } TYPED_TEST_P(BlockStoreTest, ForEachBlock_doesntListRemovedBlocks_twoblocks) { auto blockStore = this->fixture.createBlockStore(); auto block1 = blockStore->create(cpputils::Data(1)); auto block2 = blockStore->create(cpputils::Data(1)); blockStore->remove(std::move(block1)); MockForEachBlockCallback mockForEachBlockCallback; blockStore->forEachBlock(mockForEachBlockCallback.callback()); this->EXPECT_UNORDERED_EQ({block2->key()}, mockForEachBlockCallback.called_with); } TYPED_TEST_P(BlockStoreTest, Resize_Larger_FromZero) { auto blockStore = this->fixture.createBlockStore(); auto block = blockStore->create(cpputils::Data(0)); block->resize(10); EXPECT_EQ(10u, block->size()); } TYPED_TEST_P(BlockStoreTest, Resize_Larger_FromZero_BlockIsStillUsable) { auto blockStore = this->fixture.createBlockStore(); auto block = blockStore->create(cpputils::Data(0)); block->resize(10); this->TestBlockIsUsable(std::move(block), blockStore.get()); } TYPED_TEST_P(BlockStoreTest, Resize_Larger) { auto blockStore = this->fixture.createBlockStore(); auto block = blockStore->create(cpputils::Data(10)); block->resize(20); EXPECT_EQ(20u, block->size()); } TYPED_TEST_P(BlockStoreTest, Resize_Larger_BlockIsStillUsable) { auto blockStore = this->fixture.createBlockStore(); auto block = blockStore->create(cpputils::Data(10)); block->resize(20); this->TestBlockIsUsable(std::move(block), blockStore.get()); } TYPED_TEST_P(BlockStoreTest, Resize_Smaller) { auto blockStore = this->fixture.createBlockStore(); auto block = blockStore->create(cpputils::Data(10)); block->resize(5); EXPECT_EQ(5u, block->size()); } TYPED_TEST_P(BlockStoreTest, Resize_Smaller_BlockIsStillUsable) { auto blockStore = this->fixture.createBlockStore(); auto block = blockStore->create(cpputils::Data(10)); block->resize(5); this->TestBlockIsUsable(std::move(block), blockStore.get()); } TYPED_TEST_P(BlockStoreTest, Resize_Smaller_ToZero) { auto blockStore = this->fixture.createBlockStore(); auto block = blockStore->create(cpputils::Data(10)); block->resize(0); EXPECT_EQ(0u, block->size()); } TYPED_TEST_P(BlockStoreTest, Resize_Smaller_ToZero_BlockIsStillUsable) { auto blockStore = this->fixture.createBlockStore(); auto block = blockStore->create(cpputils::Data(10)); block->resize(0); this->TestBlockIsUsable(std::move(block), blockStore.get()); } #include "BlockStoreTest_Size.h" #include "BlockStoreTest_Data.h" REGISTER_TYPED_TEST_CASE_P(BlockStoreTest, CreatedBlockHasCorrectSize, LoadingUnchangedBlockHasCorrectSize, CreatedBlockData, LoadingUnchangedBlockData, LoadedBlockIsCorrect, // LoadedBlockIsCorrectWhenLoadedDirectlyAfterFlushing, AfterCreate_FlushingDoesntChangeBlock, AfterLoad_FlushingDoesntChangeBlock, AfterCreate_FlushesWhenDestructed, AfterLoad_FlushesWhenDestructed, LoadNonExistingBlock, TwoCreatedBlocksHaveDifferentKeys, BlockIsNotLoadableAfterDeleting_DeleteByBlock, BlockIsNotLoadableAfterDeleting_DeleteByKey, NumBlocksIsCorrectOnEmptyBlockstore, NumBlocksIsCorrectAfterAddingOneBlock, NumBlocksIsCorrectAfterAddingOneBlock_AfterClosingBlock, NumBlocksIsCorrectAfterRemovingTheLastBlock_DeleteByBlock, NumBlocksIsCorrectAfterRemovingTheLastBlock_DeleteByKey, NumBlocksIsCorrectAfterAddingTwoBlocks, NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingFirstBlock, NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingSecondBlock, NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingBothBlocks, NumBlocksIsCorrectAfterRemovingABlock_DeleteByBlock, NumBlocksIsCorrectAfterRemovingABlock_DeleteByKey, WriteAndReadImmediately, WriteAndReadAfterLoading, OverwriteAndRead, CanRemoveModifiedBlock, ForEachBlock_zeroblocks, ForEachBlock_oneblock, ForEachBlock_twoblocks, ForEachBlock_threeblocks, ForEachBlock_doesntListRemovedBlocks_oneblock, ForEachBlock_doesntListRemovedBlocks_twoblocks, Resize_Larger_FromZero, Resize_Larger_FromZero_BlockIsStillUsable, Resize_Larger, Resize_Larger_BlockIsStillUsable, Resize_Smaller, Resize_Smaller_BlockIsStillUsable, Resize_Smaller_ToZero, Resize_Smaller_ToZero_BlockIsStillUsable ); #endif