2015-03-04 02:05:03 +01:00
|
|
|
#include "testutils/DataTreeTest.h"
|
|
|
|
#include "testutils/TwoLevelDataFixture.h"
|
|
|
|
#include "../../../../implementations/onblocks/utils/Math.h"
|
2015-04-25 02:55:34 +02:00
|
|
|
#include <messmer/cpp-utils/data/Data.h>
|
2015-03-04 02:05:03 +01:00
|
|
|
|
|
|
|
#include <tuple>
|
|
|
|
|
|
|
|
using ::testing::WithParamInterface;
|
|
|
|
using ::testing::Values;
|
|
|
|
using ::testing::Combine;
|
|
|
|
using std::tuple;
|
|
|
|
using std::get;
|
|
|
|
using std::function;
|
|
|
|
using std::mem_fn;
|
2015-03-04 02:56:17 +01:00
|
|
|
using cpputils::dynamic_pointer_move;
|
2015-03-04 02:05:03 +01:00
|
|
|
|
|
|
|
using blobstore::onblocks::datanodestore::DataLeafNode;
|
|
|
|
using blobstore::onblocks::datanodestore::DataInnerNode;
|
|
|
|
using blobstore::onblocks::datanodestore::DataNode;
|
|
|
|
using blobstore::onblocks::datanodestore::DataNodeLayout;
|
|
|
|
using blobstore::onblocks::datatreestore::DataTree;
|
|
|
|
using blobstore::onblocks::utils::ceilDivision;
|
|
|
|
using blockstore::Key;
|
2015-04-25 02:55:34 +02:00
|
|
|
using cpputils::Data;
|
2015-06-26 15:59:18 +02:00
|
|
|
using boost::none;
|
2015-03-04 02:05:03 +01:00
|
|
|
|
2015-06-26 15:59:18 +02:00
|
|
|
using cpputils::unique_ref;
|
2015-03-04 02:05:03 +01:00
|
|
|
|
|
|
|
class DataTreeTest_ResizeNumBytes: public DataTreeTest {
|
|
|
|
public:
|
|
|
|
static constexpr DataNodeLayout LAYOUT = DataNodeLayout(BLOCKSIZE_BYTES);
|
|
|
|
|
2015-06-26 15:59:18 +02:00
|
|
|
unique_ref<DataTree> CreateTree(unique_ref<DataNode> root) {
|
2015-03-04 02:05:03 +01:00
|
|
|
Key key = root->key();
|
2015-06-26 15:59:18 +02:00
|
|
|
cpputils::to_unique_ptr(std::move(root)).reset(); // Destruct
|
|
|
|
return std::move(treeStore.load(key).get());
|
2015-03-04 02:05:03 +01:00
|
|
|
}
|
|
|
|
|
2015-06-26 15:59:18 +02:00
|
|
|
unique_ref<DataTree> CreateLeafTreeWithSize(uint32_t size) {
|
2015-03-04 02:05:03 +01:00
|
|
|
return CreateTree(CreateLeafWithSize(size));
|
|
|
|
}
|
|
|
|
|
2015-06-26 15:59:18 +02:00
|
|
|
unique_ref<DataTree> CreateTwoLeafTreeWithSecondLeafSize(uint32_t size) {
|
2015-03-04 02:05:03 +01:00
|
|
|
return CreateTree(CreateTwoLeafWithSecondLeafSize(size));
|
|
|
|
}
|
|
|
|
|
2015-06-26 15:59:18 +02:00
|
|
|
unique_ref<DataTree> CreateFullTwoLevelTreeWithLastLeafSize(uint32_t size) {
|
2015-03-04 02:05:03 +01:00
|
|
|
return CreateTree(CreateFullTwoLevelWithLastLeafSize(size));
|
|
|
|
}
|
|
|
|
|
2015-06-26 15:59:18 +02:00
|
|
|
unique_ref<DataTree> CreateThreeLevelTreeWithTwoChildrenAndLastLeafSize(uint32_t size) {
|
2015-03-04 02:05:03 +01:00
|
|
|
return CreateTree(CreateThreeLevelWithTwoChildrenAndLastLeafSize(size));
|
|
|
|
}
|
|
|
|
|
2015-06-26 15:59:18 +02:00
|
|
|
unique_ref<DataTree> CreateThreeLevelTreeWithThreeChildrenAndLastLeafSize(uint32_t size) {
|
2015-03-04 02:05:03 +01:00
|
|
|
return CreateTree(CreateThreeLevelWithThreeChildrenAndLastLeafSize(size));
|
|
|
|
}
|
|
|
|
|
2015-06-26 15:59:18 +02:00
|
|
|
unique_ref<DataTree> CreateFullThreeLevelTreeWithLastLeafSize(uint32_t size) {
|
2015-03-04 02:05:03 +01:00
|
|
|
return CreateTree(CreateFullThreeLevelWithLastLeafSize(size));
|
|
|
|
}
|
|
|
|
|
2015-06-26 15:59:18 +02:00
|
|
|
unique_ref<DataTree> CreateFourLevelMinDataTreeWithLastLeafSize(uint32_t size) {
|
2015-03-04 02:05:03 +01:00
|
|
|
return CreateTree(CreateFourLevelMinDataWithLastLeafSize(size));
|
|
|
|
}
|
|
|
|
|
|
|
|
void EXPECT_IS_LEFTMAXDATA_TREE(const Key &key) {
|
2015-06-26 15:59:18 +02:00
|
|
|
auto root = std::move(nodeStore->load(key).get());
|
2015-03-04 02:05:03 +01:00
|
|
|
DataInnerNode *inner = dynamic_cast<DataInnerNode*>(root.get());
|
|
|
|
if (inner != nullptr) {
|
2015-03-16 18:32:37 +01:00
|
|
|
for (uint32_t i = 0; i < inner->numChildren()-1; ++i) {
|
2015-03-04 02:05:03 +01:00
|
|
|
EXPECT_IS_MAXDATA_TREE(inner->getChild(i)->key());
|
|
|
|
}
|
|
|
|
EXPECT_IS_LEFTMAXDATA_TREE(inner->LastChild()->key());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EXPECT_IS_MAXDATA_TREE(const Key &key) {
|
2015-06-26 15:59:18 +02:00
|
|
|
auto root = std::move(nodeStore->load(key).get());
|
2015-03-04 02:05:03 +01:00
|
|
|
DataInnerNode *inner = dynamic_cast<DataInnerNode*>(root.get());
|
|
|
|
if (inner != nullptr) {
|
2015-03-16 18:32:37 +01:00
|
|
|
for (uint32_t i = 0; i < inner->numChildren(); ++i) {
|
2015-03-04 02:05:03 +01:00
|
|
|
EXPECT_IS_MAXDATA_TREE(inner->getChild(i)->key());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
DataLeafNode *leaf = dynamic_cast<DataLeafNode*>(root.get());
|
|
|
|
EXPECT_EQ(nodeStore->layout().maxBytesPerLeaf(), leaf->numBytes());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
constexpr DataNodeLayout DataTreeTest_ResizeNumBytes::LAYOUT;
|
|
|
|
|
2015-06-26 15:59:18 +02:00
|
|
|
class DataTreeTest_ResizeNumBytes_P: public DataTreeTest_ResizeNumBytes, public WithParamInterface<tuple<function<unique_ref<DataTree>(DataTreeTest_ResizeNumBytes*, uint32_t)>, uint32_t, uint32_t, uint32_t>> {
|
2015-03-04 02:05:03 +01:00
|
|
|
public:
|
|
|
|
DataTreeTest_ResizeNumBytes_P()
|
|
|
|
: oldLastLeafSize(get<1>(GetParam())),
|
|
|
|
tree(get<0>(GetParam())(this, oldLastLeafSize)),
|
|
|
|
newNumberOfLeaves(get<2>(GetParam())),
|
|
|
|
newLastLeafSize(get<3>(GetParam())),
|
2015-03-04 02:56:17 +01:00
|
|
|
newSize((newNumberOfLeaves-1) * LAYOUT.maxBytesPerLeaf() + newLastLeafSize),
|
|
|
|
ZEROES(LAYOUT.maxBytesPerLeaf())
|
|
|
|
{
|
|
|
|
ZEROES.FillWithZeroes();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ResizeTree(const Key &key, uint64_t size) {
|
2015-06-26 15:59:18 +02:00
|
|
|
treeStore.load(key).get()->resizeNumBytes(size);
|
2015-03-04 02:56:17 +01:00
|
|
|
}
|
|
|
|
|
2015-06-26 15:59:18 +02:00
|
|
|
unique_ref<DataLeafNode> LastLeaf(const Key &key) {
|
|
|
|
auto root = std::move(nodeStore->load(key).get());
|
2015-03-04 02:56:17 +01:00
|
|
|
auto leaf = dynamic_pointer_move<DataLeafNode>(root);
|
2015-06-26 15:59:18 +02:00
|
|
|
if (leaf != none) {
|
|
|
|
return std::move(*leaf);
|
2015-03-04 02:56:17 +01:00
|
|
|
}
|
2015-06-28 16:45:18 +02:00
|
|
|
auto inner = dynamic_pointer_move<DataInnerNode>(root).value();
|
2015-03-04 02:56:17 +01:00
|
|
|
return LastLeaf(inner->LastChild()->key());
|
|
|
|
}
|
2015-03-04 02:05:03 +01:00
|
|
|
|
|
|
|
uint32_t oldLastLeafSize;
|
2015-06-26 15:59:18 +02:00
|
|
|
unique_ref<DataTree> tree;
|
2015-03-04 02:05:03 +01:00
|
|
|
uint32_t newNumberOfLeaves;
|
|
|
|
uint32_t newLastLeafSize;
|
|
|
|
uint64_t newSize;
|
2015-03-04 02:56:17 +01:00
|
|
|
Data ZEROES;
|
2015-03-04 02:05:03 +01:00
|
|
|
};
|
|
|
|
INSTANTIATE_TEST_CASE_P(DataTreeTest_ResizeNumBytes_P, DataTreeTest_ResizeNumBytes_P,
|
|
|
|
Combine(
|
|
|
|
//Tree we're starting with
|
2015-06-26 15:59:18 +02:00
|
|
|
Values<function<unique_ref<DataTree>(DataTreeTest_ResizeNumBytes*, uint32_t)>>(
|
2015-03-04 02:05:03 +01:00
|
|
|
mem_fn(&DataTreeTest_ResizeNumBytes::CreateLeafTreeWithSize),
|
|
|
|
mem_fn(&DataTreeTest_ResizeNumBytes::CreateTwoLeafTreeWithSecondLeafSize),
|
|
|
|
mem_fn(&DataTreeTest_ResizeNumBytes::CreateFullTwoLevelTreeWithLastLeafSize),
|
|
|
|
mem_fn(&DataTreeTest_ResizeNumBytes::CreateThreeLevelTreeWithTwoChildrenAndLastLeafSize),
|
|
|
|
mem_fn(&DataTreeTest_ResizeNumBytes::CreateThreeLevelTreeWithThreeChildrenAndLastLeafSize),
|
|
|
|
mem_fn(&DataTreeTest_ResizeNumBytes::CreateFullThreeLevelTreeWithLastLeafSize),
|
|
|
|
mem_fn(&DataTreeTest_ResizeNumBytes::CreateFourLevelMinDataTreeWithLastLeafSize)
|
|
|
|
),
|
|
|
|
//Last leaf size of the start tree
|
|
|
|
Values(
|
|
|
|
0u,
|
|
|
|
1u,
|
|
|
|
10u,
|
|
|
|
DataTreeTest_ResizeNumBytes::LAYOUT.maxBytesPerLeaf()
|
|
|
|
),
|
|
|
|
//Number of leaves we're resizing to
|
|
|
|
Values(
|
|
|
|
1u,
|
|
|
|
2u,
|
|
|
|
DataTreeTest_ResizeNumBytes::LAYOUT.maxChildrenPerInnerNode(), //Full two level tree
|
|
|
|
2* DataTreeTest_ResizeNumBytes::LAYOUT.maxChildrenPerInnerNode(), //Three level tree with two children
|
|
|
|
3* DataTreeTest_ResizeNumBytes::LAYOUT.maxChildrenPerInnerNode(), //Three level tree with three children
|
|
|
|
DataTreeTest_ResizeNumBytes::LAYOUT.maxChildrenPerInnerNode() * DataTreeTest_ResizeNumBytes::LAYOUT.maxChildrenPerInnerNode(), //Full three level tree
|
|
|
|
DataTreeTest_ResizeNumBytes::LAYOUT.maxChildrenPerInnerNode() * DataTreeTest_ResizeNumBytes::LAYOUT.maxChildrenPerInnerNode() + 1 //Four level mindata tree
|
|
|
|
),
|
|
|
|
//Last leaf size of the resized tree
|
|
|
|
Values(
|
|
|
|
1u,
|
|
|
|
10u,
|
|
|
|
DataTreeTest_ResizeNumBytes::LAYOUT.maxBytesPerLeaf()
|
|
|
|
)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
TEST_P(DataTreeTest_ResizeNumBytes_P, StructureIsValid) {
|
|
|
|
tree->resizeNumBytes(newSize);
|
2015-04-10 00:10:49 +02:00
|
|
|
tree->flush();
|
2015-03-04 02:05:03 +01:00
|
|
|
EXPECT_IS_LEFTMAXDATA_TREE(tree->key());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(DataTreeTest_ResizeNumBytes_P, NumBytesIsCorrect) {
|
|
|
|
tree->resizeNumBytes(newSize);
|
2015-04-10 00:10:49 +02:00
|
|
|
tree->flush();
|
2015-03-04 02:05:03 +01:00
|
|
|
// tree->numStoredBytes() 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.
|
|
|
|
EXPECT_EQ(newSize, tree->numStoredBytes());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(DataTreeTest_ResizeNumBytes_P, DepthFlagsAreCorrect) {
|
|
|
|
tree->resizeNumBytes(newSize);
|
2015-04-10 00:10:49 +02:00
|
|
|
tree->flush();
|
2015-03-04 02:05:03 +01:00
|
|
|
uint32_t depth = ceil(log(newNumberOfLeaves)/log(DataTreeTest_ResizeNumBytes::LAYOUT.maxChildrenPerInnerNode()));
|
|
|
|
CHECK_DEPTH(depth, tree->key());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(DataTreeTest_ResizeNumBytes_P, KeyDoesntChange) {
|
|
|
|
Key key = tree->key();
|
2015-04-10 00:10:49 +02:00
|
|
|
tree->flush();
|
2015-03-04 02:05:03 +01:00
|
|
|
tree->resizeNumBytes(newSize);
|
|
|
|
EXPECT_EQ(key, tree->key());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(DataTreeTest_ResizeNumBytes_P, DataStaysIntact) {
|
|
|
|
uint32_t oldNumberOfLeaves = std::max(1u, ceilDivision(tree->numStoredBytes(), nodeStore->layout().maxBytesPerLeaf()));
|
|
|
|
TwoLevelDataFixture data(nodeStore, TwoLevelDataFixture::SizePolicy::Unchanged);
|
|
|
|
Key key = tree->key();
|
2015-06-26 15:59:18 +02:00
|
|
|
cpputils::to_unique_ptr(std::move(tree)).reset(); // Call destructor
|
|
|
|
data.FillInto(nodeStore->load(key).get().get());
|
2015-03-04 02:05:03 +01:00
|
|
|
|
2015-03-04 02:56:17 +01:00
|
|
|
ResizeTree(key, newSize);
|
|
|
|
|
|
|
|
if (oldNumberOfLeaves < newNumberOfLeaves || (oldNumberOfLeaves == newNumberOfLeaves && oldLastLeafSize < newLastLeafSize)) {
|
2015-06-26 15:59:18 +02:00
|
|
|
data.EXPECT_DATA_CORRECT(nodeStore->load(key).get().get(), oldNumberOfLeaves, oldLastLeafSize);
|
2015-03-04 02:56:17 +01:00
|
|
|
} else {
|
2015-06-26 15:59:18 +02:00
|
|
|
data.EXPECT_DATA_CORRECT(nodeStore->load(key).get().get(), newNumberOfLeaves, newLastLeafSize);
|
2015-03-04 02:56:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//Resize to zero is not caught in the parametrized test above, in the following, we test it separately.
|
|
|
|
|
|
|
|
TEST_F(DataTreeTest_ResizeNumBytes, ResizeToZero_NumBytesIsCorrect) {
|
|
|
|
auto tree = CreateThreeLevelTreeWithThreeChildrenAndLastLeafSize(10u);
|
|
|
|
tree->resizeNumBytes(0);
|
|
|
|
Key key = tree->key();
|
2015-06-26 15:59:18 +02:00
|
|
|
cpputils::to_unique_ptr(std::move(tree)).reset(); // Call destructor
|
2015-03-04 02:56:17 +01:00
|
|
|
auto leaf = LoadLeafNode(key);
|
|
|
|
EXPECT_EQ(0u, leaf->numBytes());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(DataTreeTest_ResizeNumBytes, ResizeToZero_KeyDoesntChange) {
|
|
|
|
auto tree = CreateThreeLevelTreeWithThreeChildrenAndLastLeafSize(10u);
|
|
|
|
Key key = tree->key();
|
|
|
|
tree->resizeNumBytes(0);
|
2015-04-10 00:10:49 +02:00
|
|
|
tree->flush();
|
2015-03-04 02:56:17 +01:00
|
|
|
EXPECT_EQ(key, tree->key());
|
|
|
|
}
|