Added tests ensuring that the data stays intact on shrinking

This commit is contained in:
Sebastian Messmer 2015-02-23 19:54:59 +01:00
parent 6c9d4cca39
commit f7e710f305
5 changed files with 225 additions and 86 deletions

View File

@ -1,6 +1,6 @@
#include "testutils/DataTreeGrowingTest.h" #include "testutils/DataTreeGrowingTest.h"
#include "testutils/LeafDataFixture.h" #include "../testutils/LeafDataFixture.h"
#include "testutils/TwoLevelDataFixture.h" #include "../testutils/TwoLevelDataFixture.h"
using std::unique_ptr; using std::unique_ptr;
using std::make_unique; using std::make_unique;
@ -16,22 +16,22 @@ using cpputils::dynamic_pointer_move;
class DataTreeGrowingTest_DataStaysIntact: public DataTreeGrowingTest { class DataTreeGrowingTest_DataStaysIntact: public DataTreeGrowingTest {
public: public:
unique_ptr<DataTree> CreateLeafOnlyTreeWithData(const LeafDataFixture &data) { unique_ptr<DataTree> CreateLeafOnlyTreeWithData(TwoLevelDataFixture *data) {
auto leafnode = nodeStore.createNewLeafNode(); auto leafnode = nodeStore.createNewLeafNode();
data.FillInto(leafnode.get()); data->FillInto(leafnode.get());
return make_unique<DataTree>(&nodeStore, std::move(leafnode)); return make_unique<DataTree>(&nodeStore, std::move(leafnode));
} }
unique_ptr<DataTree> CreateTwoNodeTreeWithData(const LeafDataFixture &data) { unique_ptr<DataTree> CreateTwoNodeTreeWithData(TwoLevelDataFixture *data) {
auto tree = CreateLeafOnlyTreeWithData(data); auto root = CreateInner({CreateLeaf(), CreateLeaf()});
tree->addDataLeaf(); data->FillInto(root.get());
return tree; return make_unique<DataTree>(&nodeStore, std::move(root));
} }
unique_ptr<DataTree> CreateThreeNodeChainedTreeWithData(const LeafDataFixture &data) { unique_ptr<DataTree> CreateThreeNodeChainedTreeWithData(TwoLevelDataFixture *data) {
auto leaf = nodeStore.createNewLeafNode(); auto leaf = nodeStore.createNewLeafNode();
data.FillInto(leaf.get()); data->FillInto(leaf.get());
auto inner = nodeStore.createNewInnerNode(*leaf); auto inner = nodeStore.createNewInnerNode(*leaf);
return make_unique<DataTree>(&nodeStore, nodeStore.createNewInnerNode(*inner)); return make_unique<DataTree>(&nodeStore, nodeStore.createNewInnerNode(*inner));
@ -45,26 +45,9 @@ public:
} }
unique_ptr<DataTree> CreateThreeLevelTreeWithLowerLevelFullWithData(TwoLevelDataFixture *data) { unique_ptr<DataTree> CreateThreeLevelTreeWithLowerLevelFullWithData(TwoLevelDataFixture *data) {
auto _node = LoadFirstChildOf(CreateThreeLevelTreeWithLowerLevelFullReturnRootKey()); auto root = CreateInner({CreateFullTwoLevel()});
auto node = dynamic_pointer_move<DataInnerNode>(_node); data->FillInto(root.get());
data->FillInto(node.get()); return make_unique<DataTree>(&nodeStore, std::move(root));
return make_unique<DataTree>(&nodeStore, std::move(node));
}
unique_ptr<DataNode> LoadFirstChildOf(const Key &key) {
auto root = LoadInnerNode(key);
return nodeStore.load(root->getChild(0)->key());
}
unique_ptr<DataLeafNode> LoadFirstLeafOf(const Key &key) {
auto root = LoadInnerNode(key);
return LoadLeafNode(root->getChild(0)->key());
}
unique_ptr<DataLeafNode> LoadTwoLevelFirstLeafOf(const Key &key) {
auto root = LoadInnerNode(key);
auto inner = LoadInnerNode(root->getChild(0)->key());
return LoadLeafNode(inner->getChild(0)->key());
} }
}; };
@ -74,8 +57,8 @@ TEST_F(DataTreeGrowingTest_DataStaysIntact, GrowAFullTwoLevelTree) {
tree->addDataLeaf(); tree->addDataLeaf();
tree->flush(); tree->flush();
auto node = LoadFirstChildOf(tree->key()); auto root = LoadInnerNode(tree->key());
data.EXPECT_DATA_CORRECT(*dynamic_pointer_move<DataInnerNode>(node)); data.EXPECT_DATA_CORRECT(dynamic_pointer_move<DataInnerNode>(root).get(), DataInnerNode::MAX_STORED_CHILDREN);
} }
TEST_F(DataTreeGrowingTest_DataStaysIntact, GrowAThreeLevelTreeWithLowerLevelFull) { TEST_F(DataTreeGrowingTest_DataStaysIntact, GrowAThreeLevelTreeWithLowerLevelFull) {
@ -84,8 +67,8 @@ TEST_F(DataTreeGrowingTest_DataStaysIntact, GrowAThreeLevelTreeWithLowerLevelFul
tree->addDataLeaf(); tree->addDataLeaf();
tree->flush(); tree->flush();
auto node = LoadFirstChildOf(tree->key()); auto root = LoadInnerNode(tree->key());
data.EXPECT_DATA_CORRECT(*dynamic_pointer_move<DataInnerNode>(node)); data.EXPECT_DATA_CORRECT(dynamic_pointer_move<DataInnerNode>(root).get(), DataInnerNode::MAX_STORED_CHILDREN);
} }
class DataTreeGrowingTest_DataStaysIntact_OneDataLeaf: public DataTreeGrowingTest_DataStaysIntact, public WithParamInterface<uint32_t> { class DataTreeGrowingTest_DataStaysIntact_OneDataLeaf: public DataTreeGrowingTest_DataStaysIntact, public WithParamInterface<uint32_t> {
@ -93,31 +76,31 @@ class DataTreeGrowingTest_DataStaysIntact_OneDataLeaf: public DataTreeGrowingTes
INSTANTIATE_TEST_CASE_P(DataTreeGrowingTest_DataStaysIntact_OneDataLeaf, DataTreeGrowingTest_DataStaysIntact_OneDataLeaf, Values(0, 1, DataLeafNode::MAX_STORED_BYTES-2, DataLeafNode::MAX_STORED_BYTES-1, DataLeafNode::MAX_STORED_BYTES)); INSTANTIATE_TEST_CASE_P(DataTreeGrowingTest_DataStaysIntact_OneDataLeaf, DataTreeGrowingTest_DataStaysIntact_OneDataLeaf, Values(0, 1, DataLeafNode::MAX_STORED_BYTES-2, DataLeafNode::MAX_STORED_BYTES-1, DataLeafNode::MAX_STORED_BYTES));
TEST_P(DataTreeGrowingTest_DataStaysIntact_OneDataLeaf, GrowAOneNodeTree) { TEST_P(DataTreeGrowingTest_DataStaysIntact_OneDataLeaf, GrowAOneNodeTree) {
LeafDataFixture data(GetParam()); TwoLevelDataFixture data(&nodeStore, GetParam(), true);
auto tree = CreateLeafOnlyTreeWithData(data); auto tree = CreateLeafOnlyTreeWithData(&data);
tree->addDataLeaf(); tree->addDataLeaf();
tree->flush(); tree->flush();
auto leaf = LoadFirstLeafOf(tree->key()); auto root = LoadInnerNode(tree->key());
data.EXPECT_DATA_CORRECT(*leaf); data.EXPECT_DATA_CORRECT(root.get(), 1);
} }
TEST_P(DataTreeGrowingTest_DataStaysIntact_OneDataLeaf, GrowATwoNodeTree) { TEST_P(DataTreeGrowingTest_DataStaysIntact_OneDataLeaf, GrowATwoNodeTree) {
LeafDataFixture data(GetParam()); TwoLevelDataFixture data(&nodeStore, GetParam(), true);
auto tree = CreateTwoNodeTreeWithData(data); auto tree = CreateTwoNodeTreeWithData(&data);
tree->addDataLeaf(); tree->addDataLeaf();
tree->flush(); tree->flush();
auto leaf = LoadFirstLeafOf(tree->key()); auto root = LoadInnerNode(tree->key());
data.EXPECT_DATA_CORRECT(*leaf); data.EXPECT_DATA_CORRECT(root.get(), 2);
} }
TEST_P(DataTreeGrowingTest_DataStaysIntact_OneDataLeaf, GrowAThreeNodeChainedTree) { TEST_P(DataTreeGrowingTest_DataStaysIntact_OneDataLeaf, GrowAThreeNodeChainedTree) {
LeafDataFixture data(GetParam()); TwoLevelDataFixture data(&nodeStore, GetParam(), true);
auto tree = CreateThreeNodeChainedTreeWithData(data); auto tree = CreateThreeNodeChainedTreeWithData(&data);
tree->addDataLeaf(); tree->addDataLeaf();
tree->flush(); tree->flush();
auto leaf = LoadTwoLevelFirstLeafOf(tree->key()); auto root = LoadInnerNode(tree->key());
data.EXPECT_DATA_CORRECT(*leaf); data.EXPECT_DATA_CORRECT(root.get(), 1);
} }

View File

@ -1,39 +0,0 @@
#pragma once
#ifndef BLOCKS_MESSMER_BLOBSTORE_TEST_IMPLEMENTATIONS_ONBLOCKS_DATATREESTORE_GROWING_TESTUTILS_TWOLEVELDATAFIXTURE_H_
#define BLOCKS_MESSMER_BLOBSTORE_TEST_IMPLEMENTATIONS_ONBLOCKS_DATATREESTORE_GROWING_TESTUTILS_TWOLEVELDATAFIXTURE_H_
#include <messmer/cpp-utils/macros.h>
#include <messmer/cpp-utils/pointer.h>
// A data fixture containing data for a two-level tree (one inner node with leaf children).
// The class can fill this data into the leaf children of a given inner node
// and given an inner node can check, whether the data stored is correct.
class TwoLevelDataFixture {
public:
TwoLevelDataFixture(blobstore::onblocks::datanodestore::DataNodeStore *dataNodeStore): _dataNodeStore(dataNodeStore) {}
void FillInto(blobstore::onblocks::datanodestore::DataInnerNode *node) {
for (int i = 0; i < node->numChildren(); ++i) {
auto leafnode = _dataNodeStore->load(node->getChild(i)->key());
auto leaf = cpputils::dynamic_pointer_move<blobstore::onblocks::datanodestore::DataLeafNode>(leafnode);
LeafDataFixture(size(i), i).FillInto(leaf.get());
}
}
void EXPECT_DATA_CORRECT(const blobstore::onblocks::datanodestore::DataInnerNode &node) const {
for (int i = 0; i < node.numChildren(); ++i) {
auto leafnode =_dataNodeStore->load(node.getChild(i)->key());
auto leaf = cpputils::dynamic_pointer_move<blobstore::onblocks::datanodestore::DataLeafNode>(leafnode);
LeafDataFixture(size(i), i).EXPECT_DATA_CORRECT(*leaf);
}
}
private:
blobstore::onblocks::datanodestore::DataNodeStore *_dataNodeStore;
static int size(int childIndex) {
return blobstore::onblocks::datanodestore::DataLeafNode::MAX_STORED_BYTES-childIndex;
}
};
#endif

View File

@ -0,0 +1,126 @@
#include "testutils/DataTreeShrinkingTest.h"
#include "../testutils/TwoLevelDataFixture.h"
using blobstore::onblocks::datanodestore::DataInnerNode;
using blobstore::onblocks::datatreestore::DataTree;
using blockstore::Key;
using std::make_unique;
using std::unique_ptr;
using std::function;
class DataTreeShrinkingTest_DataStaysIntact: public DataTreeShrinkingTest {
public:
unique_ptr<DataTree> TwoLevelTreeWithData(unique_ptr<DataInnerNode> root, TwoLevelDataFixture *data) {
data->FillInto(root.get());
return make_unique<DataTree>(&nodeStore, std::move(root));
}
void TestDataStaysIntactOnShrinking(unique_ptr<DataInnerNode> root, TwoLevelDataFixture *data) {
auto tree = TwoLevelTreeWithData(std::move(root), data);
tree->removeLastDataLeaf();
tree->flush();
data->EXPECT_DATA_CORRECT(nodeStore.load(tree->key()).get());
}
};
TEST_F(DataTreeShrinkingTest_DataStaysIntact, ShrinkATwoLeafTree_FullLeaves) {
TwoLevelDataFixture data(&nodeStore, 0, true);
TestDataStaysIntactOnShrinking(CreateTwoLeaf(), &data);
}
TEST_F(DataTreeShrinkingTest_DataStaysIntact, ShrinkATwoLeafTree_NonFullLeaves) {
TwoLevelDataFixture data(&nodeStore, 0, false);
TestDataStaysIntactOnShrinking(CreateTwoLeaf(), &data);
}
TEST_F(DataTreeShrinkingTest_DataStaysIntact, ShrinkAFourNodeThreeLeafTree_FullLeaves) {
TwoLevelDataFixture data(&nodeStore, 0, true);
TestDataStaysIntactOnShrinking(CreateFourNodeThreeLeaf(), &data);
}
TEST_F(DataTreeShrinkingTest_DataStaysIntact, ShrinkAFourNodeThreeLeafTree_NonFullLeaves) {
TwoLevelDataFixture data(&nodeStore, 0, false);
TestDataStaysIntactOnShrinking(CreateFourNodeThreeLeaf(), &data);
}
TEST_F(DataTreeShrinkingTest_DataStaysIntact, ShrinkATwoInnerNodeOneTwoLeavesTree_FullLeaves) {
TwoLevelDataFixture data(&nodeStore, 0, true);
TestDataStaysIntactOnShrinking(CreateTwoInnerNodeOneTwoLeaves(), &data);
}
TEST_F(DataTreeShrinkingTest_DataStaysIntact, ShrinkATwoInnerNodeOneTwoLeavesTree_NonFullLeaves) {
TwoLevelDataFixture data(&nodeStore, 0, false);
TestDataStaysIntactOnShrinking(CreateTwoInnerNodeOneTwoLeaves(), &data);
}
TEST_F(DataTreeShrinkingTest_DataStaysIntact, ShrinkATwoInnerNodeTwoOneLeavesTree_FullLeaves) {
TwoLevelDataFixture data(&nodeStore, 0, true);
TestDataStaysIntactOnShrinking(CreateTwoInnerNodeTwoOneLeaves(), &data);
}
TEST_F(DataTreeShrinkingTest_DataStaysIntact, ShrinkATwoInnerNodeTwoOneLeavesTree_NonFullLeaves) {
TwoLevelDataFixture data(&nodeStore, 0, false);
TestDataStaysIntactOnShrinking(CreateTwoInnerNodeTwoOneLeaves(), &data);
}
TEST_F(DataTreeShrinkingTest_DataStaysIntact, ShrinkAThreeLevelMinDataTree_FullLeaves) {
TwoLevelDataFixture data(&nodeStore, 0, true);
TestDataStaysIntactOnShrinking(CreateThreeLevelMinData(), &data);
}
TEST_F(DataTreeShrinkingTest_DataStaysIntact, ShrinkAThreeLevelMinDataTree_NonFullLeaves) {
TwoLevelDataFixture data(&nodeStore, 0, false);
TestDataStaysIntactOnShrinking(CreateThreeLevelMinData(), &data);
}
TEST_F(DataTreeShrinkingTest_DataStaysIntact, ShrinkAFourLevelMinDataTree_FullLeaves) {
TwoLevelDataFixture data(&nodeStore, 0, true);
TestDataStaysIntactOnShrinking(CreateFourLevelMinData(), &data);
}
TEST_F(DataTreeShrinkingTest_DataStaysIntact, ShrinkAFourLevelMinDataTree_NonFullLeaves) {
TwoLevelDataFixture data(&nodeStore, 0, false);
TestDataStaysIntactOnShrinking(CreateFourLevelMinData(), &data);
}
TEST_F(DataTreeShrinkingTest_DataStaysIntact, ShrinkAFourLevelTreeWithTwoSiblingLeaves1_FullLeaves) {
TwoLevelDataFixture data(&nodeStore, 0, true);
TestDataStaysIntactOnShrinking(CreateFourLevelWithTwoSiblingLeaves1(), &data);
}
TEST_F(DataTreeShrinkingTest_DataStaysIntact, ShrinkAFourLevelTreeWithTwoSiblingLeaves1_NonFullLeaves) {
TwoLevelDataFixture data(&nodeStore, 0, false);
TestDataStaysIntactOnShrinking(CreateFourLevelWithTwoSiblingLeaves1(), &data);
}
TEST_F(DataTreeShrinkingTest_DataStaysIntact, ShrinkAFourLevelTreeWithTwoSiblingLeaves2_FullLeaves) {
TwoLevelDataFixture data(&nodeStore, 0, true);
TestDataStaysIntactOnShrinking(CreateFourLevelWithTwoSiblingLeaves2(), &data);
}
TEST_F(DataTreeShrinkingTest_DataStaysIntact, ShrinkAFourLevelTreeWithTwoSiblingLeaves2_NonFullLeaves) {
TwoLevelDataFixture data(&nodeStore, 0, false);
TestDataStaysIntactOnShrinking(CreateFourLevelWithTwoSiblingLeaves2(), &data);
}
TEST_F(DataTreeShrinkingTest_DataStaysIntact, ShrinkATreeWithFirstChildOfRootFullThreelevelAndSecondChildMindataThreelevel_FullLeaves) {
TwoLevelDataFixture data(&nodeStore, 0, true);
TestDataStaysIntactOnShrinking(CreateWithFirstChildOfRootFullThreelevelAndSecondChildMindataThreelevel(), &data);
}
TEST_F(DataTreeShrinkingTest_DataStaysIntact, ShrinkATreeWithFirstChildOfRootFullThreelevelAndSecondChildMindataThreelevel_NonFullLeaves) {
TwoLevelDataFixture data(&nodeStore, 0, false);
TestDataStaysIntactOnShrinking(CreateWithFirstChildOfRootFullThreelevelAndSecondChildMindataThreelevel(), &data);
}
TEST_F(DataTreeShrinkingTest_DataStaysIntact, ShrinkAThreeLevelTreeWithThreeChildrenOfRoot_FullLeaves) {
TwoLevelDataFixture data(&nodeStore, 0, true);
TestDataStaysIntactOnShrinking(CreateThreeLevelWithThreeChildrenOfRoot(), &data);
}
TEST_F(DataTreeShrinkingTest_DataStaysIntact, ShrinkAThreeLevelTreeWithThreeChildrenOfRoot_NonFullLeaves) {
TwoLevelDataFixture data(&nodeStore, 0, false);
TestDataStaysIntactOnShrinking(CreateThreeLevelWithThreeChildrenOfRoot(), &data);
}

View File

@ -4,7 +4,7 @@
#include <google/gtest/gtest.h> #include <google/gtest/gtest.h>
#include "../../../../../testutils/DataBlockFixture.h" #include "../../../../testutils/DataBlockFixture.h"
// A data fixture containing data for a leaf. // A data fixture containing data for a leaf.
// The class can fill this data into a given leaf // The class can fill this data into a given leaf

View File

@ -0,0 +1,69 @@
#pragma once
#ifndef BLOCKS_MESSMER_BLOBSTORE_TEST_IMPLEMENTATIONS_ONBLOCKS_DATATREESTORE_GROWING_TESTUTILS_TWOLEVELDATAFIXTURE_H_
#define BLOCKS_MESSMER_BLOBSTORE_TEST_IMPLEMENTATIONS_ONBLOCKS_DATATREESTORE_GROWING_TESTUTILS_TWOLEVELDATAFIXTURE_H_
#include <messmer/cpp-utils/macros.h>
#include <messmer/cpp-utils/pointer.h>
#include "LeafDataFixture.h"
// A data fixture containing data for a two-level tree (one inner node with leaf children).
// The class can fill this data into the leaf children of a given inner node
// and given an inner node can check, whether the data stored is correct.
class TwoLevelDataFixture {
public:
TwoLevelDataFixture(blobstore::onblocks::datanodestore::DataNodeStore *dataNodeStore, int iv=0, bool useFullSizeLeaves = false): _dataNodeStore(dataNodeStore), _iv(iv), _useFullSizeLeaves(useFullSizeLeaves) {}
void FillInto(blobstore::onblocks::datanodestore::DataNode *node) {
// _iv-1 means there is no endLeafIndex - we fill all leaves.
ForEachLeaf(node, _iv, _iv-1, [this] (blobstore::onblocks::datanodestore::DataLeafNode *leaf, int leafIndex) {
LeafDataFixture(size(leafIndex), leafIndex).FillInto(leaf);
});
}
void EXPECT_DATA_CORRECT(blobstore::onblocks::datanodestore::DataNode *node, int maxCheckedLeaves = 0) {
ForEachLeaf(node, _iv, _iv+maxCheckedLeaves, [this] (blobstore::onblocks::datanodestore::DataLeafNode *leaf, int leafIndex) {
LeafDataFixture(size(leafIndex), leafIndex).EXPECT_DATA_CORRECT(*leaf);
});
}
private:
int ForEachLeaf(blobstore::onblocks::datanodestore::DataNode *node, int firstLeafIndex, int endLeafIndex, std::function<void (blobstore::onblocks::datanodestore::DataLeafNode*, int)> action) {
if (firstLeafIndex == endLeafIndex) {
return firstLeafIndex;
}
auto leaf = dynamic_cast<blobstore::onblocks::datanodestore::DataLeafNode*>(node);
if (leaf != nullptr) {
action(leaf, firstLeafIndex);
return firstLeafIndex + 1;
} else {
auto inner = dynamic_cast<blobstore::onblocks::datanodestore::DataInnerNode*>(node);
int leafIndex = firstLeafIndex;
for (int i = 0; i < inner->numChildren(); ++i) {
auto child = _dataNodeStore->load(inner->getChild(i)->key());
leafIndex = ForEachLeaf(child.get(), leafIndex, endLeafIndex, action);
}
return leafIndex;
}
}
blobstore::onblocks::datanodestore::DataNodeStore *_dataNodeStore;
int _iv;
bool _useFullSizeLeaves;
int size(int childIndex) {
if (_useFullSizeLeaves) {
return blobstore::onblocks::datanodestore::DataLeafNode::MAX_STORED_BYTES;
} else {
return mod(blobstore::onblocks::datanodestore::DataLeafNode::MAX_STORED_BYTES - childIndex, blobstore::onblocks::datanodestore::DataLeafNode::MAX_STORED_BYTES);
}
}
int mod(int value, int mod) {
while(value < 0) {
value += mod;
}
return value;
}
};
#endif