2014-12-13 17:48:02 +01:00
|
|
|
#include <blobstore/implementations/onblocks/datanodestore/DataLeafNode.h>
|
|
|
|
#include <blobstore/implementations/onblocks/datanodestore/DataNodeStore.h>
|
2014-12-10 17:59:45 +01:00
|
|
|
#include <gtest/gtest.h>
|
|
|
|
|
2014-12-11 01:31:21 +01:00
|
|
|
#include "blockstore/implementations/testfake/FakeBlockStore.h"
|
|
|
|
#include "blockstore/implementations/testfake/FakeBlock.h"
|
2014-12-10 17:59:45 +01:00
|
|
|
#include "blobstore/implementations/onblocks/BlobStoreOnBlocks.h"
|
|
|
|
#include "test/testutils/DataBlockFixture.h"
|
|
|
|
|
|
|
|
using ::testing::Test;
|
|
|
|
using ::testing::WithParamInterface;
|
|
|
|
using ::testing::Values;
|
|
|
|
using std::unique_ptr;
|
|
|
|
using std::make_unique;
|
|
|
|
using std::string;
|
|
|
|
|
|
|
|
using blockstore::BlockStore;
|
2014-12-11 00:20:23 +01:00
|
|
|
using blockstore::BlockWithKey;
|
2014-12-10 17:59:45 +01:00
|
|
|
using blockstore::Data;
|
2014-12-11 01:31:21 +01:00
|
|
|
using blockstore::testfake::FakeBlockStore;
|
2014-12-10 17:59:45 +01:00
|
|
|
using namespace blobstore;
|
|
|
|
using namespace blobstore::onblocks;
|
|
|
|
|
2014-12-10 22:55:02 +01:00
|
|
|
#define EXPECT_IS_PTR_TYPE(Type, ptr) EXPECT_NE(nullptr, dynamic_cast<Type*>(ptr)) << "Given pointer cannot be cast to the given type"
|
|
|
|
|
2014-12-10 17:59:45 +01:00
|
|
|
class DataLeafNodeTest: public Test {
|
|
|
|
public:
|
|
|
|
|
2014-12-11 00:20:23 +01:00
|
|
|
DataLeafNodeTest():
|
|
|
|
ZEROES(DataLeafNode::MAX_STORED_BYTES),
|
|
|
|
randomData(DataLeafNode::MAX_STORED_BYTES),
|
2014-12-13 17:43:02 +01:00
|
|
|
_blockStore(make_unique<FakeBlockStore>()),
|
|
|
|
blockStore(_blockStore.get()),
|
|
|
|
nodeStore(make_unique<DataNodeStore>(std::move(_blockStore))),
|
|
|
|
leaf(nodeStore->createNewLeafNode()) {
|
2014-12-11 00:20:23 +01:00
|
|
|
|
|
|
|
ZEROES.FillWithZeroes();
|
|
|
|
|
2014-12-10 23:34:36 +01:00
|
|
|
DataBlockFixture dataFixture(DataLeafNode::MAX_STORED_BYTES);
|
2014-12-10 17:59:45 +01:00
|
|
|
|
|
|
|
std::memcpy(randomData.data(), dataFixture.data(), randomData.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
Key WriteDataToNewLeafBlockAndReturnKey() {
|
2014-12-13 17:43:02 +01:00
|
|
|
auto newleaf = nodeStore->createNewLeafNode();
|
|
|
|
newleaf->resize(randomData.size());
|
|
|
|
newleaf->write(0, randomData.size(), randomData);
|
|
|
|
return newleaf->key();
|
2014-12-10 17:59:45 +01:00
|
|
|
}
|
|
|
|
|
2014-12-11 00:20:23 +01:00
|
|
|
void FillLeafBlockWithData() {
|
|
|
|
leaf->resize(randomData.size());
|
|
|
|
leaf->write(0, randomData.size(), randomData);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ReadDataFromLoadedLeafBlock(Key key, Data *data) {
|
2014-12-13 17:43:02 +01:00
|
|
|
auto leaf = nodeStore->load(key);
|
2014-12-10 22:55:02 +01:00
|
|
|
EXPECT_IS_PTR_TYPE(DataLeafNode, leaf.get());
|
|
|
|
leaf->read(0, data->size(), data);
|
2014-12-10 17:59:45 +01:00
|
|
|
}
|
|
|
|
|
2014-12-13 17:43:02 +01:00
|
|
|
void ResizeLeaf(const Key &key, size_t size) {
|
|
|
|
auto leaf = nodeStore->load(key);
|
|
|
|
EXPECT_IS_PTR_TYPE(DataLeafNode, leaf.get());
|
|
|
|
leaf->resize(size);
|
|
|
|
}
|
|
|
|
|
2014-12-11 00:20:23 +01:00
|
|
|
Data ZEROES;
|
2014-12-10 17:59:45 +01:00
|
|
|
Data randomData;
|
2014-12-13 17:43:02 +01:00
|
|
|
unique_ptr<BlockStore> _blockStore;
|
|
|
|
BlockStore *blockStore;
|
|
|
|
unique_ptr<DataNodeStore> nodeStore;
|
2014-12-13 17:58:11 +01:00
|
|
|
unique_ptr<DataLeafNode> leaf;
|
2014-12-10 17:59:45 +01:00
|
|
|
};
|
|
|
|
|
2014-12-13 17:58:11 +01:00
|
|
|
TEST_F(DataLeafNodeTest, InitializesCorrectly) {
|
|
|
|
leaf->InitializeNewNode();
|
|
|
|
EXPECT_EQ(0u, leaf->numBytesInThisNode());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(DataLeafNodeTest, ReinitializesCorrectly) {
|
|
|
|
leaf->resize(5);
|
|
|
|
leaf->InitializeNewNode();
|
|
|
|
EXPECT_EQ(0u, leaf->numBytesInThisNode());
|
|
|
|
}
|
|
|
|
|
2014-12-10 17:59:45 +01:00
|
|
|
TEST_F(DataLeafNodeTest, ReadWrittenDataImmediately) {
|
2014-12-10 23:34:36 +01:00
|
|
|
leaf->resize(randomData.size());
|
2014-12-10 22:55:02 +01:00
|
|
|
leaf->write(0, randomData.size(), randomData);
|
2014-12-10 17:59:45 +01:00
|
|
|
|
2014-12-10 23:34:36 +01:00
|
|
|
Data read(DataLeafNode::MAX_STORED_BYTES);
|
2014-12-10 22:55:02 +01:00
|
|
|
leaf->read(0, read.size(), &read);
|
2014-12-10 17:59:45 +01:00
|
|
|
EXPECT_EQ(0, std::memcmp(randomData.data(), read.data(), randomData.size()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(DataLeafNodeTest, ReadWrittenDataAfterReloadingBLock) {
|
|
|
|
Key key = WriteDataToNewLeafBlockAndReturnKey();
|
|
|
|
|
2014-12-10 23:34:36 +01:00
|
|
|
Data data(DataLeafNode::MAX_STORED_BYTES);
|
2014-12-11 00:20:23 +01:00
|
|
|
ReadDataFromLoadedLeafBlock(key, &data);
|
2014-12-10 17:59:45 +01:00
|
|
|
|
|
|
|
EXPECT_EQ(0, std::memcmp(randomData.data(), data.data(), randomData.size()));
|
|
|
|
}
|
|
|
|
|
2014-12-10 22:55:02 +01:00
|
|
|
TEST_F(DataLeafNodeTest, NewLeafNodeHasSizeZero) {
|
|
|
|
EXPECT_EQ(0u, leaf->numBytesInThisNode());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(DataLeafNodeTest, NewLeafNodeHasSizeZero_AfterLoading) {
|
2014-12-13 17:43:02 +01:00
|
|
|
Key key = nodeStore->createNewLeafNode()->key();
|
|
|
|
auto leaf = nodeStore->load(key);
|
2014-12-10 22:55:02 +01:00
|
|
|
|
|
|
|
EXPECT_EQ(0u, leaf->numBytesInThisNode());
|
|
|
|
}
|
|
|
|
|
2014-12-13 17:43:02 +01:00
|
|
|
class DataLeafNodeSizeTest: public DataLeafNodeTest, public WithParamInterface<unsigned int> {
|
|
|
|
public:
|
|
|
|
Key CreateLeafResizeItAndReturnKey() {
|
|
|
|
auto leaf = nodeStore->createNewLeafNode();
|
|
|
|
leaf->resize(GetParam());
|
|
|
|
return leaf->key();
|
|
|
|
}
|
|
|
|
};
|
2014-12-10 23:34:36 +01:00
|
|
|
INSTANTIATE_TEST_CASE_P(DataLeafNodeSizeTest, DataLeafNodeSizeTest, Values(0, 1, 5, 16, 32, 512, DataLeafNode::MAX_STORED_BYTES));
|
|
|
|
|
|
|
|
TEST_P(DataLeafNodeSizeTest, ResizeNode_ReadSizeImmediately) {
|
|
|
|
leaf->resize(GetParam());
|
|
|
|
EXPECT_EQ(GetParam(), leaf->numBytesInThisNode());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(DataLeafNodeSizeTest, ResizeNode_ReadSizeAfterLoading) {
|
2014-12-13 17:43:02 +01:00
|
|
|
Key key = CreateLeafResizeItAndReturnKey();
|
|
|
|
|
|
|
|
auto leaf = nodeStore->load(key);
|
2014-12-10 23:34:36 +01:00
|
|
|
EXPECT_EQ(GetParam(), leaf->numBytesInThisNode());
|
|
|
|
}
|
|
|
|
|
2014-12-11 00:20:23 +01:00
|
|
|
TEST_F(DataLeafNodeTest, SpaceIsZeroFilledWhenGrowing) {
|
|
|
|
leaf->resize(randomData.size());
|
|
|
|
|
|
|
|
Data read(randomData.size());
|
|
|
|
leaf->read(0, read.size(), &read);
|
|
|
|
EXPECT_EQ(0, std::memcmp(ZEROES.data(), read.data(), read.size()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(DataLeafNodeTest, SpaceGetsZeroFilledWhenShrinkingAndRegrowing) {
|
|
|
|
FillLeafBlockWithData();
|
|
|
|
// resize it smaller and then back to original size
|
|
|
|
uint32_t smaller_size = randomData.size() - 100;
|
|
|
|
leaf->resize(smaller_size);
|
|
|
|
leaf->resize(randomData.size());
|
|
|
|
|
|
|
|
//Check that the space was filled with zeroes
|
|
|
|
Data read(100);
|
|
|
|
leaf->read(smaller_size, read.size(), &read);
|
|
|
|
EXPECT_EQ(0, std::memcmp(ZEROES.data(), read.data(), read.size()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(DataLeafNodeTest, DataGetsZeroFilledWhenShrinking) {
|
2014-12-13 17:43:02 +01:00
|
|
|
Key key = WriteDataToNewLeafBlockAndReturnKey();
|
2014-12-11 00:20:23 +01:00
|
|
|
uint32_t smaller_size = randomData.size() - 100;
|
2014-12-13 17:43:02 +01:00
|
|
|
{
|
|
|
|
//At first, we expect there to be random data in the underlying data block
|
|
|
|
auto block = blockStore->load(key);
|
|
|
|
EXPECT_EQ(0, std::memcmp((char*)randomData.data()+smaller_size, (uint8_t*)block->data()+DataNodeView::HEADERSIZE_BYTES+smaller_size, 100));
|
|
|
|
}
|
2014-12-11 00:20:23 +01:00
|
|
|
|
|
|
|
//After shrinking, we expect there to be zeroes in the underlying data block
|
2014-12-13 17:43:02 +01:00
|
|
|
ResizeLeaf(key, smaller_size);
|
|
|
|
{
|
|
|
|
auto block = blockStore->load(leaf->key());
|
|
|
|
EXPECT_EQ(0, std::memcmp(ZEROES.data(), (uint8_t*)block->data()+DataNodeView::HEADERSIZE_BYTES+smaller_size, 100));
|
|
|
|
}
|
2014-12-11 00:20:23 +01:00
|
|
|
}
|
|
|
|
|
2014-12-10 17:59:45 +01:00
|
|
|
//TODO Write tests that only read part of the data
|