2015-03-04 02:05:03 +01:00
# include "testutils/DataTreeTest.h"
# include "testutils/TwoLevelDataFixture.h"
2016-02-11 15:19:58 +01:00
# include "blobstore/implementations/onblocks/utils/Math.h"
# include <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-07-21 15:24:49 +02:00
cpputils : : destruct ( std : : move ( root ) ) ;
2015-06-28 16:59:13 +02:00
return treeStore . load ( key ) . value ( ) ;
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-28 16:59:13 +02:00
auto root = nodeStore - > load ( key ) . value ( ) ;
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-28 16:59:13 +02:00
auto root = nodeStore - > load ( key ) . value ( ) ;
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 ) {
2015-06-28 16:59:13 +02:00
auto root = nodeStore - > load ( key ) . value ( ) ;
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 ( ) ) ;
}
2016-07-06 08:53:13 +02:00
TEST_P ( DataTreeTest_ResizeNumBytes_P , NumLeavesIsCorrect ) {
tree - > resizeNumBytes ( newSize ) ;
tree - > flush ( ) ;
// 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.
2016-07-06 08:56:44 +02:00
EXPECT_EQ ( newNumberOfLeaves , tree - > _forceComputeNumLeaves ( ) ) ;
}
TEST_P ( DataTreeTest_ResizeNumBytes_P , NumLeavesIsCorrect_FromCache ) {
tree - > numLeaves ( ) ; // fill cache with old value
tree - > resizeNumBytes ( newSize ) ;
tree - > flush ( ) ;
// 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.
2016-07-06 08:53:13 +02:00
EXPECT_EQ ( newNumberOfLeaves , tree - > numLeaves ( ) ) ;
}
2015-03-04 02:05:03 +01:00
TEST_P ( DataTreeTest_ResizeNumBytes_P , DepthFlagsAreCorrect ) {
tree - > resizeNumBytes ( newSize ) ;
2015-04-10 00:10:49 +02:00
tree - > flush ( ) ;
2016-04-02 10:13:28 +02:00
uint32_t depth = ceil ( log ( newNumberOfLeaves ) / log ( DataTreeTest_ResizeNumBytes : : LAYOUT . maxChildrenPerInnerNode ( ) ) - 0.00000000001 ) ; // The subtraction takes care of double inaccuracies if newNumberOfLeaves == maxChildrenPerInnerNode
2015-03-04 02:05:03 +01:00
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 ) {
2015-12-11 00:18:17 +01:00
uint32_t oldNumberOfLeaves = std : : max ( UINT64_C ( 1 ) , ceilDivision ( tree - > numStoredBytes ( ) , ( uint64_t ) nodeStore - > layout ( ) . maxBytesPerLeaf ( ) ) ) ;
2015-03-04 02:05:03 +01:00
TwoLevelDataFixture data ( nodeStore , TwoLevelDataFixture : : SizePolicy : : Unchanged ) ;
Key key = tree - > key ( ) ;
2015-07-21 15:24:49 +02:00
cpputils : : destruct ( std : : move ( tree ) ) ;
2015-06-26 15:59:18 +02:00
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-07-21 15:24:49 +02:00
cpputils : : destruct ( std : : move ( tree ) ) ;
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 ( ) ) ;
}