2015-03-06 16:39:40 +01:00
# include "testutils/BlobStoreTest.h"
2016-02-11 15:19:58 +01:00
# include <cpp-utils/data/Data.h>
# include <cpp-utils/data/DataFixture.h>
# include "blobstore/implementations/onblocks/datanodestore/DataNodeView.h"
2015-03-06 16:39:40 +01:00
2015-06-18 12:45:37 +02:00
using cpputils : : unique_ref ;
2015-03-06 20:40:23 +01:00
using : : testing : : WithParamInterface ;
using : : testing : : Values ;
2015-03-06 16:39:40 +01:00
using namespace blobstore ;
2015-03-06 20:40:23 +01:00
using blobstore : : onblocks : : datanodestore : : DataNodeLayout ;
2017-09-17 03:07:27 +02:00
using blockstore : : BlockId ;
2015-04-25 02:55:34 +02:00
using cpputils : : Data ;
2015-04-25 16:44:00 +02:00
using cpputils : : DataFixture ;
2015-03-06 16:39:40 +01:00
2021-01-13 04:48:27 +01:00
namespace {
2015-03-06 16:39:40 +01:00
class BlobReadWriteTest : public BlobStoreTest {
public :
2015-03-06 16:54:49 +01:00
static constexpr uint32_t LARGE_SIZE = 10 * 1024 * 1024 ;
2015-03-06 20:40:23 +01:00
static constexpr DataNodeLayout LAYOUT = DataNodeLayout ( BLOCKSIZE_BYTES ) ;
2015-03-06 16:54:49 +01:00
BlobReadWriteTest ( )
2015-04-25 16:44:00 +02:00
: randomData ( DataFixture : : generate ( LARGE_SIZE ) ) ,
2015-03-06 16:54:49 +01:00
blob ( blobStore - > create ( ) ) {
}
Data readBlob ( const Blob & blob ) {
Data data ( blob . size ( ) ) ;
blob . read ( data . data ( ) , 0 , data . size ( ) ) ;
return data ;
}
2015-03-06 20:40:23 +01:00
template < class DataClass >
void EXPECT_DATA_READS_AS ( const DataClass & expected , const Blob & actual , uint64_t offset , uint64_t size ) {
Data read ( size ) ;
actual . read ( read . data ( ) , offset , size ) ;
EXPECT_EQ ( 0 , std : : memcmp ( expected . data ( ) , read . data ( ) , size ) ) ;
}
2015-04-25 16:44:00 +02:00
Data randomData ;
2015-06-18 12:45:37 +02:00
unique_ref < Blob > blob ;
2015-03-06 16:39:40 +01:00
} ;
2015-03-06 16:54:49 +01:00
constexpr uint32_t BlobReadWriteTest : : LARGE_SIZE ;
2015-03-06 20:40:23 +01:00
constexpr DataNodeLayout BlobReadWriteTest : : LAYOUT ;
2015-03-06 16:54:49 +01:00
TEST_F ( BlobReadWriteTest , WritingImmediatelyFlushes_SmallSize ) {
blob - > resize ( 5 ) ;
blob - > write ( randomData . data ( ) , 0 , 5 ) ;
2017-09-17 03:07:27 +02:00
auto loaded = loadBlob ( blob - > blockId ( ) ) ;
2015-03-06 20:40:23 +01:00
EXPECT_DATA_READS_AS ( randomData , * loaded , 0 , 5 ) ;
2015-03-06 16:54:49 +01:00
}
TEST_F ( BlobReadWriteTest , WritingImmediatelyFlushes_LargeSize ) {
blob - > resize ( LARGE_SIZE ) ;
blob - > write ( randomData . data ( ) , 0 , LARGE_SIZE ) ;
2017-09-17 03:07:27 +02:00
auto loaded = loadBlob ( blob - > blockId ( ) ) ;
2015-03-06 20:40:23 +01:00
EXPECT_DATA_READS_AS ( randomData , * loaded , 0 , LARGE_SIZE ) ;
}
2015-09-30 15:33:09 +02:00
// Regression test for a strange bug we had
TEST_F ( BlobReadWriteTest , WritingCloseTo16ByteLimitDoesntDestroySize ) {
blob - > resize ( 1 ) ;
blob - > write ( randomData . data ( ) , 32776 , 4 ) ;
2016-02-11 15:19:58 +01:00
EXPECT_EQ ( 32780u , blob - > size ( ) ) ;
2015-09-30 15:33:09 +02:00
}
2019-01-13 08:21:12 +01:00
TEST_F ( BlobReadWriteTest , givenEmptyBlob_whenTryReadInFirstLeaf_thenFails ) {
Data data ( 5 ) ;
2023-07-08 23:48:59 +02:00
const size_t read = blob - > tryRead ( data . data ( ) , 3 , 5 ) ;
2019-01-13 08:21:12 +01:00
EXPECT_EQ ( 0 , read ) ;
}
TEST_F ( BlobReadWriteTest , givenEmptyBlob_whenTryReadInLaterLeaf_thenFails ) {
Data data ( 5 ) ;
2023-07-08 23:48:59 +02:00
const size_t read = blob - > tryRead ( data . data ( ) , 2 * LAYOUT . maxBytesPerLeaf ( ) , 5 ) ;
2019-01-13 08:21:12 +01:00
EXPECT_EQ ( 0 , read ) ;
}
TEST_F ( BlobReadWriteTest , givenEmptyBlob_whenReadInFirstLeaf_thenFails ) {
Data data ( 5 ) ;
EXPECT_ANY_THROW (
blob - > read ( data . data ( ) , 3 , 5 )
) ;
}
TEST_F ( BlobReadWriteTest , givenEmptyBlob_whenReadInLaterLeaf_thenFails ) {
Data data ( 5 ) ;
EXPECT_ANY_THROW (
blob - > read ( data . data ( ) , 2 * LAYOUT . maxBytesPerLeaf ( ) , 5 )
) ;
}
TEST_F ( BlobReadWriteTest , givenEmptyBlob_whenReadAll_thenReturnsZeroSizedData ) {
2023-07-08 23:48:59 +02:00
const Data data = blob - > readAll ( ) ;
2019-01-13 08:21:12 +01:00
EXPECT_EQ ( 0 , data . size ( ) ) ;
}
TEST_F ( BlobReadWriteTest , givenEmptyBlob_whenWrite_thenGrows ) {
Data data ( 5 ) ;
blob - > write ( data . data ( ) , 4 , 5 ) ;
EXPECT_EQ ( 9 , blob - > size ( ) ) ;
}
TEST_F ( BlobReadWriteTest , givenEmptyBlob_whenWriteZeroBytes_thenDoesntGrow ) {
Data data ( 5 ) ;
blob - > write ( data . data ( ) , 4 , 0 ) ;
EXPECT_EQ ( 0 , blob - > size ( ) ) ; ;
}
TEST_F ( BlobReadWriteTest , givenBlobResizedToZero_whenTryReadInFirstLeaf_thenFails ) {
Data data ( 5 ) ;
2023-07-08 23:48:59 +02:00
const size_t read = blob - > tryRead ( data . data ( ) , 3 , 5 ) ;
2019-01-13 08:21:12 +01:00
EXPECT_EQ ( 0 , read ) ;
}
TEST_F ( BlobReadWriteTest , givenBlobResizedToZero_whenTryReadInLaterLeaf_thenFails ) {
Data data ( 5 ) ;
2023-07-08 23:48:59 +02:00
const size_t read = blob - > tryRead ( data . data ( ) , 2 * LAYOUT . maxBytesPerLeaf ( ) , 5 ) ;
2019-01-13 08:21:12 +01:00
EXPECT_EQ ( 0 , read ) ;
}
TEST_F ( BlobReadWriteTest , givenBlobResizedToZero_whenReadInFirstLeaf_thenFails ) {
Data data ( 5 ) ;
EXPECT_ANY_THROW (
blob - > read ( data . data ( ) , 3 , 5 )
) ;
}
TEST_F ( BlobReadWriteTest , givenBlobResizedToZero_whenReadInLaterLeaf_thenFails ) {
Data data ( 5 ) ;
EXPECT_ANY_THROW (
blob - > read ( data . data ( ) , 2 * LAYOUT . maxBytesPerLeaf ( ) , 5 )
) ;
}
TEST_F ( BlobReadWriteTest , givenBlobResizedToZero_whenReadAll_thenReturnsZeroSizedData ) {
2023-07-08 23:48:59 +02:00
const Data data = blob - > readAll ( ) ;
2019-01-13 08:21:12 +01:00
EXPECT_EQ ( 0 , data . size ( ) ) ;
}
TEST_F ( BlobReadWriteTest , givenBlobResizedToZero_whenWrite_thenGrows ) {
Data data ( 5 ) ;
blob - > write ( data . data ( ) , 4 , 5 ) ;
EXPECT_EQ ( 9 , blob - > size ( ) ) ;
}
TEST_F ( BlobReadWriteTest , givenBlobResizedToZero_whenWriteZeroBytes_thenDoesntGrow ) {
Data data ( 5 ) ;
blob - > write ( data . data ( ) , 4 , 0 ) ;
EXPECT_EQ ( 0 , blob - > size ( ) ) ;
}
2015-03-06 20:40:23 +01:00
struct DataRange {
2018-09-15 23:32:58 +02:00
uint64_t blobsize ;
uint64_t offset ;
uint64_t count ;
2015-03-06 20:40:23 +01:00
} ;
class BlobReadWriteDataTest : public BlobReadWriteTest , public WithParamInterface < DataRange > {
public :
2015-04-25 16:44:00 +02:00
Data foregroundData ;
Data backgroundData ;
2015-03-06 20:40:23 +01:00
BlobReadWriteDataTest ( )
2015-04-25 16:44:00 +02:00
: foregroundData ( DataFixture : : generate ( GetParam ( ) . count , 0 ) ) ,
backgroundData ( DataFixture : : generate ( GetParam ( ) . blobsize , 1 ) ) {
}
2015-03-06 20:40:23 +01:00
template < class DataClass >
2018-09-15 23:32:58 +02:00
void EXPECT_DATA_READS_AS_OUTSIDE_OF ( const DataClass & expected , const Blob & blob , uint64_t start , uint64_t count ) {
2015-03-06 20:40:23 +01:00
Data begin ( start ) ;
Data end ( GetParam ( ) . blobsize - count - start ) ;
std : : memcpy ( begin . data ( ) , expected . data ( ) , start ) ;
2017-12-03 20:01:41 +01:00
std : : memcpy ( end . data ( ) , expected . dataOffset ( start + count ) , end . size ( ) ) ;
2015-03-06 20:40:23 +01:00
EXPECT_DATA_READS_AS ( begin , blob , 0 , start ) ;
EXPECT_DATA_READS_AS ( end , blob , start + count , end . size ( ) ) ;
}
2018-09-15 23:32:58 +02:00
void EXPECT_DATA_IS_ZEROES_OUTSIDE_OF ( const Blob & blob , uint64_t start , uint64_t count ) {
2015-03-06 20:40:23 +01:00
Data ZEROES ( GetParam ( ) . blobsize ) ;
ZEROES . FillWithZeroes ( ) ;
EXPECT_DATA_READS_AS_OUTSIDE_OF ( ZEROES , blob , start , count ) ;
}
} ;
2019-10-13 14:16:06 +02:00
INSTANTIATE_TEST_SUITE_P ( BlobReadWriteDataTest , BlobReadWriteDataTest , Values (
2015-03-06 20:40:23 +01:00
//Blob with only one leaf
2015-06-21 14:40:52 +02:00
DataRange { BlobReadWriteDataTest : : LAYOUT . maxBytesPerLeaf ( ) , 0 , BlobReadWriteDataTest : : LAYOUT . maxBytesPerLeaf ( ) } , // full size leaf, access beginning to end
DataRange { BlobReadWriteDataTest : : LAYOUT . maxBytesPerLeaf ( ) , 100 , BlobReadWriteDataTest : : LAYOUT . maxBytesPerLeaf ( ) - 200 } , // full size leaf, access middle to middle
DataRange { BlobReadWriteDataTest : : LAYOUT . maxBytesPerLeaf ( ) , 0 , BlobReadWriteDataTest : : LAYOUT . maxBytesPerLeaf ( ) - 100 } , // full size leaf, access beginning to middle
DataRange { BlobReadWriteDataTest : : LAYOUT . maxBytesPerLeaf ( ) , 100 , BlobReadWriteDataTest : : LAYOUT . maxBytesPerLeaf ( ) - 100 } , // full size leaf, access middle to end
DataRange { BlobReadWriteDataTest : : LAYOUT . maxBytesPerLeaf ( ) - 100 , 0 , BlobReadWriteDataTest : : LAYOUT . maxBytesPerLeaf ( ) - 100 } , // non-full size leaf, access beginning to end
DataRange { BlobReadWriteDataTest : : LAYOUT . maxBytesPerLeaf ( ) - 100 , 100 , BlobReadWriteDataTest : : LAYOUT . maxBytesPerLeaf ( ) - 300 } , // non-full size leaf, access middle to middle
DataRange { BlobReadWriteDataTest : : LAYOUT . maxBytesPerLeaf ( ) - 100 , 0 , BlobReadWriteDataTest : : LAYOUT . maxBytesPerLeaf ( ) - 200 } , // non-full size leaf, access beginning to middle
DataRange { BlobReadWriteDataTest : : LAYOUT . maxBytesPerLeaf ( ) - 100 , 100 , BlobReadWriteDataTest : : LAYOUT . maxBytesPerLeaf ( ) - 200 } , // non-full size leaf, access middle to end
2015-03-06 20:40:23 +01:00
//Larger blob
2015-06-21 14:40:52 +02:00
DataRange { BlobReadWriteDataTest : : LARGE_SIZE , 0 , BlobReadWriteDataTest : : LARGE_SIZE } , // access beginning to end
DataRange { BlobReadWriteDataTest : : LARGE_SIZE , 100 , BlobReadWriteDataTest : : LARGE_SIZE - 200 } , // access middle first leaf to middle last leaf
DataRange { BlobReadWriteDataTest : : LARGE_SIZE , 0 , BlobReadWriteDataTest : : LARGE_SIZE - 100 } , // access beginning to middle last leaf
DataRange { BlobReadWriteDataTest : : LARGE_SIZE , 100 , BlobReadWriteDataTest : : LARGE_SIZE - 100 } , // access middle first leaf to end
DataRange { BlobReadWriteDataTest : : LARGE_SIZE , BlobReadWriteDataTest : : LARGE_SIZE * 1 / 3 , BlobReadWriteDataTest : : LARGE_SIZE * 1 / 3 } , // access middle to middle
DataRange { BlobReadWriteDataTest : : LARGE_SIZE , 0 , BlobReadWriteDataTest : : LARGE_SIZE * 2 / 3 } , // access beginning to middle
DataRange { BlobReadWriteDataTest : : LARGE_SIZE , BlobReadWriteDataTest : : LARGE_SIZE * 1 / 3 , BlobReadWriteDataTest : : LARGE_SIZE * 2 / 3 } // access middle to end
2015-03-06 20:40:23 +01:00
) ) ;
2015-04-10 21:52:30 +02:00
TEST_P ( BlobReadWriteDataTest , WritingDoesntChangeSize ) {
blob - > resize ( GetParam ( ) . blobsize ) ;
blob - > write ( this - > foregroundData . data ( ) , GetParam ( ) . offset , GetParam ( ) . count ) ;
EXPECT_EQ ( GetParam ( ) . blobsize , blob - > size ( ) ) ;
}
2015-03-06 20:40:23 +01:00
TEST_P ( BlobReadWriteDataTest , WriteAndReadImmediately ) {
blob - > resize ( GetParam ( ) . blobsize ) ;
blob - > write ( this - > foregroundData . data ( ) , GetParam ( ) . offset , GetParam ( ) . count ) ;
EXPECT_DATA_READS_AS ( this - > foregroundData , * blob , GetParam ( ) . offset , GetParam ( ) . count ) ;
EXPECT_DATA_IS_ZEROES_OUTSIDE_OF ( * blob , GetParam ( ) . offset , GetParam ( ) . count ) ;
}
TEST_P ( BlobReadWriteDataTest , WriteAndReadAfterLoading ) {
blob - > resize ( GetParam ( ) . blobsize ) ;
blob - > write ( this - > foregroundData . data ( ) , GetParam ( ) . offset , GetParam ( ) . count ) ;
2017-09-17 03:07:27 +02:00
auto loaded = loadBlob ( blob - > blockId ( ) ) ;
2015-03-06 20:40:23 +01:00
EXPECT_DATA_READS_AS ( this - > foregroundData , * loaded , GetParam ( ) . offset , GetParam ( ) . count ) ;
EXPECT_DATA_IS_ZEROES_OUTSIDE_OF ( * loaded , GetParam ( ) . offset , GetParam ( ) . count ) ;
}
TEST_P ( BlobReadWriteDataTest , OverwriteAndRead ) {
blob - > resize ( GetParam ( ) . blobsize ) ;
blob - > write ( this - > backgroundData . data ( ) , 0 , GetParam ( ) . blobsize ) ;
blob - > write ( this - > foregroundData . data ( ) , GetParam ( ) . offset , GetParam ( ) . count ) ;
EXPECT_DATA_READS_AS ( this - > foregroundData , * blob , GetParam ( ) . offset , GetParam ( ) . count ) ;
EXPECT_DATA_READS_AS_OUTSIDE_OF ( this - > backgroundData , * blob , GetParam ( ) . offset , GetParam ( ) . count ) ;
2015-03-06 16:54:49 +01:00
}
2015-03-06 20:40:23 +01:00
TEST_P ( BlobReadWriteDataTest , WriteWholeAndReadPart ) {
blob - > resize ( GetParam ( ) . blobsize ) ;
blob - > write ( this - > backgroundData . data ( ) , 0 , GetParam ( ) . blobsize ) ;
Data read ( GetParam ( ) . count ) ;
blob - > read ( read . data ( ) , GetParam ( ) . offset , GetParam ( ) . count ) ;
2017-12-03 20:01:41 +01:00
EXPECT_EQ ( 0 , std : : memcmp ( read . data ( ) , this - > backgroundData . dataOffset ( GetParam ( ) . offset ) , GetParam ( ) . count ) ) ;
2015-03-06 20:40:23 +01:00
}
TEST_P ( BlobReadWriteDataTest , WritePartAndReadWhole ) {
blob - > resize ( GetParam ( ) . blobsize ) ;
blob - > write ( this - > backgroundData . data ( ) , 0 , GetParam ( ) . blobsize ) ;
blob - > write ( this - > foregroundData . data ( ) , GetParam ( ) . offset , GetParam ( ) . count ) ;
Data read = readBlob ( * blob ) ;
EXPECT_EQ ( 0 , std : : memcmp ( read . data ( ) , this - > backgroundData . data ( ) , GetParam ( ) . offset ) ) ;
2017-12-03 20:01:41 +01:00
EXPECT_EQ ( 0 , std : : memcmp ( read . dataOffset ( GetParam ( ) . offset ) , this - > foregroundData . data ( ) , GetParam ( ) . count ) ) ;
EXPECT_EQ ( 0 , std : : memcmp ( read . dataOffset ( GetParam ( ) . offset + GetParam ( ) . count ) , this - > backgroundData . dataOffset ( GetParam ( ) . offset + GetParam ( ) . count ) , GetParam ( ) . blobsize - GetParam ( ) . count - GetParam ( ) . offset ) ) ;
2015-03-06 20:40:23 +01:00
}
2021-01-13 04:48:27 +01:00
}