Add test cases for tree traversal

This commit is contained in:
Sebastian Messmer 2016-07-14 11:41:49 +02:00
parent 9e9369b9ed
commit 41146282ca

View File

@ -90,29 +90,27 @@ public:
}; };
constexpr DataNodeLayout DataTreeTest_ResizeByTraversing::LAYOUT; constexpr DataNodeLayout DataTreeTest_ResizeByTraversing::LAYOUT;
class DataTreeTest_ResizeByTraversing_P: public DataTreeTest_ResizeByTraversing, public WithParamInterface<tuple<function<unique_ref<DataTree>(DataTreeTest_ResizeByTraversing*, uint32_t)>, uint32_t, uint32_t>> { class DataTreeTest_ResizeByTraversing_P: public DataTreeTest_ResizeByTraversing, public WithParamInterface<tuple<function<unique_ref<DataTree>(DataTreeTest_ResizeByTraversing*, uint32_t)>, uint32_t, uint32_t, std::function<uint32_t (uint32_t oldNumberOfLeaves, uint32_t newNumberOfLeaves)>>> {
public: public:
DataTreeTest_ResizeByTraversing_P() DataTreeTest_ResizeByTraversing_P()
: oldLastLeafSize(get<1>(GetParam())), : oldLastLeafSize(get<1>(GetParam())),
tree(get<0>(GetParam())(this, oldLastLeafSize)), tree(get<0>(GetParam())(this, oldLastLeafSize)),
numberOfLeavesToAdd(get<2>(GetParam())), numberOfLeavesToAdd(get<2>(GetParam())),
newNumberOfLeaves(tree->numLeaves()+numberOfLeavesToAdd), newNumberOfLeaves(tree->numLeaves()+numberOfLeavesToAdd),
traversalBeginIndex(get<3>(GetParam())(tree->numLeaves(), newNumberOfLeaves)),
ZEROES(LAYOUT.maxBytesPerLeaf()) ZEROES(LAYOUT.maxBytesPerLeaf())
{ {
ZEROES.FillWithZeroes(); ZEROES.FillWithZeroes();
} }
void GrowTree(const Key &key, uint32_t numLeavesToAdd) { void GrowTree(const Key &key) {
auto tree = treeStore.load(key); auto tree = treeStore.load(key);
GrowTree(tree.get().get(), numLeavesToAdd); GrowTree(tree.get().get());
} }
void GrowTree(DataTree *tree, uint32_t numLeavesToAdd) { void GrowTree(DataTree *tree) {
uint32_t oldNumLeaves = tree->numLeaves();
uint32_t newNumLeaves = oldNumLeaves + numLeavesToAdd;
//TODO Test cases where beginIndex is inside of the existing leaves
uint64_t maxBytesPerLeaf = tree->maxBytesPerLeaf(); uint64_t maxBytesPerLeaf = tree->maxBytesPerLeaf();
tree->traverseLeaves(newNumLeaves-1, newNumLeaves, [] (uint32_t, DataLeafNode*){}, [maxBytesPerLeaf] (uint32_t) -> Data { return Data(maxBytesPerLeaf).FillWithZeroes();}); tree->traverseLeaves(traversalBeginIndex, newNumberOfLeaves, [] (uint32_t, DataLeafNode*){}, [maxBytesPerLeaf] (uint32_t) -> Data { return Data(maxBytesPerLeaf).FillWithZeroes();});
tree->flush(); tree->flush();
} }
@ -130,6 +128,7 @@ public:
unique_ref<DataTree> tree; unique_ref<DataTree> tree;
uint32_t numberOfLeavesToAdd; uint32_t numberOfLeavesToAdd;
uint32_t newNumberOfLeaves; uint32_t newNumberOfLeaves;
uint32_t traversalBeginIndex;
Data ZEROES; Data ZEROES;
}; };
INSTANTIATE_TEST_CASE_P(DataTreeTest_ResizeByTraversing_P, DataTreeTest_ResizeByTraversing_P, INSTANTIATE_TEST_CASE_P(DataTreeTest_ResizeByTraversing_P, DataTreeTest_ResizeByTraversing_P,
@ -160,32 +159,42 @@ INSTANTIATE_TEST_CASE_P(DataTreeTest_ResizeByTraversing_P, DataTreeTest_ResizeBy
3* DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode(), //Three level tree with three children 3* DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode(), //Three level tree with three children
DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode() * DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode(), //Full three level tree DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode() * DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode(), //Full three level tree
DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode() * DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode() + 1 //Four level mindata tree DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode() * DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode() + 1 //Four level mindata tree
),
//Decide the traversal begin index
Values(
[] (uint32_t /*oldNumberOfLeaves*/, uint32_t newNumberOfLeaves) {return newNumberOfLeaves;}, // Don't traverse any leaves, just resize (begin==end)
[] (uint32_t /*oldNumberOfLeaves*/, uint32_t newNumberOfLeaves) {return newNumberOfLeaves-1;}, // Traverse last leaf (begin==end-1)
[] (uint32_t oldNumberOfLeaves, uint32_t newNumberOfLeaves) {return (oldNumberOfLeaves+newNumberOfLeaves)/2;}, // Start traversal in middle of new leaves
[] (uint32_t oldNumberOfLeaves, uint32_t /*newNumberOfLeaves*/) {return oldNumberOfLeaves-1;}, // Start traversal with last old leaf
[] (uint32_t oldNumberOfLeaves, uint32_t /*newNumberOfLeaves*/) {return oldNumberOfLeaves;}, // Start traversal with first new leaf
[] (uint32_t /*oldNumberOfLeaves*/, uint32_t /*newNumberOfLeaves*/) {return 0;}, // Traverse full tree
[] (uint32_t /*oldNumberOfLeaves*/, uint32_t /*newNumberOfLeaves*/) {return 1;} // Traverse full tree except first leaf
) )
) )
); );
TEST_P(DataTreeTest_ResizeByTraversing_P, StructureIsValid) { TEST_P(DataTreeTest_ResizeByTraversing_P, StructureIsValid) {
GrowTree(tree.get(), numberOfLeavesToAdd); GrowTree(tree.get());
EXPECT_IS_LEFTMAXDATA_TREE(tree->key()); EXPECT_IS_LEFTMAXDATA_TREE(tree->key());
} }
TEST_P(DataTreeTest_ResizeByTraversing_P, NumLeavesIsCorrect_FromCache) { TEST_P(DataTreeTest_ResizeByTraversing_P, NumLeavesIsCorrect_FromCache) {
tree->numLeaves(); // fill cache with old value tree->numLeaves(); // fill cache with old value
GrowTree(tree.get(), numberOfLeavesToAdd); GrowTree(tree.get());
// tree->numLeaves() only goes down the right border nodes and expects the tree to be a left max data tree. // tree->numLeaves() only goes down the right border nodes and expects the tree to be a left max data tree.
// This is what the StructureIsValid test case is for. // This is what the StructureIsValid test case is for.
EXPECT_EQ(newNumberOfLeaves, tree->numLeaves()); EXPECT_EQ(newNumberOfLeaves, tree->numLeaves());
} }
TEST_P(DataTreeTest_ResizeByTraversing_P, NumLeavesIsCorrect) { TEST_P(DataTreeTest_ResizeByTraversing_P, NumLeavesIsCorrect) {
GrowTree(tree.get(), numberOfLeavesToAdd); GrowTree(tree.get());
// tree->_forceComputeNumLeaves() only goes down the right border nodes and expects the tree to be a left max data tree. // tree->_forceComputeNumLeaves() only goes down the right border nodes and expects the tree to be a left max data tree.
// This is what the StructureIsValid test case is for. // This is what the StructureIsValid test case is for.
EXPECT_EQ(newNumberOfLeaves, tree->_forceComputeNumLeaves()); EXPECT_EQ(newNumberOfLeaves, tree->_forceComputeNumLeaves());
} }
TEST_P(DataTreeTest_ResizeByTraversing_P, DepthFlagsAreCorrect) { TEST_P(DataTreeTest_ResizeByTraversing_P, DepthFlagsAreCorrect) {
GrowTree(tree.get(), numberOfLeavesToAdd); GrowTree(tree.get());
uint32_t depth = ceil(log(newNumberOfLeaves)/log(DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode())); uint32_t depth = ceil(log(newNumberOfLeaves)/log(DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode()));
CHECK_DEPTH(depth, tree->key()); CHECK_DEPTH(depth, tree->key());
} }
@ -193,7 +202,7 @@ TEST_P(DataTreeTest_ResizeByTraversing_P, DepthFlagsAreCorrect) {
TEST_P(DataTreeTest_ResizeByTraversing_P, KeyDoesntChange) { TEST_P(DataTreeTest_ResizeByTraversing_P, KeyDoesntChange) {
Key key = tree->key(); Key key = tree->key();
tree->flush(); tree->flush();
GrowTree(tree.get(), numberOfLeavesToAdd); GrowTree(tree.get());
EXPECT_EQ(key, tree->key()); EXPECT_EQ(key, tree->key());
} }
@ -204,7 +213,7 @@ TEST_P(DataTreeTest_ResizeByTraversing_P, DataStaysIntact) {
cpputils::destruct(std::move(tree)); cpputils::destruct(std::move(tree));
data.FillInto(nodeStore->load(key).get().get()); data.FillInto(nodeStore->load(key).get().get());
GrowTree(key, newNumberOfLeaves); GrowTree(key);
data.EXPECT_DATA_CORRECT(nodeStore->load(key).get().get(), oldNumberOfLeaves, oldLastLeafSize); data.EXPECT_DATA_CORRECT(nodeStore->load(key).get().get(), oldNumberOfLeaves, oldLastLeafSize);
} }