Remove tests

This commit is contained in:
Matéo Duparc 2022-06-09 15:26:04 +02:00
parent 31c831f984
commit 3df8023b21
571 changed files with 0 additions and 136805 deletions

View File

@ -1,16 +0,0 @@
if (BUILD_TESTING)
include_directories(../src)
add_subdirectory(my-gtest-main)
add_subdirectory(gitversion)
add_subdirectory(cpp-utils)
if (NOT MSVC)
# TODO Make this build on Windows
add_subdirectory(fspp)
endif()
add_subdirectory(parallelaccessstore)
add_subdirectory(blockstore)
add_subdirectory(blobstore)
add_subdirectory(cryfs)
add_subdirectory(cryfs-cli)
endif(BUILD_TESTING)

View File

@ -1,34 +0,0 @@
project (blobstore-test)
set(SOURCES
implementations/onblocks/utils/MaxZeroSubtractionTest.cpp
implementations/onblocks/utils/CeilDivisionTest.cpp
implementations/onblocks/utils/IntPowTest.cpp
implementations/onblocks/utils/CeilLogTest.cpp
implementations/onblocks/testutils/BlobStoreTest.cpp
implementations/onblocks/BlobStoreTest.cpp
implementations/onblocks/datanodestore/DataLeafNodeTest.cpp
implementations/onblocks/datanodestore/DataInnerNodeTest.cpp
implementations/onblocks/datanodestore/DataNodeViewTest.cpp
implementations/onblocks/datanodestore/DataNodeStoreTest.cpp
implementations/onblocks/datatreestore/testutils/DataTreeTest.cpp
implementations/onblocks/datatreestore/impl/GetLowestRightBorderNodeWithMoreThanOneChildOrNullTest.cpp
implementations/onblocks/datatreestore/impl/GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNullTest.cpp
implementations/onblocks/datatreestore/DataTreeTest_Performance.cpp
implementations/onblocks/datatreestore/DataTreeTest_ResizeByTraversing.cpp
implementations/onblocks/datatreestore/DataTreeTest_NumStoredBytes.cpp
implementations/onblocks/datatreestore/DataTreeTest_ResizeNumBytes.cpp
implementations/onblocks/datatreestore/DataTreeStoreTest.cpp
implementations/onblocks/datatreestore/LeafTraverserTest.cpp
implementations/onblocks/BlobSizeTest.cpp
implementations/onblocks/BlobReadWriteTest.cpp
implementations/onblocks/BigBlobsTest.cpp
)
add_executable(${PROJECT_NAME} ${SOURCES})
target_link_libraries(${PROJECT_NAME} my-gtest-main googletest blobstore)
add_test(${PROJECT_NAME} ${PROJECT_NAME})
target_enable_style_warnings(${PROJECT_NAME})
target_activate_cpp14(${PROJECT_NAME})

View File

@ -1,136 +0,0 @@
#include <gtest/gtest.h>
#include <blockstore/implementations/compressing/CompressingBlockStore.h>
#include <blockstore/implementations/compressing/compressors/RunLengthEncoding.h>
#include <blockstore/implementations/inmemory/InMemoryBlockStore2.h>
#include <blockstore/implementations/low2highlevel/LowToHighLevelBlockStore.h>
#include <cpp-utils/data/DataFixture.h>
#include <cpp-utils/data/Data.h>
#include "blobstore/implementations/onblocks/BlobStoreOnBlocks.h"
#include "blobstore/implementations/onblocks/BlobOnBlocks.h"
using namespace blobstore;
using namespace blobstore::onblocks;
using cpputils::unique_ref;
using cpputils::make_unique_ref;
using cpputils::DataFixture;
using cpputils::Data;
using blockstore::inmemory::InMemoryBlockStore2;
using blockstore::lowtohighlevel::LowToHighLevelBlockStore;
using blockstore::compressing::CompressingBlockStore;
using blockstore::compressing::RunLengthEncoding;
// Test cases, ensuring that big blobs (>4G) work (i.e. testing that we don't use any 32bit variables for blob size, etc.)
class BigBlobsTest : public ::testing::Test {
public:
static constexpr size_t BLOCKSIZE = 32 * 1024;
static constexpr uint64_t SMALL_BLOB_SIZE = UINT64_C(1024)*1024*1024*3.95; // 3.95 GB (<4GB)
static constexpr uint64_t LARGE_BLOB_SIZE = UINT64_C(1024)*1024*1024*4.05; // 4.05 GB (>4GB)
static constexpr uint64_t max_uint_32 = std::numeric_limits<uint32_t>::max();
static_assert(SMALL_BLOB_SIZE < max_uint_32, "LARGE_BLOB_SIZE should need 64bit or the test case is mute");
static_assert(LARGE_BLOB_SIZE > max_uint_32, "LARGE_BLOB_SIZE should need 64bit or the test case is mute");
unique_ref<BlobStore> blobStore = make_unique_ref<BlobStoreOnBlocks>(make_unique_ref<CompressingBlockStore<RunLengthEncoding>>(make_unique_ref<LowToHighLevelBlockStore>(make_unique_ref<InMemoryBlockStore2>())), BLOCKSIZE);
unique_ref<Blob> blob = blobStore->create();
};
constexpr size_t BigBlobsTest::BLOCKSIZE;
constexpr uint64_t BigBlobsTest::SMALL_BLOB_SIZE;
constexpr uint64_t BigBlobsTest::LARGE_BLOB_SIZE;
TEST_F(BigBlobsTest, Resize) {
//These operations are in one test case and not in many small ones, because it takes quite long to create a >4GB blob.
//Resize to >4GB
blob->resize(LARGE_BLOB_SIZE);
EXPECT_EQ(LARGE_BLOB_SIZE, blob->size());
//Grow while >4GB
blob->resize(LARGE_BLOB_SIZE + 1024);
EXPECT_EQ(LARGE_BLOB_SIZE + 1024, blob->size());
//Shrink while >4GB
blob->resize(LARGE_BLOB_SIZE);
EXPECT_EQ(LARGE_BLOB_SIZE, blob->size());
//Shrink to <4GB
blob->resize(SMALL_BLOB_SIZE);
EXPECT_EQ(SMALL_BLOB_SIZE, blob->size());
//Grow to >4GB
blob->resize(LARGE_BLOB_SIZE);
EXPECT_EQ(LARGE_BLOB_SIZE, blob->size());
//Flush >4GB blob
blob->flush();
//Destruct >4GB blob
auto blockId = blob->blockId();
cpputils::destruct(std::move(blob));
//Load >4GB blob
blob = blobStore->load(blockId).value();
//Remove >4GB blob
blobStore->remove(std::move(blob));
}
TEST_F(BigBlobsTest, GrowByWriting_Crossing4GBBorder) {
Data fixture = DataFixture::generate(2*(LARGE_BLOB_SIZE-SMALL_BLOB_SIZE));
blob->write(fixture.data(), SMALL_BLOB_SIZE, fixture.size());
EXPECT_EQ(LARGE_BLOB_SIZE+(LARGE_BLOB_SIZE-SMALL_BLOB_SIZE), blob->size());
Data loaded(fixture.size());
blob->read(loaded.data(), SMALL_BLOB_SIZE, loaded.size());
EXPECT_EQ(0, std::memcmp(loaded.data(), fixture.data(), loaded.size()));
}
TEST_F(BigBlobsTest, GrowByWriting_Outside4GBBorder_StartingSizeZero) {
Data fixture = DataFixture::generate(1024);
blob->write(fixture.data(), LARGE_BLOB_SIZE, fixture.size());
EXPECT_EQ(LARGE_BLOB_SIZE+1024, blob->size());
Data loaded(fixture.size());
blob->read(loaded.data(), LARGE_BLOB_SIZE, loaded.size());
EXPECT_EQ(0, std::memcmp(loaded.data(), fixture.data(), loaded.size()));
}
TEST_F(BigBlobsTest, GrowByWriting_Outside4GBBorder_StartingSizeOutside4GBBorder) {
blob->resize(LARGE_BLOB_SIZE);
Data fixture = DataFixture::generate(1024);
blob->write(fixture.data(), LARGE_BLOB_SIZE+1024, fixture.size());
EXPECT_EQ(LARGE_BLOB_SIZE+2048, blob->size());
Data loaded(fixture.size());
blob->read(loaded.data(), LARGE_BLOB_SIZE+1024, loaded.size());
EXPECT_EQ(0, std::memcmp(loaded.data(), fixture.data(), loaded.size()));
}
TEST_F(BigBlobsTest, ReadWriteAfterGrown_Crossing4GBBorder) {
blob->resize(LARGE_BLOB_SIZE+(LARGE_BLOB_SIZE-SMALL_BLOB_SIZE)+1024);
Data fixture = DataFixture::generate(2*(LARGE_BLOB_SIZE-SMALL_BLOB_SIZE));
blob->write(fixture.data(), SMALL_BLOB_SIZE, fixture.size());
EXPECT_EQ(LARGE_BLOB_SIZE+(LARGE_BLOB_SIZE-SMALL_BLOB_SIZE)+1024, blob->size());
Data loaded(fixture.size());
blob->read(loaded.data(), SMALL_BLOB_SIZE, loaded.size());
EXPECT_EQ(0, std::memcmp(loaded.data(), fixture.data(), loaded.size()));
}
TEST_F(BigBlobsTest, ReadWriteAfterGrown_Outside4GBBorder) {
blob->resize(LARGE_BLOB_SIZE+2048);
Data fixture = DataFixture::generate(1024);
blob->write(fixture.data(), LARGE_BLOB_SIZE, fixture.size());
EXPECT_EQ(LARGE_BLOB_SIZE+2048, blob->size());
Data loaded(fixture.size());
blob->read(loaded.data(), LARGE_BLOB_SIZE, loaded.size());
EXPECT_EQ(0, std::memcmp(loaded.data(), fixture.data(), loaded.size()));
}
//TODO Test Blob::readAll (only on 64bit systems)

View File

@ -1,256 +0,0 @@
#include "testutils/BlobStoreTest.h"
#include <cpp-utils/data/Data.h>
#include <cpp-utils/data/DataFixture.h>
#include "blobstore/implementations/onblocks/datanodestore/DataNodeView.h"
using cpputils::unique_ref;
using ::testing::WithParamInterface;
using ::testing::Values;
using namespace blobstore;
using blobstore::onblocks::datanodestore::DataNodeLayout;
using blockstore::BlockId;
using cpputils::Data;
using cpputils::DataFixture;
namespace {
class BlobReadWriteTest: public BlobStoreTest {
public:
static constexpr uint32_t LARGE_SIZE = 10 * 1024 * 1024;
static constexpr DataNodeLayout LAYOUT = DataNodeLayout(BLOCKSIZE_BYTES);
BlobReadWriteTest()
:randomData(DataFixture::generate(LARGE_SIZE)),
blob(blobStore->create()) {
}
Data readBlob(const Blob &blob) {
Data data(blob.size());
blob.read(data.data(), 0, data.size());
return data;
}
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));
}
Data randomData;
unique_ref<Blob> blob;
};
constexpr uint32_t BlobReadWriteTest::LARGE_SIZE;
constexpr DataNodeLayout BlobReadWriteTest::LAYOUT;
TEST_F(BlobReadWriteTest, WritingImmediatelyFlushes_SmallSize) {
blob->resize(5);
blob->write(randomData.data(), 0, 5);
auto loaded = loadBlob(blob->blockId());
EXPECT_DATA_READS_AS(randomData, *loaded, 0, 5);
}
TEST_F(BlobReadWriteTest, WritingImmediatelyFlushes_LargeSize) {
blob->resize(LARGE_SIZE);
blob->write(randomData.data(), 0, LARGE_SIZE);
auto loaded = loadBlob(blob->blockId());
EXPECT_DATA_READS_AS(randomData, *loaded, 0, LARGE_SIZE);
}
// Regression test for a strange bug we had
TEST_F(BlobReadWriteTest, WritingCloseTo16ByteLimitDoesntDestroySize) {
blob->resize(1);
blob->write(randomData.data(), 32776, 4);
EXPECT_EQ(32780u, blob->size());
}
TEST_F(BlobReadWriteTest, givenEmptyBlob_whenTryReadInFirstLeaf_thenFails) {
Data data(5);
size_t read = blob->tryRead(data.data(), 3, 5);
EXPECT_EQ(0, read);
}
TEST_F(BlobReadWriteTest, givenEmptyBlob_whenTryReadInLaterLeaf_thenFails) {
Data data(5);
size_t read = blob->tryRead(data.data(), 2*LAYOUT.maxBytesPerLeaf(), 5);
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) {
Data data = blob->readAll();
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);
size_t read = blob->tryRead(data.data(), 3, 5);
EXPECT_EQ(0, read);
}
TEST_F(BlobReadWriteTest, givenBlobResizedToZero_whenTryReadInLaterLeaf_thenFails) {
Data data(5);
size_t read = blob->tryRead(data.data(), 2*LAYOUT.maxBytesPerLeaf(), 5);
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) {
Data data = blob->readAll();
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());
}
struct DataRange {
uint64_t blobsize;
uint64_t offset;
uint64_t count;
};
class BlobReadWriteDataTest: public BlobReadWriteTest, public WithParamInterface<DataRange> {
public:
Data foregroundData;
Data backgroundData;
BlobReadWriteDataTest()
: foregroundData(DataFixture::generate(GetParam().count, 0)),
backgroundData(DataFixture::generate(GetParam().blobsize, 1)) {
}
template<class DataClass>
void EXPECT_DATA_READS_AS_OUTSIDE_OF(const DataClass &expected, const Blob &blob, uint64_t start, uint64_t count) {
Data begin(start);
Data end(GetParam().blobsize - count - start);
std::memcpy(begin.data(), expected.data(), start);
std::memcpy(end.data(), expected.dataOffset(start+count), end.size());
EXPECT_DATA_READS_AS(begin, blob, 0, start);
EXPECT_DATA_READS_AS(end, blob, start + count, end.size());
}
void EXPECT_DATA_IS_ZEROES_OUTSIDE_OF(const Blob &blob, uint64_t start, uint64_t count) {
Data ZEROES(GetParam().blobsize);
ZEROES.FillWithZeroes();
EXPECT_DATA_READS_AS_OUTSIDE_OF(ZEROES, blob, start, count);
}
};
INSTANTIATE_TEST_SUITE_P(BlobReadWriteDataTest, BlobReadWriteDataTest, Values(
//Blob with only one leaf
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
//Larger blob
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
));
TEST_P(BlobReadWriteDataTest, WritingDoesntChangeSize) {
blob->resize(GetParam().blobsize);
blob->write(this->foregroundData.data(), GetParam().offset, GetParam().count);
EXPECT_EQ(GetParam().blobsize, blob->size());
}
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);
auto loaded = loadBlob(blob->blockId());
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);
}
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);
EXPECT_EQ(0, std::memcmp(read.data(), this->backgroundData.dataOffset(GetParam().offset), GetParam().count));
}
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));
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));
}
}

View File

@ -1,172 +0,0 @@
#include "testutils/BlobStoreTest.h"
#include <cpp-utils/data/Data.h>
#include <cpp-utils/data/DataFixture.h>
using namespace blobstore;
using blockstore::BlockId;
using cpputils::Data;
using cpputils::DataFixture;
using cpputils::unique_ref;
class BlobSizeTest: public BlobStoreTest {
public:
BlobSizeTest(): blob(blobStore->create()) {}
static constexpr uint32_t MEDIUM_SIZE = 5 * 1024 * 1024;
static constexpr uint32_t LARGE_SIZE = 10 * 1024 * 1024;
unique_ref<Blob> blob;
};
constexpr uint32_t BlobSizeTest::MEDIUM_SIZE;
constexpr uint32_t BlobSizeTest::LARGE_SIZE;
TEST_F(BlobSizeTest, CreatedBlobIsEmpty) {
EXPECT_EQ(0u, blob->size());
}
TEST_F(BlobSizeTest, Growing_1Byte) {
blob->resize(1);
EXPECT_EQ(1u, blob->size());
}
TEST_F(BlobSizeTest, Growing_Large) {
blob->resize(LARGE_SIZE);
EXPECT_EQ(LARGE_SIZE, blob->size());
}
TEST_F(BlobSizeTest, Shrinking_Empty) {
blob->resize(LARGE_SIZE);
blob->resize(0);
EXPECT_EQ(0u, blob->size());
}
TEST_F(BlobSizeTest, Shrinking_1Byte) {
blob->resize(LARGE_SIZE);
blob->resize(1);
EXPECT_EQ(1u, blob->size());
}
TEST_F(BlobSizeTest, ResizingToItself_Empty) {
blob->resize(0);
EXPECT_EQ(0u, blob->size());
}
TEST_F(BlobSizeTest, ResizingToItself_1Byte) {
blob->resize(1);
blob->resize(1);
EXPECT_EQ(1u, blob->size());
}
TEST_F(BlobSizeTest, ResizingToItself_Large) {
blob->resize(LARGE_SIZE);
blob->resize(LARGE_SIZE);
EXPECT_EQ(LARGE_SIZE, blob->size());
}
TEST_F(BlobSizeTest, EmptyBlobStaysEmptyWhenLoading) {
BlockId blockId = blob->blockId();
reset(std::move(blob));
auto loaded = loadBlob(blockId);
EXPECT_EQ(0u, loaded->size());
}
TEST_F(BlobSizeTest, BlobSizeStaysIntactWhenLoading) {
blob->resize(LARGE_SIZE);
BlockId blockId = blob->blockId();
reset(std::move(blob));
auto loaded = loadBlob(blockId);
EXPECT_EQ(LARGE_SIZE, loaded->size());
}
TEST_F(BlobSizeTest, WritingAtEndOfBlobGrowsBlob_Empty) {
int value = 0;
blob->write(&value, 0, 4);
EXPECT_EQ(4u, blob->size());
}
TEST_F(BlobSizeTest, WritingAfterEndOfBlobGrowsBlob_Empty) {
int value = 0;
blob->write(&value, 2, 4);
EXPECT_EQ(6u, blob->size());
}
TEST_F(BlobSizeTest, WritingOverEndOfBlobGrowsBlob_NonEmpty) {
blob->resize(1);
int value = 0;
blob->write(&value, 0, 4);
EXPECT_EQ(4u, blob->size());
}
TEST_F(BlobSizeTest, WritingAtEndOfBlobGrowsBlob_NonEmpty) {
blob->resize(1);
int value = 0;
blob->write(&value, 1, 4);
EXPECT_EQ(5u, blob->size());
}
TEST_F(BlobSizeTest, WritingAfterEndOfBlobGrowsBlob_NonEmpty) {
blob->resize(1);
int value = 0;
blob->write(&value, 2, 4);
EXPECT_EQ(6u, blob->size());
}
TEST_F(BlobSizeTest, ChangingSizeImmediatelyFlushes) {
blob->resize(LARGE_SIZE);
auto loaded = loadBlob(blob->blockId());
EXPECT_EQ(LARGE_SIZE, loaded->size());
}
class BlobSizeDataTest: public BlobSizeTest {
public:
BlobSizeDataTest()
:ZEROES(LARGE_SIZE),
randomData(DataFixture::generate(LARGE_SIZE)) {
ZEROES.FillWithZeroes();
}
Data readBlob(const Blob &blob) {
Data data(blob.size());
blob.read(data.data(), 0, data.size());
return data;
}
Data ZEROES;
Data randomData;
};
TEST_F(BlobSizeDataTest, BlobIsZeroedOutAfterGrowing) {
//uint32_t LARGE_SIZE = 2*1024*1024;
blob->resize(LARGE_SIZE);
EXPECT_EQ(0, std::memcmp(readBlob(*blob).data(), ZEROES.data(), LARGE_SIZE));
}
TEST_F(BlobSizeDataTest, BlobIsZeroedOutAfterGrowingAndLoading) {
blob->resize(LARGE_SIZE);
auto loaded = loadBlob(blob->blockId());
EXPECT_EQ(0, std::memcmp(readBlob(*loaded).data(), ZEROES.data(), LARGE_SIZE));
}
TEST_F(BlobSizeDataTest, DataStaysIntactWhenGrowing) {
blob->resize(MEDIUM_SIZE);
blob->write(randomData.data(), 0, MEDIUM_SIZE);
blob->resize(LARGE_SIZE);
EXPECT_EQ(0, std::memcmp(readBlob(*blob).data(), randomData.data(), MEDIUM_SIZE));
EXPECT_EQ(0, std::memcmp(readBlob(*blob).dataOffset(MEDIUM_SIZE), ZEROES.data(), LARGE_SIZE-MEDIUM_SIZE));
}
TEST_F(BlobSizeDataTest, DataStaysIntactWhenShrinking) {
blob->resize(LARGE_SIZE);
blob->write(randomData.data(), 0, LARGE_SIZE);
blob->resize(MEDIUM_SIZE);
EXPECT_EQ(0, std::memcmp(readBlob(*blob).data(), randomData.data(), MEDIUM_SIZE));
}
TEST_F(BlobSizeDataTest, ChangedAreaIsZeroedOutWhenShrinkingAndRegrowing) {
blob->resize(LARGE_SIZE);
blob->write(randomData.data(), 0, LARGE_SIZE);
blob->resize(MEDIUM_SIZE);
blob->resize(LARGE_SIZE);
EXPECT_EQ(0, std::memcmp(readBlob(*blob).data(), randomData.data(), MEDIUM_SIZE));
EXPECT_EQ(0, std::memcmp(readBlob(*blob).dataOffset(MEDIUM_SIZE), ZEROES.data(), LARGE_SIZE-MEDIUM_SIZE));
}

View File

@ -1,43 +0,0 @@
#include "testutils/BlobStoreTest.h"
#include <cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround.h>
using blockstore::BlockId;
using boost::none;
TEST_F(BlobStoreTest, LoadNonexistingKeyOnEmptyBlobstore) {
const blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
EXPECT_EQ(none, blobStore->load(blockId));
}
TEST_F(BlobStoreTest, LoadNonexistingKeyOnNonEmptyBlobstore) {
blobStore->create();
const blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
EXPECT_EQ(none, blobStore->load(blockId));
}
TEST_F(BlobStoreTest, TwoCreatedBlobsHaveDifferentKeys) {
auto blob1 = blobStore->create();
auto blob2 = blobStore->create();
EXPECT_NE(blob1->blockId(), blob2->blockId());
}
TEST_F(BlobStoreTest, BlobIsNotLoadableAfterDeletion_DeleteDirectly) {
auto blob = blobStore->create();
BlockId blockId = blob->blockId();
blobStore->remove(std::move(blob));
EXPECT_FALSE(static_cast<bool>(blobStore->load(blockId)));
}
TEST_F(BlobStoreTest, BlobIsNotLoadableAfterDeletion_DeleteByKey) {
auto blockId = blobStore->create()->blockId();
blobStore->remove(blockId);
EXPECT_FALSE(static_cast<bool>(blobStore->load(blockId)));
}
TEST_F(BlobStoreTest, BlobIsNotLoadableAfterDeletion_DeleteAfterLoading) {
auto blob = blobStore->create();
BlockId blockId = blob->blockId();
reset(std::move(blob));
blobStore->remove(loadBlob(blockId));
EXPECT_FALSE(static_cast<bool>(blobStore->load(blockId)));
}

View File

@ -1,231 +0,0 @@
#include <gtest/gtest.h>
#include "blobstore/implementations/onblocks/datanodestore/DataInnerNode.h"
#include "blobstore/implementations/onblocks/datanodestore/DataLeafNode.h"
#include "blobstore/implementations/onblocks/datanodestore/DataNodeStore.h"
#include <blockstore/implementations/testfake/FakeBlockStore.h>
#include <blockstore/implementations/testfake/FakeBlock.h>
#include <memory>
#include <cpp-utils/pointer/cast.h>
using ::testing::Test;
using cpputils::dynamic_pointer_move;
using blockstore::BlockId;
using blockstore::testfake::FakeBlockStore;
using blockstore::BlockStore;
using cpputils::Data;
using namespace blobstore;
using namespace blobstore::onblocks;
using namespace blobstore::onblocks::datanodestore;
using cpputils::unique_ref;
using cpputils::make_unique_ref;
using std::vector;
class DataInnerNodeTest: public Test {
public:
static constexpr uint32_t BLOCKSIZE_BYTES = 1024;
DataInnerNodeTest() :
_blockStore(make_unique_ref<FakeBlockStore>()),
blockStore(_blockStore.get()),
nodeStore(make_unique_ref<DataNodeStore>(std::move(_blockStore), BLOCKSIZE_BYTES)),
ZEROES(nodeStore->layout().maxBytesPerLeaf()),
leaf(nodeStore->createNewLeafNode(Data(0))),
node(nodeStore->createNewInnerNode(1, {leaf->blockId()})) {
ZEROES.FillWithZeroes();
}
unique_ref<DataInnerNode> LoadInnerNode(const BlockId &blockId) {
auto node = nodeStore->load(blockId).value();
return dynamic_pointer_move<DataInnerNode>(node).value();
}
BlockId CreateNewInnerNodeReturnKey(const DataNode &firstChild) {
return nodeStore->createNewInnerNode(firstChild.depth()+1, {firstChild.blockId()})->blockId();
}
unique_ref<DataInnerNode> CreateNewInnerNode() {
auto new_leaf = nodeStore->createNewLeafNode(Data(0));
return nodeStore->createNewInnerNode(1, {new_leaf->blockId()});
}
unique_ref<DataInnerNode> CreateAndLoadNewInnerNode(const DataNode &firstChild) {
auto blockId = CreateNewInnerNodeReturnKey(firstChild);
return LoadInnerNode(blockId);
}
unique_ref<DataInnerNode> CreateNewInnerNode(uint8_t depth, const vector<blockstore::BlockId> &children) {
return nodeStore->createNewInnerNode(depth, children);
}
BlockId CreateNewInnerNodeReturnKey(uint8_t depth, const vector<blockstore::BlockId> &children) {
return CreateNewInnerNode(depth, children)->blockId();
}
unique_ref<DataInnerNode> CreateAndLoadNewInnerNode(uint8_t depth, const vector<blockstore::BlockId> &children) {
auto blockId = CreateNewInnerNodeReturnKey(depth, children);
return LoadInnerNode(blockId);
}
BlockId AddALeafTo(DataInnerNode *node) {
auto leaf2 = nodeStore->createNewLeafNode(Data(0));
node->addChild(*leaf2);
return leaf2->blockId();
}
BlockId CreateNodeWithDataConvertItToInnerNodeAndReturnKey() {
auto node = CreateNewInnerNode();
AddALeafTo(node.get());
AddALeafTo(node.get());
auto child = nodeStore->createNewLeafNode(Data(0));
unique_ref<DataInnerNode> converted = DataNode::convertToNewInnerNode(std::move(node), nodeStore->layout(), *child);
return converted->blockId();
}
unique_ref<DataInnerNode> CopyInnerNode(const DataInnerNode &node) {
auto copied = nodeStore->createNewNodeAsCopyFrom(node);
return dynamic_pointer_move<DataInnerNode>(copied).value();
}
BlockId InitializeInnerNodeAddLeafReturnKey() {
auto node = DataInnerNode::CreateNewNode(blockStore, nodeStore->layout(), 1, {leaf->blockId()});
AddALeafTo(node.get());
return node->blockId();
}
unique_ref<BlockStore> _blockStore;
BlockStore *blockStore;
unique_ref<DataNodeStore> nodeStore;
Data ZEROES;
unique_ref<DataLeafNode> leaf;
unique_ref<DataInnerNode> node;
private:
DISALLOW_COPY_AND_ASSIGN(DataInnerNodeTest);
};
constexpr uint32_t DataInnerNodeTest::BLOCKSIZE_BYTES;
TEST_F(DataInnerNodeTest, CorrectKeyReturnedAfterLoading) {
BlockId blockId = DataInnerNode::CreateNewNode(blockStore, nodeStore->layout(), 1, {leaf->blockId()})->blockId();
auto loaded = nodeStore->load(blockId).value();
EXPECT_EQ(blockId, loaded->blockId());
}
TEST_F(DataInnerNodeTest, InitializesCorrectly) {
auto node = DataInnerNode::CreateNewNode(blockStore, nodeStore->layout(), 1, {leaf->blockId()});
EXPECT_EQ(1u, node->numChildren());
EXPECT_EQ(leaf->blockId(), node->readChild(0).blockId());
}
TEST_F(DataInnerNodeTest, ReinitializesCorrectly) {
auto blockId = DataLeafNode::CreateNewNode(blockStore, nodeStore->layout(), Data(0))->blockId();
auto node = DataInnerNode::InitializeNewNode(blockStore->load(blockId).value(), nodeStore->layout(), 1, {leaf->blockId()});
EXPECT_EQ(1u, node->numChildren());
EXPECT_EQ(leaf->blockId(), node->readChild(0).blockId());
}
TEST_F(DataInnerNodeTest, IsCorrectlyInitializedAfterLoading) {
auto loaded = CreateAndLoadNewInnerNode(*leaf);
EXPECT_EQ(1u, loaded->numChildren());
EXPECT_EQ(leaf->blockId(), loaded->readChild(0).blockId());
}
TEST_F(DataInnerNodeTest, AddingASecondLeaf) {
BlockId leaf2_blockId = AddALeafTo(node.get());
EXPECT_EQ(2u, node->numChildren());
EXPECT_EQ(leaf->blockId(), node->readChild(0).blockId());
EXPECT_EQ(leaf2_blockId, node->readChild(1).blockId());
}
TEST_F(DataInnerNodeTest, AddingASecondLeafAndReload) {
auto leaf2 = nodeStore->createNewLeafNode(Data(0));
auto loaded = CreateAndLoadNewInnerNode(1, {leaf->blockId(), leaf2->blockId()});
EXPECT_EQ(2u, loaded->numChildren());
EXPECT_EQ(leaf->blockId(), loaded->readChild(0).blockId());
EXPECT_EQ(leaf2->blockId(), loaded->readChild(1).blockId());
}
TEST_F(DataInnerNodeTest, BuildingAThreeLevelTree) {
auto node2 = CreateNewInnerNode();
auto parent = CreateNewInnerNode(node->depth()+1, {node->blockId(), node2->blockId()});
EXPECT_EQ(2u, parent->numChildren());
EXPECT_EQ(node->blockId(), parent->readChild(0).blockId());
EXPECT_EQ(node2->blockId(), parent->readChild(1).blockId());
}
TEST_F(DataInnerNodeTest, BuildingAThreeLevelTreeAndReload) {
auto node2 = CreateNewInnerNode();
auto parent = CreateAndLoadNewInnerNode(node->depth()+1, {node->blockId(), node2->blockId()});
EXPECT_EQ(2u, parent->numChildren());
EXPECT_EQ(node->blockId(), parent->readChild(0).blockId());
EXPECT_EQ(node2->blockId(), parent->readChild(1).blockId());
}
TEST_F(DataInnerNodeTest, ConvertToInternalNode) {
auto child = nodeStore->createNewLeafNode(Data(0));
BlockId node_blockId = node->blockId();
unique_ref<DataInnerNode> converted = DataNode::convertToNewInnerNode(std::move(node), nodeStore->layout(), *child);
EXPECT_EQ(1u, converted->numChildren());
EXPECT_EQ(child->blockId(), converted->readChild(0).blockId());
EXPECT_EQ(node_blockId, converted->blockId());
}
TEST_F(DataInnerNodeTest, ConvertToInternalNodeZeroesOutChildrenRegion) {
BlockId blockId = CreateNodeWithDataConvertItToInnerNodeAndReturnKey();
auto block = blockStore->load(blockId).value();
EXPECT_EQ(0, std::memcmp(ZEROES.data(), static_cast<const uint8_t*>(block->data())+DataNodeLayout::HEADERSIZE_BYTES+sizeof(DataInnerNode::ChildEntry), nodeStore->layout().maxBytesPerLeaf()-sizeof(DataInnerNode::ChildEntry)));
}
TEST_F(DataInnerNodeTest, CopyingCreatesNewNode) {
auto copied = CopyInnerNode(*node);
EXPECT_NE(node->blockId(), copied->blockId());
}
TEST_F(DataInnerNodeTest, CopyInnerNodeWithOneChild) {
auto copied = CopyInnerNode(*node);
EXPECT_EQ(node->numChildren(), copied->numChildren());
EXPECT_EQ(node->readChild(0).blockId(), copied->readChild(0).blockId());
}
TEST_F(DataInnerNodeTest, CopyInnerNodeWithTwoChildren) {
AddALeafTo(node.get());
auto copied = CopyInnerNode(*node);
EXPECT_EQ(node->numChildren(), copied->numChildren());
EXPECT_EQ(node->readChild(0).blockId(), copied->readChild(0).blockId());
EXPECT_EQ(node->readChild(1).blockId(), copied->readChild(1).blockId());
}
TEST_F(DataInnerNodeTest, LastChildWhenOneChild) {
EXPECT_EQ(leaf->blockId(), node->readLastChild().blockId());
}
TEST_F(DataInnerNodeTest, LastChildWhenTwoChildren) {
BlockId blockId = AddALeafTo(node.get());
EXPECT_EQ(blockId, node->readLastChild().blockId());
}
TEST_F(DataInnerNodeTest, LastChildWhenThreeChildren) {
AddALeafTo(node.get());
BlockId blockId = AddALeafTo(node.get());
EXPECT_EQ(blockId, node->readLastChild().blockId());
}

View File

@ -1,345 +0,0 @@
#include "blobstore/implementations/onblocks/datanodestore/DataLeafNode.h"
#include "blobstore/implementations/onblocks/datanodestore/DataInnerNode.h"
#include "blobstore/implementations/onblocks/datanodestore/DataNodeStore.h"
#include "blobstore/implementations/onblocks/BlobStoreOnBlocks.h"
#include <gtest/gtest.h>
#include <cpp-utils/pointer/cast.h>
#include <blockstore/implementations/testfake/FakeBlockStore.h>
#include <blockstore/implementations/testfake/FakeBlock.h>
#include <cpp-utils/data/DataFixture.h>
using ::testing::Test;
using ::testing::WithParamInterface;
using ::testing::Values;
using cpputils::unique_ref;
using cpputils::make_unique_ref;
using std::string;
using cpputils::DataFixture;
using cpputils::deserialize;
//TODO Split into multiple files
using cpputils::dynamic_pointer_move;
using blockstore::BlockStore;
using cpputils::Data;
using blockstore::BlockId;
using blockstore::testfake::FakeBlockStore;
using namespace blobstore;
using namespace blobstore::onblocks;
using namespace blobstore::onblocks::datanodestore;
namespace {
#define EXPECT_IS_PTR_TYPE(Type, ptr) EXPECT_NE(nullptr, dynamic_cast<Type*>(ptr)) << "Given pointer cannot be cast to the given type"
class DataLeafNodeTest: public Test {
public:
static constexpr uint32_t BLOCKSIZE_BYTES = 1024;
static constexpr DataNodeLayout LAYOUT = DataNodeLayout(BLOCKSIZE_BYTES);
DataLeafNodeTest():
_blockStore(make_unique_ref<FakeBlockStore>()),
blockStore(_blockStore.get()),
nodeStore(make_unique_ref<DataNodeStore>(std::move(_blockStore), BLOCKSIZE_BYTES)),
ZEROES(nodeStore->layout().maxBytesPerLeaf()),
randomData(nodeStore->layout().maxBytesPerLeaf()),
leaf(nodeStore->createNewLeafNode(Data(0))) {
ZEROES.FillWithZeroes();
Data dataFixture(DataFixture::generate(nodeStore->layout().maxBytesPerLeaf()));
std::memcpy(randomData.data(), dataFixture.data(), randomData.size());
}
Data loadData(const DataLeafNode &leaf) {
Data data(leaf.numBytes());
leaf.read(data.data(), 0, leaf.numBytes());
return data;
}
BlockId WriteDataToNewLeafBlockAndReturnKey() {
auto newleaf = nodeStore->createNewLeafNode(Data(0));
newleaf->resize(randomData.size());
newleaf->write(randomData.data(), 0, randomData.size());
return newleaf->blockId();
}
void FillLeafBlockWithData() {
FillLeafBlockWithData(leaf.get());
}
void FillLeafBlockWithData(DataLeafNode *leaf_to_fill) {
leaf_to_fill->resize(randomData.size());
leaf_to_fill->write(randomData.data(), 0, randomData.size());
}
unique_ref<DataLeafNode> LoadLeafNode(const BlockId &blockId) {
auto leaf = nodeStore->load(blockId).value();
return dynamic_pointer_move<DataLeafNode>(leaf).value();
}
void ResizeLeaf(const BlockId &blockId, size_t size) {
auto leaf = LoadLeafNode(blockId);
EXPECT_IS_PTR_TYPE(DataLeafNode, leaf.get());
leaf->resize(size);
}
BlockId CreateLeafWithDataConvertItToInnerNodeAndReturnKey() {
auto leaf = nodeStore->createNewLeafNode(Data(0));
FillLeafBlockWithData(leaf.get());
auto child = nodeStore->createNewLeafNode(Data(0));
unique_ref<DataInnerNode> converted = DataNode::convertToNewInnerNode(std::move(leaf), LAYOUT, *child);
return converted->blockId();
}
unique_ref<DataLeafNode> CopyLeafNode(const DataLeafNode &node) {
auto copied = nodeStore->createNewNodeAsCopyFrom(node);
return dynamic_pointer_move<DataLeafNode>(copied).value();
}
BlockId InitializeLeafGrowAndReturnKey() {
auto leaf = DataLeafNode::CreateNewNode(blockStore, LAYOUT, Data(LAYOUT.maxBytesPerLeaf()));
leaf->resize(5);
return leaf->blockId();
}
unique_ref<BlockStore> _blockStore;
BlockStore *blockStore;
unique_ref<DataNodeStore> nodeStore;
Data ZEROES;
Data randomData;
unique_ref<DataLeafNode> leaf;
private:
DISALLOW_COPY_AND_ASSIGN(DataLeafNodeTest);
};
constexpr uint32_t DataLeafNodeTest::BLOCKSIZE_BYTES;
constexpr DataNodeLayout DataLeafNodeTest::LAYOUT;
TEST_F(DataLeafNodeTest, CorrectKeyReturnedAfterLoading) {
BlockId blockId = DataLeafNode::CreateNewNode(blockStore, LAYOUT, Data(LAYOUT.maxBytesPerLeaf()))->blockId();
auto loaded = nodeStore->load(blockId).value();
EXPECT_EQ(blockId, loaded->blockId());
}
TEST_F(DataLeafNodeTest, InitializesCorrectly) {
auto leaf = DataLeafNode::CreateNewNode(blockStore, LAYOUT, Data(5));
EXPECT_EQ(5u, leaf->numBytes());
}
TEST_F(DataLeafNodeTest, ReadWrittenDataAfterReloadingBlock) {
BlockId blockId = WriteDataToNewLeafBlockAndReturnKey();
auto loaded = LoadLeafNode(blockId);
EXPECT_EQ(randomData.size(), loaded->numBytes());
EXPECT_EQ(randomData, loadData(*loaded));
}
TEST_F(DataLeafNodeTest, NewLeafNodeHasSizeZero) {
EXPECT_EQ(0u, leaf->numBytes());
}
TEST_F(DataLeafNodeTest, NewLeafNodeHasSizeZero_AfterLoading) {
BlockId blockId = nodeStore->createNewLeafNode(Data(0))->blockId();
auto leaf = LoadLeafNode(blockId);
EXPECT_EQ(0u, leaf->numBytes());
}
class DataLeafNodeSizeTest: public DataLeafNodeTest, public WithParamInterface<unsigned int> {
public:
BlockId CreateLeafResizeItAndReturnKey() {
auto leaf = nodeStore->createNewLeafNode(Data(0));
leaf->resize(GetParam());
return leaf->blockId();
}
};
INSTANTIATE_TEST_SUITE_P(DataLeafNodeSizeTest, DataLeafNodeSizeTest, Values(0, 1, 5, 16, 32, 512, DataNodeLayout(DataLeafNodeTest::BLOCKSIZE_BYTES).maxBytesPerLeaf()));
TEST_P(DataLeafNodeSizeTest, ResizeNode_ReadSizeImmediately) {
leaf->resize(GetParam());
EXPECT_EQ(GetParam(), leaf->numBytes());
}
TEST_P(DataLeafNodeSizeTest, ResizeNode_ReadSizeAfterLoading) {
BlockId blockId = CreateLeafResizeItAndReturnKey();
auto leaf = LoadLeafNode(blockId);
EXPECT_EQ(GetParam(), leaf->numBytes());
}
TEST_F(DataLeafNodeTest, SpaceIsZeroFilledWhenGrowing) {
leaf->resize(randomData.size());
EXPECT_EQ(0, std::memcmp(ZEROES.data(), loadData(*leaf).data(), randomData.size()));
}
TEST_F(DataLeafNodeTest, SpaceGetsZeroFilledWhenShrinkingAndRegrowing) {
FillLeafBlockWithData();
// resize it smaller and then back to original size
uint32_t smaller_size = randomData.size() - 100;
leaf->resize(smaller_size);
leaf->resize(randomData.size());
//Check that the space was filled with zeroes
EXPECT_EQ(0, std::memcmp(ZEROES.data(), static_cast<const uint8_t*>(loadData(*leaf).data())+smaller_size, 100));
}
TEST_F(DataLeafNodeTest, DataGetsZeroFilledWhenShrinking) {
BlockId blockId = WriteDataToNewLeafBlockAndReturnKey();
uint32_t smaller_size = randomData.size() - 100;
{
//At first, we expect there to be random data in the underlying data block
auto block = blockStore->load(blockId).value();
EXPECT_EQ(0, std::memcmp(randomData.dataOffset(smaller_size), static_cast<const uint8_t*>(block->data())+DataNodeLayout::HEADERSIZE_BYTES+smaller_size, 100));
}
//After shrinking, we expect there to be zeroes in the underlying data block
ResizeLeaf(blockId, smaller_size);
{
auto block = blockStore->load(blockId).value();
EXPECT_EQ(0, std::memcmp(ZEROES.data(), static_cast<const uint8_t*>(block->data())+DataNodeLayout::HEADERSIZE_BYTES+smaller_size, 100));
}
}
TEST_F(DataLeafNodeTest, ShrinkingDoesntDestroyValidDataRegion) {
FillLeafBlockWithData();
uint32_t smaller_size = randomData.size() - 100;
leaf->resize(smaller_size);
//Check that the remaining data region is unchanged
EXPECT_EQ(0, std::memcmp(randomData.data(), loadData(*leaf).data(), smaller_size));
}
TEST_F(DataLeafNodeTest, ConvertToInternalNode) {
auto child = nodeStore->createNewLeafNode(Data(0));
BlockId leaf_blockId = leaf->blockId();
unique_ref<DataInnerNode> converted = DataNode::convertToNewInnerNode(std::move(leaf), LAYOUT, *child);
EXPECT_EQ(1u, converted->numChildren());
EXPECT_EQ(child->blockId(), converted->readChild(0).blockId());
EXPECT_EQ(leaf_blockId, converted->blockId());
}
TEST_F(DataLeafNodeTest, ConvertToInternalNodeZeroesOutChildrenRegion) {
BlockId blockId = CreateLeafWithDataConvertItToInnerNodeAndReturnKey();
auto block = blockStore->load(blockId).value();
EXPECT_EQ(0, std::memcmp(ZEROES.data(), static_cast<const uint8_t*>(block->data())+DataNodeLayout::HEADERSIZE_BYTES+sizeof(DataInnerNode::ChildEntry), nodeStore->layout().maxBytesPerLeaf()-sizeof(DataInnerNode::ChildEntry)));
}
TEST_F(DataLeafNodeTest, CopyingCreatesANewLeaf) {
auto copied = CopyLeafNode(*leaf);
EXPECT_NE(leaf->blockId(), copied->blockId());
}
TEST_F(DataLeafNodeTest, CopyEmptyLeaf) {
auto copied = CopyLeafNode(*leaf);
EXPECT_EQ(leaf->numBytes(), copied->numBytes());
}
TEST_F(DataLeafNodeTest, CopyDataLeaf) {
FillLeafBlockWithData();
auto copied = CopyLeafNode(*leaf);
EXPECT_EQ(leaf->numBytes(), copied->numBytes());
EXPECT_EQ(0, std::memcmp(loadData(*leaf).data(), loadData(*copied).data(), leaf->numBytes()));
//Test that they have different data regions (changing the original one doesn't change the copy)
uint8_t data = 0;
leaf->write(&data, 0, 1);
EXPECT_EQ(data, deserialize<uint8_t>(loadData(*leaf).data()));
EXPECT_NE(data, deserialize<uint8_t>(loadData(*copied).data()));
}
struct DataRange {
uint64_t leafsize;
uint64_t offset;
uint64_t count;
};
class DataLeafNodeDataTest: public DataLeafNodeTest, public WithParamInterface<DataRange> {
public:
Data foregroundData;
Data backgroundData;
DataLeafNodeDataTest():
foregroundData(DataFixture::generate(GetParam().count, 0)),
backgroundData(DataFixture::generate(GetParam().leafsize, 1)) {
}
BlockId CreateLeafWriteToItAndReturnKey(const Data &to_write) {
auto newleaf = nodeStore->createNewLeafNode(Data(0));
newleaf->resize(GetParam().leafsize);
newleaf->write(to_write.data(), GetParam().offset, GetParam().count);
return newleaf->blockId();
}
void EXPECT_DATA_READS_AS(const Data &expected, const DataLeafNode &leaf, uint64_t offset, uint64_t count) {
Data read(count);
leaf.read(read.data(), offset, count);
EXPECT_EQ(expected, read);
}
void EXPECT_DATA_READS_AS_OUTSIDE_OF(const Data &expected, const DataLeafNode &leaf, uint64_t start, uint64_t count) {
Data begin(start);
Data end(GetParam().leafsize - count - start);
std::memcpy(begin.data(), expected.data(), start);
std::memcpy(end.data(), expected.dataOffset(start+count), end.size());
EXPECT_DATA_READS_AS(begin, leaf, 0, start);
EXPECT_DATA_READS_AS(end, leaf, start + count, end.size());
}
void EXPECT_DATA_IS_ZEROES_OUTSIDE_OF(const DataLeafNode &leaf, uint64_t start, uint64_t count) {
Data ZEROES(GetParam().leafsize);
ZEROES.FillWithZeroes();
EXPECT_DATA_READS_AS_OUTSIDE_OF(ZEROES, leaf, start, count);
}
};
INSTANTIATE_TEST_SUITE_P(DataLeafNodeDataTest, DataLeafNodeDataTest, Values(
DataRange{DataLeafNodeTest::LAYOUT.maxBytesPerLeaf(), 0, DataLeafNodeTest::LAYOUT.maxBytesPerLeaf()}, // full size leaf, access beginning to end
DataRange{DataLeafNodeTest::LAYOUT.maxBytesPerLeaf(), 100, DataLeafNodeTest::LAYOUT.maxBytesPerLeaf()-200}, // full size leaf, access middle to middle
DataRange{DataLeafNodeTest::LAYOUT.maxBytesPerLeaf(), 0, DataLeafNodeTest::LAYOUT.maxBytesPerLeaf()-100}, // full size leaf, access beginning to middle
DataRange{DataLeafNodeTest::LAYOUT.maxBytesPerLeaf(), 100, DataLeafNodeTest::LAYOUT.maxBytesPerLeaf()-100}, // full size leaf, access middle to end
DataRange{DataLeafNodeTest::LAYOUT.maxBytesPerLeaf()-100, 0, DataLeafNodeTest::LAYOUT.maxBytesPerLeaf()-100}, // non-full size leaf, access beginning to end
DataRange{DataLeafNodeTest::LAYOUT.maxBytesPerLeaf()-100, 100, DataLeafNodeTest::LAYOUT.maxBytesPerLeaf()-300}, // non-full size leaf, access middle to middle
DataRange{DataLeafNodeTest::LAYOUT.maxBytesPerLeaf()-100, 0, DataLeafNodeTest::LAYOUT.maxBytesPerLeaf()-200}, // non-full size leaf, access beginning to middle
DataRange{DataLeafNodeTest::LAYOUT.maxBytesPerLeaf()-100, 100, DataLeafNodeTest::LAYOUT.maxBytesPerLeaf()-200} // non-full size leaf, access middle to end
));
TEST_P(DataLeafNodeDataTest, WriteAndReadImmediately) {
leaf->resize(GetParam().leafsize);
leaf->write(this->foregroundData.data(), GetParam().offset, GetParam().count);
EXPECT_DATA_READS_AS(this->foregroundData, *leaf, GetParam().offset, GetParam().count);
EXPECT_DATA_IS_ZEROES_OUTSIDE_OF(*leaf, GetParam().offset, GetParam().count);
}
TEST_P(DataLeafNodeDataTest, WriteAndReadAfterLoading) {
BlockId blockId = CreateLeafWriteToItAndReturnKey(this->foregroundData);
auto loaded_leaf = LoadLeafNode(blockId);
EXPECT_DATA_READS_AS(this->foregroundData, *loaded_leaf, GetParam().offset, GetParam().count);
EXPECT_DATA_IS_ZEROES_OUTSIDE_OF(*loaded_leaf, GetParam().offset, GetParam().count);
}
TEST_P(DataLeafNodeDataTest, OverwriteAndRead) {
leaf->resize(GetParam().leafsize);
leaf->write(this->backgroundData.data(), 0, GetParam().leafsize);
leaf->write(this->foregroundData.data(), GetParam().offset, GetParam().count);
EXPECT_DATA_READS_AS(this->foregroundData, *leaf, GetParam().offset, GetParam().count);
EXPECT_DATA_READS_AS_OUTSIDE_OF(this->backgroundData, *leaf, GetParam().offset, GetParam().count);
}
}

View File

@ -1,150 +0,0 @@
#include "blobstore/implementations/onblocks/datanodestore/DataInnerNode.h"
#include "blobstore/implementations/onblocks/datanodestore/DataLeafNode.h"
#include "blobstore/implementations/onblocks/datanodestore/DataNode.h"
#include "blobstore/implementations/onblocks/datanodestore/DataNodeStore.h"
#include "blobstore/implementations/onblocks/BlobStoreOnBlocks.h"
#include <gtest/gtest.h>
#include <blockstore/implementations/testfake/FakeBlockStore.h>
#include <blockstore/implementations/testfake/FakeBlock.h>
#include <cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround.h>
using ::testing::Test;
using cpputils::unique_ref;
using cpputils::make_unique_ref;
using std::string;
using boost::none;
using blockstore::BlockStore;
using blockstore::testfake::FakeBlockStore;
using blockstore::BlockId;
using cpputils::Data;
using namespace blobstore;
using namespace blobstore::onblocks;
using namespace blobstore::onblocks::datanodestore;
class DataNodeStoreTest: public Test {
public:
static constexpr uint32_t BLOCKSIZE_BYTES = 1024;
unique_ref<BlockStore> _blockStore = make_unique_ref<FakeBlockStore>();
BlockStore *blockStore = _blockStore.get();
unique_ref<DataNodeStore> nodeStore = make_unique_ref<DataNodeStore>(std::move(_blockStore), BLOCKSIZE_BYTES);
};
constexpr uint32_t DataNodeStoreTest::BLOCKSIZE_BYTES;
#define EXPECT_IS_PTR_TYPE(Type, ptr) EXPECT_NE(nullptr, dynamic_cast<Type*>(ptr)) << "Given pointer cannot be cast to the given type"
TEST_F(DataNodeStoreTest, CreateLeafNodeCreatesLeafNode) {
auto node = nodeStore->createNewLeafNode(Data(0));
EXPECT_IS_PTR_TYPE(DataLeafNode, node.get());
}
TEST_F(DataNodeStoreTest, CreateInnerNodeCreatesInnerNode) {
auto leaf = nodeStore->createNewLeafNode(Data(0));
auto node = nodeStore->createNewInnerNode(1, {leaf->blockId()});
EXPECT_IS_PTR_TYPE(DataInnerNode, node.get());
}
TEST_F(DataNodeStoreTest, LeafNodeIsRecognizedAfterStoreAndLoad) {
BlockId blockId = nodeStore->createNewLeafNode(Data(0))->blockId();
auto loaded_node = nodeStore->load(blockId).value();
EXPECT_IS_PTR_TYPE(DataLeafNode, loaded_node.get());
}
TEST_F(DataNodeStoreTest, InnerNodeWithDepth1IsRecognizedAfterStoreAndLoad) {
auto leaf = nodeStore->createNewLeafNode(Data(0));
BlockId blockId = nodeStore->createNewInnerNode(1, {leaf->blockId()})->blockId();
auto loaded_node = nodeStore->load(blockId).value();
EXPECT_IS_PTR_TYPE(DataInnerNode, loaded_node.get());
}
TEST_F(DataNodeStoreTest, InnerNodeWithDepth2IsRecognizedAfterStoreAndLoad) {
auto leaf = nodeStore->createNewLeafNode(Data(0));
auto inner = nodeStore->createNewInnerNode(1, {leaf->blockId()});
BlockId blockId = nodeStore->createNewInnerNode(2, {inner->blockId()})->blockId();
auto loaded_node = nodeStore->load(blockId).value();
EXPECT_IS_PTR_TYPE(DataInnerNode, loaded_node.get());
}
TEST_F(DataNodeStoreTest, DataNodeCrashesOnLoadIfDepthIsTooHigh) {
auto block = blockStore->create(Data(BLOCKSIZE_BYTES));
BlockId blockId = block->blockId();
{
DataNodeView view(std::move(block));
view.setDepth(DataNodeStore::MAX_DEPTH + 1);
}
EXPECT_ANY_THROW(
nodeStore->load(blockId)
);
}
TEST_F(DataNodeStoreTest, CreatedInnerNodeIsInitialized) {
auto leaf = nodeStore->createNewLeafNode(Data(0));
auto node = nodeStore->createNewInnerNode(1, {leaf->blockId()});
EXPECT_EQ(1u, node->numChildren());
EXPECT_EQ(leaf->blockId(), node->readChild(0).blockId());
}
TEST_F(DataNodeStoreTest, CreatedLeafNodeIsInitialized) {
auto leaf = nodeStore->createNewLeafNode(Data(0));
EXPECT_EQ(0u, leaf->numBytes());
}
TEST_F(DataNodeStoreTest, NodeIsNotLoadableAfterDeleting) {
auto nodekey = nodeStore->createNewLeafNode(Data(0))->blockId();
auto node = nodeStore->load(nodekey);
EXPECT_NE(none, node);
nodeStore->remove(std::move(*node));
EXPECT_EQ(none, nodeStore->load(nodekey));
}
TEST_F(DataNodeStoreTest, NumNodesIsCorrectOnEmptyNodestore) {
EXPECT_EQ(0u, nodeStore->numNodes());
}
TEST_F(DataNodeStoreTest, NumNodesIsCorrectAfterAddingOneLeafNode) {
nodeStore->createNewLeafNode(Data(0));
EXPECT_EQ(1u, nodeStore->numNodes());
}
TEST_F(DataNodeStoreTest, NumNodesIsCorrectAfterRemovingTheLastNode) {
auto leaf = nodeStore->createNewLeafNode(Data(0));
nodeStore->remove(std::move(leaf));
EXPECT_EQ(0u, nodeStore->numNodes());
}
TEST_F(DataNodeStoreTest, NumNodesIsCorrectAfterAddingTwoNodes) {
auto leaf = nodeStore->createNewLeafNode(Data(0));
auto node = nodeStore->createNewInnerNode(1, {leaf->blockId()});
EXPECT_EQ(2u, nodeStore->numNodes());
}
TEST_F(DataNodeStoreTest, NumNodesIsCorrectAfterRemovingANode) {
auto leaf = nodeStore->createNewLeafNode(Data(0));
auto node = nodeStore->createNewInnerNode(1, {leaf->blockId()});
nodeStore->remove(std::move(node));
EXPECT_EQ(1u, nodeStore->numNodes());
}
TEST_F(DataNodeStoreTest, PhysicalBlockSize_Leaf) {
auto leaf = nodeStore->createNewLeafNode(Data(0));
auto block = blockStore->load(leaf->blockId()).value();
EXPECT_EQ(BLOCKSIZE_BYTES, block->size());
}
TEST_F(DataNodeStoreTest, PhysicalBlockSize_Inner) {
auto leaf = nodeStore->createNewLeafNode(Data(0));
auto node = nodeStore->createNewInnerNode(1, {leaf->blockId()});
auto block = blockStore->load(node->blockId()).value();
EXPECT_EQ(BLOCKSIZE_BYTES, block->size());
}

View File

@ -1,98 +0,0 @@
#include "blobstore/implementations/onblocks/datanodestore/DataNodeView.h"
#include <gtest/gtest.h>
#include <blockstore/implementations/testfake/FakeBlockStore.h>
#include <blockstore/implementations/testfake/FakeBlock.h>
#include "blobstore/implementations/onblocks/BlobStoreOnBlocks.h"
#include <cpp-utils/data/DataFixture.h>
using ::testing::Test;
using ::testing::WithParamInterface;
using ::testing::Values;
using std::string;
using blockstore::BlockStore;
using blockstore::testfake::FakeBlockStore;
using cpputils::Data;
using cpputils::DataFixture;
using cpputils::unique_ref;
using cpputils::make_unique_ref;
using namespace blobstore;
using namespace blobstore::onblocks;
using namespace blobstore::onblocks::datanodestore;
class DataNodeViewTest: public Test {
public:
static constexpr uint32_t BLOCKSIZE_BYTES = 1024;
static constexpr uint32_t DATASIZE_BYTES = DataNodeLayout(DataNodeViewTest::BLOCKSIZE_BYTES).datasizeBytes();
unique_ref<BlockStore> blockStore = make_unique_ref<FakeBlockStore>();
};
class DataNodeViewDepthTest: public DataNodeViewTest, public WithParamInterface<uint8_t> {
};
INSTANTIATE_TEST_SUITE_P(DataNodeViewDepthTest, DataNodeViewDepthTest, Values(0, 1, 3, 10, 100));
TEST_P(DataNodeViewDepthTest, DepthIsStored) {
auto block = blockStore->create(Data(BLOCKSIZE_BYTES));
auto blockId = block->blockId();
{
DataNodeView view(std::move(block));
view.setDepth(GetParam());
}
DataNodeView view(blockStore->load(blockId).value());
EXPECT_EQ(GetParam(), view.Depth());
}
class DataNodeViewSizeTest: public DataNodeViewTest, public WithParamInterface<uint32_t> {
};
INSTANTIATE_TEST_SUITE_P(DataNodeViewSizeTest, DataNodeViewSizeTest, Values(0, 50, 64, 1024, 1024*1024*1024));
TEST_P(DataNodeViewSizeTest, SizeIsStored) {
auto block = blockStore->create(Data(BLOCKSIZE_BYTES));
auto blockId = block->blockId();
{
DataNodeView view(std::move(block));
view.setSize(GetParam());
}
DataNodeView view(blockStore->load(blockId).value());
EXPECT_EQ(GetParam(), view.Size());
}
TEST_F(DataNodeViewTest, DataIsStored) {
Data randomData = DataFixture::generate(DATASIZE_BYTES);
auto block = blockStore->create(Data(BLOCKSIZE_BYTES));
auto blockId = block->blockId();
{
DataNodeView view(std::move(block));
view.write(randomData.data(), 0, randomData.size());
}
DataNodeView view(blockStore->load(blockId).value());
EXPECT_EQ(0, std::memcmp(view.data(), randomData.data(), randomData.size()));
}
TEST_F(DataNodeViewTest, HeaderAndBodyDontOverlap) {
Data randomData = DataFixture::generate(DATASIZE_BYTES);
auto block = blockStore->create(Data(BLOCKSIZE_BYTES));
auto blockId = block->blockId();
{
DataNodeView view(std::move(block));
view.setDepth(3);
view.setSize(1000000000u);
view.write(randomData.data(), 0, DATASIZE_BYTES);
}
DataNodeView view(blockStore->load(blockId).value());
EXPECT_EQ(3, view.Depth());
EXPECT_EQ(1000000000u, view.Size());
EXPECT_EQ(0, std::memcmp(view.data(), randomData.data(), DATASIZE_BYTES));
}
TEST_F(DataNodeViewTest, Data) {
auto block = blockStore->create(Data(BLOCKSIZE_BYTES));
const uint8_t *blockBegin = static_cast<const uint8_t*>(block->data());
DataNodeView view(std::move(block));
EXPECT_EQ(blockBegin+DataNodeLayout::HEADERSIZE_BYTES, static_cast<const uint8_t*>(view.data()));
}
//TODO Test that header fields (and data) are also stored over reloads

View File

@ -1,69 +0,0 @@
#include "testutils/DataTreeTest.h"
#include "blobstore/implementations/onblocks/datanodestore/DataNodeStore.h"
#include "blobstore/implementations/onblocks/datatreestore/DataTreeStore.h"
#include <blockstore/implementations/testfake/FakeBlockStore.h>
#include <cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround.h>
using blockstore::BlockId;
using boost::none;
using namespace blobstore::onblocks::datatreestore;
class DataTreeStoreTest: public DataTreeTest {
};
TEST_F(DataTreeStoreTest, CorrectKeyReturned) {
BlockId blockId = treeStore.createNewTree()->blockId();
auto tree = treeStore.load(blockId).value();
EXPECT_EQ(blockId, tree->blockId());
}
TEST_F(DataTreeStoreTest, CreatedTreeIsLoadable) {
auto blockId = treeStore.createNewTree()->blockId();
auto loaded = treeStore.load(blockId);
EXPECT_NE(none, loaded);
}
TEST_F(DataTreeStoreTest, NewTreeIsLeafOnly) {
auto tree = treeStore.createNewTree();
EXPECT_IS_LEAF_NODE(tree->blockId());
}
TEST_F(DataTreeStoreTest, TreeIsNotLoadableAfterRemove_DeleteByTree) {
BlockId blockId = treeStore.createNewTree()->blockId();
auto tree = treeStore.load(blockId);
EXPECT_NE(none, tree);
treeStore.remove(std::move(*tree));
EXPECT_EQ(none, treeStore.load(blockId));
}
TEST_F(DataTreeStoreTest, TreeIsNotLoadableAfterRemove_DeleteByKey) {
BlockId blockId = treeStore.createNewTree()->blockId();
treeStore.remove(blockId);
EXPECT_EQ(none, treeStore.load(blockId));
}
TEST_F(DataTreeStoreTest, RemovingTreeRemovesAllNodesOfTheTree_DeleteByTree) {
auto tree1_blockId = CreateThreeLevelMinData()->blockId();
auto tree2_blockId = treeStore.createNewTree()->blockId();
auto tree1 = treeStore.load(tree1_blockId).value();
treeStore.remove(std::move(tree1));
//Check that the only remaining node is tree2
EXPECT_EQ(1u, nodeStore->numNodes());
EXPECT_NE(none, treeStore.load(tree2_blockId));
}
TEST_F(DataTreeStoreTest, RemovingTreeRemovesAllNodesOfTheTree_DeleteByKey) {
auto tree1_blockId = CreateThreeLevelMinData()->blockId();
auto tree2_blockId = treeStore.createNewTree()->blockId();
treeStore.remove(tree1_blockId);
//Check that the only remaining node is tree2
EXPECT_EQ(1u, nodeStore->numNodes());
EXPECT_NE(none, treeStore.load(tree2_blockId));
}

View File

@ -1,88 +0,0 @@
#include "testutils/DataTreeTest.h"
#include <gmock/gmock.h>
using ::testing::WithParamInterface;
using ::testing::Values;
using blobstore::onblocks::datanodestore::DataNodeLayout;
using blockstore::BlockId;
class DataTreeTest_NumStoredBytes: public DataTreeTest {
public:
};
TEST_F(DataTreeTest_NumStoredBytes, CreatedTreeIsEmpty) {
auto tree = treeStore.createNewTree();
EXPECT_EQ(0u, tree->numBytes());
}
class DataTreeTest_NumStoredBytes_P: public DataTreeTest_NumStoredBytes, public WithParamInterface<uint32_t> {};
INSTANTIATE_TEST_SUITE_P(EmptyLastLeaf, DataTreeTest_NumStoredBytes_P, Values(0u));
INSTANTIATE_TEST_SUITE_P(HalfFullLastLeaf, DataTreeTest_NumStoredBytes_P, Values(5u, 10u));
INSTANTIATE_TEST_SUITE_P(FullLastLeaf, DataTreeTest_NumStoredBytes_P, Values(static_cast<uint32_t>(DataNodeLayout(DataTreeTest_NumStoredBytes::BLOCKSIZE_BYTES).maxBytesPerLeaf())));
//TODO Test numLeaves() and numNodes() also two configurations with same number of bytes but different number of leaves (last leaf has 0 bytes)
TEST_P(DataTreeTest_NumStoredBytes_P, SingleLeaf) {
BlockId blockId = CreateLeafWithSize(GetParam())->blockId();
auto tree = treeStore.load(blockId).value();
EXPECT_EQ(GetParam(), tree->numBytes());
EXPECT_EQ(1, tree->numLeaves());
EXPECT_EQ(1, tree->numNodes());
}
TEST_P(DataTreeTest_NumStoredBytes_P, TwoLeafTree) {
BlockId blockId = CreateTwoLeafWithSecondLeafSize(GetParam())->blockId();
auto tree = treeStore.load(blockId).value();
EXPECT_EQ(nodeStore->layout().maxBytesPerLeaf() + GetParam(), tree->numBytes());
EXPECT_EQ(2, tree->numLeaves());
EXPECT_EQ(3, tree->numNodes());
}
TEST_P(DataTreeTest_NumStoredBytes_P, FullTwolevelTree) {
BlockId blockId = CreateFullTwoLevelWithLastLeafSize(GetParam())->blockId();
auto tree = treeStore.load(blockId).value();
EXPECT_EQ(nodeStore->layout().maxBytesPerLeaf()*(nodeStore->layout().maxChildrenPerInnerNode()-1) + GetParam(), tree->numBytes());
EXPECT_EQ(nodeStore->layout().maxChildrenPerInnerNode(), tree->numLeaves());
EXPECT_EQ(1 + nodeStore->layout().maxChildrenPerInnerNode(), tree->numNodes());
}
TEST_P(DataTreeTest_NumStoredBytes_P, ThreeLevelTreeWithOneChild) {
BlockId blockId = CreateThreeLevelWithOneChildAndLastLeafSize(GetParam())->blockId();
auto tree = treeStore.load(blockId).value();
EXPECT_EQ(nodeStore->layout().maxBytesPerLeaf() + GetParam(), tree->numBytes());
EXPECT_EQ(2, tree->numLeaves());
EXPECT_EQ(4, tree->numNodes());
}
TEST_P(DataTreeTest_NumStoredBytes_P, ThreeLevelTreeWithTwoChildren) {
BlockId blockId = CreateThreeLevelWithTwoChildrenAndLastLeafSize(GetParam())->blockId();
auto tree = treeStore.load(blockId).value();
EXPECT_EQ(nodeStore->layout().maxBytesPerLeaf()*nodeStore->layout().maxChildrenPerInnerNode() + nodeStore->layout().maxBytesPerLeaf() + GetParam(), tree->numBytes());
EXPECT_EQ(2 + nodeStore->layout().maxChildrenPerInnerNode(), tree->numLeaves());
EXPECT_EQ(5 + nodeStore->layout().maxChildrenPerInnerNode(), tree->numNodes());
}
TEST_P(DataTreeTest_NumStoredBytes_P, ThreeLevelTreeWithThreeChildren) {
BlockId blockId = CreateThreeLevelWithThreeChildrenAndLastLeafSize(GetParam())->blockId();
auto tree = treeStore.load(blockId).value();
EXPECT_EQ(2*nodeStore->layout().maxBytesPerLeaf()*nodeStore->layout().maxChildrenPerInnerNode() + nodeStore->layout().maxBytesPerLeaf() + GetParam(), tree->numBytes());
EXPECT_EQ(2 + 2*nodeStore->layout().maxChildrenPerInnerNode(), tree->numLeaves());
EXPECT_EQ(6 + 2*nodeStore->layout().maxChildrenPerInnerNode(), tree->numNodes());
}
TEST_P(DataTreeTest_NumStoredBytes_P, FullThreeLevelTree) {
BlockId blockId = CreateFullThreeLevelWithLastLeafSize(GetParam())->blockId();
auto tree = treeStore.load(blockId).value();
EXPECT_EQ(nodeStore->layout().maxBytesPerLeaf()*nodeStore->layout().maxChildrenPerInnerNode()*(nodeStore->layout().maxChildrenPerInnerNode()-1) + nodeStore->layout().maxBytesPerLeaf()*(nodeStore->layout().maxChildrenPerInnerNode()-1) + GetParam(), tree->numBytes());
EXPECT_EQ(nodeStore->layout().maxChildrenPerInnerNode()*nodeStore->layout().maxChildrenPerInnerNode(), tree->numLeaves());
EXPECT_EQ(1 + nodeStore->layout().maxChildrenPerInnerNode() + nodeStore->layout().maxChildrenPerInnerNode()*nodeStore->layout().maxChildrenPerInnerNode(), tree->numNodes());
}
TEST_P(DataTreeTest_NumStoredBytes_P, FourLevelMinDataTree) {
BlockId blockId = CreateFourLevelMinDataWithLastLeafSize(GetParam())->blockId();
auto tree = treeStore.load(blockId).value();
EXPECT_EQ(nodeStore->layout().maxBytesPerLeaf()*nodeStore->layout().maxChildrenPerInnerNode()*nodeStore->layout().maxChildrenPerInnerNode() + GetParam(), tree->numBytes());
EXPECT_EQ(1 + nodeStore->layout().maxChildrenPerInnerNode()*nodeStore->layout().maxChildrenPerInnerNode(), tree->numLeaves());
EXPECT_EQ(5 + nodeStore->layout().maxChildrenPerInnerNode() + nodeStore->layout().maxChildrenPerInnerNode()*nodeStore->layout().maxChildrenPerInnerNode(), tree->numNodes());
}

View File

@ -1,558 +0,0 @@
#include "testutils/DataTreeTest.h"
#include <gmock/gmock.h>
using blobstore::onblocks::datatreestore::DataTree;
using blockstore::BlockId;
using cpputils::Data;
class DataTreeTest_Performance: public DataTreeTest {
public:
void TraverseByWriting(DataTree *tree, uint64_t beginIndex, uint64_t endIndex) {
uint64_t offset = beginIndex * maxBytesPerLeaf;
uint64_t count = endIndex * maxBytesPerLeaf - offset;
Data data(count);
data.FillWithZeroes();
tree->writeBytes(data.data(), offset, count);
}
void TraverseByReading(DataTree *tree, uint64_t beginIndex, uint64_t endIndex) {
uint64_t offset = beginIndex * maxBytesPerLeaf;
uint64_t count = endIndex * maxBytesPerLeaf - offset;
Data data(count);
tree->readBytes(data.data(), offset, count);
}
uint64_t maxChildrenPerInnerNode = nodeStore->layout().maxChildrenPerInnerNode();
uint64_t maxBytesPerLeaf = nodeStore->layout().maxBytesPerLeaf();
};
TEST_F(DataTreeTest_Performance, DeletingDoesntLoadLeaves_Twolevel_DeleteByTree) {
auto blockId = CreateFullTwoLevel()->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
treeStore.remove(std::move(tree));
EXPECT_EQ(0u, blockStore->loadedBlocks().size());
EXPECT_EQ(0u, blockStore->createdBlocks());
EXPECT_EQ(1u + maxChildrenPerInnerNode, blockStore->removedBlocks().size());
EXPECT_EQ(0u, blockStore->distinctWrittenBlocks().size());
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, DeletingDoesntLoadLeaves_Twolevel_DeleteByKey) {
auto blockId = CreateFullTwoLevel()->blockId();
blockStore->resetCounters();
treeStore.remove(blockId);
EXPECT_EQ(1u, blockStore->loadedBlocks().size());
EXPECT_EQ(0u, blockStore->createdBlocks());
EXPECT_EQ(1u + maxChildrenPerInnerNode, blockStore->removedBlocks().size());
EXPECT_EQ(0u, blockStore->distinctWrittenBlocks().size());
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, DeletingDoesntLoadLeaves_Threelevel_DeleteByTree) {
auto blockId = CreateFullThreeLevel()->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
treeStore.remove(std::move(tree));
EXPECT_EQ(maxChildrenPerInnerNode, blockStore->loadedBlocks().size());
EXPECT_EQ(0u, blockStore->createdBlocks());
EXPECT_EQ(1u + maxChildrenPerInnerNode + maxChildrenPerInnerNode*maxChildrenPerInnerNode, blockStore->removedBlocks().size());
EXPECT_EQ(0u, blockStore->distinctWrittenBlocks().size());
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, DeletingDoesntLoadLeaves_Threelevel_DeleteByKey) {
auto blockId = CreateFullThreeLevel()->blockId();
blockStore->resetCounters();
treeStore.remove(blockId);
EXPECT_EQ(1u + maxChildrenPerInnerNode, blockStore->loadedBlocks().size());
EXPECT_EQ(0u, blockStore->createdBlocks());
EXPECT_EQ(1u + maxChildrenPerInnerNode + maxChildrenPerInnerNode*maxChildrenPerInnerNode, blockStore->removedBlocks().size());
EXPECT_EQ(0u, blockStore->distinctWrittenBlocks().size());
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_Twolevel_All_ByWriting) {
auto blockId = CreateFullTwoLevel()->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
TraverseByWriting(tree.get(), 0, maxChildrenPerInnerNode);
EXPECT_EQ(1u, blockStore->loadedBlocks().size()); // Has to load the rightmost leaf once to adapt its size, rest of the leaves aren't loaded but just overwritten
EXPECT_EQ(0u, blockStore->createdBlocks());
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(maxChildrenPerInnerNode, blockStore->distinctWrittenBlocks().size());
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_Twolevel_All_ByReading) {
auto blockId = CreateFullTwoLevel()->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
TraverseByReading(tree.get(), 0, maxChildrenPerInnerNode);
EXPECT_EQ(1u + maxChildrenPerInnerNode, blockStore->loadedBlocks().size()); // Has to read the rightmost leaf an additional time in the beginning to determine size.
EXPECT_EQ(0u, blockStore->createdBlocks());
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(0u, blockStore->distinctWrittenBlocks().size());
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_Twolevel_Some_ByWriting) {
auto blockId = CreateFullTwoLevel()->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
TraverseByWriting(tree.get(), 3, 5);
EXPECT_EQ(0u, blockStore->loadedBlocks().size());
EXPECT_EQ(0u, blockStore->createdBlocks());
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(2u, blockStore->distinctWrittenBlocks().size());
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_Twolevel_Some_ByReading) {
auto blockId = CreateFullTwoLevel()->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
TraverseByReading(tree.get(), 3, 5);
EXPECT_EQ(3u, blockStore->loadedBlocks().size()); // reads 2 leaves and the rightmost leaf to determine size
EXPECT_EQ(0u, blockStore->createdBlocks());
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(0u, blockStore->distinctWrittenBlocks().size());
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_Threelevel_All_ByWriting) {
auto blockId = CreateFullThreeLevel()->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
TraverseByWriting(tree.get(), 0, maxChildrenPerInnerNode * maxChildrenPerInnerNode);
EXPECT_EQ(maxChildrenPerInnerNode + 1, blockStore->loadedBlocks().size()); // Loads inner nodes and has to load the rightmost leaf once to adapt its size, rest of the leaves aren't loaded but just overwritten.
EXPECT_EQ(0u, blockStore->createdBlocks());
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(maxChildrenPerInnerNode*maxChildrenPerInnerNode, blockStore->distinctWrittenBlocks().size());
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_Threelevel_All_ByReading) {
auto blockId = CreateFullThreeLevel()->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
TraverseByReading(tree.get(), 0, maxChildrenPerInnerNode * maxChildrenPerInnerNode);
EXPECT_EQ(maxChildrenPerInnerNode*maxChildrenPerInnerNode + maxChildrenPerInnerNode + 2, blockStore->loadedBlocks().size()); // Loads inner nodes and leaves. Has to load the rightmost inner node and leaf an additional time at the beginning to compute size
EXPECT_EQ(0u, blockStore->createdBlocks());
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(0u, blockStore->distinctWrittenBlocks().size());
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_Threelevel_InOneInner_ByWriting) {
auto blockId = CreateFullThreeLevel()->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
TraverseByWriting(tree.get(), 3, 5);
EXPECT_EQ(1u, blockStore->loadedBlocks().size()); // Loads inner node. Doesn't load the leaves, they're just overwritten.
EXPECT_EQ(0u, blockStore->createdBlocks());
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(2u, blockStore->distinctWrittenBlocks().size());
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_Threelevel_InOneInner_ByReading) {
auto blockId = CreateFullThreeLevel()->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
TraverseByReading(tree.get(), 3, 5);
EXPECT_EQ(5u, blockStore->loadedBlocks().size()); // reads 2 leaves and the inner node, also has to read the rightmost inner node and leaf additionally at the beginning to determine size
EXPECT_EQ(0u, blockStore->createdBlocks());
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(0u, blockStore->distinctWrittenBlocks().size());
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_Threelevel_InTwoInner_ByWriting) {
auto blockId = CreateFullThreeLevel()->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
TraverseByWriting(tree.get(), 3, 3 + maxChildrenPerInnerNode);
EXPECT_EQ(2u, blockStore->loadedBlocks().size()); // Loads both inner node
EXPECT_EQ(0u, blockStore->createdBlocks());
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(maxChildrenPerInnerNode, blockStore->distinctWrittenBlocks().size());
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_Threelevel_InTwoInner_ByReading) {
auto blockId = CreateFullThreeLevel()->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
TraverseByReading(tree.get(), 3, 3 + maxChildrenPerInnerNode);
EXPECT_EQ(4u + maxChildrenPerInnerNode, blockStore->loadedBlocks().size()); // Loads both inner nodes and the requested leaves. Also has to load rightmost inner node and leaf additionally in the beginning to determine size.
EXPECT_EQ(0u, blockStore->createdBlocks());
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(0u, blockStore->distinctWrittenBlocks().size());
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_Threelevel_WholeInner_ByWriting) {
auto blockId = CreateFullThreeLevel()->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
TraverseByWriting(tree.get(), maxChildrenPerInnerNode, 2*maxChildrenPerInnerNode);
EXPECT_EQ(1u, blockStore->loadedBlocks().size()); // Loads inner node. Doesn't load the leaves, they're just overwritten.
EXPECT_EQ(0u, blockStore->createdBlocks());
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(maxChildrenPerInnerNode, blockStore->distinctWrittenBlocks().size());
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_Threelevel_WholeInner_ByReading) {
auto blockId = CreateFullThreeLevel()->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
TraverseByReading(tree.get(), maxChildrenPerInnerNode, 2*maxChildrenPerInnerNode);
EXPECT_EQ(3u + maxChildrenPerInnerNode, blockStore->loadedBlocks().size()); // Loads inner node and all requested leaves. Also has to load rightmost inner node and leaf additionally in the beginning to determine size.
EXPECT_EQ(0u, blockStore->createdBlocks());
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(0u, blockStore->distinctWrittenBlocks().size());
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_GrowingTree_StartingInside) {
auto blockId = CreateInner({CreateLeaf(), CreateLeaf()})->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
TraverseByWriting(tree.get(), 1, 4);
EXPECT_EQ(1u, blockStore->loadedBlocks().size()); // Loads last old child (for growing it)
EXPECT_EQ(2u, blockStore->createdBlocks());
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(2u, blockStore->distinctWrittenBlocks().size()); // write the data and add children to inner node
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_GrowingTree_StartingOutside_TwoLevel) {
auto blockId = CreateInner({CreateLeaf(), CreateLeaf()})->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
TraverseByWriting(tree.get(), 4, 5);
EXPECT_EQ(1u, blockStore->loadedBlocks().size()); // Loads last old leaf for growing it
EXPECT_EQ(3u, blockStore->createdBlocks());
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(1u, blockStore->distinctWrittenBlocks().size()); // add child to inner node
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_GrowingTree_StartingOutside_ThreeLevel) {
auto blockId = CreateInner({CreateFullTwoLevel(), CreateFullTwoLevel()})->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
TraverseByWriting(tree.get(), 2*maxChildrenPerInnerNode+1, 2*maxChildrenPerInnerNode+2);
EXPECT_EQ(2u, blockStore->loadedBlocks().size()); // Loads last old leaf (and its inner node) for growing it
EXPECT_EQ(3u, blockStore->createdBlocks()); // inner node and two leaves
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(1u, blockStore->distinctWrittenBlocks().size()); // add children to existing inner node
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_GrowingTree_StartingAtBeginOfChild) {
auto blockId = CreateInner({CreateFullTwoLevel(), CreateFullTwoLevel()})->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
TraverseByWriting(tree.get(), maxChildrenPerInnerNode, 3*maxChildrenPerInnerNode);
EXPECT_EQ(2u, blockStore->loadedBlocks().size()); // Loads inner node and one leaf to check whether we have to grow it. Doesn't load the leaves, but returns the keys of the leaves to the callback.
EXPECT_EQ(1u + maxChildrenPerInnerNode, blockStore->createdBlocks()); // Creates an inner node and its leaves
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(maxChildrenPerInnerNode + 1u, blockStore->distinctWrittenBlocks().size()); // write data and add children to existing inner node
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_GrowingTreeDepth_StartingInOldDepth) {
auto blockId = CreateInner({CreateLeaf(), CreateLeaf()})->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
TraverseByWriting(tree.get(), 4, maxChildrenPerInnerNode+2);
EXPECT_EQ(1u, blockStore->loadedBlocks().size()); // Loads last old leaf for growing it
EXPECT_EQ(2u + maxChildrenPerInnerNode, blockStore->createdBlocks()); // 2x new inner node + leaves
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(1u, blockStore->distinctWrittenBlocks().size()); // Add children to existing inner node
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_GrowingTreeDepth_StartingInOldDepth_ResizeLastLeaf) {
auto blockId = CreateInner({CreateLeaf(), CreateLeafWithSize(5)})->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
TraverseByWriting(tree.get(), 4, maxChildrenPerInnerNode+2);
EXPECT_EQ(1u, blockStore->loadedBlocks().size()); // Loads last old leaf for growing it
EXPECT_EQ(2u + maxChildrenPerInnerNode, blockStore->createdBlocks()); // 2x new inner node + leaves
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(2u, blockStore->distinctWrittenBlocks().size()); // Resize last leaf and add children to existing inner node
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_GrowingTreeDepth_StartingInNewDepth) {
auto blockId = CreateInner({CreateLeaf(), CreateLeaf()})->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
TraverseByWriting(tree.get(), maxChildrenPerInnerNode, maxChildrenPerInnerNode+2);
EXPECT_EQ(1u, blockStore->loadedBlocks().size()); // Loads last old leaf for growing it
EXPECT_EQ(2u + maxChildrenPerInnerNode, blockStore->createdBlocks()); // 2x new inner node + leaves
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(1u, blockStore->distinctWrittenBlocks().size()); // Add children to existing inner node
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, TraverseLeaves_GrowingTreeDepth_StartingInNewDepth_ResizeLastLeaf) {
auto blockId = CreateInner({CreateLeaf(), CreateLeafWithSize(5)})->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
TraverseByWriting(tree.get(), maxChildrenPerInnerNode, maxChildrenPerInnerNode+2);
EXPECT_EQ(1u, blockStore->loadedBlocks().size()); // Loads last old leaf for growing it
EXPECT_EQ(2u + maxChildrenPerInnerNode, blockStore->createdBlocks()); // 2x new inner node + leaves
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(2u, blockStore->distinctWrittenBlocks().size()); // Resize last leaf and add children to existing inner node
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, ResizeNumBytes_ZeroToZero) {
auto blockId = CreateLeafWithSize(0)->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
tree->resizeNumBytes(0);
EXPECT_EQ(0u, blockStore->loadedBlocks().size());
EXPECT_EQ(0u, blockStore->createdBlocks());
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(0u, blockStore->distinctWrittenBlocks().size());
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, ResizeNumBytes_GrowOneLeaf) {
auto blockId = CreateLeafWithSize(0)->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
tree->resizeNumBytes(5);
EXPECT_EQ(0u, blockStore->loadedBlocks().size());
EXPECT_EQ(0u, blockStore->createdBlocks());
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(1u, blockStore->distinctWrittenBlocks().size());
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, ResizeNumBytes_ShrinkOneLeaf) {
auto blockId = CreateLeafWithSize(5)->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
tree->resizeNumBytes(2);
EXPECT_EQ(0u, blockStore->loadedBlocks().size());
EXPECT_EQ(0u, blockStore->createdBlocks());
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(1u, blockStore->distinctWrittenBlocks().size());
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, ResizeNumBytes_ShrinkOneLeafToZero) {
auto blockId = CreateLeafWithSize(5)->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
tree->resizeNumBytes(0);
EXPECT_EQ(0u, blockStore->loadedBlocks().size());
EXPECT_EQ(0u, blockStore->createdBlocks());
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(1u, blockStore->distinctWrittenBlocks().size());
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, ResizeNumBytes_GrowOneLeafInLargerTree) {
auto blockId = CreateInner({CreateFullTwoLevel(), CreateInner({CreateLeaf(), CreateLeafWithSize(5)})})->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
tree->resizeNumBytes(maxBytesPerLeaf*(maxChildrenPerInnerNode+1)+6); // Grow by one byte
EXPECT_EQ(2u, blockStore->loadedBlocks().size()); // Load inner node and leaf
EXPECT_EQ(0u, blockStore->createdBlocks());
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(1u, blockStore->distinctWrittenBlocks().size());
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, ResizeNumBytes_GrowByOneLeaf) {
auto blockId = CreateInner({CreateLeaf(), CreateLeaf()})->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
tree->resizeNumBytes(maxBytesPerLeaf*2+1); // Grow by one byte
EXPECT_EQ(1u, blockStore->loadedBlocks().size());
EXPECT_EQ(1u, blockStore->createdBlocks());
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(1u, blockStore->distinctWrittenBlocks().size()); // add child to inner node
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, ResizeNumBytes_GrowByOneLeaf_GrowLastLeaf) {
auto blockId = CreateInner({CreateLeaf(), CreateLeafWithSize(5)})->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
tree->resizeNumBytes(maxBytesPerLeaf*2+1); // Grow by one byte
EXPECT_EQ(1u, blockStore->loadedBlocks().size());
EXPECT_EQ(1u, blockStore->createdBlocks());
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(2u, blockStore->distinctWrittenBlocks().size()); // add child to inner node and resize old last leaf
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, ResizeNumBytes_ShrinkByOneLeaf) {
auto blockId = CreateInner({CreateLeaf(), CreateLeaf(), CreateLeaf()})->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
tree->resizeNumBytes(2*maxBytesPerLeaf-1);
EXPECT_EQ(1u, blockStore->loadedBlocks().size());
EXPECT_EQ(0u, blockStore->createdBlocks());
EXPECT_EQ(1u, blockStore->removedBlocks().size());
EXPECT_EQ(2u, blockStore->distinctWrittenBlocks().size()); // resize new last leaf and remove leaf from inner node
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, ResizeNumBytes_IncreaseTreeDepth_0to1) {
auto blockId = CreateLeaf()->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
tree->resizeNumBytes(maxBytesPerLeaf+1);
EXPECT_EQ(0u, blockStore->loadedBlocks().size());
EXPECT_EQ(2u, blockStore->createdBlocks());
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(1u, blockStore->distinctWrittenBlocks().size()); // rewrite root node to be an inner node
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, ResizeNumBytes_IncreaseTreeDepth_1to2) {
auto blockId = CreateFullTwoLevel()->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
tree->resizeNumBytes(maxBytesPerLeaf*maxChildrenPerInnerNode+1);
EXPECT_EQ(1u, blockStore->loadedBlocks().size()); // check whether we have to grow last leaf
EXPECT_EQ(3u, blockStore->createdBlocks());
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(1u, blockStore->distinctWrittenBlocks().size()); // rewrite root node to be an inner node
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, ResizeNumBytes_IncreaseTreeDepth_0to2) {
auto blockId = CreateLeaf()->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
tree->resizeNumBytes(maxBytesPerLeaf*maxChildrenPerInnerNode+1);
EXPECT_EQ(0u, blockStore->loadedBlocks().size());
EXPECT_EQ(3u + maxChildrenPerInnerNode, blockStore->createdBlocks());
EXPECT_EQ(0u, blockStore->removedBlocks().size());
EXPECT_EQ(1u, blockStore->distinctWrittenBlocks().size()); // rewrite root node to be an inner node
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, ResizeNumBytes_DecreaseTreeDepth_1to0) {
auto blockId = CreateInner({CreateLeaf(), CreateLeaf()})->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
tree->resizeNumBytes(maxBytesPerLeaf);
EXPECT_EQ(2u, blockStore->loadedBlocks().size()); // read content of first leaf and load first leaf to replace root with it
EXPECT_EQ(0u, blockStore->createdBlocks());
EXPECT_EQ(2u, blockStore->removedBlocks().size());
EXPECT_EQ(1u, blockStore->distinctWrittenBlocks().size()); // rewrite root node to be a leaf
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, ResizeNumBytes_DecreaseTreeDepth_2to1) {
auto blockId = CreateInner({CreateFullTwoLevel(), CreateInner({CreateLeaf()})})->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
tree->resizeNumBytes(maxBytesPerLeaf*maxChildrenPerInnerNode);
EXPECT_EQ(4u, blockStore->loadedBlocks().size()); // load new last leaf (+inner node), load second inner node to remove its subtree, then load first child of root to replace root with its child.
EXPECT_EQ(0u, blockStore->createdBlocks());
EXPECT_EQ(3u, blockStore->removedBlocks().size());
EXPECT_EQ(1u, blockStore->distinctWrittenBlocks().size()); // rewrite root node to be a leaf
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}
TEST_F(DataTreeTest_Performance, ResizeNumBytes_DecreaseTreeDepth_2to0) {
auto blockId = CreateInner({CreateFullTwoLevel(), CreateInner({CreateLeaf()})})->blockId();
auto tree = treeStore.load(blockId).value();
blockStore->resetCounters();
tree->resizeNumBytes(maxBytesPerLeaf);
EXPECT_EQ(5u, blockStore->loadedBlocks().size()); // load new last leaf (+inner node), load second inner node to remove its subtree, then 2x load first child of root to replace root with its child.
EXPECT_EQ(0u, blockStore->createdBlocks());
EXPECT_EQ(3u + maxChildrenPerInnerNode, blockStore->removedBlocks().size());
EXPECT_EQ(2u, blockStore->distinctWrittenBlocks().size()); // remove children from inner node and rewrite root node to be a leaf
EXPECT_EQ(0u, blockStore->resizedBlocks().size());
}

View File

@ -1,234 +0,0 @@
#include "testutils/DataTreeTest.h"
#include "testutils/TwoLevelDataFixture.h"
#include "blobstore/implementations/onblocks/utils/Math.h"
#include <cpp-utils/data/Data.h>
#include <tuple>
using ::testing::WithParamInterface;
using ::testing::Values;
using ::testing::Combine;
using std::tuple;
using std::get;
using std::function;
using std::mem_fn;
using cpputils::dynamic_pointer_move;
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::BlockId;
using cpputils::Data;
using boost::none;
using cpputils::unique_ref;
class DataTreeTest_ResizeByTraversing: public DataTreeTest {
public:
static constexpr DataNodeLayout LAYOUT = DataNodeLayout(BLOCKSIZE_BYTES);
unique_ref<DataTree> CreateTree(unique_ref<DataNode> root) {
BlockId blockId = root->blockId();
cpputils::destruct(std::move(root));
return treeStore.load(blockId).value();
}
unique_ref<DataTree> CreateLeafTreeWithSize(uint32_t size) {
return CreateTree(CreateLeafWithSize(size));
}
unique_ref<DataTree> CreateTwoLeafTreeWithSecondLeafSize(uint32_t size) {
return CreateTree(CreateTwoLeafWithSecondLeafSize(size));
}
unique_ref<DataTree> CreateFullTwoLevelTreeWithLastLeafSize(uint32_t size) {
return CreateTree(CreateFullTwoLevelWithLastLeafSize(size));
}
unique_ref<DataTree> CreateThreeLevelTreeWithTwoChildrenAndLastLeafSize(uint32_t size) {
return CreateTree(CreateThreeLevelWithTwoChildrenAndLastLeafSize(size));
}
unique_ref<DataTree> CreateThreeLevelTreeWithThreeChildrenAndLastLeafSize(uint32_t size) {
return CreateTree(CreateThreeLevelWithThreeChildrenAndLastLeafSize(size));
}
unique_ref<DataTree> CreateFullThreeLevelTreeWithLastLeafSize(uint32_t size) {
return CreateTree(CreateFullThreeLevelWithLastLeafSize(size));
}
unique_ref<DataTree> CreateFourLevelMinDataTreeWithLastLeafSize(uint32_t size) {
return CreateTree(CreateFourLevelMinDataWithLastLeafSize(size));
}
// NOLINTNEXTLINE(misc-no-recursion)
void EXPECT_IS_LEFTMAXDATA_TREE(const BlockId &blockId) {
auto root = nodeStore->load(blockId).value();
DataInnerNode *inner = dynamic_cast<DataInnerNode*>(root.get());
if (inner != nullptr) {
for (uint32_t i = 0; i < inner->numChildren()-1; ++i) {
EXPECT_IS_MAXDATA_TREE(inner->readChild(i).blockId());
}
EXPECT_IS_LEFTMAXDATA_TREE(inner->readLastChild().blockId());
}
}
// NOLINTNEXTLINE(misc-no-recursion)
void EXPECT_IS_MAXDATA_TREE(const BlockId &blockId) {
auto root = nodeStore->load(blockId).value();
DataInnerNode *inner = dynamic_cast<DataInnerNode*>(root.get());
if (inner != nullptr) {
for (uint32_t i = 0; i < inner->numChildren(); ++i) {
EXPECT_IS_MAXDATA_TREE(inner->readChild(i).blockId());
}
} else {
DataLeafNode *leaf = dynamic_cast<DataLeafNode*>(root.get());
EXPECT_EQ(nodeStore->layout().maxBytesPerLeaf(), leaf->numBytes());
}
}
};
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, std::function<uint32_t (uint32_t oldNumberOfLeaves, uint32_t newNumberOfLeaves)>>> {
public:
DataTreeTest_ResizeByTraversing_P()
: oldLastLeafSize(get<1>(GetParam())),
tree(get<0>(GetParam())(this, oldLastLeafSize)),
numberOfLeavesToAdd(get<2>(GetParam())),
newNumberOfLeaves(tree->numLeaves()+numberOfLeavesToAdd),
traversalBeginIndex(get<3>(GetParam())(tree->numLeaves(), newNumberOfLeaves)),
ZEROES(LAYOUT.maxBytesPerLeaf())
{
ZEROES.FillWithZeroes();
}
void GrowTree(const BlockId &blockId) {
auto tree = treeStore.load(blockId);
GrowTree(tree.get().get());
}
void GrowTree(DataTree *tree) {
uint64_t maxBytesPerLeaf = tree->maxBytesPerLeaf();
uint64_t offset = traversalBeginIndex * maxBytesPerLeaf;
uint64_t count = newNumberOfLeaves * maxBytesPerLeaf - offset;
Data data(count);
data.FillWithZeroes();
tree->writeBytes(data.data(), offset, count);
tree->flush();
}
// NOLINTNEXTLINE(misc-no-recursion)
unique_ref<DataLeafNode> LastLeaf(const BlockId &blockId) {
auto root = nodeStore->load(blockId).value();
auto leaf = dynamic_pointer_move<DataLeafNode>(root);
if (leaf != none) {
return std::move(*leaf);
}
auto inner = dynamic_pointer_move<DataInnerNode>(root).value();
return LastLeaf(inner->readLastChild().blockId());
}
uint32_t oldLastLeafSize;
unique_ref<DataTree> tree;
uint32_t numberOfLeavesToAdd;
uint32_t newNumberOfLeaves;
uint32_t traversalBeginIndex;
Data ZEROES;
};
INSTANTIATE_TEST_SUITE_P(DataTreeTest_ResizeByTraversing_P, DataTreeTest_ResizeByTraversing_P,
Combine(
//Tree we're starting with
Values<function<unique_ref<DataTree>(DataTreeTest_ResizeByTraversing*, uint32_t)>>(
mem_fn(&DataTreeTest_ResizeByTraversing::CreateLeafTreeWithSize),
mem_fn(&DataTreeTest_ResizeByTraversing::CreateTwoLeafTreeWithSecondLeafSize),
mem_fn(&DataTreeTest_ResizeByTraversing::CreateFullTwoLevelTreeWithLastLeafSize),
mem_fn(&DataTreeTest_ResizeByTraversing::CreateThreeLevelTreeWithTwoChildrenAndLastLeafSize),
mem_fn(&DataTreeTest_ResizeByTraversing::CreateThreeLevelTreeWithThreeChildrenAndLastLeafSize),
mem_fn(&DataTreeTest_ResizeByTraversing::CreateFullThreeLevelTreeWithLastLeafSize),
mem_fn(&DataTreeTest_ResizeByTraversing::CreateFourLevelMinDataTreeWithLastLeafSize)
),
//Last leaf size of the start tree
Values(
0u,
1u,
10u,
DataTreeTest_ResizeByTraversing::LAYOUT.maxBytesPerLeaf()
),
//Number of leaves we're adding
Values(
1u,
2u,
DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode(), //Full two level tree
2* DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode(), //Three level tree with two 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() + 1 //Four level mindata tree
),
//Decide the traversal begin index
Values(
[] (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) {
GrowTree(tree.get());
EXPECT_IS_LEFTMAXDATA_TREE(tree->blockId());
}
TEST_P(DataTreeTest_ResizeByTraversing_P, NumLeavesIsCorrect_FromCache) {
tree->numLeaves(); // fill cache with old value
GrowTree(tree.get());
// 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.
EXPECT_EQ(newNumberOfLeaves, tree->numLeaves());
}
TEST_P(DataTreeTest_ResizeByTraversing_P, NumLeavesIsCorrect) {
GrowTree(tree.get());
// 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.
EXPECT_EQ(newNumberOfLeaves, tree->forceComputeNumLeaves());
}
TEST_P(DataTreeTest_ResizeByTraversing_P, DepthFlagsAreCorrect) {
GrowTree(tree.get());
uint32_t depth = ceil(log(newNumberOfLeaves)/log(DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode()));
CHECK_DEPTH(depth, tree->blockId());
}
TEST_P(DataTreeTest_ResizeByTraversing_P, KeyDoesntChange) {
BlockId blockId = tree->blockId();
tree->flush();
GrowTree(tree.get());
EXPECT_EQ(blockId, tree->blockId());
}
TEST_P(DataTreeTest_ResizeByTraversing_P, DataStaysIntact) {
uint32_t oldNumberOfLeaves = std::max(UINT64_C(1), ceilDivision(tree->numBytes(), static_cast<uint64_t>(nodeStore->layout().maxBytesPerLeaf())));
TwoLevelDataFixture data(nodeStore, TwoLevelDataFixture::SizePolicy::Unchanged);
BlockId blockId = tree->blockId();
cpputils::destruct(std::move(tree));
data.FillInto(nodeStore->load(blockId).get().get());
GrowTree(blockId);
if (traversalBeginIndex < oldNumberOfLeaves) {
// Traversal wrote over part of the pre-existing data, we can only check the data before it.
if (traversalBeginIndex != 0) {
data.EXPECT_DATA_CORRECT(nodeStore->load(blockId).get().get(), static_cast<int>(traversalBeginIndex - 1));
}
} else {
// Here, traversal was entirely outside the preexisting data, we can check all preexisting data.
data.EXPECT_DATA_CORRECT(nodeStore->load(blockId).get().get(), oldNumberOfLeaves, oldLastLeafSize);
}
}

View File

@ -1,266 +0,0 @@
#include "testutils/DataTreeTest.h"
#include "testutils/TwoLevelDataFixture.h"
#include "blobstore/implementations/onblocks/utils/Math.h"
#include <cpp-utils/data/Data.h>
#include <tuple>
using ::testing::WithParamInterface;
using ::testing::Values;
using ::testing::Combine;
using std::tuple;
using std::get;
using std::function;
using std::mem_fn;
using cpputils::dynamic_pointer_move;
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::BlockId;
using cpputils::Data;
using boost::none;
using cpputils::unique_ref;
class DataTreeTest_ResizeNumBytes: public DataTreeTest {
public:
static constexpr DataNodeLayout LAYOUT = DataNodeLayout(BLOCKSIZE_BYTES);
unique_ref<DataTree> CreateTree(unique_ref<DataNode> root) {
BlockId blockId = root->blockId();
cpputils::destruct(std::move(root));
return treeStore.load(blockId).value();
}
unique_ref<DataTree> CreateLeafTreeWithSize(uint32_t size) {
return CreateTree(CreateLeafWithSize(size));
}
unique_ref<DataTree> CreateTwoLeafTreeWithSecondLeafSize(uint32_t size) {
return CreateTree(CreateTwoLeafWithSecondLeafSize(size));
}
unique_ref<DataTree> CreateFullTwoLevelTreeWithLastLeafSize(uint32_t size) {
return CreateTree(CreateFullTwoLevelWithLastLeafSize(size));
}
unique_ref<DataTree> CreateThreeLevelTreeWithTwoChildrenAndLastLeafSize(uint32_t size) {
return CreateTree(CreateThreeLevelWithTwoChildrenAndLastLeafSize(size));
}
unique_ref<DataTree> CreateThreeLevelTreeWithThreeChildrenAndLastLeafSize(uint32_t size) {
return CreateTree(CreateThreeLevelWithThreeChildrenAndLastLeafSize(size));
}
unique_ref<DataTree> CreateFullThreeLevelTreeWithLastLeafSize(uint32_t size) {
return CreateTree(CreateFullThreeLevelWithLastLeafSize(size));
}
unique_ref<DataTree> CreateFourLevelMinDataTreeWithLastLeafSize(uint32_t size) {
return CreateTree(CreateFourLevelMinDataWithLastLeafSize(size));
}
// NOLINTNEXTLINE(misc-no-recursion)
void EXPECT_IS_LEFTMAXDATA_TREE(const BlockId &blockId) {
auto root = nodeStore->load(blockId).value();
DataInnerNode *inner = dynamic_cast<DataInnerNode*>(root.get());
if (inner != nullptr) {
for (uint32_t i = 0; i < inner->numChildren()-1; ++i) {
EXPECT_IS_MAXDATA_TREE(inner->readChild(i).blockId());
}
EXPECT_IS_LEFTMAXDATA_TREE(inner->readLastChild().blockId());
}
}
// NOLINTNEXTLINE(misc-no-recursion)
void EXPECT_IS_MAXDATA_TREE(const BlockId &blockId) {
auto root = nodeStore->load(blockId).value();
DataInnerNode *inner = dynamic_cast<DataInnerNode*>(root.get());
if (inner != nullptr) {
for (uint32_t i = 0; i < inner->numChildren(); ++i) {
EXPECT_IS_MAXDATA_TREE(inner->readChild(i).blockId());
}
} else {
DataLeafNode *leaf = dynamic_cast<DataLeafNode*>(root.get());
EXPECT_EQ(nodeStore->layout().maxBytesPerLeaf(), leaf->numBytes());
}
}
};
constexpr DataNodeLayout DataTreeTest_ResizeNumBytes::LAYOUT;
class DataTreeTest_ResizeNumBytes_P: public DataTreeTest_ResizeNumBytes, public WithParamInterface<tuple<function<unique_ref<DataTree>(DataTreeTest_ResizeNumBytes*, uint32_t)>, uint32_t, uint32_t, uint32_t>> {
public:
DataTreeTest_ResizeNumBytes_P()
: oldLastLeafSize(get<1>(GetParam())),
tree(get<0>(GetParam())(this, oldLastLeafSize)),
newNumberOfLeaves(get<2>(GetParam())),
newLastLeafSize(get<3>(GetParam())),
newSize((newNumberOfLeaves-1) * LAYOUT.maxBytesPerLeaf() + newLastLeafSize),
ZEROES(LAYOUT.maxBytesPerLeaf())
{
ZEROES.FillWithZeroes();
}
void ResizeTree(const BlockId &blockId, uint64_t size) {
treeStore.load(blockId).get()->resizeNumBytes(size);
}
// NOLINTNEXTLINE(misc-no-recursion)
unique_ref<DataLeafNode> LastLeaf(const BlockId &blockId) {
auto root = nodeStore->load(blockId).value();
auto leaf = dynamic_pointer_move<DataLeafNode>(root);
if (leaf != none) {
return std::move(*leaf);
}
auto inner = dynamic_pointer_move<DataInnerNode>(root).value();
return LastLeaf(inner->readLastChild().blockId());
}
uint32_t oldLastLeafSize;
unique_ref<DataTree> tree;
uint32_t newNumberOfLeaves;
uint32_t newLastLeafSize;
uint64_t newSize;
Data ZEROES;
};
INSTANTIATE_TEST_SUITE_P(DataTreeTest_ResizeNumBytes_P, DataTreeTest_ResizeNumBytes_P,
Combine(
//Tree we're starting with
Values<function<unique_ref<DataTree>(DataTreeTest_ResizeNumBytes*, uint32_t)>>(
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);
tree->flush();
EXPECT_IS_LEFTMAXDATA_TREE(tree->blockId());
}
TEST_P(DataTreeTest_ResizeNumBytes_P, NumBytesIsCorrect) {
tree->resizeNumBytes(newSize);
tree->flush();
// tree->numBytes() 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->numBytes());
}
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.
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.
EXPECT_EQ(newNumberOfLeaves, tree->numLeaves());
}
TEST_P(DataTreeTest_ResizeNumBytes_P, DepthFlagsAreCorrect) {
tree->resizeNumBytes(newSize);
tree->flush();
uint32_t depth = ceil(log(newNumberOfLeaves)/log(DataTreeTest_ResizeNumBytes::LAYOUT.maxChildrenPerInnerNode()) - 0.00000000001); // The subtraction takes care of double inaccuracies if newNumberOfLeaves == maxChildrenPerInnerNode
CHECK_DEPTH(depth, tree->blockId());
}
TEST_P(DataTreeTest_ResizeNumBytes_P, KeyDoesntChange) {
BlockId blockId = tree->blockId();
tree->flush();
tree->resizeNumBytes(newSize);
EXPECT_EQ(blockId, tree->blockId());
}
TEST_P(DataTreeTest_ResizeNumBytes_P, DataStaysIntact) {
uint32_t oldNumberOfLeaves = std::max(UINT64_C(1), ceilDivision(tree->numBytes(), static_cast<uint64_t>(nodeStore->layout().maxBytesPerLeaf())));
TwoLevelDataFixture data(nodeStore, TwoLevelDataFixture::SizePolicy::Unchanged);
BlockId blockId = tree->blockId();
cpputils::destruct(std::move(tree));
data.FillInto(nodeStore->load(blockId).get().get());
ResizeTree(blockId, newSize);
if (oldNumberOfLeaves < newNumberOfLeaves || (oldNumberOfLeaves == newNumberOfLeaves && oldLastLeafSize < newLastLeafSize)) {
data.EXPECT_DATA_CORRECT(nodeStore->load(blockId).get().get(), oldNumberOfLeaves, oldLastLeafSize);
} else {
data.EXPECT_DATA_CORRECT(nodeStore->load(blockId).get().get(), newNumberOfLeaves, newLastLeafSize);
}
}
TEST_P(DataTreeTest_ResizeNumBytes_P, UnneededBlocksGetDeletedWhenShrinking) {
tree->resizeNumBytes(newSize);
tree->flush();
uint64_t expectedNumNodes = 1; // 1 for the root node
uint64_t nodesOnCurrentLevel = newNumberOfLeaves;
while (nodesOnCurrentLevel > 1) {
expectedNumNodes += nodesOnCurrentLevel;
nodesOnCurrentLevel = ceilDivision(nodesOnCurrentLevel, nodeStore->layout().maxChildrenPerInnerNode());
}
EXPECT_EQ(expectedNumNodes, nodeStore->numNodes());
}
//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);
BlockId blockId = tree->blockId();
cpputils::destruct(std::move(tree));
auto leaf = LoadLeafNode(blockId);
EXPECT_EQ(0u, leaf->numBytes());
}
TEST_F(DataTreeTest_ResizeNumBytes, ResizeToZero_blockIdDoesntChange) {
auto tree = CreateThreeLevelTreeWithThreeChildrenAndLastLeafSize(10u);
BlockId blockId = tree->blockId();
tree->resizeNumBytes(0);
tree->flush();
EXPECT_EQ(blockId, tree->blockId());
}
TEST_F(DataTreeTest_ResizeNumBytes, ResizeToZero_UnneededBlocksGetDeletedWhenShrinking) {
auto tree = CreateThreeLevelTreeWithThreeChildrenAndLastLeafSize(10u);
tree->resizeNumBytes(0);
tree->flush();
EXPECT_EQ(1u, nodeStore->numNodes());
}

View File

@ -1,449 +0,0 @@
#include "testutils/DataTreeTest.h"
#include <blobstore/implementations/onblocks/datatreestore/impl/LeafTraverser.h>
#include <gmock/gmock.h>
using ::testing::Invoke;
using ::testing::Eq;
using blobstore::onblocks::datanodestore::DataLeafNode;
using blobstore::onblocks::datanodestore::DataInnerNode;
using blobstore::onblocks::datanodestore::DataNode;
using blobstore::onblocks::datatreestore::LeafHandle;
using blobstore::onblocks::datatreestore::LeafTraverser;
using blockstore::BlockId;
using cpputils::unique_ref;
using cpputils::Data;
using std::shared_ptr;
using std::make_shared;
class TraversorMock {
public:
MOCK_METHOD(void, calledExistingLeaf, (DataLeafNode*, bool, uint32_t));
MOCK_METHOD(shared_ptr<Data>, calledCreateLeaf, (uint32_t));
};
MATCHER_P(KeyEq, expected, "node blockId equals") {
return arg->blockId() == expected;
}
class LeafTraverserTest: public DataTreeTest {
public:
LeafTraverserTest() :traversor() {}
unique_ref<DataInnerNode> CreateThreeLevel() {
return CreateInner({
CreateFullTwoLevel(),
CreateFullTwoLevel(),
CreateFullTwoLevel(),
CreateFullTwoLevel(),
CreateFullTwoLevel(),
CreateInner({CreateLeaf(), CreateLeaf(), CreateLeaf()})});
}
unique_ref<DataInnerNode> CreateFourLevel() {
return CreateInner({
CreateFullThreeLevel(),
CreateFullThreeLevel(),
CreateInner({CreateFullTwoLevel(), CreateInner({CreateLeaf()})})
});
}
void EXPECT_CREATE_LEAF(uint32_t leafIndex) {
uint64_t maxBytesPerLeaf = nodeStore->layout().maxBytesPerLeaf();
EXPECT_CALL(traversor, calledCreateLeaf(Eq(leafIndex))).Times(1).WillOnce(Invoke([maxBytesPerLeaf] (uint32_t) {
return make_shared<Data>(maxBytesPerLeaf);
}));
}
void EXPECT_TRAVERSE_LEAF(const BlockId &blockId, bool isRightBorderLeaf, uint32_t leafIndex) {
EXPECT_CALL(traversor, calledExistingLeaf(KeyEq(blockId), isRightBorderLeaf, leafIndex)).Times(1);
}
void EXPECT_TRAVERSE_ALL_CHILDREN_OF(const DataInnerNode &node, bool isRightBorderNode, uint32_t firstLeafIndex) {
for (unsigned int i = 0; i < node.numChildren(); ++i) {
EXPECT_TRAVERSE_LEAF(node.readChild(i).blockId(), isRightBorderNode && i == node.numChildren()-1, firstLeafIndex+i);
}
}
void EXPECT_DONT_TRAVERSE_ANY_LEAVES() {
EXPECT_CALL(traversor, calledExistingLeaf(testing::_, testing::_, testing::_)).Times(0);
EXPECT_CALL(traversor, calledCreateLeaf(testing::_)).Times(0);
}
void TraverseLeaves(unique_ref<DataNode> root, uint32_t beginIndex, uint32_t endIndex, bool expectReadOnly) {
root->flush();
auto tree = treeStore.load(root->blockId()).value();
auto* old_root = root.get();
LeafTraverser(nodeStore, expectReadOnly).traverseAndUpdateRoot(&root, beginIndex, endIndex, [this] (uint32_t nodeIndex, bool isRightBorderNode,LeafHandle leaf) {
traversor.calledExistingLeaf(leaf.node(), isRightBorderNode, nodeIndex);
}, [this] (uint32_t nodeIndex) -> Data {
return traversor.calledCreateLeaf(nodeIndex)->copy();
}, [] (auto) {});
if (expectReadOnly) {
EXPECT_EQ(old_root, root.get());
} else {
EXPECT_NE(old_root, root.get());
}
}
TraversorMock traversor;
};
TEST_F(LeafTraverserTest, TraverseSingleLeafTree) {
unique_ref<DataNode> root = CreateLeaf();
EXPECT_TRAVERSE_LEAF(root->blockId(), true, 0);
TraverseLeaves(std::move(root), 0, 1, true);
}
TEST_F(LeafTraverserTest, TraverseNothingInSingleLeafTree1) {
unique_ref<DataNode> root = CreateLeaf();
EXPECT_DONT_TRAVERSE_ANY_LEAVES();
TraverseLeaves(std::move(root), 0, 0, true);
}
TEST_F(LeafTraverserTest, TraverseNothingInSingleLeafTree2) {
unique_ref<DataNode> root = CreateLeaf();
EXPECT_DONT_TRAVERSE_ANY_LEAVES();
TraverseLeaves(std::move(root), 1, 1, true);
}
TEST_F(LeafTraverserTest, TraverseFirstLeafOfFullTwolevelTree) {
auto root = CreateFullTwoLevel();
EXPECT_TRAVERSE_LEAF(root->readChild(0).blockId(), false, 0);
TraverseLeaves(std::move(root), 0, 1, true);
}
TEST_F(LeafTraverserTest, TraverseMiddleLeafOfFullTwolevelTree) {
auto root = CreateFullTwoLevel();
EXPECT_TRAVERSE_LEAF(root->readChild(5).blockId(), false, 5);
TraverseLeaves(std::move(root), 5, 6, true);
}
TEST_F(LeafTraverserTest, TraverseLastLeafOfFullTwolevelTree) {
auto root = CreateFullTwoLevel();
EXPECT_TRAVERSE_LEAF(root->readChild(nodeStore->layout().maxChildrenPerInnerNode()-1).blockId(), true, nodeStore->layout().maxChildrenPerInnerNode()-1);
TraverseLeaves(std::move(root), nodeStore->layout().maxChildrenPerInnerNode()-1, nodeStore->layout().maxChildrenPerInnerNode(), true);
}
TEST_F(LeafTraverserTest, TraverseNothingInFullTwolevelTree1) {
auto root = CreateFullTwoLevel();
EXPECT_DONT_TRAVERSE_ANY_LEAVES();
TraverseLeaves(std::move(root), 0, 0, true);
}
TEST_F(LeafTraverserTest, TraverseNothingInFullTwolevelTree2) {
auto root = CreateFullTwoLevel();
EXPECT_DONT_TRAVERSE_ANY_LEAVES();
TraverseLeaves(std::move(root), nodeStore->layout().maxChildrenPerInnerNode(), nodeStore->layout().maxChildrenPerInnerNode(), true);
}
TEST_F(LeafTraverserTest, TraverseFirstLeafOfThreeLevelMinDataTree) {
auto root = CreateThreeLevelMinData();
EXPECT_TRAVERSE_LEAF(LoadInnerNode(root->readChild(0).blockId())->readChild(0).blockId(), false, 0);
TraverseLeaves(std::move(root), 0, 1, true);
}
TEST_F(LeafTraverserTest, TraverseMiddleLeafOfThreeLevelMinDataTree) {
auto root = CreateThreeLevelMinData();
EXPECT_TRAVERSE_LEAF(LoadInnerNode(root->readChild(0).blockId())->readChild(5).blockId(), false, 5);
TraverseLeaves(std::move(root), 5, 6, true);
}
TEST_F(LeafTraverserTest, TraverseLastLeafOfThreeLevelMinDataTree) {
auto root = CreateThreeLevelMinData();
EXPECT_TRAVERSE_LEAF(LoadInnerNode(root->readChild(1).blockId())->readChild(0).blockId(), true, nodeStore->layout().maxChildrenPerInnerNode());
TraverseLeaves(std::move(root), nodeStore->layout().maxChildrenPerInnerNode(), nodeStore->layout().maxChildrenPerInnerNode()+1, true);
}
TEST_F(LeafTraverserTest, TraverseAllLeavesOfFullTwolevelTree) {
auto root = CreateFullTwoLevel();
EXPECT_TRAVERSE_ALL_CHILDREN_OF(*root, true, 0);
TraverseLeaves(std::move(root), 0, nodeStore->layout().maxChildrenPerInnerNode(), true);
}
TEST_F(LeafTraverserTest, TraverseAllLeavesOfThreelevelMinDataTree) {
auto root = CreateThreeLevelMinData();
EXPECT_TRAVERSE_ALL_CHILDREN_OF(*LoadInnerNode(root->readChild(0).blockId()), false, 0);
EXPECT_TRAVERSE_LEAF(LoadInnerNode(root->readChild(1).blockId())->readChild(0).blockId(), true, nodeStore->layout().maxChildrenPerInnerNode());
TraverseLeaves(std::move(root), 0, nodeStore->layout().maxChildrenPerInnerNode()+1, true);
}
TEST_F(LeafTraverserTest, TraverseFirstChildOfThreelevelMinDataTree) {
auto root = CreateThreeLevelMinData();
EXPECT_TRAVERSE_ALL_CHILDREN_OF(*LoadInnerNode(root->readChild(0).blockId()), false, 0);
TraverseLeaves(std::move(root), 0, nodeStore->layout().maxChildrenPerInnerNode(), true);
}
TEST_F(LeafTraverserTest, TraverseFirstPartOfFullTwolevelTree) {
auto root = CreateFullTwoLevel();
for (unsigned int i = 0; i < 5; ++i) {
EXPECT_TRAVERSE_LEAF(root->readChild(i).blockId(), false, i);
}
TraverseLeaves(std::move(root), 0, 5, true);
}
TEST_F(LeafTraverserTest, TraverseInnerPartOfFullTwolevelTree) {
auto root = CreateFullTwoLevel();
for (unsigned int i = 5; i < 10; ++i) {
EXPECT_TRAVERSE_LEAF(root->readChild(i).blockId(), false, i);
}
TraverseLeaves(std::move(root), 5, 10, true);
}
TEST_F(LeafTraverserTest, TraverseLastPartOfFullTwolevelTree) {
auto root = CreateFullTwoLevel();
for (unsigned int i = 5; i < nodeStore->layout().maxChildrenPerInnerNode(); ++i) {
EXPECT_TRAVERSE_LEAF(root->readChild(i).blockId(), i==nodeStore->layout().maxChildrenPerInnerNode()-1, i);
}
TraverseLeaves(std::move(root), 5, nodeStore->layout().maxChildrenPerInnerNode(), true);
}
TEST_F(LeafTraverserTest, TraverseFirstPartOfThreelevelMinDataTree) {
auto root = CreateThreeLevelMinData();
auto node = LoadInnerNode(root->readChild(0).blockId());
for (unsigned int i = 0; i < 5; ++i) {
EXPECT_TRAVERSE_LEAF(node->readChild(i).blockId(), false, i);
}
TraverseLeaves(std::move(root), 0, 5, true);
}
TEST_F(LeafTraverserTest, TraverseInnerPartOfThreelevelMinDataTree) {
auto root = CreateThreeLevelMinData();
auto node = LoadInnerNode(root->readChild(0).blockId());
for (unsigned int i = 5; i < 10; ++i) {
EXPECT_TRAVERSE_LEAF(node->readChild(i).blockId(), false, i);
}
TraverseLeaves(std::move(root), 5, 10, true);
}
TEST_F(LeafTraverserTest, TraverseLastPartOfThreelevelMinDataTree) {
auto root = CreateThreeLevelMinData();
auto node = LoadInnerNode(root->readChild(0).blockId());
for (unsigned int i = 5; i < nodeStore->layout().maxChildrenPerInnerNode(); ++i) {
EXPECT_TRAVERSE_LEAF(node->readChild(i).blockId(), false, i);
}
EXPECT_TRAVERSE_LEAF(LoadInnerNode(root->readChild(1).blockId())->readChild(0).blockId(), true, nodeStore->layout().maxChildrenPerInnerNode());
TraverseLeaves(std::move(root), 5, nodeStore->layout().maxChildrenPerInnerNode()+1, true);
}
TEST_F(LeafTraverserTest, TraverseFirstLeafOfThreelevelTree) {
auto root = CreateThreeLevel();
EXPECT_TRAVERSE_LEAF(LoadInnerNode(root->readChild(0).blockId())->readChild(0).blockId(), false, 0);
TraverseLeaves(std::move(root), 0, 1, true);
}
TEST_F(LeafTraverserTest, TraverseLastLeafOfThreelevelTree) {
auto root = CreateThreeLevel();
uint32_t numLeaves = nodeStore->layout().maxChildrenPerInnerNode() * 5 + 3;
EXPECT_TRAVERSE_LEAF(LoadInnerNode(root->readLastChild().blockId())->readLastChild().blockId(), true, numLeaves-1);
TraverseLeaves(std::move(root), numLeaves-1, numLeaves, true);
}
TEST_F(LeafTraverserTest, TraverseMiddleLeafOfThreelevelTree) {
auto root = CreateThreeLevel();
uint32_t wantedLeafIndex = nodeStore->layout().maxChildrenPerInnerNode() * 2 + 5;
EXPECT_TRAVERSE_LEAF(LoadInnerNode(root->readChild(2).blockId())->readChild(5).blockId(), false, wantedLeafIndex);
TraverseLeaves(std::move(root), wantedLeafIndex, wantedLeafIndex+1, true);
}
TEST_F(LeafTraverserTest, TraverseFirstPartOfThreelevelTree) {
auto root = CreateThreeLevel();
//Traverse all leaves in the first two children of the root
for(unsigned int i = 0; i < 2; ++i) {
EXPECT_TRAVERSE_ALL_CHILDREN_OF(*LoadInnerNode(root->readChild(i).blockId()), false, i * nodeStore->layout().maxChildrenPerInnerNode());
}
//Traverse some of the leaves in the third child of the root
auto child = LoadInnerNode(root->readChild(2).blockId());
for(unsigned int i = 0; i < 5; ++i) {
EXPECT_TRAVERSE_LEAF(child->readChild(i).blockId(), false, 2 * nodeStore->layout().maxChildrenPerInnerNode() + i);
}
TraverseLeaves(std::move(root), 0, 2 * nodeStore->layout().maxChildrenPerInnerNode() + 5, true);
}
TEST_F(LeafTraverserTest, TraverseMiddlePartOfThreelevelTree_OnlyFullChildren) {
auto root = CreateThreeLevel();
//Traverse some of the leaves in the second child of the root
auto child = LoadInnerNode(root->readChild(1).blockId());
for(unsigned int i = 5; i < nodeStore->layout().maxChildrenPerInnerNode(); ++i) {
EXPECT_TRAVERSE_LEAF(child->readChild(i).blockId(), false, nodeStore->layout().maxChildrenPerInnerNode() + i);
}
//Traverse all leaves in the third and fourth child of the root
for(unsigned int i = 2; i < 4; ++i) {
EXPECT_TRAVERSE_ALL_CHILDREN_OF(*LoadInnerNode(root->readChild(i).blockId()),false, i * nodeStore->layout().maxChildrenPerInnerNode());
}
//Traverse some of the leaves in the fifth child of the root
child = LoadInnerNode(root->readChild(4).blockId());
for(unsigned int i = 0; i < 5; ++i) {
EXPECT_TRAVERSE_LEAF(child->readChild(i).blockId(), false, 4 * nodeStore->layout().maxChildrenPerInnerNode() + i);
}
TraverseLeaves(std::move(root), nodeStore->layout().maxChildrenPerInnerNode() + 5, 4 * nodeStore->layout().maxChildrenPerInnerNode() + 5, true);
}
TEST_F(LeafTraverserTest, TraverseMiddlePartOfThreelevelTree_AlsoLastNonfullChild) {
auto root = CreateThreeLevel();
//Traverse some of the leaves in the second child of the root
auto child = LoadInnerNode(root->readChild(1).blockId());
for(unsigned int i = 5; i < nodeStore->layout().maxChildrenPerInnerNode(); ++i) {
EXPECT_TRAVERSE_LEAF(child->readChild(i).blockId(), false, nodeStore->layout().maxChildrenPerInnerNode() + i);
}
//Traverse all leaves in the third, fourth and fifth child of the root
for(unsigned int i = 2; i < 5; ++i) {
EXPECT_TRAVERSE_ALL_CHILDREN_OF(*LoadInnerNode(root->readChild(i).blockId()), false, i * nodeStore->layout().maxChildrenPerInnerNode());
}
//Traverse some of the leaves in the sixth child of the root
child = LoadInnerNode(root->readChild(5).blockId());
for(unsigned int i = 0; i < 2; ++i) {
EXPECT_TRAVERSE_LEAF(child->readChild(i).blockId(), false, 5 * nodeStore->layout().maxChildrenPerInnerNode() + i);
}
TraverseLeaves(std::move(root), nodeStore->layout().maxChildrenPerInnerNode() + 5, 5 * nodeStore->layout().maxChildrenPerInnerNode() + 2, true);
}
TEST_F(LeafTraverserTest, TraverseLastPartOfThreelevelTree) {
auto root = CreateThreeLevel();
//Traverse some of the leaves in the second child of the root
auto child = LoadInnerNode(root->readChild(1).blockId());
for(unsigned int i = 5; i < nodeStore->layout().maxChildrenPerInnerNode(); ++i) {
EXPECT_TRAVERSE_LEAF(child->readChild(i).blockId(), false, nodeStore->layout().maxChildrenPerInnerNode() + i);
}
//Traverse all leaves in the third, fourth and fifth child of the root
for(unsigned int i = 2; i < 5; ++i) {
EXPECT_TRAVERSE_ALL_CHILDREN_OF(*LoadInnerNode(root->readChild(i).blockId()), false, i * nodeStore->layout().maxChildrenPerInnerNode());
}
//Traverse all of the leaves in the sixth child of the root
child = LoadInnerNode(root->readChild(5).blockId());
for(unsigned int i = 0; i < child->numChildren(); ++i) {
EXPECT_TRAVERSE_LEAF(child->readChild(i).blockId(), i == child->numChildren()-1, 5 * nodeStore->layout().maxChildrenPerInnerNode() + i);
}
TraverseLeaves(std::move(root), nodeStore->layout().maxChildrenPerInnerNode() + 5, 5 * nodeStore->layout().maxChildrenPerInnerNode() + child->numChildren(), true);
}
TEST_F(LeafTraverserTest, TraverseAllLeavesOfThreelevelTree) {
auto root = CreateThreeLevel();
//Traverse all leaves in the third, fourth and fifth child of the root
for(unsigned int i = 0; i < 5; ++i) {
EXPECT_TRAVERSE_ALL_CHILDREN_OF(*LoadInnerNode(root->readChild(i).blockId()), false, i * nodeStore->layout().maxChildrenPerInnerNode());
}
//Traverse all of the leaves in the sixth child of the root
auto child = LoadInnerNode(root->readChild(5).blockId());
for(unsigned int i = 0; i < child->numChildren(); ++i) {
EXPECT_TRAVERSE_LEAF(child->readChild(i).blockId(), i==child->numChildren()-1, 5 * nodeStore->layout().maxChildrenPerInnerNode() + i);
}
TraverseLeaves(std::move(root), 0, 5 * nodeStore->layout().maxChildrenPerInnerNode() + child->numChildren(), true);
}
TEST_F(LeafTraverserTest, TraverseAllLeavesOfFourLevelTree) {
auto root = CreateFourLevel();
//Traverse all leaves of the full threelevel tree in the first child
auto firstChild = LoadInnerNode(root->readChild(0).blockId());
for(unsigned int i = 0; i < firstChild->numChildren(); ++i) {
EXPECT_TRAVERSE_ALL_CHILDREN_OF(*LoadInnerNode(firstChild->readChild(i).blockId()), false, i * nodeStore->layout().maxChildrenPerInnerNode());
}
//Traverse all leaves of the full threelevel tree in the second child
auto secondChild = LoadInnerNode(root->readChild(1).blockId());
for(unsigned int i = 0; i < secondChild->numChildren(); ++i) {
EXPECT_TRAVERSE_ALL_CHILDREN_OF(*LoadInnerNode(secondChild->readChild(i).blockId()), false, (nodeStore->layout().maxChildrenPerInnerNode() + i) * nodeStore->layout().maxChildrenPerInnerNode());
}
//Traverse all leaves of the non-full threelevel tree in the third child
auto thirdChild = LoadInnerNode(root->readChild(2).blockId());
EXPECT_TRAVERSE_ALL_CHILDREN_OF(*LoadInnerNode(thirdChild->readChild(0).blockId()), false, 2 * nodeStore->layout().maxChildrenPerInnerNode() * nodeStore->layout().maxChildrenPerInnerNode());
EXPECT_TRAVERSE_LEAF(LoadInnerNode(thirdChild->readChild(1).blockId())->readChild(0).blockId(), true, 2 * nodeStore->layout().maxChildrenPerInnerNode() * nodeStore->layout().maxChildrenPerInnerNode() + nodeStore->layout().maxChildrenPerInnerNode());
TraverseLeaves(std::move(root), 0, 2*nodeStore->layout().maxChildrenPerInnerNode()*nodeStore->layout().maxChildrenPerInnerNode() + nodeStore->layout().maxChildrenPerInnerNode() + 1, true);
}
TEST_F(LeafTraverserTest, TraverseMiddlePartOfFourLevelTree) {
auto root = CreateFourLevel();
//Traverse some leaves of the full threelevel tree in the first child
auto firstChild = LoadInnerNode(root->readChild(0).blockId());
auto secondChildOfFirstChild = LoadInnerNode(firstChild->readChild(1).blockId());
for(unsigned int i = 5; i < secondChildOfFirstChild->numChildren(); ++i) {
EXPECT_TRAVERSE_LEAF(secondChildOfFirstChild->readChild(i).blockId(), false, nodeStore->layout().maxChildrenPerInnerNode()+i);
}
for(unsigned int i = 2; i < firstChild->numChildren(); ++i) {
EXPECT_TRAVERSE_ALL_CHILDREN_OF(*LoadInnerNode(firstChild->readChild(i).blockId()), false, i * nodeStore->layout().maxChildrenPerInnerNode());
}
//Traverse all leaves of the full threelevel tree in the second child
auto secondChild = LoadInnerNode(root->readChild(1).blockId());
for(unsigned int i = 0; i < secondChild->numChildren(); ++i) {
EXPECT_TRAVERSE_ALL_CHILDREN_OF(*LoadInnerNode(secondChild->readChild(i).blockId()), false, (nodeStore->layout().maxChildrenPerInnerNode() + i) * nodeStore->layout().maxChildrenPerInnerNode());
}
//Traverse some leaves of the non-full threelevel tree in the third child
auto thirdChild = LoadInnerNode(root->readChild(2).blockId());
auto firstChildOfThirdChild = LoadInnerNode(thirdChild->readChild(0).blockId());
for(unsigned int i = 0; i < firstChildOfThirdChild->numChildren()-1; ++i) {
EXPECT_TRAVERSE_LEAF(firstChildOfThirdChild->readChild(i).blockId(), false, 2 * nodeStore->layout().maxChildrenPerInnerNode()*nodeStore->layout().maxChildrenPerInnerNode()+i);
}
TraverseLeaves(std::move(root), nodeStore->layout().maxChildrenPerInnerNode()+5, 2*nodeStore->layout().maxChildrenPerInnerNode()*nodeStore->layout().maxChildrenPerInnerNode() + nodeStore->layout().maxChildrenPerInnerNode() -1, true);
}
TEST_F(LeafTraverserTest, LastLeafIsAlreadyResizedInCallback) {
unique_ref<DataNode> root = CreateLeaf();
root->flush();
auto* old_root = root.get();
auto tree = treeStore.load(root->blockId()).value();
LeafTraverser(nodeStore, false).traverseAndUpdateRoot(&root, 0, 2, [this] (uint32_t leafIndex, bool /*isRightBorderNode*/, LeafHandle leaf) {
if (leafIndex == 0) {
EXPECT_EQ(nodeStore->layout().maxBytesPerLeaf(), leaf.node()->numBytes());
} else {
EXPECT_TRUE(false) << "only two nodes";
}
}, [] (uint32_t /*nodeIndex*/) -> Data {
return Data(1);
}, [] (auto) {});
EXPECT_NE(old_root, root.get()); // expect that we grew the tree
}
TEST_F(LeafTraverserTest, LastLeafIsAlreadyResizedInCallback_TwoLevel) {
unique_ref<DataNode> root = CreateFullTwoLevelWithLastLeafSize(5);
root->flush();
auto* old_root = root.get();
auto tree = treeStore.load(root->blockId()).value();
LeafTraverser(nodeStore, false).traverseAndUpdateRoot(&root, 0, nodeStore->layout().maxChildrenPerInnerNode()+1, [this] (uint32_t /*leafIndex*/, bool /*isRightBorderNode*/, LeafHandle leaf) {
EXPECT_EQ(nodeStore->layout().maxBytesPerLeaf(), leaf.node()->numBytes());
}, [] (uint32_t /*nodeIndex*/) -> Data {
return Data(1);
}, [] (auto) {});
EXPECT_NE(old_root, root.get()); // expect that we grew the tree
}
TEST_F(LeafTraverserTest, ResizeFromOneLeafToMultipleLeaves) {
auto root = CreateLeaf();
EXPECT_TRAVERSE_LEAF(root->blockId(), false, 0);
//EXPECT_CALL(traversor, calledExistingLeaf(_, false, 0)).Times(1);
for (uint32_t i = 1; i < 10; ++i) {
EXPECT_CREATE_LEAF(i);
}
TraverseLeaves(std::move(root), 0, 10, false);
}
////TODO Refactor the test cases that are too long

View File

@ -1,87 +0,0 @@
#include <gtest/gtest.h>
#include "../testutils/DataTreeTest.h"
#include "blobstore/implementations/onblocks/datatreestore/DataTree.h"
#include "blobstore/implementations/onblocks/datanodestore/DataLeafNode.h"
#include "blobstore/implementations/onblocks/datanodestore/DataInnerNode.h"
#include <blockstore/implementations/testfake/FakeBlockStore.h>
#include "blobstore/implementations/onblocks/datatreestore/impl/algorithms.h"
using blockstore::BlockId;
using cpputils::Data;
using namespace blobstore::onblocks::datatreestore::algorithms;
class GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNullTest: public DataTreeTest {
public:
struct TestData {
BlockId rootNode;
BlockId expectedResult;
};
void check(const TestData &testData) {
auto root = nodeStore->load(testData.rootNode).value();
auto result = GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNull(nodeStore, root.get());
EXPECT_EQ(testData.expectedResult, result->blockId());
}
TestData CreateTwoRightBorderNodes() {
auto node = CreateInner({CreateLeaf()});
return TestData{node->blockId(), node->blockId()};
}
TestData CreateThreeRightBorderNodes() {
auto node = CreateInner({CreateLeaf()});
auto root = CreateInner({node.get()});
return TestData{root->blockId(), node->blockId()};
}
TestData CreateThreeRightBorderNodes_LastFull() {
auto root = CreateInner({CreateFullTwoLevel()});
return TestData{root->blockId(), root->blockId()};
}
TestData CreateLargerTree() {
auto node = CreateInner({CreateLeaf(), CreateLeaf()});
auto root = CreateInner({CreateFullTwoLevel().get(), node.get()});
return TestData{root->blockId(), node->blockId()};
}
};
TEST_F(GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNullTest, Leaf) {
auto leaf = nodeStore->createNewLeafNode(Data(0));
auto result = GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNull(nodeStore, leaf.get());
EXPECT_EQ(nullptr, result.get());
}
TEST_F(GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNullTest, TwoRightBorderNodes) {
auto testData = CreateTwoRightBorderNodes();
check(testData);
}
TEST_F(GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNullTest, ThreeRightBorderNodes) {
auto testData = CreateThreeRightBorderNodes();
check(testData);
}
TEST_F(GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNullTest, ThreeRightBorderNodes_LastFull) {
auto testData = CreateThreeRightBorderNodes_LastFull();
check(testData);
}
TEST_F(GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNullTest, LargerTree) {
auto testData = CreateLargerTree();
check(testData);
}
TEST_F(GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNullTest, FullTwoLevelTree) {
auto root = CreateFullTwoLevel();
auto result = GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNull(nodeStore, root.get());
EXPECT_EQ(nullptr, result.get());
}
TEST_F(GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNullTest, FullThreeLevelTree) {
auto root = CreateFullThreeLevel();
auto result = GetLowestInnerRightBorderNodeWithLessThanKChildrenOrNull(nodeStore, root.get());
EXPECT_EQ(nullptr, result.get());
}

View File

@ -1,119 +0,0 @@
#include <gtest/gtest.h>
#include "../testutils/DataTreeTest.h"
#include "blobstore/implementations/onblocks/datatreestore/DataTree.h"
#include "blobstore/implementations/onblocks/datanodestore/DataLeafNode.h"
#include "blobstore/implementations/onblocks/datanodestore/DataInnerNode.h"
#include <blockstore/implementations/testfake/FakeBlockStore.h>
#include "blobstore/implementations/onblocks/datatreestore/impl/algorithms.h"
using blockstore::BlockId;
using namespace blobstore::onblocks::datatreestore::algorithms;
class GetLowestRightBorderNodeWithMoreThanOneChildOrNullTest: public DataTreeTest {
public:
struct TestData {
BlockId rootNode;
BlockId expectedResult;
};
void check(const TestData &testData) {
auto root = nodeStore->load(testData.rootNode).value();
auto result = GetLowestRightBorderNodeWithMoreThanOneChildOrNull(nodeStore, root.get());
EXPECT_EQ(testData.expectedResult, result->blockId());
}
BlockId CreateLeafOnlyTree() {
return CreateLeaf()->blockId();
}
BlockId CreateTwoRightBorderNodes() {
return CreateInner({CreateLeaf()})->blockId();
}
BlockId CreateThreeRightBorderNodes() {
return CreateInner({CreateInner({CreateLeaf()})})->blockId();
}
TestData CreateThreeRightBorderNodes_LastFull() {
auto node = CreateFullTwoLevel();
auto root = CreateInner({node.get()});
return TestData{root->blockId(), node->blockId()};
}
TestData CreateLargerTree() {
auto node = CreateInner({CreateLeaf(), CreateLeaf()});
auto root = CreateInner({CreateFullTwoLevel().get(), node.get()});
return TestData{root->blockId(), node->blockId()};
}
TestData CreateThreeLevelTreeWithRightBorderSingleNodeChain() {
auto root = CreateInner({CreateFullTwoLevel(), CreateInner({CreateLeaf()})});
return TestData{root->blockId(), root->blockId()};
}
TestData CreateThreeLevelTree() {
auto node = CreateInner({CreateLeaf(), CreateLeaf()});
auto root = CreateInner({CreateFullTwoLevel().get(), node.get()});
return TestData{root->blockId(), node->blockId()};
}
TestData CreateFullTwoLevelTree() {
auto node = CreateFullTwoLevel();
return TestData{node->blockId(), node->blockId()};
}
TestData CreateFullThreeLevelTree() {
auto root = CreateFullThreeLevel();
return TestData{root->blockId(), root->readLastChild().blockId()};
}
};
TEST_F(GetLowestRightBorderNodeWithMoreThanOneChildOrNullTest, Leaf) {
auto leaf = nodeStore->load(CreateLeafOnlyTree()).value();
auto result = GetLowestRightBorderNodeWithMoreThanOneChildOrNull(nodeStore, leaf.get());
EXPECT_EQ(nullptr, result.get());
}
TEST_F(GetLowestRightBorderNodeWithMoreThanOneChildOrNullTest, TwoRightBorderNodes) {
auto node = nodeStore->load(CreateTwoRightBorderNodes()).value();
auto result = GetLowestRightBorderNodeWithMoreThanOneChildOrNull(nodeStore, node.get());
EXPECT_EQ(nullptr, result.get());
}
TEST_F(GetLowestRightBorderNodeWithMoreThanOneChildOrNullTest, ThreeRightBorderNodes) {
auto node = nodeStore->load(CreateThreeRightBorderNodes()).value();
auto result = GetLowestRightBorderNodeWithMoreThanOneChildOrNull(nodeStore, node.get());
EXPECT_EQ(nullptr, result.get());
}
TEST_F(GetLowestRightBorderNodeWithMoreThanOneChildOrNullTest, ThreeRightBorderNodes_LastFull) {
auto testData = CreateThreeRightBorderNodes_LastFull();
check(testData);
}
TEST_F(GetLowestRightBorderNodeWithMoreThanOneChildOrNullTest, LargerTree) {
auto testData = CreateLargerTree();
check(testData);
}
TEST_F(GetLowestRightBorderNodeWithMoreThanOneChildOrNullTest, FullTwoLevelTree) {
auto testData = CreateFullTwoLevelTree();
check(testData);
}
TEST_F(GetLowestRightBorderNodeWithMoreThanOneChildOrNullTest, FullThreeLevelTree) {
auto testData = CreateFullThreeLevelTree();
check(testData);
}
TEST_F(GetLowestRightBorderNodeWithMoreThanOneChildOrNullTest, ThreeLevelTreeWithRightBorderSingleNodeChain) {
auto testData = CreateThreeLevelTreeWithRightBorderSingleNodeChain();
check(testData);
}
TEST_F(GetLowestRightBorderNodeWithMoreThanOneChildOrNullTest, ThreeLevelTree) {
auto testData = CreateThreeLevelTree();
check(testData);
}

View File

@ -1,242 +0,0 @@
#include "DataTreeTest.h"
#include <blockstore/implementations/testfake/FakeBlockStore.h>
#include <cpp-utils/pointer/cast.h>
#include <cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround.h>
using blobstore::onblocks::datanodestore::DataNodeStore;
using blobstore::onblocks::datanodestore::DataNode;
using blobstore::onblocks::datanodestore::DataInnerNode;
using blobstore::onblocks::datanodestore::DataLeafNode;
using blobstore::onblocks::datatreestore::DataTree;
using blockstore::mock::MockBlockStore;
using blockstore::BlockId;
using cpputils::unique_ref;
using cpputils::make_unique_ref;
using std::initializer_list;
using std::vector;
using boost::none;
using cpputils::dynamic_pointer_move;
using cpputils::Data;
constexpr uint32_t DataTreeTest::BLOCKSIZE_BYTES;
DataTreeTest::DataTreeTest()
:_blockStore(make_unique_ref<MockBlockStore>()),
blockStore(_blockStore.get()),
_nodeStore(make_unique_ref<DataNodeStore>(std::move(_blockStore), BLOCKSIZE_BYTES)),
nodeStore(_nodeStore.get()),
treeStore(std::move(_nodeStore)) {
}
unique_ref<DataLeafNode> DataTreeTest::CreateLeaf() {
return nodeStore->createNewLeafNode(Data(nodeStore->layout().maxBytesPerLeaf()));
}
unique_ref<DataInnerNode> DataTreeTest::CreateInner(initializer_list<unique_ref<DataNode>> children) {
vector<const DataNode*> childrenVector(children.size());
std::transform(children.begin(), children.end(), childrenVector.begin(), [](const unique_ref<DataNode> &ptr) {return ptr.get();});
return CreateInner(childrenVector);
}
unique_ref<DataInnerNode> DataTreeTest::CreateInner(initializer_list<const DataNode*> children) {
return CreateInner(vector<const DataNode*>(children));
}
unique_ref<DataInnerNode> DataTreeTest::CreateInner(vector<const DataNode*> children) {
ASSERT(children.size() >= 1, "An inner node must have at least one child");
vector<BlockId> childrenKeys;
childrenKeys.reserve(children.size());
for (const DataNode *child : children) {
ASSERT(child->depth() == (*children.begin())->depth(), "Children with different depth");
childrenKeys.push_back(child->blockId());
}
auto node = nodeStore->createNewInnerNode((*children.begin())->depth()+1, childrenKeys);
return node;
}
unique_ref<DataTree> DataTreeTest::CreateLeafOnlyTree() {
auto blockId = CreateLeaf()->blockId();
return treeStore.load(blockId).value();
}
void DataTreeTest::FillNode(DataInnerNode *node) {
for(unsigned int i=node->numChildren(); i < nodeStore->layout().maxChildrenPerInnerNode(); ++i) {
node->addChild(*CreateLeaf());
}
}
void DataTreeTest::FillNodeTwoLevel(DataInnerNode *node) {
for(unsigned int i=node->numChildren(); i < nodeStore->layout().maxChildrenPerInnerNode(); ++i) {
node->addChild(*CreateFullTwoLevel());
}
}
unique_ref<DataInnerNode> DataTreeTest::CreateFullTwoLevel() {
auto root = CreateInner({CreateLeaf().get()});
FillNode(root.get());
return root;
}
unique_ref<DataInnerNode> DataTreeTest::CreateThreeLevelMinData() {
return CreateInner({
CreateFullTwoLevel(),
CreateInner({CreateLeaf()})
});
}
unique_ref<DataInnerNode> DataTreeTest::CreateFourLevelMinData() {
return CreateInner({
CreateFullThreeLevel(),
CreateInner({CreateInner({CreateLeaf()})})
});
}
unique_ref<DataInnerNode> DataTreeTest::CreateFullThreeLevel() {
auto root = CreateInner({CreateFullTwoLevel().get()});
FillNodeTwoLevel(root.get());
return root;
}
unique_ref<DataInnerNode> DataTreeTest::LoadInnerNode(const BlockId &blockId) {
auto node = nodeStore->load(blockId).value();
auto casted = dynamic_pointer_move<DataInnerNode>(node);
EXPECT_NE(none, casted) << "Is not an inner node";
return std::move(*casted);
}
unique_ref<DataLeafNode> DataTreeTest::LoadLeafNode(const BlockId &blockId) {
auto node = nodeStore->load(blockId).value();
auto casted = dynamic_pointer_move<DataLeafNode>(node);
EXPECT_NE(none, casted) << "Is not a leaf node";
return std::move(*casted);
}
unique_ref<DataInnerNode> DataTreeTest::CreateTwoLeaf() {
return CreateInner({CreateLeaf().get(), CreateLeaf().get()});
}
unique_ref<DataTree> DataTreeTest::CreateTwoLeafTree() {
auto blockId = CreateTwoLeaf()->blockId();
return treeStore.load(blockId).value();
}
unique_ref<DataLeafNode> DataTreeTest::CreateLeafWithSize(uint32_t size) {
auto leaf = CreateLeaf();
leaf->resize(size);
return leaf;
}
unique_ref<DataInnerNode> DataTreeTest::CreateTwoLeafWithSecondLeafSize(uint32_t size) {
return CreateInner({
CreateLeafWithSize(nodeStore->layout().maxBytesPerLeaf()),
CreateLeafWithSize(size)
});
}
unique_ref<DataInnerNode> DataTreeTest::CreateFullTwoLevelWithLastLeafSize(uint32_t size) {
auto root = CreateFullTwoLevel();
for (uint32_t i = 0; i < root->numChildren()-1; ++i) {
LoadLeafNode(root->readChild(i).blockId())->resize(nodeStore->layout().maxBytesPerLeaf());
}
LoadLeafNode(root->readLastChild().blockId())->resize(size);
return root;
}
unique_ref<DataInnerNode> DataTreeTest::CreateThreeLevelWithOneChildAndLastLeafSize(uint32_t size) {
return CreateInner({
CreateInner({
CreateLeafWithSize(nodeStore->layout().maxBytesPerLeaf()),
CreateLeafWithSize(size)
})
});
}
unique_ref<DataInnerNode> DataTreeTest::CreateThreeLevelWithTwoChildrenAndLastLeafSize(uint32_t size) {
return CreateInner({
CreateFullTwoLevelWithLastLeafSize(nodeStore->layout().maxBytesPerLeaf()),
CreateInner({
CreateLeafWithSize(nodeStore->layout().maxBytesPerLeaf()),
CreateLeafWithSize(size)
})
});
}
unique_ref<DataInnerNode> DataTreeTest::CreateThreeLevelWithThreeChildrenAndLastLeafSize(uint32_t size) {
return CreateInner({
CreateFullTwoLevelWithLastLeafSize(nodeStore->layout().maxBytesPerLeaf()),
CreateFullTwoLevelWithLastLeafSize(nodeStore->layout().maxBytesPerLeaf()),
CreateInner({
CreateLeafWithSize(nodeStore->layout().maxBytesPerLeaf()),
CreateLeafWithSize(size)
})
});
}
unique_ref<DataInnerNode> DataTreeTest::CreateFullThreeLevelWithLastLeafSize(uint32_t size) {
auto root = CreateFullThreeLevel();
for (uint32_t i = 0; i < root->numChildren(); ++i) {
auto node = LoadInnerNode(root->readChild(i).blockId());
for (uint32_t j = 0; j < node->numChildren(); ++j) {
LoadLeafNode(node->readChild(j).blockId())->resize(nodeStore->layout().maxBytesPerLeaf());
}
}
LoadLeafNode(LoadInnerNode(root->readLastChild().blockId())->readLastChild().blockId())->resize(size);
return root;
}
unique_ref<DataInnerNode> DataTreeTest::CreateFourLevelMinDataWithLastLeafSize(uint32_t size) {
return CreateInner({
CreateFullThreeLevelWithLastLeafSize(nodeStore->layout().maxBytesPerLeaf()),
CreateInner({CreateInner({CreateLeafWithSize(size)})})
});
}
void DataTreeTest::EXPECT_IS_LEAF_NODE(const BlockId &blockId) {
auto node = LoadLeafNode(blockId);
EXPECT_NE(nullptr, node.get());
}
void DataTreeTest::EXPECT_IS_INNER_NODE(const BlockId &blockId) {
auto node = LoadInnerNode(blockId);
EXPECT_NE(nullptr, node.get());
}
void DataTreeTest::EXPECT_IS_TWONODE_CHAIN(const BlockId &blockId) {
auto node = LoadInnerNode(blockId);
EXPECT_EQ(1u, node->numChildren());
EXPECT_IS_LEAF_NODE(node->readChild(0).blockId());
}
void DataTreeTest::EXPECT_IS_FULL_TWOLEVEL_TREE(const BlockId &blockId) {
auto node = LoadInnerNode(blockId);
EXPECT_EQ(nodeStore->layout().maxChildrenPerInnerNode(), node->numChildren());
for (unsigned int i = 0; i < node->numChildren(); ++i) {
EXPECT_IS_LEAF_NODE(node->readChild(i).blockId());
}
}
void DataTreeTest::EXPECT_IS_FULL_THREELEVEL_TREE(const BlockId &blockId) {
auto root = LoadInnerNode(blockId);
EXPECT_EQ(nodeStore->layout().maxChildrenPerInnerNode(), root->numChildren());
for (unsigned int i = 0; i < root->numChildren(); ++i) {
auto node = LoadInnerNode(root->readChild(i).blockId());
EXPECT_EQ(nodeStore->layout().maxChildrenPerInnerNode(), node->numChildren());
for (unsigned int j = 0; j < node->numChildren(); ++j) {
EXPECT_IS_LEAF_NODE(node->readChild(j).blockId());
}
}
}
// NOLINTNEXTLINE(misc-no-recursion)
void DataTreeTest::CHECK_DEPTH(int depth, const BlockId &blockId) {
if (depth == 0) {
EXPECT_IS_LEAF_NODE(blockId);
} else {
auto node = LoadInnerNode(blockId);
EXPECT_EQ(depth, node->depth());
for (uint32_t i = 0; i < node->numChildren(); ++i) {
CHECK_DEPTH(depth-1, node->readChild(i).blockId());
}
}
}

View File

@ -1,68 +0,0 @@
#pragma once
#ifndef MESSMER_BLOBSTORE_TEST_IMPLEMENTATIONS_ONBLOCKS_DATATREESTORE_DATATREETEST_H_
#define MESSMER_BLOBSTORE_TEST_IMPLEMENTATIONS_ONBLOCKS_DATATREESTORE_DATATREETEST_H_
#include <gtest/gtest.h>
#include <blockstore/implementations/testfake/FakeBlockStore.h>
#include "blobstore/implementations/onblocks/datanodestore/DataNodeStore.h"
#include "blobstore/implementations/onblocks/datanodestore/DataInnerNode.h"
#include "blobstore/implementations/onblocks/datanodestore/DataLeafNode.h"
#include "blobstore/implementations/onblocks/datatreestore/DataTree.h"
#include "blobstore/implementations/onblocks/datatreestore/DataTreeStore.h"
#include "blockstore/implementations/mock/MockBlockStore.h"
class DataTreeTest: public ::testing::Test {
public:
DataTreeTest();
static constexpr uint32_t BLOCKSIZE_BYTES = 256;
cpputils::unique_ref<blobstore::onblocks::datanodestore::DataLeafNode> CreateLeaf();
cpputils::unique_ref<blobstore::onblocks::datanodestore::DataInnerNode> CreateInner(std::vector<const blobstore::onblocks::datanodestore::DataNode *> children);
cpputils::unique_ref<blobstore::onblocks::datanodestore::DataInnerNode> CreateInner(std::initializer_list<const blobstore::onblocks::datanodestore::DataNode *> children);
cpputils::unique_ref<blobstore::onblocks::datanodestore::DataInnerNode> CreateInner(std::initializer_list<cpputils::unique_ref<blobstore::onblocks::datanodestore::DataNode>> children);
cpputils::unique_ref<blobstore::onblocks::datatreestore::DataTree> CreateLeafOnlyTree();
cpputils::unique_ref<blobstore::onblocks::datanodestore::DataInnerNode> CreateTwoLeaf();
cpputils::unique_ref<blobstore::onblocks::datatreestore::DataTree> CreateTwoLeafTree();
void FillNode(blobstore::onblocks::datanodestore::DataInnerNode *node);
void FillNodeTwoLevel(blobstore::onblocks::datanodestore::DataInnerNode *node);
cpputils::unique_ref<blobstore::onblocks::datanodestore::DataInnerNode> CreateFullTwoLevel();
cpputils::unique_ref<blobstore::onblocks::datanodestore::DataInnerNode> CreateFullThreeLevel();
cpputils::unique_ref<blobstore::onblocks::datanodestore::DataInnerNode> CreateThreeLevelMinData();
cpputils::unique_ref<blobstore::onblocks::datanodestore::DataInnerNode> CreateFourLevelMinData();
cpputils::unique_ref<blobstore::onblocks::datanodestore::DataInnerNode> LoadInnerNode(const blockstore::BlockId &blockId);
cpputils::unique_ref<blobstore::onblocks::datanodestore::DataLeafNode> LoadLeafNode(const blockstore::BlockId &blockId);
cpputils::unique_ref<blobstore::onblocks::datanodestore::DataLeafNode> CreateLeafWithSize(uint32_t size);
cpputils::unique_ref<blobstore::onblocks::datanodestore::DataInnerNode> CreateTwoLeafWithSecondLeafSize(uint32_t size);
cpputils::unique_ref<blobstore::onblocks::datanodestore::DataInnerNode> CreateFullTwoLevelWithLastLeafSize(uint32_t size);
cpputils::unique_ref<blobstore::onblocks::datanodestore::DataInnerNode> CreateThreeLevelWithOneChildAndLastLeafSize(uint32_t size);
cpputils::unique_ref<blobstore::onblocks::datanodestore::DataInnerNode> CreateThreeLevelWithTwoChildrenAndLastLeafSize(uint32_t size);
cpputils::unique_ref<blobstore::onblocks::datanodestore::DataInnerNode> CreateThreeLevelWithThreeChildrenAndLastLeafSize(uint32_t size);
cpputils::unique_ref<blobstore::onblocks::datanodestore::DataInnerNode> CreateFullThreeLevelWithLastLeafSize(uint32_t size);
cpputils::unique_ref<blobstore::onblocks::datanodestore::DataInnerNode> CreateFourLevelMinDataWithLastLeafSize(uint32_t size);
cpputils::unique_ref<blockstore::mock::MockBlockStore> _blockStore;
blockstore::mock::MockBlockStore *blockStore;
cpputils::unique_ref<blobstore::onblocks::datanodestore::DataNodeStore> _nodeStore;
blobstore::onblocks::datanodestore::DataNodeStore *nodeStore;
blobstore::onblocks::datatreestore::DataTreeStore treeStore;
void EXPECT_IS_LEAF_NODE(const blockstore::BlockId &blockId);
void EXPECT_IS_INNER_NODE(const blockstore::BlockId &blockId);
void EXPECT_IS_TWONODE_CHAIN(const blockstore::BlockId &blockId);
void EXPECT_IS_FULL_TWOLEVEL_TREE(const blockstore::BlockId &blockId);
void EXPECT_IS_FULL_THREELEVEL_TREE(const blockstore::BlockId &blockId);
void CHECK_DEPTH(int depth, const blockstore::BlockId &blockId);
private:
DISALLOW_COPY_AND_ASSIGN(DataTreeTest);
};
#endif

View File

@ -1,40 +0,0 @@
#pragma once
#ifndef MESSMER_BLOBSTORE_TEST_IMPLEMENTATIONS_ONBLOCKS_DATATREESTORE_GROWING_TESTUTILS_LEAFDATAFIXTURE_H_
#define MESSMER_BLOBSTORE_TEST_IMPLEMENTATIONS_ONBLOCKS_DATATREESTORE_GROWING_TESTUTILS_LEAFDATAFIXTURE_H_
#include <gtest/gtest.h>
#include <cpp-utils/data/DataFixture.h>
// A data fixture containing data for a leaf.
// The class can fill this data into a given leaf
// and check, whether the data stored in a given leaf is correct.
class LeafDataFixture {
public:
LeafDataFixture(int size, int iv = 0): _data(cpputils::DataFixture::generate(size, iv)) {}
void FillInto(blobstore::onblocks::datanodestore::DataLeafNode *leaf) const {
leaf->resize(_data.size());
leaf->write(_data.data(), 0, _data.size());
}
void EXPECT_DATA_CORRECT(const blobstore::onblocks::datanodestore::DataLeafNode &leaf, int onlyCheckNumBytes = -1) const {
if (onlyCheckNumBytes == -1) {
EXPECT_EQ(_data.size(), leaf.numBytes());
EXPECT_EQ(0, std::memcmp(_data.data(), loadData(leaf).data(), _data.size()));
} else {
EXPECT_LE(onlyCheckNumBytes, static_cast<int>(leaf.numBytes()));
EXPECT_EQ(0, std::memcmp(_data.data(), loadData(leaf).data(), onlyCheckNumBytes));
}
}
private:
static cpputils::Data loadData(const blobstore::onblocks::datanodestore::DataLeafNode &leaf) {
cpputils::Data data(leaf.numBytes());
leaf.read(data.data(), 0, leaf.numBytes());
return data;
}
cpputils::Data _data;
};
#endif

View File

@ -1,87 +0,0 @@
#pragma once
#ifndef MESSMER_BLOBSTORE_TEST_IMPLEMENTATIONS_ONBLOCKS_DATATREESTORE_GROWING_TESTUTILS_TWOLEVELDATAFIXTURE_H_
#define MESSMER_BLOBSTORE_TEST_IMPLEMENTATIONS_ONBLOCKS_DATATREESTORE_GROWING_TESTUTILS_TWOLEVELDATAFIXTURE_H_
#include <cpp-utils/macros.h>
#include <cpp-utils/pointer/cast.h>
#include "LeafDataFixture.h"
#include <cpp-utils/assert/assert.h>
//TODO Rename, since we now allow any number of levels
// 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:
enum class SizePolicy {
Random,
Full,
Unchanged
};
TwoLevelDataFixture(blobstore::onblocks::datanodestore::DataNodeStore *dataNodeStore, SizePolicy sizePolicy, int iv=0): _dataNodeStore(dataNodeStore), _iv(iv), _sizePolicy(sizePolicy) {}
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, leaf), leafIndex).FillInto(leaf);
});
}
void EXPECT_DATA_CORRECT(blobstore::onblocks::datanodestore::DataNode *node, int maxCheckedLeaves = 0, int lastLeafMaxCheckedBytes = -1) {
ForEachLeaf(node, _iv, _iv+maxCheckedLeaves, [this, maxCheckedLeaves, lastLeafMaxCheckedBytes] (blobstore::onblocks::datanodestore::DataLeafNode *leaf, int leafIndex) {
if (leafIndex == _iv+maxCheckedLeaves-1) {
// It is the last leaf
LeafDataFixture(size(leafIndex, leaf), leafIndex).EXPECT_DATA_CORRECT(*leaf, lastLeafMaxCheckedBytes);
} else {
LeafDataFixture(size(leafIndex, leaf), leafIndex).EXPECT_DATA_CORRECT(*leaf);
}
});
}
private:
// NOLINTNEXTLINE(misc-no-recursion)
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 (uint32_t i = 0; i < inner->numChildren(); ++i) {
auto child = _dataNodeStore->load(inner->readChild(i).blockId()).value();
leafIndex = ForEachLeaf(child.get(), leafIndex, endLeafIndex, action);
}
return leafIndex;
}
}
blobstore::onblocks::datanodestore::DataNodeStore *_dataNodeStore;
int _iv;
SizePolicy _sizePolicy;
int size(int childIndex, blobstore::onblocks::datanodestore::DataLeafNode *leaf) {
switch (_sizePolicy) {
case SizePolicy::Full:
return _dataNodeStore->layout().maxBytesPerLeaf();
case SizePolicy::Random:
return mod(static_cast<int>(_dataNodeStore->layout().maxBytesPerLeaf() - childIndex), static_cast<int>(_dataNodeStore->layout().maxBytesPerLeaf()));
case SizePolicy::Unchanged:
return leaf->numBytes();
default:
ASSERT(false, "Unknown size policy");
}
}
int mod(int value, int mod) {
while(value < 0) {
value += mod;
}
return value;
}
};
#endif

View File

@ -1,15 +0,0 @@
#include "BlobStoreTest.h"
#include <blockstore/implementations/testfake/FakeBlockStore.h>
#include "blobstore/implementations/onblocks/BlobStoreOnBlocks.h"
#include <cpp-utils/pointer/gcc_4_8_compatibility.h>
using blobstore::onblocks::BlobStoreOnBlocks;
using blockstore::testfake::FakeBlockStore;
using cpputils::make_unique_ref;
constexpr uint32_t BlobStoreTest::BLOCKSIZE_BYTES;
BlobStoreTest::BlobStoreTest()
: blobStore(make_unique_ref<BlobStoreOnBlocks>(make_unique_ref<FakeBlockStore>(), BLOCKSIZE_BYTES)) {
}

View File

@ -1,29 +0,0 @@
#pragma once
#ifndef MESSMER_BLOBSTORE_TEST_IMPLEMENTATIONS_ONBLOCKS_TESTUTILS_BLOBSTORETEST_H_
#define MESSMER_BLOBSTORE_TEST_IMPLEMENTATIONS_ONBLOCKS_TESTUTILS_BLOBSTORETEST_H_
#include <gtest/gtest.h>
#include "blobstore/interface/BlobStore.h"
class BlobStoreTest: public ::testing::Test {
public:
BlobStoreTest();
static constexpr uint32_t BLOCKSIZE_BYTES = 4096;
cpputils::unique_ref<blobstore::BlobStore> blobStore;
cpputils::unique_ref<blobstore::Blob> loadBlob(const blockstore::BlockId &blockId) {
auto loaded = blobStore->load(blockId);
EXPECT_TRUE(static_cast<bool>(loaded));
return std::move(*loaded);
}
void reset(cpputils::unique_ref<blobstore::Blob> ref) {
UNUSED(ref);
//ref is moved into here and then destructed
}
};
#endif

View File

@ -1,88 +0,0 @@
#include <gtest/gtest.h>
#include "blobstore/implementations/onblocks/utils/Math.h"
#include <limits>
using namespace blobstore::onblocks::utils;
using ::testing::Test;
using std::numeric_limits;
class CeilDivisionTest: public Test {};
TEST_F(CeilDivisionTest, Divide0_4) {
EXPECT_EQ(0, ceilDivision(0, 4));
}
TEST_F(CeilDivisionTest, Divide1_4) {
EXPECT_EQ(1, ceilDivision(1, 4));
}
TEST_F(CeilDivisionTest, Divide2_4) {
EXPECT_EQ(1, ceilDivision(2, 4));
}
TEST_F(CeilDivisionTest, Divide3_4) {
EXPECT_EQ(1, ceilDivision(3, 4));
}
TEST_F(CeilDivisionTest, Divide4_4) {
EXPECT_EQ(1, ceilDivision(4, 4));
}
TEST_F(CeilDivisionTest, Divide5_4) {
EXPECT_EQ(2, ceilDivision(5, 4));
}
TEST_F(CeilDivisionTest, Divide6_4) {
EXPECT_EQ(2, ceilDivision(6, 4));
}
TEST_F(CeilDivisionTest, Divide7_4) {
EXPECT_EQ(2, ceilDivision(7, 4));
}
TEST_F(CeilDivisionTest, Divide8_4) {
EXPECT_EQ(2, ceilDivision(8, 4));
}
TEST_F(CeilDivisionTest, Divide9_4) {
EXPECT_EQ(3, ceilDivision(9, 4));
}
TEST_F(CeilDivisionTest, Divide0_1) {
EXPECT_EQ(0, ceilDivision(0, 1));
}
TEST_F(CeilDivisionTest, Divide5_1) {
EXPECT_EQ(5, ceilDivision(5, 1));
}
TEST_F(CeilDivisionTest, Divide7_2) {
EXPECT_EQ(4, ceilDivision(7, 2));
}
TEST_F(CeilDivisionTest, Divide8_2) {
EXPECT_EQ(4, ceilDivision(8, 2));
}
TEST_F(CeilDivisionTest, Divide9_2) {
EXPECT_EQ(5, ceilDivision(9, 2));
}
TEST_F(CeilDivisionTest, Divide1_1) {
EXPECT_EQ(1, ceilDivision(1, 1));
}
TEST_F(CeilDivisionTest, Divide5_5) {
EXPECT_EQ(1, ceilDivision(5, 5));
}
TEST_F(CeilDivisionTest, DivideLargeByItself) {
EXPECT_EQ(1, ceilDivision(183495303, 183495303));
}
TEST_F(CeilDivisionTest, 64bit) {
uint64_t base = UINT64_C(1024)*1024*1024*1024;
EXPECT_GT(base, std::numeric_limits<uint32_t>::max());
EXPECT_EQ(base/1024, ceilDivision(base, UINT64_C(1024)));
}

View File

@ -1,35 +0,0 @@
#include <gtest/gtest.h>
#include "blobstore/implementations/onblocks/utils/Math.h"
#include <limits>
using namespace blobstore::onblocks::utils;
using ::testing::Test;
using std::numeric_limits;
class CeilLogTest: public Test {};
TEST_F(CeilLogTest, Log3_1) {
EXPECT_EQ(0, ceilLog(3, 1));
}
TEST_F(CeilLogTest, Log3_2) {
EXPECT_EQ(1, ceilLog(3, 2));
}
TEST_F(CeilLogTest, Log3_3) {
EXPECT_EQ(1, ceilLog(3, 3));
}
TEST_F(CeilLogTest, Log3_4) {
EXPECT_EQ(2, ceilLog(3, 4));
}
TEST_F(CeilLogTest, 64bit) {
uint64_t value = UINT64_C(1024)*1024*1024*1024;
EXPECT_GT(value, std::numeric_limits<uint32_t>::max());
EXPECT_EQ(4u, ceilLog(UINT64_C(1024), value));
}
//TODO ...

View File

@ -1,77 +0,0 @@
#include <gtest/gtest.h>
#include "blobstore/implementations/onblocks/utils/Math.h"
using namespace blobstore::onblocks::utils;
using ::testing::Test;
class IntPowTest: public Test {};
TEST_F(IntPowTest, ExponentAndBaseAreZero) {
EXPECT_EQ(1, intPow(0, 0));
}
TEST_F(IntPowTest, ExponentIsZero1) {
EXPECT_EQ(1, intPow(1, 0));
}
TEST_F(IntPowTest, ExponentIsZero2) {
EXPECT_EQ(1, intPow(1000, 0));
}
TEST_F(IntPowTest, BaseIsZero1) {
EXPECT_EQ(0, intPow(0, 1));
}
TEST_F(IntPowTest, BaseIsZero2) {
EXPECT_EQ(0, intPow(0, 1000));
}
TEST_F(IntPowTest, ExponentIsOne1) {
EXPECT_EQ(0, intPow(0, 1));
}
TEST_F(IntPowTest, ExponentIsOne2) {
EXPECT_EQ(2, intPow(2, 1));
}
TEST_F(IntPowTest, ExponentIsOne3) {
EXPECT_EQ(1000, intPow(1000, 1));
}
TEST_F(IntPowTest, BaseIsTwo1) {
EXPECT_EQ(1024, intPow(2, 10));
}
TEST_F(IntPowTest, BaseIsTwo2) {
EXPECT_EQ(1024*1024, intPow(2, 20));
}
TEST_F(IntPowTest, BaseIsTwo3) {
EXPECT_EQ(1024*1024*1024, intPow(2, 30));
}
TEST_F(IntPowTest, BaseIsTen1) {
EXPECT_EQ(100, intPow(10, 2));
}
TEST_F(IntPowTest, BaseIsTen2) {
EXPECT_EQ(1000000, intPow(10, 6));
}
TEST_F(IntPowTest, ArbitraryNumbers1) {
EXPECT_EQ(4096, intPow(4, 6));
}
TEST_F(IntPowTest, ArbitraryNumbers2) {
EXPECT_EQ(1296, intPow(6, 4));
}
TEST_F(IntPowTest, ArbitraryNumbers3) {
EXPECT_EQ(282475249, intPow(7, 10));
}
TEST_F(IntPowTest, 64bit) {
uint64_t value = UINT64_C(1024)*1024*1024*1024;
EXPECT_GT(value, std::numeric_limits<uint32_t>::max());
EXPECT_EQ(value*value*value, intPow(value, UINT64_C(3)));
}

View File

@ -1,90 +0,0 @@
#include <gtest/gtest.h>
#include "blobstore/implementations/onblocks/utils/Math.h"
#include <limits>
using namespace blobstore::onblocks::utils;
using ::testing::Test;
using std::numeric_limits;
class MaxZeroSubtractionTest: public Test {};
TEST_F(MaxZeroSubtractionTest, SubtractToZero1) {
EXPECT_EQ(0, maxZeroSubtraction(0, 0));
}
TEST_F(MaxZeroSubtractionTest, SubtractToZero2) {
EXPECT_EQ(0, maxZeroSubtraction(5, 5));
}
TEST_F(MaxZeroSubtractionTest, SubtractToZero3) {
EXPECT_EQ(0, maxZeroSubtraction(184930, 184930));
}
TEST_F(MaxZeroSubtractionTest, SubtractToZero4) {
EXPECT_EQ(0u, maxZeroSubtraction(numeric_limits<uint32_t>::max()-1, numeric_limits<uint32_t>::max()-1));
}
TEST_F(MaxZeroSubtractionTest, SubtractToZero5) {
EXPECT_EQ(0u, maxZeroSubtraction(numeric_limits<uint32_t>::max(), numeric_limits<uint32_t>::max()));
}
TEST_F(MaxZeroSubtractionTest, SubtractPositive1) {
EXPECT_EQ(1, maxZeroSubtraction(5, 4));
}
TEST_F(MaxZeroSubtractionTest, SubtractPositive2) {
EXPECT_EQ(181081, maxZeroSubtraction(184930, 3849));
}
TEST_F(MaxZeroSubtractionTest, SubtractPositive3) {
EXPECT_EQ(numeric_limits<uint32_t>::max()-1, maxZeroSubtraction(numeric_limits<uint32_t>::max(), UINT32_C(1)));
}
TEST_F(MaxZeroSubtractionTest, SubtractPositive4) {
EXPECT_EQ(5u, maxZeroSubtraction(numeric_limits<uint32_t>::max(), numeric_limits<uint32_t>::max()-5));
}
TEST_F(MaxZeroSubtractionTest, SubtractNegative1) {
EXPECT_EQ(0, maxZeroSubtraction(4, 5));
}
TEST_F(MaxZeroSubtractionTest, SubtractNegative2) {
EXPECT_EQ(0, maxZeroSubtraction(3849, 184930));
}
TEST_F(MaxZeroSubtractionTest, SubtractNegative3) {
EXPECT_EQ(0u, maxZeroSubtraction(numeric_limits<uint32_t>::max()-1, numeric_limits<uint32_t>::max()));
}
TEST_F(MaxZeroSubtractionTest, SubtractNegative4) {
EXPECT_EQ(0u, maxZeroSubtraction(numeric_limits<uint32_t>::max()-5, numeric_limits<uint32_t>::max()));
}
TEST_F(MaxZeroSubtractionTest, SubtractNegative5) {
EXPECT_EQ(0u, maxZeroSubtraction(UINT32_C(5), numeric_limits<uint32_t>::max()));
}
TEST_F(MaxZeroSubtractionTest, SubtractFromZero1) {
EXPECT_EQ(0, maxZeroSubtraction(0, 1));
}
TEST_F(MaxZeroSubtractionTest, SubtractFromZero2) {
EXPECT_EQ(0, maxZeroSubtraction(0, 184930));
}
TEST_F(MaxZeroSubtractionTest, SubtractFromZero3) {
EXPECT_EQ(0u, maxZeroSubtraction(UINT32_C(0), numeric_limits<uint32_t>::max()));
}
TEST_F(MaxZeroSubtractionTest, 64bit_valid) {
uint64_t value = UINT64_C(1024)*1024*1024*1024;
EXPECT_GT(value, std::numeric_limits<uint32_t>::max());
EXPECT_EQ(value*1024-value, maxZeroSubtraction(value*1024, value));
}
TEST_F(MaxZeroSubtractionTest, 64bit_zero) {
uint64_t value = UINT64_C(1024)*1024*1024*1024;
EXPECT_GT(value, std::numeric_limits<uint32_t>::max());
EXPECT_EQ(0u, maxZeroSubtraction(value, value*1024));
}

View File

@ -1,49 +0,0 @@
project (blockstore-test)
set(SOURCES
utils/BlockStoreUtilsTest.cpp
interface/BlockStoreTest.cpp
interface/BlockStore2Test.cpp
interface/BlockTest.cpp
implementations/testfake/TestFakeBlockStoreTest.cpp
implementations/mock/MockBlockStoreTest.cpp
implementations/inmemory/InMemoryBlockStoreTest.cpp
implementations/parallelaccess/ParallelAccessBlockStoreTest_Generic.cpp
implementations/parallelaccess/ParallelAccessBlockStoreTest_Specific.cpp
implementations/compressing/CompressingBlockStoreTest.cpp
implementations/compressing/compressors/testutils/CompressorTest.cpp
implementations/encrypted/EncryptedBlockStoreTest_Generic.cpp
implementations/encrypted/EncryptedBlockStoreTest_Specific.cpp
implementations/ondisk/OnDiskBlockStoreTest_Generic.cpp
implementations/ondisk/OnDiskBlockStoreTest_Specific.cpp
implementations/ondisk/OnDiskBlockTest/OnDiskBlockCreateTest.cpp
implementations/ondisk/OnDiskBlockTest/OnDiskBlockFlushTest.cpp
implementations/ondisk/OnDiskBlockTest/OnDiskBlockLoadTest.cpp
implementations/caching/CachingBlockStore2Test_Generic.cpp
implementations/caching/CachingBlockStore2Test_Specific.cpp
implementations/caching/cache/QueueMapTest_Values.cpp
implementations/caching/cache/testutils/MinimalKeyType.cpp
implementations/caching/cache/testutils/CopyableMovableValueType.cpp
implementations/caching/cache/testutils/MinimalValueType.cpp
implementations/caching/cache/testutils/QueueMapTest.cpp
implementations/caching/cache/testutils/CacheTest.cpp
implementations/caching/cache/QueueMapTest_Size.cpp
implementations/caching/cache/CacheTest_MoveConstructor.cpp
implementations/caching/cache/CacheTest_PushAndPop.cpp
implementations/caching/cache/QueueMapTest_MoveConstructor.cpp
implementations/caching/cache/QueueMapTest_MemoryLeak.cpp
implementations/caching/cache/CacheTest_RaceCondition.cpp
implementations/caching/cache/PeriodicTaskTest.cpp
implementations/caching/cache/QueueMapTest_Peek.cpp
implementations/integrity/KnownBlockVersionsTest.cpp
implementations/integrity/IntegrityBlockStoreTest_Generic.cpp
implementations/integrity/IntegrityBlockStoreTest_Specific.cpp
implementations/low2highlevel/LowToHighLevelBlockStoreTest.cpp
)
add_executable(${PROJECT_NAME} ${SOURCES})
target_link_libraries(${PROJECT_NAME} my-gtest-main googletest blockstore)
add_test(${PROJECT_NAME} ${PROJECT_NAME})
target_enable_style_warnings(${PROJECT_NAME})
target_activate_cpp14(${PROJECT_NAME})

View File

@ -1,37 +0,0 @@
#include <blockstore/implementations/low2highlevel/LowToHighLevelBlockStore.h>
#include "blockstore/implementations/caching/CachingBlockStore2.h"
#include "blockstore/implementations/inmemory/InMemoryBlockStore2.h"
#include "../../testutils/BlockStoreTest.h"
#include "../../testutils/BlockStore2Test.h"
#include <gtest/gtest.h>
using blockstore::BlockStore;
using blockstore::BlockStore2;
using blockstore::caching::CachingBlockStore2;
using blockstore::lowtohighlevel::LowToHighLevelBlockStore;
using blockstore::inmemory::InMemoryBlockStore2;
using cpputils::make_unique_ref;
using cpputils::unique_ref;
class CachingBlockStoreTestFixture: public BlockStoreTestFixture {
public:
unique_ref<BlockStore> createBlockStore() override {
return make_unique_ref<LowToHighLevelBlockStore>(
make_unique_ref<CachingBlockStore2>(make_unique_ref<InMemoryBlockStore2>())
);
}
};
INSTANTIATE_TYPED_TEST_SUITE_P(Caching2, BlockStoreTest, CachingBlockStoreTestFixture);
class CachingBlockStore2TestFixture: public BlockStore2TestFixture {
public:
unique_ref<BlockStore2> createBlockStore() override {
return make_unique_ref<CachingBlockStore2>(make_unique_ref<InMemoryBlockStore2>());
}
};
INSTANTIATE_TYPED_TEST_SUITE_P(Caching, BlockStore2Test, CachingBlockStore2TestFixture);

View File

@ -1,54 +0,0 @@
#include <gtest/gtest.h>
#include "blockstore/implementations/caching/CachingBlockStore2.h"
#include "blockstore/implementations/inmemory/InMemoryBlockStore2.h"
using ::testing::Test;
using cpputils::Data;
using blockstore::inmemory::InMemoryBlockStore2;
using namespace blockstore::caching;
class CachingBlockStore2Test: public Test {
public:
CachingBlockStore2Test():
baseBlockStore(new InMemoryBlockStore2),
blockStore(std::move(cpputils::nullcheck(std::unique_ptr<InMemoryBlockStore2>(baseBlockStore)).value())) {
}
InMemoryBlockStore2 *baseBlockStore;
CachingBlockStore2 blockStore;
};
TEST_F(CachingBlockStore2Test, PhysicalBlockSize_zerophysical) {
EXPECT_EQ(0u, blockStore.blockSizeFromPhysicalBlockSize(0));
}
TEST_F(CachingBlockStore2Test, PhysicalBlockSize_zerovirtual) {
auto blockId = blockStore.create(Data(0));
blockStore.flush();
auto base = baseBlockStore->load(blockId).value();
EXPECT_EQ(0u, blockStore.blockSizeFromPhysicalBlockSize(base.size()));
}
TEST_F(CachingBlockStore2Test, PhysicalBlockSize_negativeboundaries) {
// This tests that a potential if/else in blockSizeFromPhysicalBlockSize that catches negative values has the
// correct boundary set. We test the highest value that is negative and the smallest value that is positive.
auto blockId = blockStore.create(Data(0));
blockStore.flush();
auto physicalSizeForVirtualSizeZero = baseBlockStore->load(blockId).value().size();
if (physicalSizeForVirtualSizeZero > 0) {
EXPECT_EQ(0u, blockStore.blockSizeFromPhysicalBlockSize(physicalSizeForVirtualSizeZero - 1));
}
EXPECT_EQ(0u, blockStore.blockSizeFromPhysicalBlockSize(physicalSizeForVirtualSizeZero));
EXPECT_EQ(1u, blockStore.blockSizeFromPhysicalBlockSize(physicalSizeForVirtualSizeZero + 1));
}
TEST_F(CachingBlockStore2Test, PhysicalBlockSize_positive) {
auto blockId = blockStore.create(Data(10*1024u));
blockStore.flush();
auto base = baseBlockStore->load(blockId).value();
EXPECT_EQ(10*1024u, blockStore.blockSizeFromPhysicalBlockSize(base.size()));
}
// TODO Add test cases that flushing the block store doesn't destroy things (i.e. all test cases from BlockStoreTest, but with flushes inbetween)

View File

@ -1,35 +0,0 @@
#include <gtest/gtest.h>
#include <cpp-utils/pointer/unique_ref.h>
#include "blockstore/implementations/caching/cache/Cache.h"
#include "testutils/MinimalKeyType.h"
#include "testutils/CopyableMovableValueType.h"
using namespace blockstore::caching;
using cpputils::unique_ref;
using cpputils::make_unique_ref;
using ::testing::Test;
//Test that Cache uses a move constructor for Value if possible
class CacheTest_MoveConstructor: public Test {
public:
CacheTest_MoveConstructor(): cache(make_unique_ref<Cache<MinimalKeyType, CopyableMovableValueType, 100>>("test")) {
CopyableMovableValueType::numCopyConstructorCalled = 0;
}
unique_ref<Cache<MinimalKeyType, CopyableMovableValueType, 100>> cache;
};
TEST_F(CacheTest_MoveConstructor, MoveIntoCache) {
cache->push(MinimalKeyType::create(0), CopyableMovableValueType(2));
CopyableMovableValueType val = cache->pop(MinimalKeyType::create(0)).value();
val.value(); //Access it to avoid the compiler optimizing the assignment away
EXPECT_EQ(0, CopyableMovableValueType::numCopyConstructorCalled);
}
TEST_F(CacheTest_MoveConstructor, CopyIntoCache) {
CopyableMovableValueType value(2);
cache->push(MinimalKeyType::create(0), value);
CopyableMovableValueType val = cache->pop(MinimalKeyType::create(0)).value();
val.value(); //Access it to avoid the compiler optimizing the assignment away
EXPECT_EQ(1, CopyableMovableValueType::numCopyConstructorCalled);
}

View File

@ -1,133 +0,0 @@
#include "testutils/CacheTest.h"
#include "blockstore/implementations/caching/cache/Cache.h"
#include "testutils/MinimalKeyType.h"
#include "testutils/MinimalValueType.h"
#include <cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround.h>
using namespace blockstore::caching;
class CacheTest_PushAndPop: public CacheTest {};
TEST_F(CacheTest_PushAndPop, PopNonExistingEntry_EmptyCache) {
EXPECT_EQ(boost::none, pop(10));
}
TEST_F(CacheTest_PushAndPop, PopNonExistingEntry_NonEmptyCache) {
push(9, 10);
EXPECT_EQ(boost::none, pop(10));
}
TEST_F(CacheTest_PushAndPop, PopNonExistingEntry_FullCache) {
//Add a lot of even numbered keys
for (int i = 0; i < static_cast<int>(MAX_ENTRIES); ++i) {
push(2*i, 2*i);
}
//Request an odd numbered key
EXPECT_EQ(boost::none, pop(9));
}
TEST_F(CacheTest_PushAndPop, OneEntry) {
push(10, 20);
EXPECT_EQ(20, pop(10).value());
}
TEST_F(CacheTest_PushAndPop, MultipleEntries) {
push(10, 20);
push(20, 30);
push(30, 40);
EXPECT_EQ(30, pop(20).value());
EXPECT_EQ(20, pop(10).value());
EXPECT_EQ(40, pop(30).value());
}
TEST_F(CacheTest_PushAndPop, FullCache) {
for(int i = 0; i < static_cast<int>(MAX_ENTRIES); ++i) {
push(i, 2*i);
}
for(int i = 0; i < static_cast<int>(MAX_ENTRIES); ++i) {
EXPECT_EQ(2*i, pop(i).value());
}
}
TEST_F(CacheTest_PushAndPop, FullCache_PushNonOrdered_PopOrdered) {
for(int i = 1; i < static_cast<int>(MAX_ENTRIES); i += 2) {
push(i, 2*i);
}
for(int i = 0; i < static_cast<int>(MAX_ENTRIES); i += 2) {
push(i, 2*i);
}
for(int i = 0; i < static_cast<int>(MAX_ENTRIES); ++i) {
EXPECT_EQ(2*i, pop(i).value());
}
}
TEST_F(CacheTest_PushAndPop, FullCache_PushOrdered_PopNonOrdered) {
for(int i = 0; i < static_cast<int>(MAX_ENTRIES); ++i) {
push(i, 2*i);
}
for(int i = 1; i < static_cast<int>(MAX_ENTRIES); i += 2) {
EXPECT_EQ(2*i, pop(i).value());
}
for(int i = 0; i < static_cast<int>(MAX_ENTRIES); i += 2) {
EXPECT_EQ(2*i, pop(i).value());
}
}
int roundDownToEven(int number) {
if (number % 2 == 0) {
return number;
} else {
return number - 1;
}
}
int roundDownToOdd(int number) {
if (number % 2 != 0) {
return number;
} else {
return number - 1;
}
}
TEST_F(CacheTest_PushAndPop, FullCache_PushNonOrdered_PopNonOrdered) {
for(int i = roundDownToEven(MAX_ENTRIES - 1); i >= 0; i -= 2) {
push(i, 2*i);
}
for(int i = 1; i < static_cast<int>(MAX_ENTRIES); i += 2) {
push(i, 2*i);
}
for(int i = roundDownToOdd(MAX_ENTRIES-1); i >= 0; i -= 2) {
EXPECT_EQ(2*i, pop(i).value());
}
for(int i = 0; i < static_cast<int>(MAX_ENTRIES); i += 2) {
EXPECT_EQ(2*i, pop(i).value());
}
}
TEST_F(CacheTest_PushAndPop, MoreThanFullCache) {
for(int i = 0; i < static_cast<int>(MAX_ENTRIES + 2); ++i) {
push(i, 2*i);
}
//Check that the oldest two elements got deleted automatically
EXPECT_EQ(boost::none, pop(0));
EXPECT_EQ(boost::none, pop(1));
//Check the other elements are still there
for(int i = 2; i < static_cast<int>(MAX_ENTRIES + 2); ++i) {
EXPECT_EQ(2*i, pop(i).value());
}
}
TEST_F(CacheTest_PushAndPop, AfterTimeout) {
constexpr double TIMEOUT1_SEC = Cache::MAX_LIFETIME_SEC * 3/4;
constexpr double TIMEOUT2_SEC = Cache::PURGE_LIFETIME_SEC * 3/4;
static_assert(TIMEOUT1_SEC + TIMEOUT2_SEC > Cache::MAX_LIFETIME_SEC, "Ensure that our chosen timeouts push the first entry out of the cache");
push(10, 20);
boost::this_thread::sleep_for(boost::chrono::milliseconds(static_cast<int>(1000 * TIMEOUT1_SEC)));
push(20, 30);
boost::this_thread::sleep_for(boost::chrono::milliseconds(static_cast<int>(1000 * TIMEOUT2_SEC)));
EXPECT_EQ(boost::none, pop(10));
EXPECT_EQ(30, pop(20).value());
}

View File

@ -1,109 +0,0 @@
#include "testutils/CacheTest.h"
#include <chrono>
#include <thread>
#include <memory>
#include <future>
#include <cpp-utils/lock/ConditionBarrier.h>
using namespace blockstore::caching;
using std::chrono::seconds;
using std::string;
using cpputils::ConditionBarrier;
using std::unique_ptr;
using std::make_unique;
using std::future;
// Regression tests for a race condition.
// An element could be in the process of being thrown out of the cache and while the destructor is running, another
// thread calls pop() for the element and gets none returned. But since the destructor isn't finished yet, the data from
// the cache element also isn't completely written back yet and an application loading it runs into a race condition.
class ObjectWithLongDestructor {
public:
ObjectWithLongDestructor(ConditionBarrier *onDestructorStarted, std::atomic<bool> *destructorFinished)
: _onDestructorStarted(onDestructorStarted), _destructorFinished(destructorFinished) {}
~ObjectWithLongDestructor() {
_onDestructorStarted->release();
std::this_thread::sleep_for(seconds(1));
*_destructorFinished = true;
}
private:
ConditionBarrier *_onDestructorStarted;
std::atomic<bool> *_destructorFinished;
DISALLOW_COPY_AND_ASSIGN(ObjectWithLongDestructor);
};
class CacheTest_RaceCondition: public ::testing::Test {
public:
CacheTest_RaceCondition(): cache("test"), destructorStarted(), destructorFinished(false) {}
static constexpr unsigned int MAX_ENTRIES = 100;
Cache<int, unique_ptr<ObjectWithLongDestructor>, MAX_ENTRIES> cache;
ConditionBarrier destructorStarted;
std::atomic<bool> destructorFinished;
int pushObjectWithLongDestructor() {
cache.push(2, make_unique<ObjectWithLongDestructor>(&destructorStarted, &destructorFinished));
return 2;
}
int pushDummyObject() {
cache.push(3, nullptr);
return 3;
}
future<void> causeCacheOverflowInOtherThread() {
//Add maximum+1 element in another thread (this causes the cache to flush the first element in another thread)
return std::async(std::launch::async, [this] {
for(unsigned int i = 0; i < MAX_ENTRIES+1; ++i) {
cache.push(MAX_ENTRIES+i, nullptr);
}
});
}
void EXPECT_POP_BLOCKS_UNTIL_DESTRUCTOR_FINISHED(int key) {
EXPECT_FALSE(destructorFinished);
cache.pop(key);
EXPECT_TRUE(destructorFinished);
}
void EXPECT_POP_DOESNT_BLOCK_UNTIL_DESTRUCTOR_FINISHED(int key) {
EXPECT_FALSE(destructorFinished);
cache.pop(key);
EXPECT_FALSE(destructorFinished);
}
};
TEST_F(CacheTest_RaceCondition, PopBlocksWhileRequestedElementIsThrownOut_ByAge) {
auto id = pushObjectWithLongDestructor();
destructorStarted.wait();
EXPECT_POP_BLOCKS_UNTIL_DESTRUCTOR_FINISHED(id);
}
TEST_F(CacheTest_RaceCondition, PopDoesntBlockWhileOtherElementIsThrownOut_ByAge) {
pushObjectWithLongDestructor();
auto id = pushDummyObject();
destructorStarted.wait();
EXPECT_POP_DOESNT_BLOCK_UNTIL_DESTRUCTOR_FINISHED(id);
}
TEST_F(CacheTest_RaceCondition, PopBlocksWhileRequestedElementIsThrownOut_ByPush) {
auto id = pushObjectWithLongDestructor();
auto future = causeCacheOverflowInOtherThread();
destructorStarted.wait();
EXPECT_POP_BLOCKS_UNTIL_DESTRUCTOR_FINISHED(id);
}
TEST_F(CacheTest_RaceCondition, PopDoesntBlockWhileOtherElementIsThrownOut_ByPush) {
pushObjectWithLongDestructor();
auto id = pushDummyObject();
auto future = causeCacheOverflowInOtherThread();
destructorStarted.wait();
EXPECT_POP_DOESNT_BLOCK_UNTIL_DESTRUCTOR_FINISHED(id);
}

View File

@ -1,63 +0,0 @@
#include <gtest/gtest.h>
#include "blockstore/implementations/caching/cache/PeriodicTask.h"
#include <mutex>
#include <condition_variable>
#include <atomic>
using ::testing::Test;
using std::mutex;
using std::unique_lock;
using std::condition_variable;
using namespace blockstore::caching;
class AtomicCounter {
public:
AtomicCounter(int count): _mutex(), _cv(), _counter(count) {}
void decrease() {
unique_lock<mutex> lock(_mutex);
--_counter;
_cv.notify_all();
}
void waitForZero() {
unique_lock<mutex> lock(_mutex);
_cv.wait(lock, [this] () {return _counter <= 0;});
}
private:
mutex _mutex;
condition_variable _cv;
int _counter;
};
class PeriodicTaskTest: public Test {
};
TEST_F(PeriodicTaskTest, DoesntDeadlockInDestructorWhenDestructedImmediately) {
PeriodicTask task([](){}, 1, "test");
}
TEST_F(PeriodicTaskTest, CallsCallbackAtLeast10Times) {
AtomicCounter counter(10);
PeriodicTask task([&counter](){
counter.decrease();
}, 0.001, "test");
counter.waitForZero();
}
TEST_F(PeriodicTaskTest, DoesntCallCallbackAfterDestruction) {
std::atomic<int> callCount(0);
{
PeriodicTask task([&callCount](){
callCount += 1;
}, 0.001, "test");
}
int callCountDirectlyAfterDestruction = callCount;
boost::this_thread::sleep_for(boost::chrono::seconds(1));
EXPECT_EQ(callCountDirectlyAfterDestruction, callCount);
}

View File

@ -1,87 +0,0 @@
#include "testutils/QueueMapTest.h"
// Tests that QueueMap calls destructors correctly.
// This is needed, because QueueMap does its own memory management.
class QueueMapTest_MemoryLeak: public QueueMapTest {
public:
void EXPECT_NUM_INSTANCES(int num) {
EXPECT_EQ(num, MinimalKeyType::instances);
EXPECT_EQ(num, MinimalValueType::instances);
}
};
TEST_F(QueueMapTest_MemoryLeak, Empty) {
EXPECT_NUM_INSTANCES(0);
}
TEST_F(QueueMapTest_MemoryLeak, AfterPushingOne) {
push(2, 3);
EXPECT_NUM_INSTANCES(1);
}
TEST_F(QueueMapTest_MemoryLeak, AfterPushingTwo) {
push(2, 3);
push(3, 4);
EXPECT_NUM_INSTANCES(2);
}
TEST_F(QueueMapTest_MemoryLeak, AfterPushingTwoAndPoppingOldest) {
push(2, 3);
push(3, 4);
pop();
EXPECT_NUM_INSTANCES(1);
}
TEST_F(QueueMapTest_MemoryLeak, AfterPushingTwoAndPoppingFirst) {
push(2, 3);
push(3, 4);
pop(2);
EXPECT_NUM_INSTANCES(1);
}
TEST_F(QueueMapTest_MemoryLeak, AfterPushingTwoAndPoppingLast) {
push(2, 3);
push(3, 4);
pop(3);
EXPECT_NUM_INSTANCES(1);
}
TEST_F(QueueMapTest_MemoryLeak, AfterPushingOnePoppingOne) {
push(2, 3);
pop();
EXPECT_NUM_INSTANCES(0);
}
TEST_F(QueueMapTest_MemoryLeak, AfterPushingOnePoppingOnePerKey) {
push(2, 3);
pop(2);
EXPECT_NUM_INSTANCES(0);
}
TEST_F(QueueMapTest_MemoryLeak, AfterPushingOnePoppingOnePushingOne) {
push(2, 3);
pop();
push(3, 4);
EXPECT_NUM_INSTANCES(1);
}
TEST_F(QueueMapTest_MemoryLeak, AfterPushingOnePoppingOnePerKeyPushingOne) {
push(2, 3);
pop(2);
push(3, 4);
EXPECT_NUM_INSTANCES(1);
}
TEST_F(QueueMapTest_MemoryLeak, AfterPushingOnePoppingOnePushingSame) {
push(2, 3);
pop();
push(2, 3);
EXPECT_NUM_INSTANCES(1);
}
TEST_F(QueueMapTest_MemoryLeak, AfterPushingOnePoppingOnePerKeyPushingSame) {
push(2, 3);
pop(2);
push(2, 3);
EXPECT_NUM_INSTANCES(1);
}

View File

@ -1,50 +0,0 @@
#include <gtest/gtest.h>
#include <cpp-utils/pointer/unique_ref.h>
#include "blockstore/implementations/caching/cache/QueueMap.h"
#include "testutils/MinimalKeyType.h"
#include "testutils/CopyableMovableValueType.h"
using namespace blockstore::caching;
using ::testing::Test;
using cpputils::unique_ref;
using cpputils::make_unique_ref;
//Test that QueueMap uses a move constructor for Value if possible
class QueueMapTest_MoveConstructor: public Test {
public:
QueueMapTest_MoveConstructor(): map(make_unique_ref<QueueMap<MinimalKeyType, CopyableMovableValueType>>()) {
CopyableMovableValueType::numCopyConstructorCalled = 0;
}
unique_ref<QueueMap<MinimalKeyType, CopyableMovableValueType>> map;
};
TEST_F(QueueMapTest_MoveConstructor, PushingAndPopping_MoveIntoMap) {
map->push(MinimalKeyType::create(0), CopyableMovableValueType(2));
CopyableMovableValueType val = map->pop().value();
val.value(); //Access it to avoid the compiler optimizing the assignment away
EXPECT_EQ(0, CopyableMovableValueType::numCopyConstructorCalled);
}
TEST_F(QueueMapTest_MoveConstructor, PushingAndPoppingPerKey_MoveIntoMap) {
map->push(MinimalKeyType::create(0), CopyableMovableValueType(2));
CopyableMovableValueType val = map->pop(MinimalKeyType::create(0)).value();
val.value(); //Access it to avoid the compiler optimizing the assignment away
EXPECT_EQ(0, CopyableMovableValueType::numCopyConstructorCalled);
}
TEST_F(QueueMapTest_MoveConstructor, PushingAndPopping_CopyIntoMap) {
CopyableMovableValueType value(2);
map->push(MinimalKeyType::create(0), value);
CopyableMovableValueType val = map->pop().value();
val.value(); //Access it to avoid the compiler optimizing the assignment away
EXPECT_EQ(1, CopyableMovableValueType::numCopyConstructorCalled);
}
TEST_F(QueueMapTest_MoveConstructor, PushingAndPoppingPerKey_CopyIntoMap) {
CopyableMovableValueType value(2);
map->push(MinimalKeyType::create(0), value);
CopyableMovableValueType val = map->pop(MinimalKeyType::create(0)).value();
val.value(); //Access it to avoid the compiler optimizing the assignment away
EXPECT_EQ(1, CopyableMovableValueType::numCopyConstructorCalled);
}

View File

@ -1,34 +0,0 @@
#include "testutils/QueueMapTest.h"
#include <boost/optional/optional_io.hpp>
class QueueMapPeekTest: public QueueMapTest {};
TEST_F(QueueMapPeekTest, PoppingFromEmpty) {
EXPECT_EQ(boost::none, peek());
}
TEST_F(QueueMapPeekTest, PushingOne) {
push(3, 2);
EXPECT_EQ(2, peek().value());
}
TEST_F(QueueMapPeekTest, PushingTwo) {
push(2, 3);
push(3, 4);
EXPECT_EQ(3, peek().value());
EXPECT_EQ(3, peek().value());
EXPECT_EQ(3, pop().value());
EXPECT_EQ(4, peek().value());
EXPECT_EQ(4, peek().value());
EXPECT_EQ(4, pop().value());
EXPECT_EQ(boost::none, peek());
EXPECT_EQ(boost::none, pop());
}
TEST_F(QueueMapPeekTest, AfterPushingTwoAndPoppingFirst) {
push(2, 3);
push(3, 4);
pop(2);
EXPECT_EQ(boost::none, pop(2));
EXPECT_EQ(4, peek().value());
}

View File

@ -1,79 +0,0 @@
#include "testutils/QueueMapTest.h"
class QueueMapTest_Size: public QueueMapTest {};
TEST_F(QueueMapTest_Size, Empty) {
EXPECT_EQ(0, size());
}
TEST_F(QueueMapTest_Size, AfterPushingOne) {
push(2, 3);
EXPECT_EQ(1, size());
}
TEST_F(QueueMapTest_Size, AfterPushingTwo) {
push(2, 3);
push(3, 4);
EXPECT_EQ(2, size());
}
TEST_F(QueueMapTest_Size, AfterPushingTwoAndPoppingOldest) {
push(2, 3);
push(3, 4);
pop();
EXPECT_EQ(1, size());
}
TEST_F(QueueMapTest_Size, AfterPushingTwoAndPoppingFirst) {
push(2, 3);
push(3, 4);
pop(2);
EXPECT_EQ(1, size());
}
TEST_F(QueueMapTest_Size, AfterPushingTwoAndPoppingLast) {
push(2, 3);
push(3, 4);
pop(3);
EXPECT_EQ(1, size());
}
TEST_F(QueueMapTest_Size, AfterPushingOnePoppingOne) {
push(2, 3);
pop();
EXPECT_EQ(0, size());
}
TEST_F(QueueMapTest_Size, AfterPushingOnePoppingOnePerKey) {
push(2, 3);
pop(2);
EXPECT_EQ(0, size());
}
TEST_F(QueueMapTest_Size, AfterPushingOnePoppingOnePushingOne) {
push(2, 3);
pop();
push(3, 4);
EXPECT_EQ(1, size());
}
TEST_F(QueueMapTest_Size, AfterPushingOnePoppingOnePerKeyPushingOne) {
push(2, 3);
pop(2);
push(3, 4);
EXPECT_EQ(1, size());
}
TEST_F(QueueMapTest_Size, AfterPushingOnePoppingOnePushingSame) {
push(2, 3);
pop();
push(2, 3);
EXPECT_EQ(1, size());
}
TEST_F(QueueMapTest_Size, AfterPushingOnePoppingOnePerKeyPushingSame) {
push(2, 3);
pop(2);
push(2, 3);
EXPECT_EQ(1, size());
}

View File

@ -1,151 +0,0 @@
#include "testutils/QueueMapTest.h"
#include <cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround.h>
class QueueMapTest_Values: public QueueMapTest {};
TEST_F(QueueMapTest_Values, PoppingFromEmpty) {
EXPECT_EQ(boost::none, pop());
}
TEST_F(QueueMapTest_Values, PoppingFromEmptyPerKey) {
EXPECT_EQ(boost::none, pop(2));
}
TEST_F(QueueMapTest_Values, PoppingNonexistingPerKey) {
push(3, 2);
EXPECT_EQ(boost::none, pop(2));
}
TEST_F(QueueMapTest_Values, PushingOne) {
push(3, 2);
EXPECT_EQ(2, pop(3).value());
EXPECT_EQ(boost::none, pop());
}
TEST_F(QueueMapTest_Values, PushingTwo) {
push(2, 3);
push(3, 4);
EXPECT_EQ(3, pop().value());
EXPECT_EQ(4, pop().value());
EXPECT_EQ(boost::none, pop());
}
TEST_F(QueueMapTest_Values, AfterPushingTwoAndPoppingFirst) {
push(2, 3);
push(3, 4);
pop(2);
EXPECT_EQ(boost::none, pop(2));
EXPECT_EQ(4, pop(3).value());
EXPECT_EQ(boost::none, pop());
}
TEST_F(QueueMapTest_Values, AfterPushingTwoAndPoppingLast) {
push(2, 3);
push(3, 4);
pop(3);
EXPECT_EQ(boost::none, pop(3));
EXPECT_EQ(3, pop(2).value());
EXPECT_EQ(boost::none, pop());
}
TEST_F(QueueMapTest_Values, AfterPushingOnePoppingOne) {
push(2, 3);
pop();
EXPECT_EQ(boost::none, pop());
EXPECT_EQ(boost::none, pop(2));
}
TEST_F(QueueMapTest_Values, AfterPushingOnePoppingOnePerKey) {
push(2, 3);
pop(2);
EXPECT_EQ(boost::none, pop());
EXPECT_EQ(boost::none, pop(2));
}
TEST_F(QueueMapTest_Values, AfterPushingOnePoppingOnePushingOne) {
push(2, 3);
pop();
push(3, 4);
EXPECT_EQ(boost::none, pop(2));
EXPECT_EQ(4, pop(3).value());
EXPECT_EQ(boost::none, pop());
}
TEST_F(QueueMapTest_Values, AfterPushingOnePoppingOnePerKeyPushingOne) {
push(2, 3);
pop(2);
push(3, 4);
EXPECT_EQ(boost::none, pop(2));
EXPECT_EQ(4, pop(3).value());
EXPECT_EQ(boost::none, pop());
}
TEST_F(QueueMapTest_Values, PushingSomePoppingMiddlePerKey) {
push(1, 2);
push(2, 3);
push(3, 4);
push(4, 5);
push(5, 6);
EXPECT_EQ(3, pop(2).value());
EXPECT_EQ(5, pop(4).value());
EXPECT_EQ(2, pop().value());
EXPECT_EQ(4, pop().value());
EXPECT_EQ(6, pop().value());
EXPECT_EQ(boost::none, pop());
}
TEST_F(QueueMapTest_Values, PushingSomePoppingFirstPerKey) {
push(1, 2);
push(2, 3);
push(3, 4);
push(4, 5);
push(5, 6);
EXPECT_EQ(2, pop(1).value());
EXPECT_EQ(3, pop(2).value());
EXPECT_EQ(4, pop().value());
EXPECT_EQ(5, pop().value());
EXPECT_EQ(6, pop().value());
EXPECT_EQ(boost::none, pop());
}
TEST_F(QueueMapTest_Values, PushingSomePoppingLastPerKey) {
push(1, 2);
push(2, 3);
push(3, 4);
push(4, 5);
push(5, 6);
EXPECT_EQ(6, pop(5).value());
EXPECT_EQ(5, pop(4).value());
EXPECT_EQ(2, pop().value());
EXPECT_EQ(3, pop().value());
EXPECT_EQ(4, pop().value());
EXPECT_EQ(boost::none, pop());
}
//This test forces the underlying datastructure (std::map or std::unordered_map) to grow and reallocate memory.
//So it tests, that QueueMap still works after reallocating memory.
TEST_F(QueueMapTest_Values, ManyValues) {
//Push 1 million entries
for (int i = 0; i < 1000000; ++i) {
push(i, 2*i);
}
//pop every other one by key
for (int i = 0; i < 1000000; i += 2) {
EXPECT_EQ(2*i, pop(i).value());
}
//pop the rest in queue order
for (int i = 1; i < 1000000; i += 2) {
EXPECT_EQ(2*i, peek().value());
EXPECT_EQ(2*i, pop().value());
}
EXPECT_EQ(0, size());
EXPECT_EQ(boost::none, pop());
EXPECT_EQ(boost::none, peek());
}
TEST_F(QueueMapTest_Values, PushAlreadyExistingValue) {
push(2, 3);
EXPECT_ANY_THROW(
push(2, 4);
);
}

View File

@ -1,13 +0,0 @@
#include "CacheTest.h"
void CacheTest::push(int key, int value) {
return _cache.push(MinimalKeyType::create(key), MinimalValueType::create(value));
}
boost::optional<int> CacheTest::pop(int key) {
boost::optional<MinimalValueType> entry = _cache.pop(MinimalKeyType::create(key));
if (!entry) {
return boost::none;
}
return entry->value();
}

View File

@ -1,29 +0,0 @@
#pragma once
#ifndef MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_QUEUEMAPTEST_H_
#define MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_QUEUEMAPTEST_H_
#include <gtest/gtest.h>
#include "blockstore/implementations/caching/cache/Cache.h"
#include "MinimalKeyType.h"
#include "MinimalValueType.h"
#include <boost/optional.hpp>
// This class is a parent class for tests on QueueMap.
// It offers functions to work with a QueueMap test object which is built using types having only the minimal type requirements.
// Furthermore, the class checks that there are no memory leaks left after destructing the QueueMap (by counting leftover instances of Keys/Values).
class CacheTest: public ::testing::Test {
public:
CacheTest(): _cache("test") {}
void push(int key, int value);
boost::optional<int> pop(int key);
static constexpr unsigned int MAX_ENTRIES = 100;
using Cache = blockstore::caching::Cache<MinimalKeyType, MinimalValueType, MAX_ENTRIES>;
private:
Cache _cache;
};
#endif

View File

@ -1,3 +0,0 @@
#include "CopyableMovableValueType.h"
int CopyableMovableValueType::numCopyConstructorCalled = 0;

View File

@ -1,34 +0,0 @@
#pragma once
#ifndef MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_COPYABLEMOVABLEVALUETYPE_H_
#define MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_COPYABLEMOVABLEVALUETYPE_H_
class CopyableMovableValueType {
public:
static int numCopyConstructorCalled;
CopyableMovableValueType(int value): _value(value) {}
CopyableMovableValueType(const CopyableMovableValueType &rhs): CopyableMovableValueType(rhs._value) {
++numCopyConstructorCalled;
}
// NOLINTNEXTLINE(cert-oop54-cpp)
CopyableMovableValueType &operator=(const CopyableMovableValueType &rhs) {
_value = rhs._value;
++numCopyConstructorCalled;
return *this;
}
CopyableMovableValueType(CopyableMovableValueType &&rhs) noexcept: CopyableMovableValueType(rhs._value) {
//Don't increase numCopyConstructorCalled
}
CopyableMovableValueType &operator=(CopyableMovableValueType &&rhs) noexcept {
//Don't increase numCopyConstructorCalled
_value = rhs._value;
return *this;
}
int value() const {
return _value;
}
private:
int _value;
};
#endif

View File

@ -1,3 +0,0 @@
#include "MinimalKeyType.h"
std::atomic<int> MinimalKeyType::instances(0);

View File

@ -1,48 +0,0 @@
#pragma once
#ifndef MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_MINIMALKEYTYPE_H_
#define MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_MINIMALKEYTYPE_H_
#include <unordered_map>
#include <atomic>
// This is a not-default-constructible Key type
class MinimalKeyType {
public:
static std::atomic<int> instances;
static MinimalKeyType create(int value) {
return MinimalKeyType(value);
}
MinimalKeyType(const MinimalKeyType &rhs): MinimalKeyType(rhs.value()) {
}
~MinimalKeyType() {
--instances;
}
int value() const {
return _value;
}
private:
MinimalKeyType(int value): _value(value) {
++instances;
}
int _value;
};
namespace std {
template <> struct hash<MinimalKeyType> {
size_t operator()(const MinimalKeyType &obj) const {
return obj.value();
}
};
}
inline bool operator==(const MinimalKeyType &lhs, const MinimalKeyType &rhs) {
return lhs.value() == rhs.value();
}
#endif

View File

@ -1,3 +0,0 @@
#include "MinimalValueType.h"
std::atomic<int> MinimalValueType::instances(0);

View File

@ -1,53 +0,0 @@
#pragma once
#ifndef MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_MINIMALVALUETYPE_H_
#define MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_MINIMALVALUETYPE_H_
#include <cpp-utils/macros.h>
#include <cassert>
#include <cpp-utils/assert/assert.h>
#include <atomic>
// This is a not-default-constructible non-copyable but moveable Value type
class MinimalValueType {
public:
static std::atomic<int> instances;
static MinimalValueType create(int value) {
return MinimalValueType(value);
}
MinimalValueType(MinimalValueType &&rhs) noexcept: MinimalValueType(rhs.value()) {
rhs._isMoved = true;
}
MinimalValueType &operator=(MinimalValueType &&rhs) noexcept {
_value = rhs.value();
_isMoved = false;
rhs._isMoved = true;
return *this;
}
~MinimalValueType() {
ASSERT(!_isDestructed, "Object was already destructed before");
--instances;
_isDestructed = true;
}
int value() const {
ASSERT(!_isMoved && !_isDestructed, "Object is invalid");
return _value;
}
private:
MinimalValueType(int value): _value(value), _isMoved(false), _isDestructed(false) {
++instances;
}
int _value;
bool _isMoved;
bool _isDestructed;
DISALLOW_COPY_AND_ASSIGN(MinimalValueType);
};
#endif

View File

@ -1,44 +0,0 @@
#include "QueueMapTest.h"
QueueMapTest::QueueMapTest(): _map(cpputils::make_unique_ref<blockstore::caching::QueueMap<MinimalKeyType, MinimalValueType>>()) {
MinimalKeyType::instances = 0;
MinimalValueType::instances = 0;
}
QueueMapTest::~QueueMapTest() {
cpputils::destruct(std::move(_map));
EXPECT_EQ(0, MinimalKeyType::instances);
EXPECT_EQ(0, MinimalValueType::instances);
}
void QueueMapTest::push(int key, int value) {
_map->push(MinimalKeyType::create(key), MinimalValueType::create(value));
}
boost::optional<int> QueueMapTest::pop() {
auto elem = _map->pop();
if (!elem) {
return boost::none;
}
return elem.value().value();
}
boost::optional<int> QueueMapTest::pop(int key) {
auto elem = _map->pop(MinimalKeyType::create(key));
if (!elem) {
return boost::none;
}
return elem.value().value();
}
boost::optional<int> QueueMapTest::peek() {
auto elem = _map->peek();
if (!elem) {
return boost::none;
}
return elem.value().value();
}
int QueueMapTest::size() {
return _map->size();
}

View File

@ -1,31 +0,0 @@
#pragma once
#ifndef MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_QUEUEMAPTEST_H_
#define MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_QUEUEMAPTEST_H_
#include <gtest/gtest.h>
#include <cpp-utils/pointer/unique_ref.h>
#include "blockstore/implementations/caching/cache/QueueMap.h"
#include "MinimalKeyType.h"
#include "MinimalValueType.h"
#include <boost/optional.hpp>
// This class is a parent class for tests on QueueMap.
// It offers functions to work with a QueueMap test object which is built using types having only the minimal type requirements.
// Furthermore, the class checks that there are no memory leaks left after destructing the QueueMap (by counting leftover instances of Keys/Values).
class QueueMapTest: public ::testing::Test {
public:
QueueMapTest();
~QueueMapTest();
void push(int key, int value);
boost::optional<int> pop();
boost::optional<int> pop(int key);
boost::optional<int> peek();
int size();
private:
cpputils::unique_ref<blockstore::caching::QueueMap<MinimalKeyType, MinimalValueType>> _map;
};
#endif

View File

@ -1,27 +0,0 @@
#include "blockstore/implementations/compressing/CompressingBlockStore.h"
#include "blockstore/implementations/compressing/compressors/Gzip.h"
#include "blockstore/implementations/compressing/compressors/RunLengthEncoding.h"
#include "blockstore/implementations/testfake/FakeBlockStore.h"
#include "../../testutils/BlockStoreTest.h"
#include <gtest/gtest.h>
using blockstore::BlockStore;
using blockstore::compressing::CompressingBlockStore;
using blockstore::compressing::Gzip;
using blockstore::compressing::RunLengthEncoding;
using blockstore::testfake::FakeBlockStore;
using cpputils::make_unique_ref;
using cpputils::unique_ref;
template<class Compressor>
class CompressingBlockStoreTestFixture: public BlockStoreTestFixture {
public:
unique_ref<BlockStore> createBlockStore() override {
return make_unique_ref<CompressingBlockStore<Compressor>>(make_unique_ref<FakeBlockStore>());
}
};
INSTANTIATE_TYPED_TEST_SUITE_P(Compressing_Gzip, BlockStoreTest, CompressingBlockStoreTestFixture<Gzip>);
INSTANTIATE_TYPED_TEST_SUITE_P(Compressing_RunLengthEncoding, BlockStoreTest, CompressingBlockStoreTestFixture<RunLengthEncoding>);

View File

@ -1,92 +0,0 @@
#include <gtest/gtest.h>
#include "blockstore/implementations/compressing/compressors/Gzip.h"
#include "blockstore/implementations/compressing/compressors/RunLengthEncoding.h"
#include <cpp-utils/data/DataFixture.h>
using namespace blockstore::compressing;
using cpputils::Data;
using cpputils::DataFixture;
template<class Compressor>
class CompressorTest: public ::testing::Test {
public:
void EXPECT_COMPRESS_AND_DECOMPRESS_IS_IDENTITY(const Data &data) {
Data compressed = Compressor::Compress(data);
Data decompressed = Compressor::Decompress(compressed.data(), compressed.size());
EXPECT_EQ(data, decompressed);
}
};
TYPED_TEST_SUITE_P(CompressorTest);
TYPED_TEST_P(CompressorTest, Empty) {
Data empty(0);
this->EXPECT_COMPRESS_AND_DECOMPRESS_IS_IDENTITY(empty);
}
TYPED_TEST_P(CompressorTest, ArbitraryData) {
Data fixture = DataFixture::generate(10240);
this->EXPECT_COMPRESS_AND_DECOMPRESS_IS_IDENTITY(fixture);
}
TYPED_TEST_P(CompressorTest, Zeroes) {
Data zeroes(10240);
zeroes.FillWithZeroes();
this->EXPECT_COMPRESS_AND_DECOMPRESS_IS_IDENTITY(zeroes);
}
TYPED_TEST_P(CompressorTest, Runs) {
Data data(4096);
std::memset(data.dataOffset(0), 0xF2, 1024);
std::memset(data.dataOffset(1024), 0x00, 1024);
std::memset(data.dataOffset(2048), 0x01, 2048);
this->EXPECT_COMPRESS_AND_DECOMPRESS_IS_IDENTITY(data);
}
TYPED_TEST_P(CompressorTest, RunsAndArbitrary) {
Data data(4096);
std::memset(data.dataOffset(0), 0xF2, 1024);
std::memcpy(data.dataOffset(1024), DataFixture::generate(1024).data(), 1024);
std::memset(data.dataOffset(2048), 0x01, 2048);
this->EXPECT_COMPRESS_AND_DECOMPRESS_IS_IDENTITY(data);
}
TYPED_TEST_P(CompressorTest, LargeData) {
// this is larger than what fits into 16bit (16bit are for example used as run length indicator in RunLengthEncoding)
Data fixture = DataFixture::generate(200000);
this->EXPECT_COMPRESS_AND_DECOMPRESS_IS_IDENTITY(fixture);
}
TYPED_TEST_P(CompressorTest, LargeRuns) {
// each run is larger than what fits into 16bit (16bit are for example used as run length indicator in RunLengthEncoding)
constexpr size_t RUN_SIZE = 200000;
Data data(3*RUN_SIZE);
std::memset(data.dataOffset(0), 0xF2, RUN_SIZE);
std::memset(data.dataOffset(RUN_SIZE), 0x00, RUN_SIZE);
std::memset(data.dataOffset(2*RUN_SIZE), 0x01, RUN_SIZE);
this->EXPECT_COMPRESS_AND_DECOMPRESS_IS_IDENTITY(data);
}
TYPED_TEST_P(CompressorTest, LargeRunsAndArbitrary) {
// each run is larger than what fits into 16bit (16bit are for example used as run length indicator in RunLengthEncoding)
constexpr size_t RUN_SIZE = 200000;
Data data(3*RUN_SIZE);
std::memset(data.dataOffset(0), 0xF2, RUN_SIZE);
std::memcpy(data.dataOffset(RUN_SIZE), DataFixture::generate(RUN_SIZE).data(), RUN_SIZE);
std::memset(data.dataOffset(2*RUN_SIZE), 0x01, RUN_SIZE);
this->EXPECT_COMPRESS_AND_DECOMPRESS_IS_IDENTITY(data);
}
REGISTER_TYPED_TEST_SUITE_P(CompressorTest,
Empty,
ArbitraryData,
Zeroes,
Runs,
RunsAndArbitrary,
LargeData,
LargeRuns,
LargeRunsAndArbitrary
);
INSTANTIATE_TYPED_TEST_SUITE_P(Gzip, CompressorTest, Gzip);
INSTANTIATE_TYPED_TEST_SUITE_P(RunLengthEncoding, CompressorTest, RunLengthEncoding);

View File

@ -1,65 +0,0 @@
#include <cpp-utils/crypto/symmetric/ciphers.h>
#include <cpp-utils/crypto/symmetric/Cipher.h>
#include <blockstore/implementations/low2highlevel/LowToHighLevelBlockStore.h>
#include "blockstore/implementations/encrypted/EncryptedBlockStore2.h"
#include "blockstore/implementations/testfake/FakeBlockStore.h"
#include "blockstore/implementations/inmemory/InMemoryBlockStore2.h"
#include "../../testutils/BlockStoreTest.h"
#include "../../testutils/BlockStore2Test.h"
//TODO Move FakeAuthenticatedCipher out of test folder to normal folder. Dependencies should not point into tests of other modules.
#include "cpp-utils/crypto/symmetric/testutils/FakeAuthenticatedCipher.h"
#include <gtest/gtest.h>
using blockstore::BlockStore;
using blockstore::BlockStore2;
using blockstore::encrypted::EncryptedBlockStore2;
using blockstore::lowtohighlevel::LowToHighLevelBlockStore;
using blockstore::inmemory::InMemoryBlockStore2;
using cpputils::AES256_GCM;
using cpputils::AES256_CFB;
using cpputils::FakeAuthenticatedCipher;
using cpputils::DataFixture;
using cpputils::make_unique_ref;
using cpputils::unique_ref;
template<class Cipher>
class EncryptedBlockStoreTestFixture: public BlockStoreTestFixture {
public:
unique_ref<BlockStore> createBlockStore() override {
return make_unique_ref<LowToHighLevelBlockStore>(
make_unique_ref<EncryptedBlockStore2<Cipher>>(make_unique_ref<InMemoryBlockStore2>(), createKeyFixture())
);
}
private:
static typename Cipher::EncryptionKey createKeyFixture(int seed = 0) {
return Cipher::EncryptionKey::FromString(
DataFixture::generate(Cipher::KEYSIZE, seed).ToString()
);
}
};
INSTANTIATE_TYPED_TEST_SUITE_P(Encrypted_FakeCipher, BlockStoreTest, EncryptedBlockStoreTestFixture<FakeAuthenticatedCipher>);
INSTANTIATE_TYPED_TEST_SUITE_P(Encrypted_AES256_GCM, BlockStoreTest, EncryptedBlockStoreTestFixture<AES256_GCM>);
INSTANTIATE_TYPED_TEST_SUITE_P(Encrypted_AES256_CFB, BlockStoreTest, EncryptedBlockStoreTestFixture<AES256_CFB>);
template<class Cipher>
class EncryptedBlockStore2TestFixture: public BlockStore2TestFixture {
public:
unique_ref<BlockStore2> createBlockStore() override {
return make_unique_ref<EncryptedBlockStore2<Cipher>>(make_unique_ref<InMemoryBlockStore2>(), createKeyFixture());
}
private:
static typename Cipher::EncryptionKey createKeyFixture(int seed = 0) {
return Cipher::EncryptionKey::FromString(
DataFixture::generate(Cipher::KEYSIZE, seed).ToString()
);
}
};
INSTANTIATE_TYPED_TEST_SUITE_P(Encrypted_FakeCipher, BlockStore2Test, EncryptedBlockStore2TestFixture<FakeAuthenticatedCipher>);
INSTANTIATE_TYPED_TEST_SUITE_P(Encrypted_AES256_GCM, BlockStore2Test, EncryptedBlockStore2TestFixture<AES256_GCM>);
INSTANTIATE_TYPED_TEST_SUITE_P(Encrypted_AES256_CFB, BlockStore2Test, EncryptedBlockStore2TestFixture<AES256_CFB>);

View File

@ -1,132 +0,0 @@
#include <gtest/gtest.h>
#include "cpp-utils/crypto/symmetric/testutils/FakeAuthenticatedCipher.h"
#include "blockstore/implementations/encrypted/EncryptedBlockStore2.h"
#include "blockstore/implementations/inmemory/InMemoryBlockStore2.h"
#include "blockstore/utils/BlockStoreUtils.h"
#include "../../testutils/gtest_printers.h"
#include <cpp-utils/data/DataFixture.h>
using ::testing::Test;
using cpputils::DataFixture;
using cpputils::Data;
using cpputils::unique_ref;
using cpputils::make_unique_ref;
using cpputils::FakeAuthenticatedCipher;
using blockstore::inmemory::InMemoryBlockStore2;
using namespace blockstore::encrypted;
class EncryptedBlockStoreTest: public Test {
public:
static constexpr unsigned int BLOCKSIZE = 1024;
EncryptedBlockStoreTest():
baseBlockStore(new InMemoryBlockStore2),
blockStore(make_unique_ref<EncryptedBlockStore2<FakeAuthenticatedCipher>>(std::move(cpputils::nullcheck(std::unique_ptr<InMemoryBlockStore2>(baseBlockStore)).value()), FakeAuthenticatedCipher::Key1())),
data(DataFixture::generate(BLOCKSIZE)) {
}
InMemoryBlockStore2 *baseBlockStore;
unique_ref<EncryptedBlockStore2<FakeAuthenticatedCipher>> blockStore;
Data data;
blockstore::BlockId CreateBlockDirectlyWithFixtureAndReturnKey() {
return CreateBlockReturnKey(data);
}
blockstore::BlockId CreateBlockReturnKey(const Data &initData) {
return blockStore->create(initData.copy());
}
blockstore::BlockId CreateBlockWriteFixtureToItAndReturnKey() {
auto blockId = blockStore->create(Data(data.size()));
blockStore->store(blockId, data);
return blockId;
}
void ModifyBaseBlock(const blockstore::BlockId &blockId) {
auto block = baseBlockStore->load(blockId).value();
CryptoPP::byte* middle_byte = static_cast<CryptoPP::byte*>(block.data()) + 10;
*middle_byte = *middle_byte + 1;
baseBlockStore->store(blockId, block);
}
blockstore::BlockId CopyBaseBlock(const blockstore::BlockId &blockId) {
auto source = baseBlockStore->load(blockId).value();
return baseBlockStore->create(source);
}
private:
DISALLOW_COPY_AND_ASSIGN(EncryptedBlockStoreTest);
};
TEST_F(EncryptedBlockStoreTest, LoadingWithSameKeyWorks_WriteOnCreate) {
auto blockId = CreateBlockDirectlyWithFixtureAndReturnKey();
auto loaded = blockStore->load(blockId);
EXPECT_NE(boost::none, loaded);
EXPECT_EQ(data.size(), loaded->size());
EXPECT_EQ(0, std::memcmp(data.data(), loaded->data(), data.size()));
}
TEST_F(EncryptedBlockStoreTest, LoadingWithSameKeyWorks_WriteSeparately) {
auto blockId = CreateBlockWriteFixtureToItAndReturnKey();
auto loaded = blockStore->load(blockId);
EXPECT_NE(boost::none, loaded);
EXPECT_EQ(data.size(), loaded->size());
EXPECT_EQ(0, std::memcmp(data.data(), loaded->data(), data.size()));
}
TEST_F(EncryptedBlockStoreTest, LoadingWithDifferentKeyDoesntWork_WriteOnCreate) {
auto blockId = CreateBlockDirectlyWithFixtureAndReturnKey();
blockStore->_setKey(FakeAuthenticatedCipher::Key2());
auto loaded = blockStore->load(blockId);
EXPECT_EQ(boost::none, loaded);
}
TEST_F(EncryptedBlockStoreTest, LoadingWithDifferentKeyDoesntWork_WriteSeparately) {
auto blockId = CreateBlockWriteFixtureToItAndReturnKey();
blockStore->_setKey(FakeAuthenticatedCipher::Key2());
auto loaded = blockStore->load(blockId);
EXPECT_EQ(boost::none, loaded);
}
TEST_F(EncryptedBlockStoreTest, LoadingModifiedBlockFails_WriteOnCreate) {
auto blockId = CreateBlockDirectlyWithFixtureAndReturnKey();
ModifyBaseBlock(blockId);
auto loaded = blockStore->load(blockId);
EXPECT_EQ(boost::none, loaded);
}
TEST_F(EncryptedBlockStoreTest, LoadingModifiedBlockFails_WriteSeparately) {
auto blockId = CreateBlockWriteFixtureToItAndReturnKey();
ModifyBaseBlock(blockId);
auto loaded = blockStore->load(blockId);
EXPECT_EQ(boost::none, loaded);
}
TEST_F(EncryptedBlockStoreTest, PhysicalBlockSize_zerophysical) {
EXPECT_EQ(0u, blockStore->blockSizeFromPhysicalBlockSize(0));
}
TEST_F(EncryptedBlockStoreTest, PhysicalBlockSize_zerovirtual) {
auto blockId = CreateBlockReturnKey(Data(0));
auto base = baseBlockStore->load(blockId).value();
EXPECT_EQ(0u, blockStore->blockSizeFromPhysicalBlockSize(base.size()));
}
TEST_F(EncryptedBlockStoreTest, PhysicalBlockSize_negativeboundaries) {
// This tests that a potential if/else in blockSizeFromPhysicalBlockSize that catches negative values has the
// correct boundary set. We test the highest value that is negative and the smallest value that is positive.
auto physicalSizeForVirtualSizeZero = baseBlockStore->load(CreateBlockReturnKey(Data(0))).value().size();
if (physicalSizeForVirtualSizeZero > 0) {
EXPECT_EQ(0u, blockStore->blockSizeFromPhysicalBlockSize(physicalSizeForVirtualSizeZero - 1));
}
EXPECT_EQ(0u, blockStore->blockSizeFromPhysicalBlockSize(physicalSizeForVirtualSizeZero));
EXPECT_EQ(1u, blockStore->blockSizeFromPhysicalBlockSize(physicalSizeForVirtualSizeZero + 1));
}
TEST_F(EncryptedBlockStoreTest, PhysicalBlockSize_positive) {
auto blockId = CreateBlockReturnKey(Data(10*1024));
auto base = baseBlockStore->load(blockId).value();
EXPECT_EQ(10*1024u, blockStore->blockSizeFromPhysicalBlockSize(base.size()));
}

View File

@ -1,35 +0,0 @@
#include "blockstore/implementations/low2highlevel/LowToHighLevelBlockStore.h"
#include "blockstore/implementations/inmemory/InMemoryBlockStore2.h"
#include "../../testutils/BlockStoreTest.h"
#include "../../testutils/BlockStore2Test.h"
#include <gtest/gtest.h>
#include <cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround.h>
#include <blockstore/implementations/low2highlevel/LowToHighLevelBlockStore.h>
using blockstore::BlockStore;
using blockstore::BlockStore2;
using blockstore::lowtohighlevel::LowToHighLevelBlockStore;
using blockstore::inmemory::InMemoryBlockStore2;
using cpputils::unique_ref;
using cpputils::make_unique_ref;
class InMemoryBlockStoreTestFixture: public BlockStoreTestFixture {
public:
unique_ref<BlockStore> createBlockStore() override {
return make_unique_ref<LowToHighLevelBlockStore>(
make_unique_ref<InMemoryBlockStore2>()
);
}
};
INSTANTIATE_TYPED_TEST_SUITE_P(InMemory, BlockStoreTest, InMemoryBlockStoreTestFixture);
class InMemoryBlockStore2TestFixture: public BlockStore2TestFixture {
public:
unique_ref<BlockStore2> createBlockStore() override {
return make_unique_ref<InMemoryBlockStore2>();
}
};
INSTANTIATE_TYPED_TEST_SUITE_P(InMemory, BlockStore2Test, InMemoryBlockStore2TestFixture);

View File

@ -1,62 +0,0 @@
#include "blockstore/implementations/integrity/IntegrityBlockStore2.h"
#include "blockstore/implementations/low2highlevel/LowToHighLevelBlockStore.h"
#include "blockstore/implementations/inmemory/InMemoryBlockStore2.h"
#include "../../testutils/BlockStoreTest.h"
#include "../../testutils/BlockStore2Test.h"
#include <gtest/gtest.h>
#include <cpp-utils/tempfile/TempFile.h>
using blockstore::BlockStore;
using blockstore::BlockStore2;
using blockstore::integrity::IntegrityBlockStore2;
using blockstore::lowtohighlevel::LowToHighLevelBlockStore;
using blockstore::inmemory::InMemoryBlockStore2;
using cpputils::make_unique_ref;
using cpputils::unique_ref;
using cpputils::TempFile;
template<bool AllowIntegrityViolations, bool MissingBlockIsIntegrityViolation>
class IntegrityBlockStoreTestFixture: public BlockStoreTestFixture {
public:
IntegrityBlockStoreTestFixture() :stateFile(false) {}
TempFile stateFile;
unique_ref<BlockStore> createBlockStore() override {
return make_unique_ref<LowToHighLevelBlockStore>(
make_unique_ref<IntegrityBlockStore2>(make_unique_ref<InMemoryBlockStore2>(), stateFile.path(), 0x12345678, AllowIntegrityViolations, MissingBlockIsIntegrityViolation, [] {})
);
}
};
using IntegrityBlockStoreTestFixture_multiclient = IntegrityBlockStoreTestFixture<false, false>;
using IntegrityBlockStoreTestFixture_singleclient = IntegrityBlockStoreTestFixture<false, true>;
using IntegrityBlockStoreTestFixture_multiclient_allowIntegrityViolations = IntegrityBlockStoreTestFixture<true, false>;
using IntegrityBlockStoreTestFixture_singleclient_allowIntegrityViolations = IntegrityBlockStoreTestFixture<true, true>;
INSTANTIATE_TYPED_TEST_SUITE_P(Integrity_multiclient, BlockStoreTest, IntegrityBlockStoreTestFixture_multiclient);
INSTANTIATE_TYPED_TEST_SUITE_P(Integrity_singleclient, BlockStoreTest, IntegrityBlockStoreTestFixture_singleclient);
INSTANTIATE_TYPED_TEST_SUITE_P(Integrity_multiclient_allowIntegrityViolations, BlockStoreTest, IntegrityBlockStoreTestFixture_multiclient_allowIntegrityViolations);
INSTANTIATE_TYPED_TEST_SUITE_P(Integrity_singleclient_allowIntegrityViolations, BlockStoreTest, IntegrityBlockStoreTestFixture_singleclient_allowIntegrityViolations);
template<bool AllowIntegrityViolations, bool MissingBlockIsIntegrityViolation>
class IntegrityBlockStore2TestFixture: public BlockStore2TestFixture {
public:
IntegrityBlockStore2TestFixture() :stateFile(false) {}
TempFile stateFile;
unique_ref<BlockStore2> createBlockStore() override {
return make_unique_ref<IntegrityBlockStore2>(make_unique_ref<InMemoryBlockStore2>(), stateFile.path(), 0x12345678, AllowIntegrityViolations, MissingBlockIsIntegrityViolation, [] {});
}
};
using IntegrityBlockStore2TestFixture_multiclient = IntegrityBlockStore2TestFixture<false, false>;
using IntegrityBlockStore2TestFixture_singleclient = IntegrityBlockStore2TestFixture<false, true>;
using IntegrityBlockStore2TestFixture_multiclient_allowIntegrityViolations = IntegrityBlockStore2TestFixture<true, false>;
using IntegrityBlockStore2TestFixture_singleclient_allowIntegrityViolations = IntegrityBlockStore2TestFixture<true, true>;
INSTANTIATE_TYPED_TEST_SUITE_P(Integrity_multiclient, BlockStore2Test, IntegrityBlockStore2TestFixture_multiclient);
INSTANTIATE_TYPED_TEST_SUITE_P(Integrity_singleclient, BlockStore2Test, IntegrityBlockStore2TestFixture_singleclient);
INSTANTIATE_TYPED_TEST_SUITE_P(Integrity_multiclient_allowIntegrityViolations, BlockStore2Test, IntegrityBlockStore2TestFixture_multiclient_allowIntegrityViolations);
INSTANTIATE_TYPED_TEST_SUITE_P(Integrity_singleclient_allowIntegrityViolations, BlockStore2Test, IntegrityBlockStore2TestFixture_singleclient_allowIntegrityViolations);

View File

@ -1,388 +0,0 @@
#include <gtest/gtest.h>
#include "blockstore/implementations/integrity/IntegrityBlockStore2.h"
#include "blockstore/implementations/inmemory/InMemoryBlockStore2.h"
#include "blockstore/utils/BlockStoreUtils.h"
#include <cpp-utils/data/DataFixture.h>
#include <cpp-utils/tempfile/TempFile.h>
#include "../../testutils/gtest_printers.h"
using ::testing::Test;
using cpputils::DataFixture;
using cpputils::Data;
using cpputils::unique_ref;
using cpputils::make_unique_ref;
using cpputils::TempFile;
using cpputils::serialize;
using cpputils::deserialize;
using boost::none;
using std::unique_ptr;
using blockstore::inmemory::InMemoryBlockStore2;
using namespace blockstore::integrity;
namespace {
class FakeCallback final {
public:
FakeCallback(): wasCalled_(false) {}
bool wasCalled() const {
return wasCalled_;
}
std::function<void ()> callback() {
return [this] () {
wasCalled_ = true;
};
}
private:
bool wasCalled_;
};
}
template<bool AllowIntegrityViolations, bool MissingBlockIsIntegrityViolation>
class IntegrityBlockStoreTest: public Test {
public:
static constexpr unsigned int BLOCKSIZE = 1024;
IntegrityBlockStoreTest():
stateFile(false),
onIntegrityViolation(),
baseBlockStore(new InMemoryBlockStore2),
blockStore(make_unique_ref<IntegrityBlockStore2>(std::move(cpputils::nullcheck(std::unique_ptr<InMemoryBlockStore2>(baseBlockStore)).value()), stateFile.path(), myClientId, AllowIntegrityViolations, MissingBlockIsIntegrityViolation, onIntegrityViolation.callback())),
data(DataFixture::generate(BLOCKSIZE)) {
}
static constexpr uint32_t myClientId = 0x12345678;
TempFile stateFile;
FakeCallback onIntegrityViolation;
InMemoryBlockStore2 *baseBlockStore;
unique_ref<IntegrityBlockStore2> blockStore;
Data data;
blockstore::BlockId CreateBlockReturnKey() {
return CreateBlockReturnKey(data);
}
blockstore::BlockId CreateBlockReturnKey(const Data &initData) {
return blockStore->create(initData.copy());
}
Data loadBaseBlock(const blockstore::BlockId &blockId) {
return baseBlockStore->load(blockId).value();
}
Data loadBlock(const blockstore::BlockId &blockId) {
return blockStore->load(blockId).value();
}
void modifyBlock(const blockstore::BlockId &blockId) {
auto block = blockStore->load(blockId).value();
CryptoPP::byte* first_byte = static_cast<CryptoPP::byte*>(block.data());
*first_byte = *first_byte + 1;
blockStore->store(blockId, block);
}
void rollbackBaseBlock(const blockstore::BlockId &blockId, const Data &data) {
baseBlockStore->store(blockId, data);
}
void decreaseVersionNumber(const blockstore::BlockId &blockId) {
auto baseBlock = baseBlockStore->load(blockId).value();
void* versionPtr = static_cast<uint8_t*>(baseBlock.data()) + IntegrityBlockStore2::VERSION_HEADER_OFFSET;
uint64_t version = deserialize<uint64_t>(versionPtr);
ASSERT(version > 1, "Can't decrease the lowest allowed version number");
serialize<uint64_t>(versionPtr, version-1);
baseBlockStore->store(blockId, baseBlock);
}
void increaseVersionNumber(const blockstore::BlockId &blockId) {
auto baseBlock = baseBlockStore->load(blockId).value();
void* versionPtr = static_cast<uint8_t*>(baseBlock.data()) + IntegrityBlockStore2::VERSION_HEADER_OFFSET;
uint64_t version = deserialize<uint64_t>(versionPtr);
serialize<uint64_t>(versionPtr, version+1);
baseBlockStore->store(blockId, baseBlock);
}
void changeClientId(const blockstore::BlockId &blockId) {
auto baseBlock = baseBlockStore->load(blockId).value();
void* clientIdPtr = static_cast<uint8_t*>(baseBlock.data()) + IntegrityBlockStore2::CLIENTID_HEADER_OFFSET;
uint64_t clientId = deserialize<uint64_t>(clientIdPtr);
serialize<uint64_t>(clientIdPtr, clientId+1);
baseBlockStore->store(blockId, baseBlock);
}
void deleteBlock(const blockstore::BlockId &blockId) {
blockStore->remove(blockId);
}
void insertBaseBlock(const blockstore::BlockId &blockId, Data data) {
EXPECT_TRUE(baseBlockStore->tryCreate(blockId, data));
}
private:
DISALLOW_COPY_AND_ASSIGN(IntegrityBlockStoreTest);
};
using IntegrityBlockStoreTest_Default = IntegrityBlockStoreTest<false, false>;
using IntegrityBlockStoreTest_MissingBlockIsIntegrityViolation = IntegrityBlockStoreTest<false, true>;
using IntegrityBlockStoreTest_AllowIntegrityViolations = IntegrityBlockStoreTest<true, false>;
using IntegrityBlockStoreTest_AllowIntegrityViolations_MissingBlockIsIntegrityViolation = IntegrityBlockStoreTest<true, true>;
template<bool AllowIntegrityViolations, bool MissingBlockIsIntegrityViolation>
constexpr uint32_t IntegrityBlockStoreTest<AllowIntegrityViolations, MissingBlockIsIntegrityViolation>::myClientId;
// Test that a decreasing version number is not allowed
TEST_F(IntegrityBlockStoreTest_Default, RollbackPrevention_DoesntAllowDecreasingVersionNumberForSameClient_1) {
auto blockId = CreateBlockReturnKey();
Data oldBaseBlock = loadBaseBlock(blockId);
modifyBlock(blockId);
rollbackBaseBlock(blockId, oldBaseBlock);
EXPECT_EQ(boost::none, blockStore->load(blockId));
EXPECT_TRUE(onIntegrityViolation.wasCalled());
}
// Test that a decreasing version number is allowed if allowIntegrityViolations is set.
TEST_F(IntegrityBlockStoreTest_AllowIntegrityViolations, RollbackPrevention_AllowsDecreasingVersionNumberForSameClient_1) {
auto blockId = CreateBlockReturnKey();
Data oldBaseBlock = loadBaseBlock(blockId);
modifyBlock(blockId);
rollbackBaseBlock(blockId, oldBaseBlock);
EXPECT_NE(boost::none, blockStore->load(blockId));
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
TEST_F(IntegrityBlockStoreTest_Default, RollbackPrevention_DoesntAllowDecreasingVersionNumberForSameClient_2) {
auto blockId = CreateBlockReturnKey();
// Increase the version number
modifyBlock(blockId);
// Decrease the version number again
decreaseVersionNumber(blockId);
EXPECT_EQ(boost::none, blockStore->load(blockId));
EXPECT_TRUE(onIntegrityViolation.wasCalled());
}
TEST_F(IntegrityBlockStoreTest_AllowIntegrityViolations, RollbackPrevention_AllowsDecreasingVersionNumberForSameClient_2) {
auto blockId = CreateBlockReturnKey();
// Increase the version number
modifyBlock(blockId);
// Decrease the version number again
decreaseVersionNumber(blockId);
EXPECT_NE(boost::none, blockStore->load(blockId));
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
// Test that a different client doesn't need to have a higher version number (i.e. version numbers are per client).
TEST_F(IntegrityBlockStoreTest_Default, RollbackPrevention_DoesAllowDecreasingVersionNumberForDifferentClient) {
auto blockId = CreateBlockReturnKey();
// Increase the version number
modifyBlock(blockId);
// Fake a modification by a different client with lower version numbers
changeClientId(blockId);
decreaseVersionNumber(blockId);
EXPECT_NE(boost::none, blockStore->load(blockId));
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
TEST_F(IntegrityBlockStoreTest_AllowIntegrityViolations, RollbackPrevention_DoesAllowDecreasingVersionNumberForDifferentClient) {
auto blockId = CreateBlockReturnKey();
// Increase the version number
modifyBlock(blockId);
// Fake a modification by a different client with lower version numbers
changeClientId(blockId);
decreaseVersionNumber(blockId);
EXPECT_NE(boost::none, blockStore->load(blockId));
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
// Test that it doesn't allow a rollback to the "newest" block of a client, when this block was superseded by a version of a different client
TEST_F(IntegrityBlockStoreTest_Default, RollbackPrevention_DoesntAllowSameVersionNumberForOldClient) {
auto blockId = CreateBlockReturnKey();
// Increase the version number
modifyBlock(blockId);
Data oldBaseBlock = loadBaseBlock(blockId);
// Fake a modification by a different client with lower version numbers
changeClientId(blockId);
loadBlock(blockId); // make the block store know about this other client's modification
// Rollback to old client
rollbackBaseBlock(blockId, oldBaseBlock);
EXPECT_EQ(boost::none, blockStore->load(blockId));
EXPECT_TRUE(onIntegrityViolation.wasCalled());
}
// Test that it does allow a rollback to the "newest" block of a client, when this block was superseded by a version of a different client, but integrity violations are allowed
TEST_F(IntegrityBlockStoreTest_AllowIntegrityViolations, RollbackPrevention_AllowsSameVersionNumberForOldClient) {
auto blockId = CreateBlockReturnKey();
// Increase the version number
modifyBlock(blockId);
Data oldBaseBlock = loadBaseBlock(blockId);
// Fake a modification by a different client with lower version numbers
changeClientId(blockId);
loadBlock(blockId); // make the block store know about this other client's modification
// Rollback to old client
rollbackBaseBlock(blockId, oldBaseBlock);
EXPECT_NE(boost::none, blockStore->load(blockId));
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
// Test that deleted blocks cannot be re-introduced
TEST_F(IntegrityBlockStoreTest_Default, RollbackPrevention_DoesntAllowReintroducingDeletedBlocks) {
auto blockId = CreateBlockReturnKey();
Data oldBaseBlock = loadBaseBlock(blockId);
deleteBlock(blockId);
insertBaseBlock(blockId, std::move(oldBaseBlock));
EXPECT_EQ(boost::none, blockStore->load(blockId));
EXPECT_TRUE(onIntegrityViolation.wasCalled());
}
// Test that deleted blocks can be re-introduced if integrity violations are allowed
TEST_F(IntegrityBlockStoreTest_AllowIntegrityViolations, RollbackPrevention_AllowsReintroducingDeletedBlocks) {
auto blockId = CreateBlockReturnKey();
Data oldBaseBlock = loadBaseBlock(blockId);
deleteBlock(blockId);
insertBaseBlock(blockId, std::move(oldBaseBlock));
EXPECT_NE(boost::none, blockStore->load(blockId));
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
// This can happen if a client synchronization is delayed. Another client might have won the conflict and pushed a new version for the deleted block.
TEST_F(IntegrityBlockStoreTest_Default, RollbackPrevention_AllowsReintroducingDeletedBlocksWithNewVersionNumber) {
auto blockId = CreateBlockReturnKey();
Data oldBaseBlock = loadBaseBlock(blockId);
deleteBlock(blockId);
insertBaseBlock(blockId, std::move(oldBaseBlock));
increaseVersionNumber(blockId);
EXPECT_NE(boost::none, blockStore->load(blockId));
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
TEST_F(IntegrityBlockStoreTest_AllowIntegrityViolations, RollbackPrevention_AllowsReintroducingDeletedBlocksWithNewVersionNumber) {
auto blockId = CreateBlockReturnKey();
Data oldBaseBlock = loadBaseBlock(blockId);
deleteBlock(blockId);
insertBaseBlock(blockId, std::move(oldBaseBlock));
increaseVersionNumber(blockId);
EXPECT_NE(boost::none, blockStore->load(blockId));
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
// Check that in a multi-client scenario, missing blocks are not integrity errors, because another client might have deleted them.
TEST_F(IntegrityBlockStoreTest_Default, DeletionPrevention_AllowsDeletingBlocksWhenDeactivated) {
auto blockId = blockStore->create(Data(0));
baseBlockStore->remove(blockId);
EXPECT_EQ(boost::none, blockStore->load(blockId));
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
TEST_F(IntegrityBlockStoreTest_AllowIntegrityViolations, DeletionPrevention_AllowsDeletingBlocksWhenDeactivated) {
auto blockId = blockStore->create(Data(0));
baseBlockStore->remove(blockId);
EXPECT_EQ(boost::none, blockStore->load(blockId));
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
// Check that in a single-client scenario, missing blocks are integrity errors.
TEST_F(IntegrityBlockStoreTest_MissingBlockIsIntegrityViolation, DeletionPrevention_DoesntAllowDeletingBlocksWhenActivated) {
auto blockId = blockStore->create(Data(0));
baseBlockStore->remove(blockId);
EXPECT_EQ(boost::none, blockStore->load(blockId));
EXPECT_TRUE(onIntegrityViolation.wasCalled());
}
// Check that in a single-client scenario, missing blocks don't throw if integrity violations are allowed.
TEST_F(IntegrityBlockStoreTest_AllowIntegrityViolations_MissingBlockIsIntegrityViolation, DeletionPrevention_AllowsDeletingBlocksWhenActivated) {
auto blockId = blockStore->create(Data(0));
baseBlockStore->remove(blockId);
EXPECT_EQ(boost::none, blockStore->load(blockId));
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
// Check that in a multi-client scenario, missing blocks are not integrity errors, because another client might have deleted them.
TEST_F(IntegrityBlockStoreTest_Default, DeletionPrevention_InForEachBlock_AllowsDeletingBlocksWhenDeactivated) {
auto blockId = blockStore->create(Data(0));
baseBlockStore->remove(blockId);
int count = 0;
blockStore->forEachBlock([&count] (const blockstore::BlockId &) {
++count;
});
EXPECT_EQ(0, count);
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
TEST_F(IntegrityBlockStoreTest_AllowIntegrityViolations, DeletionPrevention_InForEachBlock_AllowsDeletingBlocksWhenDeactivated) {
auto blockId = blockStore->create(Data(0));
baseBlockStore->remove(blockId);
int count = 0;
blockStore->forEachBlock([&count] (const blockstore::BlockId &) {
++count;
});
EXPECT_EQ(0, count);
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
// Check that in a single-client scenario, missing blocks are integrity errors.
TEST_F(IntegrityBlockStoreTest_MissingBlockIsIntegrityViolation, DeletionPrevention_InForEachBlock_DoesntAllowDeletingBlocksWhenActivated) {
auto blockId = blockStore->create(Data(0));
baseBlockStore->remove(blockId);
blockStore->forEachBlock([] (const blockstore::BlockId &) {});
EXPECT_TRUE(onIntegrityViolation.wasCalled());
}
// Check that in a single-client scenario, missing blocks don't throw if integrity violations are allowed.
TEST_F(IntegrityBlockStoreTest_AllowIntegrityViolations_MissingBlockIsIntegrityViolation, DeletionPrevention_InForEachBlock_AllowsDeletingBlocksWhenActivated) {
auto blockId = blockStore->create(Data(0));
baseBlockStore->remove(blockId);
blockStore->forEachBlock([] (const blockstore::BlockId &) {});
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
TEST_F(IntegrityBlockStoreTest_Default, LoadingWithDifferentBlockIdFails) {
auto blockId = CreateBlockReturnKey();
blockstore::BlockId key2 = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
baseBlockStore->store(key2, baseBlockStore->load(blockId).value());
EXPECT_EQ(boost::none, blockStore->load(key2));
EXPECT_TRUE(onIntegrityViolation.wasCalled());
}
TEST_F(IntegrityBlockStoreTest_AllowIntegrityViolations, LoadingWithDifferentBlockIdDoesntFail) {
auto blockId = CreateBlockReturnKey();
blockstore::BlockId key2 = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
baseBlockStore->store(key2, baseBlockStore->load(blockId).value());
EXPECT_NE(boost::none, blockStore->load(key2));
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
// TODO Test more integrity cases:
// - RollbackPrevention_DoesntAllowReintroducingDeletedBlocks with different client id (i.e. trying to re-introduce the newest block of a different client)
// - RollbackPrevention_AllowsReintroducingDeletedBlocksWithNewVersionNumber with different client id
// - Think about more...
// TODO Test that disabling integrity checks allows all these cases
TEST_F(IntegrityBlockStoreTest_Default, PhysicalBlockSize_zerophysical) {
EXPECT_EQ(0u, blockStore->blockSizeFromPhysicalBlockSize(0));
}
TEST_F(IntegrityBlockStoreTest_Default, PhysicalBlockSize_zerovirtual) {
auto blockId = CreateBlockReturnKey(Data(0));
auto base = baseBlockStore->load(blockId).value();
EXPECT_EQ(0u, blockStore->blockSizeFromPhysicalBlockSize(base.size()));
}
TEST_F(IntegrityBlockStoreTest_Default, PhysicalBlockSize_negativeboundaries) {
// This tests that a potential if/else in blockSizeFromPhysicalBlockSize that catches negative values has the
// correct boundary set. We test the highest value that is negative and the smallest value that is positive.
auto physicalSizeForVirtualSizeZero = baseBlockStore->load(CreateBlockReturnKey(Data(0))).value().size();
if (physicalSizeForVirtualSizeZero > 0) {
EXPECT_EQ(0u, blockStore->blockSizeFromPhysicalBlockSize(physicalSizeForVirtualSizeZero - 1));
}
EXPECT_EQ(0u, blockStore->blockSizeFromPhysicalBlockSize(physicalSizeForVirtualSizeZero));
EXPECT_EQ(1u, blockStore->blockSizeFromPhysicalBlockSize(physicalSizeForVirtualSizeZero + 1));
}
TEST_F(IntegrityBlockStoreTest_Default, PhysicalBlockSize_positive) {
auto blockId = CreateBlockReturnKey(Data(10*1024));
auto base = baseBlockStore->load(blockId).value();
EXPECT_EQ(10*1024u, blockStore->blockSizeFromPhysicalBlockSize(base.size()));
}

View File

@ -1,354 +0,0 @@
#include <gtest/gtest.h>
#include <blockstore/implementations/integrity/KnownBlockVersions.h>
#include <cpp-utils/tempfile/TempFile.h>
using blockstore::integrity::KnownBlockVersions;
using blockstore::BlockId;
using cpputils::TempFile;
using std::unordered_set;
class KnownBlockVersionsTest : public ::testing::Test {
public:
KnownBlockVersionsTest() :stateFile(false), testobj(stateFile.path(), myClientId) {}
blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
blockstore::BlockId blockId2 = blockstore::BlockId::FromString("C772972491BB4932A1389EE14BC7090A");
static constexpr uint32_t myClientId = 0x12345678;
static constexpr uint32_t clientId = 0x23456789;
static constexpr uint32_t clientId2 = 0x34567890;
TempFile stateFile;
KnownBlockVersions testobj;
void setVersion(KnownBlockVersions *testobj, uint32_t clientId, const blockstore::BlockId &blockId, uint64_t version) {
if (!testobj->checkAndUpdateVersion(clientId, blockId, version)) {
throw std::runtime_error("Couldn't increase version");
}
}
void EXPECT_VERSION_IS(uint64_t version, KnownBlockVersions *testobj, blockstore::BlockId &blockId, uint32_t clientId) {
EXPECT_FALSE(testobj->checkAndUpdateVersion(clientId, blockId, version-1));
EXPECT_TRUE(testobj->checkAndUpdateVersion(clientId, blockId, version+1));
}
};
TEST_F(KnownBlockVersionsTest, setandget) {
setVersion(&testobj, clientId, blockId, 5);
EXPECT_EQ(5u, testobj.getBlockVersion(clientId, blockId));
}
TEST_F(KnownBlockVersionsTest, setandget_isPerClientId) {
setVersion(&testobj, clientId, blockId, 5);
setVersion(&testobj, clientId2, blockId, 3);
EXPECT_EQ(5u, testobj.getBlockVersion(clientId, blockId));
EXPECT_EQ(3u, testobj.getBlockVersion(clientId2, blockId));
}
TEST_F(KnownBlockVersionsTest, setandget_isPerBlock) {
setVersion(&testobj, clientId, blockId, 5);
setVersion(&testobj, clientId, blockId2, 3);
EXPECT_EQ(5u, testobj.getBlockVersion(clientId, blockId));
EXPECT_EQ(3u, testobj.getBlockVersion(clientId, blockId2));
}
TEST_F(KnownBlockVersionsTest, setandget_allowsIncreasing) {
setVersion(&testobj, clientId, blockId, 5);
setVersion(&testobj, clientId, blockId, 6);
EXPECT_EQ(6u, testobj.getBlockVersion(clientId, blockId));
}
TEST_F(KnownBlockVersionsTest, setandget_doesntAllowDecreasing) {
setVersion(&testobj, clientId, blockId, 5);
EXPECT_ANY_THROW(
setVersion(&testobj, clientId, blockId, 4);
);
}
TEST_F(KnownBlockVersionsTest, myClientId_isConsistent) {
EXPECT_EQ(testobj.myClientId(), testobj.myClientId());
}
TEST_F(KnownBlockVersionsTest, incrementVersion_newentry) {
auto version = testobj.incrementVersion(blockId);
EXPECT_EQ(1u, version);
EXPECT_EQ(1u, testobj.getBlockVersion(testobj.myClientId(), blockId));
}
TEST_F(KnownBlockVersionsTest, incrementVersion_oldentry) {
setVersion(&testobj, testobj.myClientId(), blockId, 5);
auto version = testobj.incrementVersion(blockId);
EXPECT_EQ(6u, version);
EXPECT_EQ(6u, testobj.getBlockVersion(testobj.myClientId(), blockId));
}
TEST_F(KnownBlockVersionsTest, checkAndUpdateVersion_newentry) {
EXPECT_TRUE(testobj.checkAndUpdateVersion(clientId, blockId, 5));
EXPECT_EQ(5u, testobj.getBlockVersion(clientId, blockId));
}
TEST_F(KnownBlockVersionsTest, checkAndUpdateVersion_oldentry_sameClientSameVersion) {
setVersion(&testobj, clientId, blockId, 5);
EXPECT_TRUE(testobj.checkAndUpdateVersion(clientId, blockId, 5));
EXPECT_EQ(5u, testobj.getBlockVersion(clientId, blockId));
}
TEST_F(KnownBlockVersionsTest, checkAndUpdateVersion_oldentry_sameClientLowerVersion) {
setVersion(&testobj, clientId, blockId, 5);
EXPECT_FALSE(testobj.checkAndUpdateVersion(clientId, blockId, 4));
EXPECT_EQ(5u, testobj.getBlockVersion(clientId, blockId));
}
TEST_F(KnownBlockVersionsTest, checkAndUpdateVersion_oldentry_sameClientNewerVersion) {
setVersion(&testobj, clientId, blockId, 5);
EXPECT_TRUE(testobj.checkAndUpdateVersion(clientId, blockId, 6));
EXPECT_EQ(6u, testobj.getBlockVersion(clientId, blockId));
}
TEST_F(KnownBlockVersionsTest, checkAndUpdateVersion_oldentry_differentClientSameVersion) {
setVersion(&testobj, clientId, blockId, 5);
EXPECT_TRUE(testobj.checkAndUpdateVersion(clientId2, blockId, 5));
EXPECT_EQ(5u, testobj.getBlockVersion(clientId, blockId));
EXPECT_EQ(5u, testobj.getBlockVersion(clientId2, blockId));
}
TEST_F(KnownBlockVersionsTest, checkAndUpdateVersion_oldentry_differentClientLowerVersion) {
setVersion(&testobj, clientId, blockId, 5);
EXPECT_TRUE(testobj.checkAndUpdateVersion(clientId2, blockId, 3));
EXPECT_EQ(5u, testobj.getBlockVersion(clientId, blockId));
EXPECT_EQ(3u, testobj.getBlockVersion(clientId2, blockId));
}
TEST_F(KnownBlockVersionsTest, checkAndUpdateVersion_oldentry_differentClientHigherVersion) {
setVersion(&testobj, clientId, blockId, 5);
EXPECT_TRUE(testobj.checkAndUpdateVersion(clientId2, blockId, 7));
EXPECT_EQ(5u, testobj.getBlockVersion(clientId, blockId));
EXPECT_EQ(7u, testobj.getBlockVersion(clientId2, blockId));
}
TEST_F(KnownBlockVersionsTest, checkAndUpdateVersion_oldentry_oldClientLowerVersion) {
setVersion(&testobj, clientId, blockId, 5);
EXPECT_TRUE(testobj.checkAndUpdateVersion(clientId2, blockId, 7));
EXPECT_FALSE(testobj.checkAndUpdateVersion(clientId, blockId, 3));
EXPECT_EQ(5u, testobj.getBlockVersion(clientId, blockId));
EXPECT_EQ(7u, testobj.getBlockVersion(clientId2, blockId));
}
TEST_F(KnownBlockVersionsTest, checkAndUpdateVersion_oldentry_oldClientSameVersion) {
setVersion(&testobj, clientId, blockId, 5);
EXPECT_TRUE(testobj.checkAndUpdateVersion(clientId2, blockId, 7));
EXPECT_FALSE(testobj.checkAndUpdateVersion(clientId, blockId, 5)); // Don't allow rollback to old client's newest block, if it was superseded by another client
EXPECT_EQ(5u, testobj.getBlockVersion(clientId, blockId));
EXPECT_EQ(7u, testobj.getBlockVersion(clientId2, blockId));
}
TEST_F(KnownBlockVersionsTest, checkAndUpdateVersion_oldentry_oldClientHigherVersion) {
setVersion(&testobj, clientId, blockId, 5);
EXPECT_TRUE(testobj.checkAndUpdateVersion(clientId2, blockId, 7));
EXPECT_TRUE(testobj.checkAndUpdateVersion(clientId, blockId, 6));
EXPECT_EQ(6u, testobj.getBlockVersion(clientId, blockId));
EXPECT_EQ(7u, testobj.getBlockVersion(clientId2, blockId));
}
TEST_F(KnownBlockVersionsTest, checkAndUpdateVersion_oldentry_oldClientLowerVersion_oldClientIsSelf) {
setVersion(&testobj, testobj.myClientId(), blockId, 5);
EXPECT_TRUE(testobj.checkAndUpdateVersion(clientId2, blockId, 7));
EXPECT_FALSE(testobj.checkAndUpdateVersion(testobj.myClientId(), blockId, 3));
EXPECT_EQ(5u, testobj.getBlockVersion(testobj.myClientId(), blockId));
EXPECT_EQ(7u, testobj.getBlockVersion(clientId2, blockId));
}
TEST_F(KnownBlockVersionsTest, checkAndUpdateVersion_oldentry_oldClientSameVersion_oldClientIsSelf) {
setVersion(&testobj, testobj.myClientId(), blockId, 5);
EXPECT_TRUE(testobj.checkAndUpdateVersion(clientId2, blockId, 7));
EXPECT_FALSE(testobj.checkAndUpdateVersion(testobj.myClientId(), blockId, 5)); // Don't allow rollback to old client's newest block, if it was superseded by another client
EXPECT_EQ(5u, testobj.getBlockVersion(testobj.myClientId(), blockId));
EXPECT_EQ(7u, testobj.getBlockVersion(clientId2, blockId));
}
TEST_F(KnownBlockVersionsTest, checkAndUpdateVersion_oldentry_oldClientHigherVersion_oldClientIsSelf) {
setVersion(&testobj, testobj.myClientId(), blockId, 4);
EXPECT_TRUE(testobj.checkAndUpdateVersion(clientId2, blockId, 7));
EXPECT_TRUE(testobj.checkAndUpdateVersion(testobj.myClientId(), blockId, 6));
EXPECT_EQ(6u, testobj.getBlockVersion(testobj.myClientId(), blockId));
EXPECT_EQ(7u, testobj.getBlockVersion(clientId2, blockId));
}
TEST_F(KnownBlockVersionsTest, checkAndUpdateVersion_oldentry_oldClientLowerVersion_newClientIsSelf) {
setVersion(&testobj, clientId, blockId, 5);
setVersion(&testobj, testobj.myClientId(), blockId, 7);
EXPECT_FALSE(testobj.checkAndUpdateVersion(clientId, blockId, 3));
EXPECT_EQ(5u, testobj.getBlockVersion(clientId, blockId));
EXPECT_EQ(7u, testobj.getBlockVersion(testobj.myClientId(), blockId));
}
TEST_F(KnownBlockVersionsTest, checkAndUpdateVersion_oldentry_oldClientSameVersion_newClientIsSelf) {
setVersion(&testobj, clientId, blockId, 5);
setVersion(&testobj, testobj.myClientId(), blockId, 7);
EXPECT_FALSE(testobj.checkAndUpdateVersion(clientId, blockId, 5)); // Don't allow rollback to old client's newest block, if it was superseded by another client
EXPECT_EQ(5u, testobj.getBlockVersion(clientId, blockId));
EXPECT_EQ(7u, testobj.getBlockVersion(testobj.myClientId(), blockId));
}
TEST_F(KnownBlockVersionsTest, checkAndUpdateVersion_oldentry_oldClientHigherVersion_newClientIsSelf) {
setVersion(&testobj, clientId, blockId, 5);
setVersion(&testobj, testobj.myClientId(), blockId, 7);
EXPECT_TRUE(testobj.checkAndUpdateVersion(clientId, blockId, 6));
EXPECT_EQ(6u, testobj.getBlockVersion(clientId, blockId));
EXPECT_EQ(7u, testobj.getBlockVersion(testobj.myClientId(), blockId));
}
TEST_F(KnownBlockVersionsTest, checkAndUpdate_twoEntriesDontInfluenceEachOther_differentKeys) {
// Setup
EXPECT_TRUE(testobj.checkAndUpdateVersion(clientId, blockId, 100));
EXPECT_TRUE(testobj.checkAndUpdateVersion(clientId, blockId2, 100));
EXPECT_TRUE(testobj.checkAndUpdateVersion(clientId, blockId, 150));
// Checks
EXPECT_VERSION_IS(150, &testobj, blockId, clientId);
EXPECT_VERSION_IS(100, &testobj, blockId2, clientId);
}
TEST_F(KnownBlockVersionsTest, checkAndUpdate_twoEntriesDontInfluenceEachOther_differentClientIds) {
// Setup
EXPECT_TRUE(testobj.checkAndUpdateVersion(clientId, blockId, 100));
EXPECT_TRUE(testobj.checkAndUpdateVersion(clientId2, blockId, 100));
EXPECT_TRUE(testobj.checkAndUpdateVersion(clientId, blockId, 150));
EXPECT_VERSION_IS(150, &testobj, blockId, clientId);
EXPECT_VERSION_IS(100, &testobj, blockId, clientId2);
}
TEST_F(KnownBlockVersionsTest, checkAndUpdate_allowsRollbackToSameClientWithSameVersionNumber) {
EXPECT_TRUE(testobj.checkAndUpdateVersion(clientId, blockId, 100));
EXPECT_TRUE(testobj.checkAndUpdateVersion(clientId, blockId, 100));
}
TEST_F(KnownBlockVersionsTest, checkAndUpdate_doesntAllowRollbackToOldClientWithSameVersionNumber) {
EXPECT_TRUE(testobj.checkAndUpdateVersion(clientId, blockId, 100));
EXPECT_TRUE(testobj.checkAndUpdateVersion(clientId2, blockId, 10));
EXPECT_FALSE(testobj.checkAndUpdateVersion(clientId, blockId, 100));
}
TEST_F(KnownBlockVersionsTest, saveAndLoad_empty) {
TempFile stateFile(false);
{
KnownBlockVersions _1(stateFile.path(), myClientId);
}
EXPECT_TRUE(KnownBlockVersions(stateFile.path(), myClientId).checkAndUpdateVersion(clientId, blockId, 1));
}
TEST_F(KnownBlockVersionsTest, saveAndLoad_oneentry) {
TempFile stateFile(false);
EXPECT_TRUE(KnownBlockVersions(stateFile.path(), myClientId).checkAndUpdateVersion(clientId, blockId, 100));
KnownBlockVersions obj(stateFile.path(), myClientId);
EXPECT_EQ(100u, obj.getBlockVersion(clientId, blockId));
}
TEST_F(KnownBlockVersionsTest, saveAndLoad_threeentries) {
TempFile stateFile(false);
{
KnownBlockVersions obj(stateFile.path(), myClientId);
EXPECT_TRUE(obj.checkAndUpdateVersion(obj.myClientId(), blockId, 100));
EXPECT_TRUE(obj.checkAndUpdateVersion(obj.myClientId(), blockId2, 50));
EXPECT_TRUE(obj.checkAndUpdateVersion(clientId, blockId, 150));
}
KnownBlockVersions obj(stateFile.path(), myClientId);
EXPECT_EQ(100u, obj.getBlockVersion(obj.myClientId(), blockId));
EXPECT_EQ(50u, obj.getBlockVersion(obj.myClientId(), blockId2));
EXPECT_EQ(150u, obj.getBlockVersion(clientId, blockId));
}
TEST_F(KnownBlockVersionsTest, saveAndLoad_lastUpdateClientIdIsStored) {
{
KnownBlockVersions obj(stateFile.path(), myClientId);
EXPECT_TRUE(obj.checkAndUpdateVersion(clientId, blockId, 100));
EXPECT_TRUE(obj.checkAndUpdateVersion(clientId2, blockId, 10));
}
KnownBlockVersions obj(stateFile.path(), myClientId);
EXPECT_FALSE(obj.checkAndUpdateVersion(clientId, blockId, 100));
EXPECT_TRUE(obj.checkAndUpdateVersion(clientId2, blockId, 10));
EXPECT_TRUE(obj.checkAndUpdateVersion(clientId, blockId, 101));
}
TEST_F(KnownBlockVersionsTest, markAsDeleted_doesntAllowReIntroducing_sameClientId) {
setVersion(&testobj, clientId, blockId, 5);
testobj.markBlockAsDeleted(blockId);
EXPECT_FALSE(testobj.checkAndUpdateVersion(clientId, blockId, 5));
}
TEST_F(KnownBlockVersionsTest, markAsDeleted_doesntAllowReIntroducing_oldClientId) {
setVersion(&testobj, clientId, blockId, 5);
setVersion(&testobj, clientId2, blockId, 5);
testobj.markBlockAsDeleted(blockId);
EXPECT_FALSE(testobj.checkAndUpdateVersion(clientId, blockId, 5));
}
TEST_F(KnownBlockVersionsTest, markAsDeleted_checkAndUpdateDoesntDestroyState) {
setVersion(&testobj, clientId, blockId, 5);
setVersion(&testobj, clientId2, blockId, 5);
testobj.markBlockAsDeleted(blockId);
EXPECT_FALSE(testobj.checkAndUpdateVersion(clientId, blockId, 5));
// Check block is still deleted
EXPECT_FALSE(testobj.blockShouldExist(blockId));
}
TEST_F(KnownBlockVersionsTest, blockShouldExist_unknownBlock) {
EXPECT_FALSE(testobj.blockShouldExist(blockId));
}
TEST_F(KnownBlockVersionsTest, blockShouldExist_knownBlock) {
setVersion(&testobj, clientId, blockId, 5);
EXPECT_TRUE(testobj.blockShouldExist(blockId));
}
TEST_F(KnownBlockVersionsTest, blockShouldExist_deletedBlock) {
setVersion(&testobj, clientId, blockId, 5);
testobj.markBlockAsDeleted(blockId);
EXPECT_FALSE(testobj.blockShouldExist(blockId));
}
TEST_F(KnownBlockVersionsTest, path) {
KnownBlockVersions obj(stateFile.path(), myClientId);
EXPECT_EQ(stateFile.path(), obj.path());
}
TEST_F(KnownBlockVersionsTest, existingBlocks_empty) {
EXPECT_EQ(unordered_set<BlockId>({}), testobj.existingBlocks());
}
TEST_F(KnownBlockVersionsTest, existingBlocks_oneentry) {
setVersion(&testobj, clientId, blockId, 5);
EXPECT_EQ(unordered_set<BlockId>({blockId}), testobj.existingBlocks());
}
TEST_F(KnownBlockVersionsTest, existingBlocks_twoentries) {
setVersion(&testobj, clientId, blockId, 5);
setVersion(&testobj, clientId2, blockId2, 5);
EXPECT_EQ(unordered_set<BlockId>({blockId, blockId2}), testobj.existingBlocks());
}
TEST_F(KnownBlockVersionsTest, existingBlocks_twoentries_sameKey) {
setVersion(&testobj, clientId, blockId, 5);
setVersion(&testobj, clientId2, blockId, 5);
EXPECT_EQ(unordered_set<BlockId>({blockId}), testobj.existingBlocks());
}
TEST_F(KnownBlockVersionsTest, existingBlocks_deletedEntry) {
setVersion(&testobj, clientId, blockId, 5);
setVersion(&testobj, clientId2, blockId2, 5);
testobj.markBlockAsDeleted(blockId2);
EXPECT_EQ(unordered_set<BlockId>({blockId}), testobj.existingBlocks());
}
TEST_F(KnownBlockVersionsTest, existingBlocks_deletedEntries) {
setVersion(&testobj, clientId, blockId, 5);
setVersion(&testobj, clientId2, blockId2, 5);
testobj.markBlockAsDeleted(blockId);
testobj.markBlockAsDeleted(blockId2);
EXPECT_EQ(unordered_set<BlockId>({}), testobj.existingBlocks());
}

View File

@ -1,25 +0,0 @@
#include "blockstore/implementations/low2highlevel/LowToHighLevelBlockStore.h"
#include "blockstore/implementations/testfake/FakeBlockStore.h"
#include "blockstore/implementations/inmemory/InMemoryBlockStore2.h"
#include "../../testutils/BlockStoreTest.h"
#include <gtest/gtest.h>
#include <cpp-utils/tempfile/TempFile.h>
using blockstore::BlockStore;
using blockstore::lowtohighlevel::LowToHighLevelBlockStore;
using blockstore::inmemory::InMemoryBlockStore2;
using cpputils::make_unique_ref;
using cpputils::unique_ref;
class LowToHighLevelBlockStoreTestFixture: public BlockStoreTestFixture {
public:
LowToHighLevelBlockStoreTestFixture() {}
unique_ref<BlockStore> createBlockStore() override {
return make_unique_ref<LowToHighLevelBlockStore>(make_unique_ref<InMemoryBlockStore2>());
}
};
INSTANTIATE_TYPED_TEST_SUITE_P(LowToHighLevel, BlockStoreTest, LowToHighLevelBlockStoreTestFixture);

View File

@ -1,20 +0,0 @@
#include "blockstore/implementations/mock/MockBlock.h"
#include "blockstore/implementations/mock/MockBlockStore.h"
#include "../../testutils/BlockStoreTest.h"
#include <gtest/gtest.h>
#include <cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround.h>
using blockstore::BlockStore;
using blockstore::mock::MockBlockStore;
using blockstore::testfake::FakeBlockStore;
using cpputils::unique_ref;
using cpputils::make_unique_ref;
class MockBlockStoreTestFixture: public BlockStoreTestFixture {
public:
unique_ref<BlockStore> createBlockStore() override {
return make_unique_ref<MockBlockStore>(make_unique_ref<FakeBlockStore>());
}
};
INSTANTIATE_TYPED_TEST_SUITE_P(Mock, BlockStoreTest, MockBlockStoreTestFixture);

View File

@ -1,45 +0,0 @@
#include "blockstore/implementations/low2highlevel/LowToHighLevelBlockStore.h"
#include "blockstore/implementations/ondisk/OnDiskBlockStore2.h"
#include "../../testutils/BlockStoreTest.h"
#include "../../testutils/BlockStore2Test.h"
#include <gtest/gtest.h>
#include <cpp-utils/tempfile/TempDir.h>
using blockstore::BlockStore;
using blockstore::ondisk::OnDiskBlockStore2;
using blockstore::BlockStore2;
using blockstore::lowtohighlevel::LowToHighLevelBlockStore;
using cpputils::TempDir;
using cpputils::unique_ref;
using cpputils::make_unique_ref;
class OnDiskBlockStoreTestFixture: public BlockStoreTestFixture {
public:
OnDiskBlockStoreTestFixture(): tempdir() {}
unique_ref<BlockStore> createBlockStore() override {
return make_unique_ref<LowToHighLevelBlockStore>(
make_unique_ref<OnDiskBlockStore2>(tempdir.path())
);
}
private:
TempDir tempdir;
};
INSTANTIATE_TYPED_TEST_SUITE_P(OnDisk, BlockStoreTest, OnDiskBlockStoreTestFixture);
class OnDiskBlockStore2TestFixture: public BlockStore2TestFixture {
public:
OnDiskBlockStore2TestFixture(): tempdir() {}
unique_ref<BlockStore2> createBlockStore() override {
return make_unique_ref<OnDiskBlockStore2>(tempdir.path());
}
private:
TempDir tempdir;
};
INSTANTIATE_TYPED_TEST_SUITE_P(OnDisk, BlockStore2Test, OnDiskBlockStore2TestFixture);

View File

@ -1,67 +0,0 @@
#include <gtest/gtest.h>
#include "blockstore/implementations/ondisk/OnDiskBlockStore2.h"
#include <cpp-utils/tempfile/TempDir.h>
using ::testing::Test;
using cpputils::TempDir;
using cpputils::Data;
using std::ifstream;
using blockstore::BlockId;
using namespace blockstore::ondisk;
class OnDiskBlockStoreTest: public Test {
public:
OnDiskBlockStoreTest():
baseDir(),
blockStore(baseDir.path()) {
}
TempDir baseDir;
OnDiskBlockStore2 blockStore;
blockstore::BlockId CreateBlockReturnKey(const Data &initData) {
return blockStore.create(initData.copy());
}
uint64_t getPhysicalBlockSize(const BlockId &blockId) {
ifstream stream((baseDir.path() / blockId.ToString().substr(0,3) / blockId.ToString().substr(3)).c_str());
stream.seekg(0, stream.end);
return stream.tellg();
}
};
TEST_F(OnDiskBlockStoreTest, PhysicalBlockSize_zerophysical) {
EXPECT_EQ(0u, blockStore.blockSizeFromPhysicalBlockSize(0));
}
TEST_F(OnDiskBlockStoreTest, PhysicalBlockSize_zerovirtual) {
auto blockId = CreateBlockReturnKey(Data(0));
auto baseSize = getPhysicalBlockSize(blockId);
EXPECT_EQ(0u, blockStore.blockSizeFromPhysicalBlockSize(baseSize));
}
TEST_F(OnDiskBlockStoreTest, PhysicalBlockSize_negativeboundaries) {
// This tests that a potential if/else in blockSizeFromPhysicalBlockSize that catches negative values has the
// correct boundary set. We test the highest value that is negative and the smallest value that is positive.
auto physicalSizeForVirtualSizeZero = getPhysicalBlockSize(CreateBlockReturnKey(Data(0)));
if (physicalSizeForVirtualSizeZero > 0) {
EXPECT_EQ(0u, blockStore.blockSizeFromPhysicalBlockSize(physicalSizeForVirtualSizeZero - 1));
}
EXPECT_EQ(0u, blockStore.blockSizeFromPhysicalBlockSize(physicalSizeForVirtualSizeZero));
EXPECT_EQ(1u, blockStore.blockSizeFromPhysicalBlockSize(physicalSizeForVirtualSizeZero + 1));
}
TEST_F(OnDiskBlockStoreTest, PhysicalBlockSize_positive) {
auto blockId = CreateBlockReturnKey(Data(10*1024));
auto baseSize = getPhysicalBlockSize(blockId);
EXPECT_EQ(10*1024u, blockStore.blockSizeFromPhysicalBlockSize(baseSize));
}
TEST_F(OnDiskBlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocksWithSameKeyPrefix) {
const BlockId key1 = BlockId::FromString("4CE72ECDD20877A12ADBF4E3927C0A13");
const BlockId key2 = BlockId::FromString("4CE72ECDD20877A12ADBF4E3927C0A14");
EXPECT_TRUE(blockStore.tryCreate(key1, cpputils::Data(0)));
EXPECT_TRUE(blockStore.tryCreate(key2, cpputils::Data(0)));
EXPECT_EQ(2u, blockStore.numBlocks());
}

View File

@ -1,88 +0,0 @@
#include <gtest/gtest.h>
#include <cpp-utils/tempfile/TempFile.h>
#include <cpp-utils/tempfile/TempDir.h>
// TODO This should be ported to BlockStore2
/*
using ::testing::Test;
using ::testing::WithParamInterface;
using ::testing::Values;
using cpputils::Data;
using cpputils::TempFile;
using cpputils::TempDir;
using cpputils::unique_ref;
using namespace blockstore;
using namespace blockstore::ondisk;
namespace bf = boost::filesystem;
class OnDiskBlockCreateTest: public Test {
public:
OnDiskBlockCreateTest()
// Don't create the temp file yet (therefore pass false to the TempFile constructor)
: dir(),
key(BlockId::FromString("1491BB4932A389EE14BC7090AC772972")),
file(dir.path() / blockId.ToString().substr(0,3) / blockId.ToString().substr(3), false) {
}
TempDir dir;
BlockId key;
TempFile file;
};
TEST_F(OnDiskBlockCreateTest, CreatingBlockCreatesFile) {
EXPECT_FALSE(bf::exists(file.path()));
auto block = OnDiskBlock::CreateOnDisk(dir.path(), blockId, Data(0));
EXPECT_TRUE(bf::exists(file.path()));
EXPECT_TRUE(bf::is_regular_file(file.path()));
}
TEST_F(OnDiskBlockCreateTest, CreatingExistingBlockReturnsNull) {
auto block1 = OnDiskBlock::CreateOnDisk(dir.path(), blockId, Data(0));
auto block2 = OnDiskBlock::CreateOnDisk(dir.path(), blockId, Data(0));
EXPECT_TRUE((bool)block1);
EXPECT_FALSE((bool)block2);
}
class OnDiskBlockCreateSizeTest: public OnDiskBlockCreateTest, public WithParamInterface<size_t> {
public:
unique_ref<OnDiskBlock> block;
Data ZEROES;
OnDiskBlockCreateSizeTest():
block(OnDiskBlock::CreateOnDisk(dir.path(), blockId, Data(GetParam()).FillWithZeroes()).value()),
ZEROES(block->size())
{
ZEROES.FillWithZeroes();
}
};
INSTANTIATE_TEST_SUITE_P(OnDiskBlockCreateSizeTest, OnDiskBlockCreateSizeTest, Values(0, 1, 5, 1024, 10*1024*1024));
TEST_P(OnDiskBlockCreateSizeTest, OnDiskSizeIsCorrect) {
Data fileContent = Data::LoadFromFile(file.path()).value();
EXPECT_EQ(GetParam() + OnDiskBlock::formatVersionHeaderSize(), fileContent.size());
}
TEST_P(OnDiskBlockCreateSizeTest, OnDiskBlockIsZeroedOut) {
Data fileContent = Data::LoadFromFile(file.path()).value();
Data fileContentWithoutHeader(fileContent.size() - OnDiskBlock::formatVersionHeaderSize());
std::memcpy(fileContentWithoutHeader.data(), fileContent.dataOffset(OnDiskBlock::formatVersionHeaderSize()), fileContentWithoutHeader.size());
EXPECT_EQ(ZEROES, fileContentWithoutHeader);
}
// This test is also tested by OnDiskBlockStoreTest, but there the block is created using the BlockStore interface.
// Here, we create it using OnDiskBlock::CreateOnDisk()
TEST_P(OnDiskBlockCreateSizeTest, InMemorySizeIsCorrect) {
EXPECT_EQ(GetParam(), block->size());
}
// This test is also tested by OnDiskBlockStoreTest, but there the block is created using the BlockStore interface.
// Here, we create it using OnDiskBlock::CreateOnDisk()
TEST_P(OnDiskBlockCreateSizeTest, InMemoryBlockIsZeroedOut) {
EXPECT_EQ(0, std::memcmp(ZEROES.data(), block->data(), block->size()));
}
*/

View File

@ -1,119 +0,0 @@
#include <cpp-utils/data/DataFixture.h>
#include <gtest/gtest.h>
#include <cpp-utils/tempfile/TempFile.h>
#include <cpp-utils/tempfile/TempDir.h>
// TODO This should be ported to BlockStore2
/*
using ::testing::Test;
using ::testing::WithParamInterface;
using ::testing::Values;
using cpputils::Data;
using cpputils::DataFixture;
using cpputils::TempFile;
using cpputils::TempDir;
using cpputils::unique_ref;
using namespace blockstore;
using namespace blockstore::ondisk;
namespace bf = boost::filesystem;
class OnDiskBlockFlushTest: public Test, public WithParamInterface<size_t> {
public:
OnDiskBlockFlushTest()
// Don't create the temp file yet (therefore pass false to the TempFile constructor)
: dir(),
key(BlockId::FromString("1491BB4932A389EE14BC7090AC772972")),
file(dir.path() / blockId.ToString().substr(0,3) / blockId.ToString().substr(3), false),
randomData(DataFixture::generate(GetParam())) {
}
TempDir dir;
BlockId key;
TempFile file;
Data randomData;
unique_ref<OnDiskBlock> CreateBlockAndLoadItFromDisk() {
{
OnDiskBlock::CreateOnDisk(dir.path(), blockId, randomData.copy()).value();
}
return OnDiskBlock::LoadFromDisk(dir.path(), blockId).value();
}
unique_ref<OnDiskBlock> CreateBlock() {
return OnDiskBlock::CreateOnDisk(dir.path(), blockId, randomData.copy()).value();
}
void WriteDataToBlock(const unique_ref<OnDiskBlock> &block) {
block->write(randomData.data(), 0, randomData.size());
}
void EXPECT_BLOCK_DATA_CORRECT(const unique_ref<OnDiskBlock> &block) {
EXPECT_EQ(randomData.size(), block->size());
EXPECT_EQ(0, std::memcmp(randomData.data(), block->data(), randomData.size()));
}
void EXPECT_STORED_FILE_DATA_CORRECT() {
Data fileContent = Data::LoadFromFile(file.path()).value();
Data fileContentWithoutHeader(fileContent.size() - OnDiskBlock::formatVersionHeaderSize());
std::memcpy(fileContentWithoutHeader.data(), fileContent.dataOffset(OnDiskBlock::formatVersionHeaderSize()), fileContentWithoutHeader.size());
EXPECT_EQ(randomData, fileContentWithoutHeader);
}
};
INSTANTIATE_TEST_SUITE_P(OnDiskBlockFlushTest, OnDiskBlockFlushTest, Values((size_t)0, (size_t)1, (size_t)1024, (size_t)4096, (size_t)10*1024*1024));
// This test is also tested by OnDiskBlockStoreTest, but there the block is created using the BlockStore interface.
// Here, we create it using OnDiskBlock::CreateOnDisk()
TEST_P(OnDiskBlockFlushTest, AfterCreate_FlushingDoesntChangeBlock) {
auto block = CreateBlock();
WriteDataToBlock(block);
EXPECT_BLOCK_DATA_CORRECT(block);
}
// This test is also tested by OnDiskBlockStoreTest, but there the block is created using the BlockStore interface.
// Here, we create it using OnDiskBlock::CreateOnDisk() / OnDiskBlock::LoadFromDisk()
TEST_P(OnDiskBlockFlushTest, AfterLoad_FlushingDoesntChangeBlock) {
auto block = CreateBlockAndLoadItFromDisk();
WriteDataToBlock(block);
EXPECT_BLOCK_DATA_CORRECT(block);
}
TEST_P(OnDiskBlockFlushTest, AfterCreate_FlushingWritesCorrectData) {
auto block = CreateBlock();
WriteDataToBlock(block);
EXPECT_STORED_FILE_DATA_CORRECT();
}
TEST_P(OnDiskBlockFlushTest, AfterLoad_FlushingWritesCorrectData) {
auto block = CreateBlockAndLoadItFromDisk();
WriteDataToBlock(block);
EXPECT_STORED_FILE_DATA_CORRECT();
}
// This test is also tested by OnDiskBlockStoreTest, but there it can only checks block content by loading it again.
// Here, we check the content on disk.
TEST_P(OnDiskBlockFlushTest, AfterCreate_FlushesWhenDestructed) {
{
auto block = CreateBlock();
WriteDataToBlock(block);
}
EXPECT_STORED_FILE_DATA_CORRECT();
}
// This test is also tested by OnDiskBlockStoreTest, but there it can only checks block content by loading it again.
// Here, we check the content on disk.
TEST_P(OnDiskBlockFlushTest, AfterLoad_FlushesWhenDestructed) {
{
auto block = CreateBlockAndLoadItFromDisk();
WriteDataToBlock(block);
}
EXPECT_STORED_FILE_DATA_CORRECT();
}
*/

View File

@ -1,82 +0,0 @@
#include <cpp-utils/data/DataFixture.h>
#include "blockstore/utils/FileDoesntExistException.h"
#include <gtest/gtest.h>
#include <cpp-utils/data/Data.h>
#include <cpp-utils/tempfile/TempFile.h>
#include <cpp-utils/tempfile/TempDir.h>
#include <cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround.h>
#include <fstream>
// TODO This should be ported to BlockStore2
/*
using ::testing::Test;
using ::testing::WithParamInterface;
using ::testing::Values;
using std::ofstream;
using std::ios;
using cpputils::Data;
using cpputils::DataFixture;
using cpputils::TempFile;
using cpputils::TempDir;
using cpputils::unique_ref;
using namespace blockstore;
using namespace blockstore::ondisk;
namespace bf = boost::filesystem;
class OnDiskBlockLoadTest: public Test, public WithParamInterface<size_t> {
public:
OnDiskBlockLoadTest():
dir(),
key(BlockId::FromString("1491BB4932A389EE14BC7090AC772972")),
file(dir.path() / blockId.ToString(), false) {
}
TempDir dir;
BlockId key;
TempFile file;
void CreateBlockWithSize(size_t size) {
Data data(size);
OnDiskBlock::CreateOnDisk(dir.path(), blockId, std::move(data));
}
void StoreData(Data data) {
OnDiskBlock::CreateOnDisk(dir.path(), blockId, std::move(data));
}
unique_ref<OnDiskBlock> LoadBlock() {
return OnDiskBlock::LoadFromDisk(dir.path(), blockId).value();
}
void EXPECT_BLOCK_DATA_EQ(const Data &expected, const OnDiskBlock &actual) {
EXPECT_EQ(expected.size(), actual.size());
EXPECT_EQ(0, std::memcmp(expected.data(), actual.data(), expected.size()));
}
};
INSTANTIATE_TEST_SUITE_P(OnDiskBlockLoadTest, OnDiskBlockLoadTest, Values(0, 1, 5, 1024, 10*1024*1024));
TEST_P(OnDiskBlockLoadTest, LoadsCorrectSize) {
CreateBlockWithSize(GetParam());
auto block = LoadBlock();
EXPECT_EQ(GetParam(), block->size());
}
TEST_P(OnDiskBlockLoadTest, LoadedDataIsCorrect) {
Data randomData = DataFixture::generate(GetParam());
StoreData(randomData.copy());
auto block = LoadBlock();
EXPECT_BLOCK_DATA_EQ(randomData, *block);
}
TEST_F(OnDiskBlockLoadTest, LoadNotExistingBlock) {
BlockId key2 = BlockId::FromString("272EE5517627CFA147A971A8E6E747E0");
EXPECT_EQ(boost::none, OnDiskBlock::LoadFromDisk(dir.path(), key2));
}
*/

View File

@ -1,23 +0,0 @@
#include "blockstore/implementations/parallelaccess/ParallelAccessBlockStore.h"
#include "blockstore/implementations/testfake/FakeBlockStore.h"
#include "../../testutils/BlockStoreTest.h"
#include <gtest/gtest.h>
using blockstore::BlockStore;
using blockstore::parallelaccess::ParallelAccessBlockStore;
using blockstore::testfake::FakeBlockStore;
using cpputils::make_unique_ref;
using cpputils::unique_ref;
class ParallelAccessBlockStoreTestFixture: public BlockStoreTestFixture {
public:
unique_ref<BlockStore> createBlockStore() override {
return make_unique_ref<ParallelAccessBlockStore>(make_unique_ref<FakeBlockStore>());
}
};
INSTANTIATE_TYPED_TEST_SUITE_P(ParallelAccess, BlockStoreTest, ParallelAccessBlockStoreTestFixture);
//TODO Add specific tests ensuring that loading the same block twice doesn't load it twice from the underlying blockstore

View File

@ -1,52 +0,0 @@
#include <gtest/gtest.h>
#include "blockstore/implementations/parallelaccess/ParallelAccessBlockStore.h"
#include "blockstore/implementations/testfake/FakeBlockStore.h"
using ::testing::Test;
using cpputils::Data;
using blockstore::testfake::FakeBlockStore;
using namespace blockstore::parallelaccess;
class ParallelAccessBlockStoreTest: public Test {
public:
ParallelAccessBlockStoreTest():
baseBlockStore(new FakeBlockStore),
blockStore(std::move(cpputils::nullcheck(std::unique_ptr<FakeBlockStore>(baseBlockStore)).value())) {
}
FakeBlockStore *baseBlockStore;
ParallelAccessBlockStore blockStore;
blockstore::BlockId CreateBlockReturnKey(const Data &initData) {
return blockStore.create(initData)->blockId();
}
};
TEST_F(ParallelAccessBlockStoreTest, PhysicalBlockSize_zerophysical) {
EXPECT_EQ(0u, blockStore.blockSizeFromPhysicalBlockSize(0));
}
TEST_F(ParallelAccessBlockStoreTest, PhysicalBlockSize_zerovirtual) {
auto blockId = CreateBlockReturnKey(Data(0));
auto base = baseBlockStore->load(blockId).value();
EXPECT_EQ(0u, blockStore.blockSizeFromPhysicalBlockSize(base->size()));
}
TEST_F(ParallelAccessBlockStoreTest, PhysicalBlockSize_negativeboundaries) {
// This tests that a potential if/else in blockSizeFromPhysicalBlockSize that catches negative values has the
// correct boundary set. We test the highest value that is negative and the smallest value that is positive.
auto physicalSizeForVirtualSizeZero = baseBlockStore->load(CreateBlockReturnKey(Data(0))).value()->size();
if (physicalSizeForVirtualSizeZero > 0) {
EXPECT_EQ(0u, blockStore.blockSizeFromPhysicalBlockSize(physicalSizeForVirtualSizeZero - 1));
}
EXPECT_EQ(0u, blockStore.blockSizeFromPhysicalBlockSize(physicalSizeForVirtualSizeZero));
EXPECT_EQ(1u, blockStore.blockSizeFromPhysicalBlockSize(physicalSizeForVirtualSizeZero + 1));
}
TEST_F(ParallelAccessBlockStoreTest, PhysicalBlockSize_positive) {
auto blockId = CreateBlockReturnKey(Data(10*1024));
auto base = baseBlockStore->load(blockId).value();
EXPECT_EQ(10*1024u, blockStore.blockSizeFromPhysicalBlockSize(base->size()));
}

View File

@ -1,19 +0,0 @@
#include "blockstore/implementations/testfake/FakeBlock.h"
#include "blockstore/implementations/testfake/FakeBlockStore.h"
#include "../../testutils/BlockStoreTest.h"
#include <gtest/gtest.h>
#include <cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround.h>
using blockstore::BlockStore;
using blockstore::testfake::FakeBlockStore;
using cpputils::unique_ref;
using cpputils::make_unique_ref;
class FakeBlockStoreTestFixture: public BlockStoreTestFixture {
public:
unique_ref<BlockStore> createBlockStore() override {
return make_unique_ref<FakeBlockStore>();
}
};
INSTANTIATE_TYPED_TEST_SUITE_P(TestFake, BlockStoreTest, FakeBlockStoreTestFixture);

View File

@ -1,145 +0,0 @@
#include "blockstore/interface/BlockStore2.h"
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <cpp-utils/data/DataFixture.h>
using ::testing::Test;
using ::testing::Return;
using ::testing::Invoke;
using ::testing::Eq;
using ::testing::ByRef;
using std::string;
using cpputils::Data;
using cpputils::DataFixture;
using boost::optional;
namespace boost {
inline void PrintTo(const optional<cpputils::Data> &, ::std::ostream *os) {
*os << "optional<Data>";
}
}
using namespace blockstore;
class BlockStore2Mock: public BlockStore2 {
public:
MOCK_METHOD(BlockId, createBlockId, (), (const, override));
MOCK_METHOD(bool, tryCreate, (const BlockId &blockId, const cpputils::Data &data), (override));
MOCK_METHOD(void, store, (const BlockId &, const Data &data), (override));
MOCK_METHOD(optional<Data>, load, (const BlockId &), (const, override));
MOCK_METHOD(bool, remove, (const BlockId &), (override));
MOCK_METHOD(uint64_t, numBlocks, (), (const, override));
MOCK_METHOD(uint64_t, estimateNumFreeBytes, (), (const, override));
MOCK_METHOD(uint64_t, blockSizeFromPhysicalBlockSize, (uint64_t), (const, override));
MOCK_METHOD(void, forEachBlock, (std::function<void (const blockstore::BlockId &)>), (const, override));
};
class BlockStore2Test: public Test {
public:
BlockStore2Test() :blockStoreMock(), blockStore(blockStoreMock),
blockId1(BlockId::FromString("1491BB4932A389EE14BC7090AC772972")),
blockId2(BlockId::FromString("AC772971491BB4932A389EE14BC7090A")),
blockId3(BlockId::FromString("1BB4932A38AC77C7090A2971499EE14B")) {}
BlockStore2Mock blockStoreMock;
BlockStore2 &blockStore;
const BlockId blockId1;
const BlockId blockId2;
const BlockId blockId3;
Data createDataWithSize(size_t size) {
Data fixture(DataFixture::generate(size));
Data data(size);
std::memcpy(data.data(), fixture.data(), size);
return data;
}
};
TEST_F(BlockStore2Test, DataIsPassedThrough0) {
Data data = createDataWithSize(0);
EXPECT_CALL(blockStoreMock, createBlockId()).WillOnce(Return(blockId1));
EXPECT_CALL(blockStoreMock, tryCreate(testing::_, Eq(ByRef(data)))).WillOnce(Return(true));
EXPECT_EQ(blockId1, blockStore.create(data));
}
TEST_F(BlockStore2Test, DataIsPassedThrough1) {
Data data = createDataWithSize(1);
EXPECT_CALL(blockStoreMock, createBlockId()).WillOnce(Return(blockId1));
EXPECT_CALL(blockStoreMock, tryCreate(testing::_, Eq(ByRef(data)))).WillOnce(Return(true));
EXPECT_EQ(blockId1, blockStore.create(data));
}
TEST_F(BlockStore2Test, DataIsPassedThrough1024) {
Data data = createDataWithSize(1024);
EXPECT_CALL(blockStoreMock, createBlockId()).WillOnce(Return(blockId1));
EXPECT_CALL(blockStoreMock, tryCreate(testing::_, Eq(ByRef(data)))).WillOnce(Return(true));
EXPECT_EQ(blockId1, blockStore.create(data));
}
TEST_F(BlockStore2Test, BlockIdIsCorrect) {
Data data = createDataWithSize(1024);
EXPECT_CALL(blockStoreMock, createBlockId()).WillOnce(Return(blockId1));
EXPECT_CALL(blockStoreMock, tryCreate(blockId1, testing::_)).WillOnce(Return(true));
EXPECT_EQ(blockId1, blockStore.create(data));
}
TEST_F(BlockStore2Test, TwoBlocksGetDifferentIds) {
EXPECT_CALL(blockStoreMock, createBlockId())
.WillOnce(Return(blockId1))
.WillOnce(Return(blockId2));
EXPECT_CALL(blockStoreMock, tryCreate(testing::_, testing::_))
.WillOnce(Invoke([this](const BlockId &blockId, const Data &) {
EXPECT_EQ(blockId1, blockId);
return true;
}))
.WillOnce(Invoke([this](const BlockId &blockId, const Data &) {
EXPECT_EQ(blockId2, blockId);
return true;
}));
Data data = createDataWithSize(1024);
EXPECT_EQ(blockId1, blockStore.create(data));
EXPECT_EQ(blockId2, blockStore.create(data));
}
TEST_F(BlockStore2Test, WillTryADifferentIdIfKeyAlreadyExists) {
Data data = createDataWithSize(1024);
EXPECT_CALL(blockStoreMock, createBlockId())
.WillOnce(Return(blockId1))
.WillOnce(Return(blockId2));
EXPECT_CALL(blockStoreMock, tryCreate(testing::_, Eq(ByRef(data))))
.WillOnce(Invoke([this](const BlockId &blockId, const Data &) {
EXPECT_EQ(blockId1, blockId);
return false;
}))
.WillOnce(Invoke([this](const BlockId &blockId, const Data &) {
EXPECT_EQ(blockId2, blockId);
return true;
}));
EXPECT_EQ(blockId2, blockStore.create(data));
}
TEST_F(BlockStore2Test, WillTryADifferentIdIfIdAlreadyExistsTwoTimes) {
Data data = createDataWithSize(1024);
EXPECT_CALL(blockStoreMock, createBlockId())
.WillOnce(Return(blockId1))
.WillOnce(Return(blockId2))
.WillOnce(Return(blockId3));
EXPECT_CALL(blockStoreMock, tryCreate(testing::_, Eq(ByRef(data))))
.WillOnce(Invoke([this](const BlockId &blockId, const Data &) {
EXPECT_EQ(blockId1, blockId);
return false;
}))
.WillOnce(Invoke([this](const BlockId &blockId, const Data &) {
EXPECT_EQ(blockId2, blockId);
return false;
}))
.WillOnce(Invoke([this](const BlockId &blockId, const Data &) {
EXPECT_EQ(blockId3, blockId);
return true;
}));
EXPECT_EQ(blockId3, blockStore.create(data));
}

View File

@ -1,159 +0,0 @@
#include "blockstore/interface/BlockStore.h"
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <cpp-utils/data/DataFixture.h>
#include <cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround.h>
using ::testing::Test;
using ::testing::Return;
using ::testing::Invoke;
using ::testing::Eq;
using ::testing::ByRef;
using ::testing::Action;
using std::string;
using cpputils::Data;
using cpputils::DataFixture;
using cpputils::unique_ref;
using cpputils::make_unique_ref;
using boost::optional;
using namespace blockstore;
class BlockStoreMock: public BlockStore {
public:
MOCK_METHOD(BlockId, createBlockId, (), (override));
MOCK_METHOD(optional<unique_ref<Block>>, tryCreate, (const BlockId &, Data data), (override));
MOCK_METHOD(unique_ref<Block>, overwrite, (const BlockId &, Data data), (override));
MOCK_METHOD(optional<unique_ref<Block>>, load, (const BlockId &), (override));
MOCK_METHOD(void, remove, (unique_ref<Block>), (override));
MOCK_METHOD(void, remove, (const BlockId &), (override));
MOCK_METHOD(uint64_t, numBlocks, (), (const, override));
MOCK_METHOD(uint64_t, estimateNumFreeBytes, (), (const, override));
MOCK_METHOD(uint64_t, blockSizeFromPhysicalBlockSize, (uint64_t), (const, override));
MOCK_METHOD(void, forEachBlock, (std::function<void (const blockstore::BlockId &)>), (const, override));
};
class BlockMock: public Block {
public:
BlockMock(): Block(BlockId::Random()) {}
MOCK_METHOD(const void*, data, (), (const, override));
MOCK_METHOD(void, write, (const void*, uint64_t, uint64_t), (override));
MOCK_METHOD(void, flush, (), (override));
MOCK_METHOD(size_t, size, (), (const, override));
MOCK_METHOD(void, resize, (size_t), (override));
};
class BlockStoreTest: public Test {
public:
BlockStoreTest() :blockStoreMock(), blockStore(blockStoreMock),
blockId1(BlockId::FromString("1491BB4932A389EE14BC7090AC772972")),
blockId2(BlockId::FromString("AC772971491BB4932A389EE14BC7090A")),
blockId3(BlockId::FromString("1BB4932A38AC77C7090A2971499EE14B")) {}
BlockStoreMock blockStoreMock;
BlockStore &blockStore;
const BlockId blockId1;
const BlockId blockId2;
const BlockId blockId3;
Data createDataWithSize(size_t size) {
Data fixture(DataFixture::generate(size));
Data data(size);
std::memcpy(data.data(), fixture.data(), size);
return data;
}
};
const Action<optional<unique_ref<Block>>(const BlockId &, cpputils::Data)> ReturnNewBlockMock = Invoke(
[] (const BlockId&, cpputils::Data) {
return optional<unique_ref<Block>>(unique_ref<Block>(make_unique_ref<BlockMock>()));
});
TEST_F(BlockStoreTest, DataIsPassedThrough0) {
Data data = createDataWithSize(0);
EXPECT_CALL(blockStoreMock, createBlockId()).WillOnce(Return(blockId1));
EXPECT_CALL(blockStoreMock, tryCreate(testing::_, Eq(ByRef(data)))).WillOnce(ReturnNewBlockMock);
blockStore.create(data);
}
TEST_F(BlockStoreTest, DataIsPassedThrough1) {
Data data = createDataWithSize(1);
EXPECT_CALL(blockStoreMock, createBlockId()).WillOnce(Return(blockId1));
EXPECT_CALL(blockStoreMock, tryCreate(testing::_, Eq(ByRef(data)))).WillOnce(ReturnNewBlockMock);
blockStore.create(data);
}
TEST_F(BlockStoreTest, DataIsPassedThrough1024) {
Data data = createDataWithSize(1024);
EXPECT_CALL(blockStoreMock, createBlockId()).WillOnce(Return(blockId1));
EXPECT_CALL(blockStoreMock, tryCreate(testing::_, Eq(ByRef(data)))).WillOnce(ReturnNewBlockMock);
blockStore.create(data);
}
TEST_F(BlockStoreTest, BlockIdIsCorrect) {
Data data = createDataWithSize(1024);
EXPECT_CALL(blockStoreMock, createBlockId()).WillOnce(Return(blockId1));
EXPECT_CALL(blockStoreMock, tryCreate(blockId1, testing::_)).WillOnce(ReturnNewBlockMock);
blockStore.create(data);
}
TEST_F(BlockStoreTest, TwoBlocksGetDifferentIds) {
EXPECT_CALL(blockStoreMock, createBlockId())
.WillOnce(Return(blockId1))
.WillOnce(Return(blockId2));
EXPECT_CALL(blockStoreMock, tryCreate(testing::_, testing::_))
.WillOnce(Invoke([this](const BlockId &blockId, Data) {
EXPECT_EQ(blockId1, blockId);
return optional<unique_ref<Block>>(unique_ref<Block>(make_unique_ref<BlockMock>()));
}))
.WillOnce(Invoke([this](const BlockId &blockId, Data) {
EXPECT_EQ(blockId2, blockId);
return optional<unique_ref<Block>>(unique_ref<Block>(make_unique_ref<BlockMock>()));
}));
Data data = createDataWithSize(1024);
blockStore.create(data);
blockStore.create(data);
}
TEST_F(BlockStoreTest, WillTryADifferentIdIfKeyAlreadyExists) {
Data data = createDataWithSize(1024);
EXPECT_CALL(blockStoreMock, createBlockId())
.WillOnce(Return(blockId1))
.WillOnce(Return(blockId2));
EXPECT_CALL(blockStoreMock, tryCreate(testing::_, Eq(ByRef(data))))
.WillOnce(Invoke([this](const BlockId &blockId, Data ) {
EXPECT_EQ(blockId1, blockId);
return boost::none;
}))
.WillOnce(Invoke([this](const BlockId &blockId, Data ) {
EXPECT_EQ(blockId2, blockId);
return optional<unique_ref<Block>>(unique_ref<Block>(make_unique_ref<BlockMock>()));
}));
blockStore.create(data);
}
TEST_F(BlockStoreTest, WillTryADifferentIdIfIdAlreadyExistsTwoTimes) {
Data data = createDataWithSize(1024);
EXPECT_CALL(blockStoreMock, createBlockId())
.WillOnce(Return(blockId1))
.WillOnce(Return(blockId2))
.WillOnce(Return(blockId3));
EXPECT_CALL(blockStoreMock, tryCreate(testing::_, Eq(ByRef(data))))
.WillOnce(Invoke([this](const BlockId &blockId, Data) {
EXPECT_EQ(blockId1, blockId);
return boost::none;
}))
.WillOnce(Invoke([this](const BlockId &blockId, Data) {
EXPECT_EQ(blockId2, blockId);
return boost::none;
}))
.WillOnce(Invoke([this](const BlockId &blockId, Data) {
EXPECT_EQ(blockId3, blockId);
return optional<unique_ref<Block>>(unique_ref<Block>(make_unique_ref<BlockMock>()));
}));
blockStore.create(data);
}

View File

@ -1,4 +0,0 @@
/*
* Tests that the header can be included without needing additional header includes as dependencies.
*/
#include "blockstore/interface/Block.h"

View File

@ -1,479 +0,0 @@
#pragma once
#ifndef MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_TESTUTILS_BLOCKSTORE2TEST_H_
#define MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_TESTUTILS_BLOCKSTORE2TEST_H_
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <cpp-utils/data/DataFixture.h>
#include <cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround.h>
#include "blockstore/interface/BlockStore2.h"
namespace boost {
inline void PrintTo(const optional<cpputils::Data> &, ::std::ostream *os) {
*os << "optional<Data>";
}
}
class BlockStore2TestFixture {
public:
virtual ~BlockStore2TestFixture() {}
virtual cpputils::unique_ref<blockstore::BlockStore2> createBlockStore() = 0;
};
template<class ConcreteBlockStoreTestFixture>
class BlockStore2Test: public ::testing::Test {
public:
BlockStore2Test() :fixture(), blockStore(this->fixture.createBlockStore()) {}
BOOST_STATIC_ASSERT_MSG(
(std::is_base_of<BlockStore2TestFixture, ConcreteBlockStoreTestFixture>::value),
"Given test fixture for instantiating the (type parameterized) BlockStoreTest must inherit from BlockStoreTestFixture"
);
ConcreteBlockStoreTestFixture fixture;
cpputils::unique_ref<blockstore::BlockStore2> blockStore;
template<class Entry>
void EXPECT_UNORDERED_EQ(const std::vector<Entry> &expected, std::vector<Entry> actual) {
EXPECT_EQ(expected.size(), actual.size());
for (const Entry &expectedEntry : expected) {
removeOne(&actual, expectedEntry);
}
}
template<class Entry>
void removeOne(std::vector<Entry> *entries, const Entry &toRemove) {
auto found = std::find(entries->begin(), entries->end(), toRemove);
if (found != entries->end()) {
entries->erase(found);
return;
}
EXPECT_TRUE(false);
}
};
TYPED_TEST_SUITE_P(BlockStore2Test);
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenCallingTryCreateOnExistingBlock_thenFails) {
blockstore::BlockId blockId = this->blockStore->create(cpputils::Data(1024));
EXPECT_FALSE(this->blockStore->tryCreate(blockId, cpputils::Data(1024)));
}
TYPED_TEST_P(BlockStore2Test, givenEmptyBlockStore_whenCallingTryCreateOnNonExistingBlock_thenSucceeds) {
blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
EXPECT_TRUE(this->blockStore->tryCreate(blockId, cpputils::Data(1024)));
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenCallingTryCreateOnNonExistingBlock_thenSucceeds) {
this->blockStore->create(cpputils::Data(512));
blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
EXPECT_TRUE(this->blockStore->tryCreate(blockId, cpputils::Data(1024)));
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenLoadExistingBlock_thenSucceeds) {
blockstore::BlockId blockId = this->blockStore->create(cpputils::Data(1024));
EXPECT_NE(boost::none, this->blockStore->load(blockId));
}
TYPED_TEST_P(BlockStore2Test, givenEmptyBlockStore_whenLoadNonexistingBlock_thenFails) {
const blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
EXPECT_EQ(boost::none, this->blockStore->load(blockId));
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenLoadNonexistingBlock_thenFails) {
this->blockStore->create(cpputils::Data(512));
const blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
EXPECT_EQ(boost::none, this->blockStore->load(blockId));
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenStoringExistingBlock_thenSucceeds) {
blockstore::BlockId blockId = this->blockStore->create(cpputils::Data(1024));
this->blockStore->store(blockId, cpputils::Data(1024));
}
TYPED_TEST_P(BlockStore2Test, givenEmptyBlockStore_whenStoringNonexistingBlock_thenSucceeds) {
const blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
this->blockStore->store(blockId, cpputils::Data(1024));
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenStoringNonexistingBlock_thenSucceeds) {
this->blockStore->create(cpputils::Data(512));
const blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
this->blockStore->store(blockId, cpputils::Data(1024));
}
TYPED_TEST_P(BlockStore2Test, givenEmptyBlockStore_whenCreatingTwoBlocks_thenTheyGetDifferentKeys) {
blockstore::BlockId blockId1 = this->blockStore->create(cpputils::Data(1024));
blockstore::BlockId blockId2 = this->blockStore->create(cpputils::Data(1024));
EXPECT_NE(blockId1, blockId2);
}
TYPED_TEST_P(BlockStore2Test, givenOtherwiseEmptyBlockStore_whenRemovingBlock_thenBlockIsNotLoadableAnymore) {
blockstore::BlockId blockId = this->blockStore->create(cpputils::Data(1024));
EXPECT_NE(boost::none, this->blockStore->load(blockId));
EXPECT_TRUE(this->blockStore->remove(blockId));
EXPECT_EQ(boost::none, this->blockStore->load(blockId));
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenRemovingBlock_thenBlockIsNotLoadableAnymore) {
blockstore::BlockId blockId = this->blockStore->create(cpputils::Data(1024));
this->blockStore->create(cpputils::Data(512));
EXPECT_NE(boost::none, this->blockStore->load(blockId));
EXPECT_TRUE(this->blockStore->remove(blockId));
EXPECT_EQ(boost::none, this->blockStore->load(blockId));
}
TYPED_TEST_P(BlockStore2Test, givenOtherwiseEmptyBlockStore_whenRemovingExistingBlock_thenSucceeds) {
blockstore::BlockId blockId = this->blockStore->create(cpputils::Data(1024));
EXPECT_TRUE(this->blockStore->remove(blockId));
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenRemovingExistingBlock_thenSucceeds) {
blockstore::BlockId blockId = this->blockStore->create(cpputils::Data(1024));
this->blockStore->create(cpputils::Data(512));
EXPECT_EQ(true, this->blockStore->remove(blockId));
}
TYPED_TEST_P(BlockStore2Test, givenEmptyBlockStore_whenRemovingNonexistingBlock_thenFails) {
const blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
auto result = this->blockStore->remove(blockId);
EXPECT_EQ(false, result);
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenRemovingNonexistingBlock_thenFails) {
blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772973");
blockstore::BlockId differentKey = blockstore::BlockId::FromString("290AC2C7097274A389EE14B91B72B493");
ASSERT_TRUE(this->blockStore->tryCreate(blockId, cpputils::Data(1024)));
EXPECT_EQ(false, this->blockStore->remove(differentKey));
}
TYPED_TEST_P(BlockStore2Test, givenEmptyBlockStore_whenCreatingAndLoadingEmptyBlock_thenCorrectBlockLoads) {
auto blockId = this->blockStore->create(cpputils::Data(0));
auto loaded = this->blockStore->load(blockId).value();
EXPECT_EQ(0u, loaded.size());
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenCreatingAndLoadingEmptyBlock_thenCorrectBlockLoads) {
this->blockStore->create(cpputils::Data(512));
auto blockId = this->blockStore->create(cpputils::Data(0));
auto loaded = this->blockStore->load(blockId).value();
EXPECT_EQ(0u, loaded.size());
}
TYPED_TEST_P(BlockStore2Test, givenEmptyBlockStore_whenCreatingAndLoadingNonEmptyBlock_thenCorrectBlockLoads) {
cpputils::Data data = cpputils::DataFixture::generate(1024);
auto blockId = this->blockStore->create(data.copy());
auto loaded = this->blockStore->load(blockId).value();
EXPECT_EQ(loaded, data);
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenCreatingAndLoadingNonEmptyBlock_thenCorrectBlockLoads) {
this->blockStore->create(cpputils::Data(512));
cpputils::Data data = cpputils::DataFixture::generate(1024);
auto blockId = this->blockStore->create(data.copy());
auto loaded = this->blockStore->load(blockId).value();
EXPECT_EQ(loaded, data);
}
TYPED_TEST_P(BlockStore2Test, givenEmptyBlockStore_whenTryCreatingAndLoadingEmptyBlock_thenCorrectBlockLoads) {
blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772973");
ASSERT_TRUE(this->blockStore->tryCreate(blockId, cpputils::Data(0)));
auto loaded = this->blockStore->load(blockId).value();
EXPECT_EQ(0u, loaded.size());
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenTryCreatingAndLoadingEmptyBlock_thenCorrectBlockLoads) {
blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772973");
this->blockStore->create(cpputils::Data(512));
ASSERT_TRUE(this->blockStore->tryCreate(blockId, cpputils::Data(0)));
auto loaded = this->blockStore->load(blockId).value();
EXPECT_EQ(0u, loaded.size());
}
TYPED_TEST_P(BlockStore2Test, givenEmptyBlockStore_whenTryCreatingAndLoadingNonEmptyBlock_thenCorrectBlockLoads) {
blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772973");
cpputils::Data data = cpputils::DataFixture::generate(1024);
ASSERT_TRUE(this->blockStore->tryCreate(blockId, data.copy()));
auto loaded = this->blockStore->load(blockId).value();
EXPECT_EQ(loaded, data);
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenTryCreatingAndLoadingNonEmptyBlock_thenCorrectBlockLoads) {
blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772973");
this->blockStore->create(cpputils::Data(512));
cpputils::Data data = cpputils::DataFixture::generate(1024);
ASSERT_TRUE(this->blockStore->tryCreate(blockId, data.copy()));
auto loaded = this->blockStore->load(blockId).value();
EXPECT_EQ(loaded, data);
}
TYPED_TEST_P(BlockStore2Test, givenEmptyBlockStore_whenStoringAndLoadingNonExistingEmptyBlock_thenCorrectBlockLoads) {
const blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
this->blockStore->store(blockId, cpputils::Data(0));
auto loaded = this->blockStore->load(blockId).value();
EXPECT_EQ(0u, loaded.size());
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenStoringAndLoadingNonExistingEmptyBlock_thenCorrectBlockLoads) {
this->blockStore->create(cpputils::Data(512));
const blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
this->blockStore->store(blockId, cpputils::Data(0));
auto loaded = this->blockStore->load(blockId).value();
EXPECT_EQ(0u, loaded.size());
}
TYPED_TEST_P(BlockStore2Test, givenEmptyBlockStore_whenStoringAndLoadingNonExistingNonEmptyBlock_thenCorrectBlockLoads) {
const blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
cpputils::Data data = cpputils::DataFixture::generate(1024);
this->blockStore->store(blockId, data.copy());
auto loaded = this->blockStore->load(blockId).value();
EXPECT_EQ(data, loaded);
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenStoringAndLoadingNonExistingNonEmptyBlock_thenCorrectBlockLoads) {
this->blockStore->create(cpputils::Data(512));
const blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
cpputils::Data data = cpputils::DataFixture::generate(1024);
this->blockStore->store(blockId, data.copy());
auto loaded = this->blockStore->load(blockId).value();
EXPECT_EQ(data, loaded);
}
TYPED_TEST_P(BlockStore2Test, givenEmptyBlockStore_whenStoringAndLoadingExistingEmptyBlock_thenCorrectBlockLoads) {
auto blockId = this->blockStore->create(cpputils::Data(512));
this->blockStore->store(blockId, cpputils::Data(0));
auto loaded = this->blockStore->load(blockId).value();
EXPECT_EQ(0u, loaded.size());
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenStoringAndLoadingExistingEmptyBlock_thenCorrectBlockLoads) {
this->blockStore->create(cpputils::Data(512));
auto blockId = this->blockStore->create(cpputils::Data(512));
this->blockStore->store(blockId, cpputils::Data(0));
auto loaded = this->blockStore->load(blockId).value();
EXPECT_EQ(0u, loaded.size());
}
TYPED_TEST_P(BlockStore2Test, givenEmptyBlockStore_whenStoringAndLoadingExistingNonEmptyBlock_thenCorrectBlockLoads) {
auto blockId = this->blockStore->create(cpputils::Data(512));
cpputils::Data data = cpputils::DataFixture::generate(1024);
this->blockStore->store(blockId, data.copy());
auto loaded = this->blockStore->load(blockId).value();
EXPECT_EQ(data, loaded);
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenStoringAndLoadingExistingNonEmptyBlock_thenCorrectBlockLoads) {
this->blockStore->create(cpputils::Data(512));
auto blockId = this->blockStore->create(cpputils::Data(512));
cpputils::Data data = cpputils::DataFixture::generate(1024);
this->blockStore->store(blockId, data.copy());
auto loaded = this->blockStore->load(blockId).value();
EXPECT_EQ(data, loaded);
}
TYPED_TEST_P(BlockStore2Test, givenEmptyBlockStore_whenLoadingNonExistingBlock_thenFails) {
const blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
EXPECT_EQ(boost::none, this->blockStore->load(blockId));
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenLoadingNonExistingBlock_thenFails) {
this->blockStore->create(cpputils::Data(512));
const blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
EXPECT_EQ(boost::none, this->blockStore->load(blockId));
}
TYPED_TEST_P(BlockStore2Test, NumBlocksIsCorrectOnEmptyBlockstore) {
auto blockStore = this->fixture.createBlockStore();
EXPECT_EQ(0u, blockStore->numBlocks());
}
TYPED_TEST_P(BlockStore2Test, NumBlocksIsCorrectAfterCreatingOneBlock) {
auto blockStore = this->fixture.createBlockStore();
blockStore->create(cpputils::Data(1));
EXPECT_EQ(1u, blockStore->numBlocks());
}
TYPED_TEST_P(BlockStore2Test, NumBlocksIsCorrectAfterRemovingTheLastBlock) {
auto blockStore = this->fixture.createBlockStore();
blockstore::BlockId blockId = blockStore->create(cpputils::Data(1));
EXPECT_TRUE(blockStore->remove(blockId));
EXPECT_EQ(0u, blockStore->numBlocks());
}
TYPED_TEST_P(BlockStore2Test, NumBlocksIsCorrectAfterCreatingTwoBlocks) {
auto blockStore = this->fixture.createBlockStore();
blockStore->create(cpputils::Data(1));
blockStore->create(cpputils::Data(0));
EXPECT_EQ(2u, blockStore->numBlocks());
}
TYPED_TEST_P(BlockStore2Test, NumBlocksIsCorrectAfterRemovingABlock) {
auto blockStore = this->fixture.createBlockStore();
blockstore::BlockId blockId = blockStore->create(cpputils::Data(1));
blockStore->create(cpputils::Data(1));
EXPECT_TRUE(blockStore->remove(blockId));
EXPECT_EQ(1u, blockStore->numBlocks());
}
TYPED_TEST_P(BlockStore2Test, NumBlocksIsCorrectAfterStoringANewBlock) {
const blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
auto blockStore = this->fixture.createBlockStore();
blockStore->store(blockId, cpputils::Data(1));
EXPECT_EQ(1u, blockStore->numBlocks());
}
TYPED_TEST_P(BlockStore2Test, NumBlocksIsCorrectAfterStoringAnExistingBlock) {
auto blockStore = this->fixture.createBlockStore();
blockstore::BlockId blockId = blockStore->create(cpputils::Data(1));
blockStore->store(blockId, cpputils::Data(1));
EXPECT_EQ(1u, blockStore->numBlocks());
}
TYPED_TEST_P(BlockStore2Test, ForEachBlock_zeroblocks) {
auto blockStore = this->fixture.createBlockStore();
MockForEachBlockCallback mockForEachBlockCallback;
blockStore->forEachBlock(mockForEachBlockCallback.callback());
this->EXPECT_UNORDERED_EQ({}, mockForEachBlockCallback.called_with);
}
TYPED_TEST_P(BlockStore2Test, ForEachBlock_oneblock) {
auto blockStore = this->fixture.createBlockStore();
auto blockId = blockStore->create(cpputils::Data(1));
MockForEachBlockCallback mockForEachBlockCallback;
blockStore->forEachBlock(mockForEachBlockCallback.callback());
this->EXPECT_UNORDERED_EQ({blockId}, mockForEachBlockCallback.called_with);
}
TYPED_TEST_P(BlockStore2Test, ForEachBlock_twoblocks) {
auto blockStore = this->fixture.createBlockStore();
auto blockId1 = blockStore->create(cpputils::Data(1));
auto blockId2 = blockStore->create(cpputils::Data(1));
MockForEachBlockCallback mockForEachBlockCallback;
blockStore->forEachBlock(mockForEachBlockCallback.callback());
this->EXPECT_UNORDERED_EQ({blockId1, blockId2}, mockForEachBlockCallback.called_with);
}
TYPED_TEST_P(BlockStore2Test, ForEachBlock_threeblocks) {
auto blockStore = this->fixture.createBlockStore();
auto blockId1 = blockStore->create(cpputils::Data(1));
auto blockId2 = blockStore->create(cpputils::Data(1));
auto blockId3 = blockStore->create(cpputils::Data(1));
MockForEachBlockCallback mockForEachBlockCallback;
blockStore->forEachBlock(mockForEachBlockCallback.callback());
this->EXPECT_UNORDERED_EQ({blockId1, blockId2, blockId3}, mockForEachBlockCallback.called_with);
}
TYPED_TEST_P(BlockStore2Test, ForEachBlock_doesntListRemovedBlocks_oneblock) {
auto blockStore = this->fixture.createBlockStore();
auto blockId1 = blockStore->create(cpputils::Data(1));
EXPECT_TRUE(blockStore->remove(blockId1));
MockForEachBlockCallback mockForEachBlockCallback;
blockStore->forEachBlock(mockForEachBlockCallback.callback());
this->EXPECT_UNORDERED_EQ({}, mockForEachBlockCallback.called_with);
}
TYPED_TEST_P(BlockStore2Test, ForEachBlock_doesntListRemovedBlocks_twoblocks) {
auto blockStore = this->fixture.createBlockStore();
auto blockId1 = blockStore->create(cpputils::Data(1));
auto blockId2 = blockStore->create(cpputils::Data(1));
EXPECT_TRUE(blockStore->remove(blockId1));
MockForEachBlockCallback mockForEachBlockCallback;
blockStore->forEachBlock(mockForEachBlockCallback.callback());
this->EXPECT_UNORDERED_EQ({blockId2}, mockForEachBlockCallback.called_with);
}
TYPED_TEST_P(BlockStore2Test, TryCreateTwoBlocksWithSameBlockIdAndSameSize) {
auto blockStore = this->fixture.createBlockStore();
blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
EXPECT_TRUE(blockStore->tryCreate(blockId, cpputils::Data(1024)));
EXPECT_FALSE(blockStore->tryCreate(blockId, cpputils::Data(1024)));
}
TYPED_TEST_P(BlockStore2Test, TryCreateTwoBlocksWithSameBlockIdAndDifferentSize) {
auto blockStore = this->fixture.createBlockStore();
blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
EXPECT_TRUE(blockStore->tryCreate(blockId, cpputils::Data(1024)));
EXPECT_FALSE(blockStore->tryCreate(blockId, cpputils::Data(4096)));
}
TYPED_TEST_P(BlockStore2Test, TryCreateTwoBlocksWithSameBlockIdAndFirstNullSize) {
auto blockStore = this->fixture.createBlockStore();
blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
EXPECT_TRUE(blockStore->tryCreate(blockId, cpputils::Data(0)));
EXPECT_FALSE(blockStore->tryCreate(blockId, cpputils::Data(1024)));
}
/*
TYPED_TEST_P(BlockStore2Test, TryCreateTwoBlocksWithSameBlockIdAndSecondNullSize) {
auto blockStore = this->fixture.createBlockStore();
blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
EXPECT_TRUE(blockStore->tryCreate(blockId, cpputils::Data(1024)));
EXPECT_FALSE(blockStore->tryCreate(blockId, cpputils::Data(0)));
}
TYPED_TEST_P(BlockStore2Test, TryCreateTwoBlocksWithSameBlockIdAndBothNullSize) {
auto blockStore = this->fixture.createBlockStore();
blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
EXPECT_TRUE(blockStore->tryCreate(blockId, cpputils::Data(0)));
EXPECT_FALSE(blockStore->tryCreate(blockId, cpputils::Data(0)));
}*/
REGISTER_TYPED_TEST_SUITE_P(BlockStore2Test,
givenNonEmptyBlockStore_whenCallingTryCreateOnExistingBlock_thenFails,
givenEmptyBlockStore_whenCallingTryCreateOnNonExistingBlock_thenSucceeds,
givenNonEmptyBlockStore_whenCallingTryCreateOnNonExistingBlock_thenSucceeds,
givenNonEmptyBlockStore_whenLoadExistingBlock_thenSucceeds,
givenEmptyBlockStore_whenLoadNonexistingBlock_thenFails,
givenNonEmptyBlockStore_whenLoadNonexistingBlock_thenFails,
givenNonEmptyBlockStore_whenStoringExistingBlock_thenSucceeds,
givenEmptyBlockStore_whenStoringNonexistingBlock_thenSucceeds,
givenNonEmptyBlockStore_whenStoringNonexistingBlock_thenSucceeds,
givenEmptyBlockStore_whenCreatingTwoBlocks_thenTheyGetDifferentKeys,
givenOtherwiseEmptyBlockStore_whenRemovingBlock_thenBlockIsNotLoadableAnymore,
givenNonEmptyBlockStore_whenRemovingBlock_thenBlockIsNotLoadableAnymore,
givenOtherwiseEmptyBlockStore_whenRemovingExistingBlock_thenSucceeds,
givenNonEmptyBlockStore_whenRemovingExistingBlock_thenSucceeds,
givenEmptyBlockStore_whenRemovingNonexistingBlock_thenFails,
givenNonEmptyBlockStore_whenRemovingNonexistingBlock_thenFails,
givenEmptyBlockStore_whenCreatingAndLoadingEmptyBlock_thenCorrectBlockLoads,
givenNonEmptyBlockStore_whenCreatingAndLoadingEmptyBlock_thenCorrectBlockLoads,
givenEmptyBlockStore_whenCreatingAndLoadingNonEmptyBlock_thenCorrectBlockLoads,
givenNonEmptyBlockStore_whenCreatingAndLoadingNonEmptyBlock_thenCorrectBlockLoads,
givenEmptyBlockStore_whenTryCreatingAndLoadingEmptyBlock_thenCorrectBlockLoads,
givenNonEmptyBlockStore_whenTryCreatingAndLoadingEmptyBlock_thenCorrectBlockLoads,
givenEmptyBlockStore_whenTryCreatingAndLoadingNonEmptyBlock_thenCorrectBlockLoads,
givenNonEmptyBlockStore_whenTryCreatingAndLoadingNonEmptyBlock_thenCorrectBlockLoads,
givenEmptyBlockStore_whenStoringAndLoadingNonExistingEmptyBlock_thenCorrectBlockLoads,
givenNonEmptyBlockStore_whenStoringAndLoadingNonExistingEmptyBlock_thenCorrectBlockLoads,
givenEmptyBlockStore_whenStoringAndLoadingNonExistingNonEmptyBlock_thenCorrectBlockLoads,
givenNonEmptyBlockStore_whenStoringAndLoadingNonExistingNonEmptyBlock_thenCorrectBlockLoads,
givenEmptyBlockStore_whenStoringAndLoadingExistingEmptyBlock_thenCorrectBlockLoads,
givenNonEmptyBlockStore_whenStoringAndLoadingExistingEmptyBlock_thenCorrectBlockLoads,
givenEmptyBlockStore_whenStoringAndLoadingExistingNonEmptyBlock_thenCorrectBlockLoads,
givenNonEmptyBlockStore_whenStoringAndLoadingExistingNonEmptyBlock_thenCorrectBlockLoads,
givenEmptyBlockStore_whenLoadingNonExistingBlock_thenFails,
givenNonEmptyBlockStore_whenLoadingNonExistingBlock_thenFails,
NumBlocksIsCorrectOnEmptyBlockstore,
NumBlocksIsCorrectAfterCreatingOneBlock,
NumBlocksIsCorrectAfterRemovingTheLastBlock,
NumBlocksIsCorrectAfterCreatingTwoBlocks,
NumBlocksIsCorrectAfterRemovingABlock,
NumBlocksIsCorrectAfterStoringANewBlock,
NumBlocksIsCorrectAfterStoringAnExistingBlock,
ForEachBlock_zeroblocks,
ForEachBlock_oneblock,
ForEachBlock_twoblocks,
ForEachBlock_threeblocks,
ForEachBlock_doesntListRemovedBlocks_oneblock,
ForEachBlock_doesntListRemovedBlocks_twoblocks,
TryCreateTwoBlocksWithSameBlockIdAndSameSize,
TryCreateTwoBlocksWithSameBlockIdAndDifferentSize,
TryCreateTwoBlocksWithSameBlockIdAndFirstNullSize
//TODO Just disabled because gtest doesn't allow more template parameters. Fix and reenable!
// see https://github.com/google/googletest/issues/1267
//TryCreateTwoBlocksWithSameBlockIdAndSecondNullSize,
//TryCreateTwoBlocksWithSameBlockIdAndBothNullSize
);
#endif

View File

@ -1,406 +0,0 @@
#pragma once
#ifndef MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_TESTUTILS_BLOCKSTORETEST_H_
#define MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_TESTUTILS_BLOCKSTORETEST_H_
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <cpp-utils/data/DataFixture.h>
#include <cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround.h>
#include "blockstore/interface/BlockStore.h"
class MockForEachBlockCallback final {
public:
std::function<void (const blockstore::BlockId &)> callback() {
return [this] (const blockstore::BlockId &blockId) {
called_with.push_back(blockId);
};
}
std::vector<blockstore::BlockId> called_with;
};
class BlockStoreTestFixture {
public:
virtual ~BlockStoreTestFixture() {}
virtual cpputils::unique_ref<blockstore::BlockStore> createBlockStore() = 0;
};
template<class ConcreteBlockStoreTestFixture>
class BlockStoreTest: public ::testing::Test {
public:
BlockStoreTest() :fixture() {}
BOOST_STATIC_ASSERT_MSG(
(std::is_base_of<BlockStoreTestFixture, ConcreteBlockStoreTestFixture>::value),
"Given test fixture for instantiating the (type parameterized) BlockStoreTest must inherit from BlockStoreTestFixture"
);
ConcreteBlockStoreTestFixture fixture;
void TestBlockIsUsable(cpputils::unique_ref<blockstore::Block> block, blockstore::BlockStore *blockStore) {
// Write full block space and check it was correctly written
cpputils::Data fixture = cpputils::DataFixture::generate(block->size());
block->write(fixture.data(), 0, fixture.size());
EXPECT_EQ(0, std::memcmp(fixture.data(), block->data(), fixture.size()));
// Store and reload block and check data is still correct
auto blockId = block->blockId();
cpputils::destruct(std::move(block));
block = blockStore->load(blockId).value();
EXPECT_EQ(0, std::memcmp(fixture.data(), block->data(), fixture.size()));
}
template<class Entry>
void EXPECT_UNORDERED_EQ(const std::vector<Entry> &expected, std::vector<Entry> actual) {
EXPECT_EQ(expected.size(), actual.size());
for (const Entry &expectedEntry : expected) {
removeOne(&actual, expectedEntry);
}
}
template<class Entry>
void removeOne(std::vector<Entry> *entries, const Entry &toRemove) {
auto found = std::find(entries->begin(), entries->end(), toRemove);
if (found != entries->end()) {
entries->erase(found);
return;
}
EXPECT_TRUE(false);
}
};
TYPED_TEST_SUITE_P(BlockStoreTest);
TYPED_TEST_P(BlockStoreTest, TwoCreatedBlocksHaveDifferentBlockIds) {
auto blockStore = this->fixture.createBlockStore();
auto block1 = blockStore->create(cpputils::Data(1024));
auto block2 = blockStore->create(cpputils::Data(1024));
EXPECT_NE(block1->blockId(), block2->blockId());
}
TYPED_TEST_P(BlockStoreTest, BlockIsNotLoadableAfterDeleting_DeleteByBlock) {
auto blockStore = this->fixture.createBlockStore();
auto blockId = blockStore->create(cpputils::Data(1024))->blockId();
auto block = blockStore->load(blockId);
EXPECT_NE(boost::none, block);
blockStore->remove(std::move(*block));
EXPECT_EQ(boost::none, blockStore->load(blockId));
}
TYPED_TEST_P(BlockStoreTest, BlockIsNotLoadableAfterDeleting_DeleteByBlockId) {
auto blockStore = this->fixture.createBlockStore();
auto blockId = blockStore->create(cpputils::Data(1024))->blockId();
blockStore->remove(blockId);
EXPECT_EQ(boost::none, blockStore->load(blockId));
}
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectOnEmptyBlockstore) {
auto blockStore = this->fixture.createBlockStore();
EXPECT_EQ(0u, blockStore->numBlocks());
}
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingOneBlock) {
auto blockStore = this->fixture.createBlockStore();
auto block = blockStore->create(cpputils::Data(1));
EXPECT_EQ(1u, blockStore->numBlocks());
}
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingOneBlock_AfterClosingBlock) {
auto blockStore = this->fixture.createBlockStore();
blockStore->create(cpputils::Data(1));
EXPECT_EQ(1u, blockStore->numBlocks());
}
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterRemovingTheLastBlock_DeleteByBlock) {
auto blockStore = this->fixture.createBlockStore();
auto block = blockStore->create(cpputils::Data(1));
blockStore->remove(std::move(block));
EXPECT_EQ(0u, blockStore->numBlocks());
}
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterRemovingTheLastBlock_DeleteByBlockId) {
auto blockStore = this->fixture.createBlockStore();
auto blockId = blockStore->create(cpputils::Data(1))->blockId();
blockStore->remove(blockId);
EXPECT_EQ(0u, blockStore->numBlocks());
}
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks) {
auto blockStore = this->fixture.createBlockStore();
auto block1 = blockStore->create(cpputils::Data(1));
auto block2 = blockStore->create(cpputils::Data(0));
EXPECT_EQ(2u, blockStore->numBlocks());
}
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingFirstBlock) {
auto blockStore = this->fixture.createBlockStore();
blockStore->create(cpputils::Data(1));
auto block2 = blockStore->create(cpputils::Data(0));
EXPECT_EQ(2u, blockStore->numBlocks());
}
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingSecondBlock) {
auto blockStore = this->fixture.createBlockStore();
auto block1 = blockStore->create(cpputils::Data(1));
blockStore->create(cpputils::Data(0));
EXPECT_EQ(2u, blockStore->numBlocks());
}
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingBothBlocks) {
auto blockStore = this->fixture.createBlockStore();
blockStore->create(cpputils::Data(1));
blockStore->create(cpputils::Data(0));
EXPECT_EQ(2u, blockStore->numBlocks());
}
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterRemovingABlock_DeleteByBlock) {
auto blockStore = this->fixture.createBlockStore();
auto block = blockStore->create(cpputils::Data(1));
blockStore->create(cpputils::Data(1));
blockStore->remove(std::move(block));
EXPECT_EQ(1u, blockStore->numBlocks());
}
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterRemovingABlock_DeleteByBlockId) {
auto blockStore = this->fixture.createBlockStore();
auto blockId = blockStore->create(cpputils::Data(1))->blockId();
blockStore->create(cpputils::Data(1));
blockStore->remove(blockId);
EXPECT_EQ(1u, blockStore->numBlocks());
}
TYPED_TEST_P(BlockStoreTest, CanRemoveModifiedBlock) {
auto blockStore = this->fixture.createBlockStore();
auto block = blockStore->create(cpputils::Data(5));
block->write("data", 0, 4);
blockStore->remove(std::move(block));
EXPECT_EQ(0u, blockStore->numBlocks());
}
TYPED_TEST_P(BlockStoreTest, ForEachBlock_zeroblocks) {
auto blockStore = this->fixture.createBlockStore();
MockForEachBlockCallback mockForEachBlockCallback;
blockStore->forEachBlock(mockForEachBlockCallback.callback());
this->EXPECT_UNORDERED_EQ({}, mockForEachBlockCallback.called_with);
}
TYPED_TEST_P(BlockStoreTest, ForEachBlock_oneblock) {
auto blockStore = this->fixture.createBlockStore();
auto block = blockStore->create(cpputils::Data(1));
MockForEachBlockCallback mockForEachBlockCallback;
blockStore->forEachBlock(mockForEachBlockCallback.callback());
this->EXPECT_UNORDERED_EQ({block->blockId()}, mockForEachBlockCallback.called_with);
}
TYPED_TEST_P(BlockStoreTest, ForEachBlock_twoblocks) {
auto blockStore = this->fixture.createBlockStore();
auto block1 = blockStore->create(cpputils::Data(1));
auto block2 = blockStore->create(cpputils::Data(1));
MockForEachBlockCallback mockForEachBlockCallback;
blockStore->forEachBlock(mockForEachBlockCallback.callback());
this->EXPECT_UNORDERED_EQ({block1->blockId(), block2->blockId()}, mockForEachBlockCallback.called_with);
}
TYPED_TEST_P(BlockStoreTest, ForEachBlock_threeblocks) {
auto blockStore = this->fixture.createBlockStore();
auto block1 = blockStore->create(cpputils::Data(1));
auto block2 = blockStore->create(cpputils::Data(1));
auto block3 = blockStore->create(cpputils::Data(1));
MockForEachBlockCallback mockForEachBlockCallback;
blockStore->forEachBlock(mockForEachBlockCallback.callback());
this->EXPECT_UNORDERED_EQ({block1->blockId(), block2->blockId(), block3->blockId()}, mockForEachBlockCallback.called_with);
}
TYPED_TEST_P(BlockStoreTest, ForEachBlock_doesntListRemovedBlocks_oneblock) {
auto blockStore = this->fixture.createBlockStore();
auto block1 = blockStore->create(cpputils::Data(1));
blockStore->remove(std::move(block1));
MockForEachBlockCallback mockForEachBlockCallback;
blockStore->forEachBlock(mockForEachBlockCallback.callback());
this->EXPECT_UNORDERED_EQ({}, mockForEachBlockCallback.called_with);
}
TYPED_TEST_P(BlockStoreTest, ForEachBlock_doesntListRemovedBlocks_twoblocks) {
auto blockStore = this->fixture.createBlockStore();
auto block1 = blockStore->create(cpputils::Data(1));
auto block2 = blockStore->create(cpputils::Data(1));
blockStore->remove(std::move(block1));
MockForEachBlockCallback mockForEachBlockCallback;
blockStore->forEachBlock(mockForEachBlockCallback.callback());
this->EXPECT_UNORDERED_EQ({block2->blockId()}, mockForEachBlockCallback.called_with);
}
TYPED_TEST_P(BlockStoreTest, Resize_Larger_FromZero) {
auto blockStore = this->fixture.createBlockStore();
auto block = blockStore->create(cpputils::Data(0));
block->resize(10);
EXPECT_EQ(10u, block->size());
}
TYPED_TEST_P(BlockStoreTest, Resize_Larger_FromZero_BlockIsStillUsable) {
auto blockStore = this->fixture.createBlockStore();
auto block = blockStore->create(cpputils::Data(0));
block->resize(10);
this->TestBlockIsUsable(std::move(block), blockStore.get());
}
TYPED_TEST_P(BlockStoreTest, Resize_Larger) {
auto blockStore = this->fixture.createBlockStore();
auto block = blockStore->create(cpputils::Data(10));
block->resize(20);
EXPECT_EQ(20u, block->size());
}
TYPED_TEST_P(BlockStoreTest, Resize_Larger_BlockIsStillUsable) {
auto blockStore = this->fixture.createBlockStore();
auto block = blockStore->create(cpputils::Data(10));
block->resize(20);
this->TestBlockIsUsable(std::move(block), blockStore.get());
}
TYPED_TEST_P(BlockStoreTest, Resize_Smaller) {
auto blockStore = this->fixture.createBlockStore();
auto block = blockStore->create(cpputils::Data(10));
block->resize(5);
EXPECT_EQ(5u, block->size());
}
TYPED_TEST_P(BlockStoreTest, Resize_Smaller_BlockIsStillUsable) {
auto blockStore = this->fixture.createBlockStore();
auto block = blockStore->create(cpputils::Data(10));
block->resize(5);
this->TestBlockIsUsable(std::move(block), blockStore.get());
}
TYPED_TEST_P(BlockStoreTest, Resize_Smaller_ToZero) {
auto blockStore = this->fixture.createBlockStore();
auto block = blockStore->create(cpputils::Data(10));
block->resize(0);
EXPECT_EQ(0u, block->size());
}
TYPED_TEST_P(BlockStoreTest, Resize_Smaller_ToZero_BlockIsStillUsable) {
auto blockStore = this->fixture.createBlockStore();
auto block = blockStore->create(cpputils::Data(10));
block->resize(0);
this->TestBlockIsUsable(std::move(block), blockStore.get());
}
/*
TYPED_TEST_P(BlockStoreTest, TryCreateTwoBlocksWithSameBlockIdAndSameSize) {
auto blockStore = this->fixture.createBlockStore();
blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
auto block = blockStore->tryCreate(blockId, cpputils::Data(1024));
(*block)->flush(); //TODO Ideally, flush shouldn't be necessary here.
auto block2 = blockStore->tryCreate(blockId, cpputils::Data(1024));
EXPECT_NE(boost::none, block);
EXPECT_EQ(boost::none, block2);
}
TYPED_TEST_P(BlockStoreTest, TryCreateTwoBlocksWithSameBlockIdAndDifferentSize) {
auto blockStore = this->fixture.createBlockStore();
blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
auto block = blockStore->tryCreate(blockId, cpputils::Data(1024));
(*block)->flush(); //TODO Ideally, flush shouldn't be necessary here.
auto block2 = blockStore->tryCreate(blockId, cpputils::Data(4096));
EXPECT_NE(boost::none, block);
EXPECT_EQ(boost::none, block2);
}
TYPED_TEST_P(BlockStoreTest, TryCreateTwoBlocksWithSameBlockIdAndFirstNullSize) {
auto blockStore = this->fixture.createBlockStore();
blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
auto block = blockStore->tryCreate(blockId, cpputils::Data(0));
(*block)->flush(); //TODO Ideally, flush shouldn't be necessary here.
auto block2 = blockStore->tryCreate(blockId, cpputils::Data(1024));
EXPECT_NE(boost::none, block);
EXPECT_EQ(boost::none, block2);
}
TYPED_TEST_P(BlockStoreTest, TryCreateTwoBlocksWithSameBlockIdAndSecondNullSize) {
auto blockStore = this->fixture.createBlockStore();
blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
auto block = blockStore->tryCreate(blockId, cpputils::Data(1024));
(*block)->flush(); //TODO Ideally, flush shouldn't be necessary here.
auto block2 = blockStore->tryCreate(blockId, cpputils::Data(0));
EXPECT_NE(boost::none, block);
EXPECT_EQ(boost::none, block2);
}
TYPED_TEST_P(BlockStoreTest, TryCreateTwoBlocksWithSameBlockIdAndBothNullSize) {
auto blockStore = this->fixture.createBlockStore();
blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
auto block = blockStore->tryCreate(blockId, cpputils::Data(0));
(*block)->flush(); //TODO Ideally, flush shouldn't be necessary here.
auto block2 = blockStore->tryCreate(blockId, cpputils::Data(0));
EXPECT_NE(boost::none, block);
EXPECT_EQ(boost::none, block2);
}*/
#include "BlockStoreTest_Size.h"
#include "BlockStoreTest_Data.h"
REGISTER_TYPED_TEST_SUITE_P(BlockStoreTest,
CreatedBlockHasCorrectSize,
LoadingUnchangedBlockHasCorrectSize,
CreatedBlockData,
LoadingUnchangedBlockData,
LoadedBlockIsCorrect,
// LoadedBlockIsCorrectWhenLoadedDirectlyAfterFlushing,
AfterCreate_FlushingDoesntChangeBlock,
AfterLoad_FlushingDoesntChangeBlock,
AfterCreate_FlushesWhenDestructed,
AfterLoad_FlushesWhenDestructed,
LoadNonExistingBlock,
TwoCreatedBlocksHaveDifferentBlockIds,
BlockIsNotLoadableAfterDeleting_DeleteByBlock,
BlockIsNotLoadableAfterDeleting_DeleteByBlockId,
NumBlocksIsCorrectOnEmptyBlockstore,
NumBlocksIsCorrectAfterAddingOneBlock,
NumBlocksIsCorrectAfterAddingOneBlock_AfterClosingBlock,
NumBlocksIsCorrectAfterRemovingTheLastBlock_DeleteByBlock,
NumBlocksIsCorrectAfterRemovingTheLastBlock_DeleteByBlockId,
NumBlocksIsCorrectAfterAddingTwoBlocks,
NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingFirstBlock,
NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingSecondBlock,
NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingBothBlocks,
NumBlocksIsCorrectAfterRemovingABlock_DeleteByBlock,
NumBlocksIsCorrectAfterRemovingABlock_DeleteByBlockId,
WriteAndReadImmediately,
WriteAndReadAfterLoading,
WriteTwiceAndRead,
OverwriteSameSizeAndReadImmediately,
OverwriteSameSizeAndReadAfterLoading,
OverwriteSmallerSizeAndReadImmediately,
OverwriteSmallerSizeAndReadAfterLoading,
OverwriteLargerSizeAndReadAfterLoading,
OverwriteLargerSizeAndReadImmediately,
OverwriteNonexistingAndReadAfterLoading,
OverwriteNonexistingAndReadImmediately,
CanRemoveModifiedBlock,
ForEachBlock_zeroblocks,
ForEachBlock_oneblock,
ForEachBlock_twoblocks,
ForEachBlock_threeblocks,
ForEachBlock_doesntListRemovedBlocks_oneblock,
ForEachBlock_doesntListRemovedBlocks_twoblocks,
Resize_Larger_FromZero,
Resize_Larger_FromZero_BlockIsStillUsable,
Resize_Larger,
Resize_Larger_BlockIsStillUsable,
Resize_Smaller,
Resize_Smaller_BlockIsStillUsable,
Resize_Smaller_ToZero,
Resize_Smaller_ToZero_BlockIsStillUsable
//TODO Just disabled because gtest doesn't allow more template parameters. Fix and reenable!
// see https://github.com/google/googletest/issues/1267
//TryCreateTwoBlocksWithSameBlockIdAndSameSize,
//TryCreateTwoBlocksWithSameBlockIdAndDifferentSize,
//TryCreateTwoBlocksWithSameBlockIdAndFirstNullSize,
//TryCreateTwoBlocksWithSameBlockIdAndSecondNullSize,
//TryCreateTwoBlocksWithSameBlockIdAndBothNullSize,
);
#endif

View File

@ -1,175 +0,0 @@
#pragma once
#ifndef MESSMER_BLOCKSTORE_TEST_TESTUTILS_BLOCKSTORETEST_DATA_H_
#define MESSMER_BLOCKSTORE_TEST_TESTUTILS_BLOCKSTORETEST_DATA_H_
// This file is meant to be included by BlockStoreTest.h only
struct DataRange {
uint64_t blocksize;
uint64_t offset;
uint64_t count;
};
class BlockStoreDataParametrizedTest {
public:
BlockStoreDataParametrizedTest(cpputils::unique_ref<blockstore::BlockStore> blockStore_, const DataRange &testData_)
: blockStore(std::move(blockStore_)),
testData(testData_),
foregroundData(cpputils::DataFixture::generate(testData.count, 0)),
backgroundData(cpputils::DataFixture::generate(testData.blocksize, 1)) {
}
void TestWriteAndReadImmediately() {
auto block = blockStore->create(cpputils::Data(testData.blocksize).FillWithZeroes());
block->write(foregroundData.data(), testData.offset, testData.count);
EXPECT_DATA_READS_AS(foregroundData, *block, testData.offset, testData.count);
EXPECT_DATA_IS_ZEROES_OUTSIDE_OF(*block, testData.offset, testData.count);
}
void TestWriteAndReadAfterLoading() {
blockstore::BlockId blockId = CreateBlockWriteToItAndReturnKey(foregroundData);
auto loaded_block = blockStore->load(blockId).value();
EXPECT_DATA_READS_AS(foregroundData, *loaded_block, testData.offset, testData.count);
EXPECT_DATA_IS_ZEROES_OUTSIDE_OF(*loaded_block, testData.offset, testData.count);
}
void TestWriteTwiceAndRead() {
auto block = blockStore->create(cpputils::Data(testData.blocksize));
block->write(backgroundData.data(), 0, testData.blocksize);
block->write(foregroundData.data(), testData.offset, testData.count);
EXPECT_DATA_READS_AS(foregroundData, *block, testData.offset, testData.count);
EXPECT_DATA_READS_AS_OUTSIDE_OF(backgroundData, *block, testData.offset, testData.count);
}
void TestOverwriteSameSizeAndReadImmediately() {
auto blockId = blockStore->create(cpputils::Data(testData.blocksize))->blockId();
auto block = blockStore->overwrite(blockId, backgroundData.copy());
EXPECT_EQ(testData.blocksize, block->size());
EXPECT_DATA_READS_AS(backgroundData, *block, 0, testData.blocksize);
}
void TestOverwriteSameSizeAndReadAfterLoading() {
auto blockId = blockStore->create(cpputils::Data(testData.blocksize))->blockId();
blockStore->overwrite(blockId, backgroundData.copy());
auto block = blockStore->load(blockId).value();
EXPECT_EQ(testData.blocksize, block->size());
EXPECT_DATA_READS_AS(backgroundData, *block, 0, testData.blocksize);
}
void TestOverwriteSmallerSizeAndReadImmediately() {
auto blockId = blockStore->create(cpputils::Data(testData.blocksize))->blockId();
auto block = blockStore->overwrite(blockId, foregroundData.copy());
EXPECT_EQ(testData.count, block->size());
EXPECT_DATA_READS_AS(foregroundData, *block, 0, testData.count);
}
void TestOverwriteSmallerSizeAndReadAfterLoading() {
auto blockId = blockStore->create(cpputils::Data(testData.blocksize))->blockId();
blockStore->overwrite(blockId, foregroundData.copy());
auto block = blockStore->load(blockId).value();
EXPECT_EQ(testData.count, block->size());
EXPECT_DATA_READS_AS(foregroundData, *block, 0, testData.count);
}
void TestOverwriteLargerSizeAndReadImmediately() {
auto blockId = blockStore->create(cpputils::Data(testData.count))->blockId();
auto block = blockStore->overwrite(blockId, backgroundData.copy());
EXPECT_EQ(testData.blocksize, block->size());
EXPECT_DATA_READS_AS(backgroundData, *block, 0, testData.blocksize);
}
void TestOverwriteLargerSizeAndReadAfterLoading() {
auto blockId = blockStore->create(cpputils::Data(testData.count))->blockId();
blockStore->overwrite(blockId, backgroundData.copy());
auto block = blockStore->load(blockId).value();
EXPECT_EQ(testData.blocksize, block->size());
EXPECT_DATA_READS_AS(backgroundData, *block, 0, testData.blocksize);
}
void TestOverwriteNonexistingAndReadImmediately() {
auto blockId = blockStore->createBlockId();
auto block = blockStore->overwrite(blockId, backgroundData.copy());
EXPECT_EQ(testData.blocksize, block->size());
EXPECT_DATA_READS_AS(backgroundData, *block, 0, testData.blocksize);
}
void TestOverwriteNonexistingAndReadAfterLoading() {
auto blockId = blockStore->createBlockId();
blockStore->overwrite(blockId, backgroundData.copy());
auto block = blockStore->load(blockId).value();
EXPECT_EQ(testData.blocksize, block->size());
EXPECT_DATA_READS_AS(backgroundData, *block, 0, testData.blocksize);
}
private:
cpputils::unique_ref<blockstore::BlockStore> blockStore;
DataRange testData;
cpputils::Data foregroundData;
cpputils::Data backgroundData;
blockstore::BlockId CreateBlockWriteToItAndReturnKey(const cpputils::Data &to_write) {
auto newblock = blockStore->create(cpputils::Data(testData.blocksize).FillWithZeroes());
newblock->write(to_write.data(), testData.offset, testData.count);
return newblock->blockId();
}
void EXPECT_DATA_READS_AS(const cpputils::Data &expected, const blockstore::Block &block, uint64_t offset, uint64_t count) {
cpputils::Data read(count);
std::memcpy(read.data(), static_cast<const uint8_t*>(block.data()) + offset, count);
EXPECT_EQ(expected, read);
}
void EXPECT_DATA_READS_AS_OUTSIDE_OF(const cpputils::Data &expected, const blockstore::Block &block, uint64_t start, uint64_t count) {
cpputils::Data begin(start);
cpputils::Data end(testData.blocksize - count - start);
std::memcpy(begin.data(), expected.data(), start);
std::memcpy(end.data(), expected.dataOffset(start+count), end.size());
EXPECT_DATA_READS_AS(begin, block, 0, start);
EXPECT_DATA_READS_AS(end, block, start + count, end.size());
}
void EXPECT_DATA_IS_ZEROES_OUTSIDE_OF(const blockstore::Block &block, uint64_t start, uint64_t count) {
cpputils::Data ZEROES(testData.blocksize);
ZEROES.FillWithZeroes();
EXPECT_DATA_READS_AS_OUTSIDE_OF(ZEROES, block, start, count);
}
};
inline std::vector<DataRange> DATA_RANGES() {
return {
DataRange{1024, 0, 1024}, // full size leaf, access beginning to end
DataRange{1024, 100, 1024 - 200}, // full size leaf, access middle to middle
DataRange{1024, 0, 1024 - 100}, // full size leaf, access beginning to middle
DataRange{1024, 100, 1024 - 100}, // full size leaf, access middle to end
DataRange{1024 - 100, 0, 1024 - 100}, // non-full size leaf, access beginning to end
DataRange{1024 - 100, 100, 1024 - 300}, // non-full size leaf, access middle to middle
DataRange{1024 - 100, 0, 1024 - 200}, // non-full size leaf, access beginning to middle
DataRange{1024 - 100, 100, 1024 - 200} // non-full size leaf, access middle to end
};
};
#define TYPED_TEST_P_FOR_ALL_DATA_RANGES(TestName) \
TYPED_TEST_P(BlockStoreTest, TestName) { \
for (auto dataRange: DATA_RANGES()) { \
BlockStoreDataParametrizedTest(this->fixture.createBlockStore(), dataRange) \
.Test##TestName(); \
} \
}
TYPED_TEST_P_FOR_ALL_DATA_RANGES(WriteAndReadImmediately);
TYPED_TEST_P_FOR_ALL_DATA_RANGES(WriteAndReadAfterLoading);
TYPED_TEST_P_FOR_ALL_DATA_RANGES(WriteTwiceAndRead);
TYPED_TEST_P_FOR_ALL_DATA_RANGES(OverwriteSameSizeAndReadImmediately);
TYPED_TEST_P_FOR_ALL_DATA_RANGES(OverwriteSameSizeAndReadAfterLoading);
TYPED_TEST_P_FOR_ALL_DATA_RANGES(OverwriteSmallerSizeAndReadImmediately);
TYPED_TEST_P_FOR_ALL_DATA_RANGES(OverwriteSmallerSizeAndReadAfterLoading);
TYPED_TEST_P_FOR_ALL_DATA_RANGES(OverwriteLargerSizeAndReadImmediately);
TYPED_TEST_P_FOR_ALL_DATA_RANGES(OverwriteLargerSizeAndReadAfterLoading);
TYPED_TEST_P_FOR_ALL_DATA_RANGES(OverwriteNonexistingAndReadImmediately);
TYPED_TEST_P_FOR_ALL_DATA_RANGES(OverwriteNonexistingAndReadAfterLoading);
#endif

View File

@ -1,164 +0,0 @@
#pragma once
#ifndef MESSMER_BLOCKSTORE_TEST_TESTUTILS_BLOCKSTORETEST_SIZE_H_
#define MESSMER_BLOCKSTORE_TEST_TESTUTILS_BLOCKSTORETEST_SIZE_H_
// This file is meant to be included by BlockStoreTest.h only
#include <cpp-utils/data/Data.h>
#include <cpp-utils/data/DataFixture.h>
class BlockStoreSizeParameterizedTest {
public:
BlockStoreSizeParameterizedTest(cpputils::unique_ref<blockstore::BlockStore> blockStore_, size_t size_): blockStore(std::move(blockStore_)), size(size_) {}
void TestCreatedBlockHasCorrectSize() {
auto block = CreateBlock();
EXPECT_EQ(size, block->size());
}
void TestLoadingUnchangedBlockHasCorrectSize() {
blockstore::BlockId blockId = CreateBlock()->blockId();
auto loaded_block = blockStore->load(blockId).value();
EXPECT_EQ(size, loaded_block->size());
}
void TestCreatedBlockData() {
cpputils::Data data = cpputils::DataFixture::generate(size);
auto block = blockStore->create(data);
EXPECT_EQ(0, std::memcmp(data.data(), block->data(), size));
}
void TestLoadingUnchangedBlockData() {
cpputils::Data data = cpputils::DataFixture::generate(size);
blockstore::BlockId blockId = blockStore->create(data)->blockId();
auto loaded_block = blockStore->load(blockId).value();
EXPECT_EQ(0, std::memcmp(data.data(), loaded_block->data(), size));
}
void TestLoadedBlockIsCorrect() {
cpputils::Data randomData = cpputils::DataFixture::generate(size);
auto loaded_block = StoreDataToBlockAndLoadIt(randomData);
EXPECT_EQ(size, loaded_block->size());
EXPECT_EQ(0, std::memcmp(randomData.data(), loaded_block->data(), size));
}
void TestLoadedBlockIsCorrectWhenLoadedDirectlyAfterFlushing() {
cpputils::Data randomData = cpputils::DataFixture::generate(size);
auto loaded_block = StoreDataToBlockAndLoadItDirectlyAfterFlushing(randomData);
EXPECT_EQ(size, loaded_block->size());
EXPECT_EQ(0, std::memcmp(randomData.data(), loaded_block->data(), size));
}
void TestAfterCreate_FlushingDoesntChangeBlock() {
cpputils::Data randomData = cpputils::DataFixture::generate(size);
auto block = CreateBlock();
WriteDataToBlock(block.get(), randomData);
block->flush();
EXPECT_BLOCK_DATA_CORRECT(*block, randomData);
}
void TestAfterLoad_FlushingDoesntChangeBlock() {
cpputils::Data randomData = cpputils::DataFixture::generate(size);
auto block = CreateBlockAndLoadIt();
WriteDataToBlock(block.get(), randomData);
block->flush();
EXPECT_BLOCK_DATA_CORRECT(*block, randomData);
}
void TestAfterCreate_FlushesWhenDestructed() {
cpputils::Data randomData = cpputils::DataFixture::generate(size);
blockstore::BlockId blockId = blockstore::BlockId::Null();
{
auto block = blockStore->create(cpputils::Data(size));
blockId = block->blockId();
WriteDataToBlock(block.get(), randomData);
}
auto loaded_block = blockStore->load(blockId).value();
EXPECT_BLOCK_DATA_CORRECT(*loaded_block, randomData);
}
void TestAfterLoad_FlushesWhenDestructed() {
cpputils::Data randomData = cpputils::DataFixture::generate(size);
blockstore::BlockId blockId = blockstore::BlockId::Null();
{
blockId = CreateBlock()->blockId();
auto block = blockStore->load(blockId).value();
WriteDataToBlock(block.get(), randomData);
}
auto loaded_block = blockStore->load(blockId).value();
EXPECT_BLOCK_DATA_CORRECT(*loaded_block, randomData);
}
void TestLoadNonExistingBlock() {
EXPECT_EQ(boost::none, blockStore->load(blockId));
}
private:
const blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
cpputils::unique_ref<blockstore::BlockStore> blockStore;
size_t size;
cpputils::Data ZEROES(size_t size) {
cpputils::Data ZEROES(size);
ZEROES.FillWithZeroes();
return ZEROES;
}
cpputils::unique_ref<blockstore::Block> StoreDataToBlockAndLoadIt(const cpputils::Data &data) {
blockstore::BlockId blockId = StoreDataToBlockAndGetKey(data);
return blockStore->load(blockId).value();
}
blockstore::BlockId StoreDataToBlockAndGetKey(const cpputils::Data &data) {
return blockStore->create(data)->blockId();
}
cpputils::unique_ref<blockstore::Block> StoreDataToBlockAndLoadItDirectlyAfterFlushing(const cpputils::Data &data) {
auto block = blockStore->create(data);
block->flush();
return blockStore->load(block->blockId()).value();
}
cpputils::unique_ref<blockstore::Block> CreateBlockAndLoadIt() {
blockstore::BlockId blockId = CreateBlock()->blockId();
return blockStore->load(blockId).value();
}
cpputils::unique_ref<blockstore::Block> CreateBlock() {
return blockStore->create(cpputils::Data(size));
}
void WriteDataToBlock(blockstore::Block *block, const cpputils::Data &randomData) {
block->write(randomData.data(), 0, randomData.size());
}
void EXPECT_BLOCK_DATA_CORRECT(const blockstore::Block &block, const cpputils::Data &randomData) {
EXPECT_EQ(randomData.size(), block.size());
EXPECT_EQ(0, std::memcmp(randomData.data(), block.data(), randomData.size()));
}
};
constexpr std::array<size_t, 5> SIZES = {{0, 1, 1024, 4096, 10*1024*1024}};
#define TYPED_TEST_P_FOR_ALL_SIZES(TestName) \
TYPED_TEST_P(BlockStoreTest, TestName) { \
for (auto size: SIZES) { \
BlockStoreSizeParameterizedTest(this->fixture.createBlockStore(), size) \
.Test##TestName(); \
} \
} \
TYPED_TEST_P_FOR_ALL_SIZES(CreatedBlockHasCorrectSize);
TYPED_TEST_P_FOR_ALL_SIZES(LoadingUnchangedBlockHasCorrectSize);
TYPED_TEST_P_FOR_ALL_SIZES(CreatedBlockData);
TYPED_TEST_P_FOR_ALL_SIZES(LoadingUnchangedBlockData);
TYPED_TEST_P_FOR_ALL_SIZES(LoadedBlockIsCorrect);
//TYPED_TEST_P_FOR_ALL_SIZES(LoadedBlockIsCorrectWhenLoadedDirectlyAfterFlushing);
TYPED_TEST_P_FOR_ALL_SIZES(AfterCreate_FlushingDoesntChangeBlock);
TYPED_TEST_P_FOR_ALL_SIZES(AfterLoad_FlushingDoesntChangeBlock);
TYPED_TEST_P_FOR_ALL_SIZES(AfterCreate_FlushesWhenDestructed);
TYPED_TEST_P_FOR_ALL_SIZES(AfterLoad_FlushesWhenDestructed);
TYPED_TEST_P_FOR_ALL_SIZES(LoadNonExistingBlock);
#endif

View File

@ -1,21 +0,0 @@
#pragma once
#ifndef MESSMER_BLOCKSTORE_TEST_TESTUTILS_GTESTPRINTERS_H_
#define MESSMER_BLOCKSTORE_TEST_TESTUTILS_GTESTPRINTERS_H_
namespace cpputils {
inline void PrintTo(const Data& /*data*/, ::std::ostream* os) {
*os << "cpputils::Data";
}
inline void PrintTo(const boost::optional<Data>& data, ::std::ostream* os) {
if (data == boost::none) {
*os << "none";
} else {
PrintTo(*data, os);
}
}
}
#endif

View File

@ -1,111 +0,0 @@
#include "blockstore/implementations/testfake/FakeBlockStore.h"
#include <cpp-utils/data/DataFixture.h>
#include "blockstore/utils/BlockStoreUtils.h"
#include <gtest/gtest.h>
#include <memory>
using ::testing::Test;
using cpputils::Data;
using cpputils::DataFixture;
using cpputils::unique_ref;
using cpputils::make_unique_ref;
using namespace blockstore;
using namespace blockstore::utils;
using blockstore::testfake::FakeBlockStore;
class BlockStoreUtilsTest: public Test {
public:
unsigned int SIZE = 1024 * 1024;
BlockStoreUtilsTest():
ZEROES(SIZE),
dataFixture(DataFixture::generate(SIZE)),
blockStore(make_unique_ref<FakeBlockStore>()) {
ZEROES.FillWithZeroes();
}
Data ZEROES;
Data dataFixture;
unique_ref<BlockStore> blockStore;
};
TEST_F(BlockStoreUtilsTest, FillWithZeroes) {
auto block = blockStore->create(Data(SIZE));
block->write(dataFixture.data(), 0, SIZE);
EXPECT_NE(0, std::memcmp(ZEROES.data(), block->data(), SIZE));
fillWithZeroes(block.get());
EXPECT_EQ(0, std::memcmp(ZEROES.data(), block->data(), SIZE));
}
class BlockStoreUtilsTest_CopyToNewBlock: public BlockStoreUtilsTest {};
TEST_F(BlockStoreUtilsTest_CopyToNewBlock, CopyEmptyBlock) {
auto block = blockStore->create(Data(0));
auto block2 = copyToNewBlock(blockStore.get(), *block);
EXPECT_EQ(0u, block2->size());
}
TEST_F(BlockStoreUtilsTest_CopyToNewBlock, CopyZeroBlock) {
auto block = blockStore->create(ZEROES);
auto block2 = copyToNewBlock(blockStore.get(), *block);
EXPECT_EQ(SIZE, block2->size());
EXPECT_EQ(0, std::memcmp(ZEROES.data(), block2->data(), SIZE));
}
TEST_F(BlockStoreUtilsTest_CopyToNewBlock, CopyDataBlock) {
auto block = blockStore->create(Data(SIZE));
block->write(dataFixture.data(), 0, SIZE);
auto block2 = copyToNewBlock(blockStore.get(), *block);
EXPECT_EQ(SIZE, block2->size());
EXPECT_EQ(0, std::memcmp(dataFixture.data(), block2->data(), SIZE));
}
TEST_F(BlockStoreUtilsTest_CopyToNewBlock, OriginalBlockUnchanged) {
auto block = blockStore->create(Data(SIZE));
block->write(dataFixture.data(), 0, SIZE);
auto block2 = copyToNewBlock(blockStore.get(), *block);
EXPECT_EQ(SIZE, block->size());
EXPECT_EQ(0, std::memcmp(dataFixture.data(), block->data(), SIZE));
}
class BlockStoreUtilsTest_CopyToExistingBlock: public BlockStoreUtilsTest {};
TEST_F(BlockStoreUtilsTest_CopyToExistingBlock, CopyEmptyBlock) {
auto block = blockStore->create(Data(0));
auto block2 = blockStore->create(Data(0));
copyTo(block2.get(), *block);
}
TEST_F(BlockStoreUtilsTest_CopyToExistingBlock, CopyZeroBlock) {
auto block = blockStore->create(ZEROES);
auto block2 = blockStore->create(Data(SIZE));
block2->write(dataFixture.data(), 0, SIZE);
copyTo(block2.get(), *block);
EXPECT_EQ(0, std::memcmp(ZEROES.data(), block2->data(), SIZE));
}
TEST_F(BlockStoreUtilsTest_CopyToExistingBlock, CopyDataBlock) {
auto block = blockStore->create(Data(SIZE));
block->write(dataFixture.data(), 0, SIZE);
auto block2 = blockStore->create(Data(SIZE));
copyTo(block2.get(), *block);
EXPECT_EQ(0, std::memcmp(dataFixture.data(), block2->data(), SIZE));
}
TEST_F(BlockStoreUtilsTest_CopyToExistingBlock, OriginalBlockUnchanged) {
auto block = blockStore->create(Data(SIZE));
block->write(dataFixture.data(), 0, SIZE);
auto block2 = blockStore->create(Data(SIZE));
copyTo(block2.get(), *block);
EXPECT_EQ(0, std::memcmp(dataFixture.data(), block->data(), SIZE));
}

View File

@ -1,79 +0,0 @@
project (cpp-utils-test)
set(SOURCES
crypto/symmetric/CipherTest.cpp
crypto/kdf/SCryptTest.cpp
crypto/kdf/SCryptParametersTest.cpp
crypto/hash/HashTest.cpp
MacrosIncludeTest.cpp
pointer/unique_ref_test.cpp
pointer/cast_include_test.cpp
pointer/cast_test.cpp
pointer/unique_ref_boost_optional_gtest_workaround_include_test.cpp
pointer/optional_ownership_ptr_include_test.cpp
pointer/optional_ownership_ptr_test.cpp
pointer/unique_ref_include_test.cpp
process/daemonize_include_test.cpp
process/subprocess_include_test.cpp
process/SubprocessTest.cpp
process/SignalCatcherTest.cpp
process/SignalHandlerTest.cpp
tempfile/TempFileTest.cpp
tempfile/TempFileIncludeTest.cpp
tempfile/TempDirIncludeTest.cpp
tempfile/TempDirTest.cpp
network/CurlHttpClientTest.cpp
network/FakeHttpClientTest.cpp
io/DontEchoStdinToStdoutRAIITest.cpp
io/ConsoleIncludeTest.cpp
io/ConsoleTest_AskYesNo.cpp
io/ConsoleTest_Print.cpp
io/ConsoleTest_Ask.cpp
io/ConsoleTest_AskPassword.cpp
io/ProgressBarTest.cpp
random/RandomIncludeTest.cpp
lock/LockPoolIncludeTest.cpp
lock/ConditionBarrierIncludeTest.cpp
lock/MutexPoolLockIncludeTest.cpp
data/FixedSizeDataTest.cpp
data/DataFixtureIncludeTest.cpp
data/DataFixtureTest.cpp
data/DataTest.cpp
data/FixedSizeDataIncludeTest.cpp
data/SerializationHelperTest.cpp
data/DataIncludeTest.cpp
logging/LoggingLevelTest.cpp
logging/LoggerTest.cpp
logging/LoggingTest.cpp
logging/LoggerIncludeTest.cpp
logging/LoggingIncludeTest.cpp
assert/assert_release_test.cpp
assert/backtrace_test.cpp
assert/assert_debug_test.cpp
system/GetTotalMemoryTest.cpp
system/TimeTest.cpp
system/PathTest.cpp
system/FiletimeTest.cpp
system/MemoryTest.cpp
system/HomedirTest.cpp
system/EnvTest.cpp
thread/debugging_test.cpp
thread/LeftRightTest.cpp
value_type/ValueTypeTest.cpp
either_test.cpp
)
add_executable(${PROJECT_NAME}_exit_status process/exit_status.cpp)
target_activate_cpp14(${PROJECT_NAME}_exit_status)
add_executable(${PROJECT_NAME}_exit_signal assert/exit_signal.cpp)
target_activate_cpp14(${PROJECT_NAME}_exit_signal)
target_link_libraries(${PROJECT_NAME}_exit_signal cpp-utils)
add_executable(${PROJECT_NAME} ${SOURCES})
target_link_libraries(${PROJECT_NAME} my-gtest-main googletest cpp-utils)
add_dependencies(${PROJECT_NAME} ${PROJECT_NAME}_exit_status ${PROJECT_NAME}_exit_signal)
add_test(${PROJECT_NAME} ${PROJECT_NAME})
target_enable_style_warnings(${PROJECT_NAME})
target_activate_cpp14(${PROJECT_NAME})

View File

@ -1,3 +0,0 @@
#include "cpp-utils/macros.h"
// Test that macros.h can be included without needing additional dependencies

View File

@ -1,58 +0,0 @@
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#ifdef NDEBUG
#define REAL_NDEBUG_
#endif
//Include the ASSERT macro for a debug build
#undef NDEBUG
#include "cpp-utils/assert/assert.h"
TEST(AssertTest_DebugBuild, DoesntDieIfTrue) {
ASSERT(true, "bla");
}
TEST(AssertTest_DebugBuild, DiesIfFalse) {
EXPECT_DEATH(
ASSERT(false, "bla"),
""
);
}
TEST(AssertTest_DebugBuild, whenDisablingAbort_thenThrowsIfFalse) {
cpputils::_assert::DisableAbortOnFailedAssertionRAII _disableAbort;
EXPECT_THROW(
ASSERT(false, "bla"),
cpputils::AssertFailed
);
}
TEST(AssertTest_DebugBuild, AssertMessage) {
#if defined(_MSC_VER)
constexpr const char* EXPECTED = R"(Assertion \[2==5\] failed in .*assert_debug_test.cpp:\d+: my message)";
#else
constexpr const char* EXPECTED = R"(Assertion \[2==5\] failed in .*assert_debug_test.cpp:[0-9]+: my message)";
#endif
EXPECT_DEATH(
ASSERT(2==5, "my message"),
EXPECTED
);
}
#if !(defined(_MSC_VER) && defined(REAL_NDEBUG_))
TEST(AssertTest_DebugBuild, AssertMessageContainsBacktrace) {
EXPECT_DEATH(
ASSERT(2==5, "my message"),
"cpputils::"
);
}
#else
TEST(AssertTest_DebugBuild, AssertMessageContainsBacktrace) {
EXPECT_DEATH(
ASSERT(2==5, "my message"),
"#1"
);
}
#endif

View File

@ -1,64 +0,0 @@
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <regex>
#ifdef NDEBUG
#define REAL_NDEBUG_
#endif
//Include the ASSERT macro for a release build
#ifndef NDEBUG
#define NDEBUG 1
#endif
#include "cpp-utils/assert/assert.h"
using testing::HasSubstr;
TEST(AssertTest_ReleaseBuild, DoesntThrowIfTrue) {
ASSERT(true, "bla");
}
TEST(AssertTest_ReleaseBuild, ThrowsIfFalse) {
EXPECT_THROW(
ASSERT(false, "bla"),
cpputils::AssertFailed
);
}
TEST(AssertTest_ReleaseBuild, AssertMessage) {
try {
ASSERT(2==5, "my message");
FAIL();
} catch (const cpputils::AssertFailed &e) {
std::string msg = e.what();
// For some reason, the following doesn't seem to work in MSVC. Possibly because of the multiline string?
/*EXPECT_THAT(e.what(), MatchesRegex(
R"(Assertion \[2==5\] failed in .*assert_release_test.cpp:27: my message)"
));*/
EXPECT_TRUE(std::regex_search(e.what(), std::regex(R"(Assertion \[2==5\] failed in .*assert_release_test.cpp:30: my message)")));
}
}
#if !(defined(_MSC_VER) && defined(REAL_NDEBUG_))
TEST(AssertTest_ReleaseBuild, AssertMessageContainsBacktrace) {
try {
ASSERT(2==5, "my message");
FAIL();
} catch (const cpputils::AssertFailed &e) {
EXPECT_THAT(e.what(), HasSubstr(
"cpputils::"
));
}
}
#else
TEST(AssertTest_ReleaseBuild, AssertMessageContainsBacktrace) {
try {
ASSERT(2==5, "my message");
FAIL();
} catch (const cpputils::AssertFailed &e) {
EXPECT_THAT(e.what(), HasSubstr(
"#1"
));
}
}
#endif

View File

@ -1,230 +0,0 @@
#include <gmock/gmock.h>
#include <csignal>
#include "cpp-utils/assert/backtrace.h"
#include "cpp-utils/process/subprocess.h"
#include <boost/filesystem.hpp>
#include "my-gtest-main.h"
using std::string;
using testing::HasSubstr;
namespace bf = boost::filesystem;
namespace
{
std::string call_process_exiting_with(const std::string &kind, const std::string &signal = "")
{
#if defined(_MSC_VER)
auto executable = bf::canonical(get_executable().parent_path()) / "cpp-utils-test_exit_signal.exe";
#else
auto executable = bf::canonical(get_executable().parent_path()) / "cpp-utils-test_exit_signal";
#endif
if (!bf::exists(executable))
{
throw std::runtime_error(executable.string() + " not found.");
}
auto result = cpputils::Subprocess::call(executable, {kind, signal}, "");
return result.output_stderr;
}
}
#if !(defined(_MSC_VER) && defined(NDEBUG))
TEST(BacktraceTest, ContainsTopLevelLine)
{
string backtrace = cpputils::backtrace();
EXPECT_THAT(backtrace, HasSubstr("BacktraceTest"));
EXPECT_THAT(backtrace, HasSubstr("ContainsTopLevelLine"));
}
#endif
namespace
{
std::string call_process_exiting_with_nullptr_violation()
{
return call_process_exiting_with("nullptr");
}
std::string call_process_exiting_with_exception(const std::string &message)
{
return call_process_exiting_with("exception", message);
}
}
#if defined(_MSC_VER)
#include <Windows.h>
namespace
{
std::string call_process_exiting_with_sigsegv()
{
return call_process_exiting_with("signal", std::to_string(EXCEPTION_ACCESS_VIOLATION));
}
std::string call_process_exiting_with_sigill()
{
return call_process_exiting_with("signal", std::to_string(EXCEPTION_ILLEGAL_INSTRUCTION));
}
std::string call_process_exiting_with_code(DWORD code)
{
return call_process_exiting_with("signal", std::to_string(code));
}
}
#else
namespace
{
std::string call_process_exiting_with_sigsegv()
{
return call_process_exiting_with("signal", std::to_string(SIGSEGV));
}
std::string call_process_exiting_with_sigabrt()
{
return call_process_exiting_with("signal", std::to_string(SIGABRT));
}
std::string call_process_exiting_with_sigill()
{
return call_process_exiting_with("signal", std::to_string(SIGILL));
}
}
#endif
TEST(BacktraceTest, DoesntCrashOnCaughtException)
{
// This is needed to make sure we don't use some kind of vectored exception handler on Windows
// that ignores the call stack and always jumps on when an exception happens.
cpputils::showBacktraceOnCrash();
try
{
throw std::logic_error("exception");
}
catch (const std::logic_error &e)
{
// intentionally empty
}
}
#if !(defined(_MSC_VER) && defined(NDEBUG))
TEST(BacktraceTest, ContainsBacktrace)
{
string backtrace = cpputils::backtrace();
#if defined(_MSC_VER)
EXPECT_THAT(backtrace, HasSubstr("testing::Test::Run"));
#else
EXPECT_THAT(backtrace, HasSubstr("BacktraceTest_ContainsBacktrace_Test::TestBody"));
#endif
}
TEST(BacktraceTest, ShowBacktraceOnNullptrAccess)
{
auto output = call_process_exiting_with_nullptr_violation();
#if defined(_MSC_VER)
EXPECT_THAT(output, HasSubstr("handle_exit_signal"));
#else
EXPECT_THAT(output, HasSubstr("cpputils::backtrace"));
#endif
}
TEST(BacktraceTest, ShowBacktraceOnSigSegv)
{
auto output = call_process_exiting_with_sigsegv();
#if defined(_MSC_VER)
EXPECT_THAT(output, HasSubstr("handle_exit_signal"));
#else
EXPECT_THAT(output, HasSubstr("cpputils::backtrace"));
#endif
}
TEST(BacktraceTest, ShowBacktraceOnUnhandledException)
{
auto output = call_process_exiting_with_exception("my_exception_message");
#if defined(_MSC_VER)
EXPECT_THAT(output, HasSubstr("handle_exit_signal"));
#else
EXPECT_THAT(output, HasSubstr("cpputils::backtrace"));
#endif
}
TEST(BacktraceTest, ShowBacktraceOnSigIll)
{
auto output = call_process_exiting_with_sigill();
#if defined(_MSC_VER)
EXPECT_THAT(output, HasSubstr("handle_exit_signal"));
#else
EXPECT_THAT(output, HasSubstr("cpputils::backtrace"));
#endif
}
#else
TEST(BacktraceTest, ContainsBacktrace)
{
string backtrace = cpputils::backtrace();
EXPECT_THAT(backtrace, HasSubstr("#0"));
}
TEST(BacktraceTest, ShowBacktraceOnNullptrAccess)
{
auto output = call_process_exiting_with_nullptr_violation();
EXPECT_THAT(output, HasSubstr("#1"));
}
TEST(BacktraceTest, ShowBacktraceOnSigSegv)
{
auto output = call_process_exiting_with_sigsegv();
EXPECT_THAT(output, HasSubstr("#1"));
}
TEST(BacktraceTest, ShowBacktraceOnUnhandledException)
{
auto output = call_process_exiting_with_exception("my_exception_message");
EXPECT_THAT(output, HasSubstr("#1"));
}
TEST(BacktraceTest, ShowBacktraceOnSigIll)
{
auto output = call_process_exiting_with_sigill();
EXPECT_THAT(output, HasSubstr("#1"));
}
#endif
#if !defined(_MSC_VER)
TEST(BacktraceTest, ShowBacktraceOnSigAbrt)
{
auto output = call_process_exiting_with_sigabrt();
EXPECT_THAT(output, HasSubstr("cpputils::backtrace"));
}
TEST(BacktraceTest, ShowBacktraceOnSigAbrt_ShowsCorrectSignalName)
{
auto output = call_process_exiting_with_sigabrt();
EXPECT_THAT(output, HasSubstr("SIGABRT"));
}
#endif
#if !defined(_MSC_VER)
constexpr const char *sigsegv_message = "SIGSEGV";
constexpr const char *sigill_message = "SIGILL";
#else
constexpr const char *sigsegv_message = "EXCEPTION_ACCESS_VIOLATION";
constexpr const char *sigill_message = "EXCEPTION_ILLEGAL_INSTRUCTION";
#endif
TEST(BacktraceTest, ShowBacktraceOnSigSegv_ShowsCorrectSignalName)
{
auto output = call_process_exiting_with_sigsegv();
EXPECT_THAT(output, HasSubstr(sigsegv_message));
}
TEST(BacktraceTest, ShowBacktraceOnSigIll_ShowsCorrectSignalName)
{
auto output = call_process_exiting_with_sigill();
EXPECT_THAT(output, HasSubstr(sigill_message));
}
#if !defined(_MSC_VER)
TEST(BacktraceTest, ShowBacktraceOnUnhandledException_ShowsCorrectExceptionMessage)
{
auto output = call_process_exiting_with_exception("my_exception_message");
EXPECT_THAT(output, HasSubstr("my_exception_message"));
}
#endif
#if defined(_MSC_VER)
TEST(BacktraceTest, UnknownCode_ShowsCorrectSignalName)
{
auto output = call_process_exiting_with_code(0x1234567);
EXPECT_THAT(output, HasSubstr("UNKNOWN_CODE(0x1234567)"));
}
#endif

View File

@ -1,36 +0,0 @@
#include <cpp-utils/assert/backtrace.h>
#include <csignal>
#include <stdexcept>
#if defined(_MSC_VER)
#include <Windows.h>
#endif
void handle_exit_signal(char **argv) {
const std::string kind = argv[1];
if (kind == "exception") {
throw std::logic_error(argv[2]);
} else if (kind == "nullptr") {
int* ptr = nullptr;
*ptr = 5; // NOLINT
} else if (kind == "signal") {
#if defined(_MSC_VER)
DWORD code = std::atoll(argv[2]);
::RaiseException(code, EXCEPTION_NONCONTINUABLE, 0, NULL);
#else
int code = static_cast<int>(std::strtol(argv[2], nullptr, 10));
::raise(code);
#endif
}
}
int main(int /*argc*/, char* argv[]) {
cpputils::showBacktraceOnCrash();
#if defined(_MSC_VER)
// don't show windows error box
_set_abort_behavior(0, _WRITE_ABORT_MSG);
#endif
handle_exit_signal(argv);
return 0;
}

View File

@ -1,45 +0,0 @@
#include <gtest/gtest.h>
#include <cpp-utils/crypto/hash/Hash.h>
#include <cpp-utils/data/DataFixture.h>
using namespace cpputils::hash;
using cpputils::DataFixture;
using cpputils::Data;
TEST(HashTest, generateSalt_isIndeterministic) {
EXPECT_NE(generateSalt(), generateSalt());
}
TEST(HashTest, hash_setsSaltCorrectly) {
Salt salt = generateSalt();
Data data = DataFixture::generate(1024);
EXPECT_EQ(salt, hash(data, salt).salt);
}
TEST(HashTest, hash_isDeterministicWithSameDataSameSalt) {
Salt salt = generateSalt();
Data data = DataFixture::generate(1024);
EXPECT_EQ(hash(data, salt).digest, hash(data, salt).digest);
}
TEST(HashTest, hash_isIndeterministicWithSameDataDifferentSalt) {
Salt salt1 = generateSalt();
Salt salt2 = generateSalt();
Data data = DataFixture::generate(1024);
EXPECT_NE(hash(data, salt1).digest, hash(data, salt2).digest);
}
TEST(HashTest, hash_isIndeterministicWithDifferentDataSameSalt) {
Salt salt = generateSalt();
Data data1 = DataFixture::generate(1024, 1);
Data data2 = DataFixture::generate(1024, 2);
EXPECT_NE(hash(data1, salt).digest, hash(data2, salt).digest);
}
TEST(HashTest, hash_isIndeterministicWithDifferentDataDifferentSalt) {
Salt salt1 = generateSalt();
Salt salt2 = generateSalt();
Data data1 = DataFixture::generate(1024, 1);
Data data2 = DataFixture::generate(1024, 2);
EXPECT_NE(hash(data1, salt1).digest, hash(data2, salt2).digest);
}

View File

@ -1,83 +0,0 @@
#include <gtest/gtest.h>
#include <cpp-utils/crypto/kdf/SCryptParameters.h>
#include <cpp-utils/data/DataFixture.h>
#include <sstream>
using namespace cpputils;
class SCryptParametersTest : public ::testing::Test {
public:
SCryptParameters SaveAndLoad(const SCryptParameters &source) {
Data serialized = source.serialize();
return SCryptParameters::deserialize(serialized);
}
};
TEST_F(SCryptParametersTest, Salt) {
SCryptParameters cfg(DataFixture::generate(32), 0, 0, 0);
EXPECT_EQ(DataFixture::generate(32), cfg.salt());
}
TEST_F(SCryptParametersTest, Salt_Move) {
SCryptParameters cfg(DataFixture::generate(32), 0, 0, 0);
SCryptParameters moved = std::move(cfg);
EXPECT_EQ(DataFixture::generate(32), moved.salt());
}
TEST_F(SCryptParametersTest, Salt_SaveAndLoad) {
SCryptParameters cfg(DataFixture::generate(32), 0, 0, 0);
SCryptParameters loaded = SaveAndLoad(cfg);
EXPECT_EQ(DataFixture::generate(32), loaded.salt());
}
TEST_F(SCryptParametersTest, N) {
SCryptParameters cfg(Data(0), 1024, 0, 0);
EXPECT_EQ(1024u, cfg.n());
}
TEST_F(SCryptParametersTest, N_Move) {
SCryptParameters cfg(Data(0), 1024, 0, 0);
SCryptParameters moved = std::move(cfg);
EXPECT_EQ(1024u, moved.n());
}
TEST_F(SCryptParametersTest, N_SaveAndLoad) {
SCryptParameters cfg(Data(0), 1024, 0, 0);
SCryptParameters loaded = SaveAndLoad(cfg);
EXPECT_EQ(1024u, loaded.n());
}
TEST_F(SCryptParametersTest, r) {
SCryptParameters cfg(Data(0), 0, 8, 0);
EXPECT_EQ(8u, cfg.r());
}
TEST_F(SCryptParametersTest, r_Move) {
SCryptParameters cfg(Data(0), 0, 8, 0);
SCryptParameters moved = std::move(cfg);
EXPECT_EQ(8u, moved.r());
}
TEST_F(SCryptParametersTest, r_SaveAndLoad) {
SCryptParameters cfg(Data(0), 0, 8, 0);
SCryptParameters loaded = SaveAndLoad(cfg);
EXPECT_EQ(8u, loaded.r());
}
TEST_F(SCryptParametersTest, p) {
SCryptParameters cfg(Data(0), 0, 0, 16);
EXPECT_EQ(16u, cfg.p());
}
TEST_F(SCryptParametersTest, p_Move) {
SCryptParameters cfg(Data(0), 0, 0, 16);
SCryptParameters moved = std::move(cfg);
EXPECT_EQ(16u, moved.p());
}
TEST_F(SCryptParametersTest, p_SaveAndLoad) {
SCryptParameters cfg(Data(0), 0, 0, 16);
SCryptParameters loaded = SaveAndLoad(cfg);
EXPECT_EQ(16u, loaded.p());
}

View File

@ -1,68 +0,0 @@
#include <gtest/gtest.h>
#include "cpp-utils/crypto/kdf/Scrypt.h"
using namespace cpputils;
using std::string;
class SCryptTest : public ::testing::Test {
public:
bool keyEquals(const EncryptionKey& lhs, const EncryptionKey& rhs) {
ASSERT(lhs.binaryLength() == rhs.binaryLength(), "Keys must have equal size to be comparable");
return 0 == std::memcmp(lhs.data(), rhs.data(), lhs.binaryLength());
}
};
TEST_F(SCryptTest, GeneratedKeyIsReproductible_448) {
SCrypt scrypt(SCrypt::TestSettings);
auto derivedKey = scrypt.deriveNewKey(56, "mypassword");
auto rederivedKey = scrypt.deriveExistingKey(56, "mypassword", derivedKey.kdfParameters);
EXPECT_TRUE(keyEquals(derivedKey.key, rederivedKey));
}
TEST_F(SCryptTest, GeneratedKeyIsReproductible_256) {
SCrypt scrypt(SCrypt::TestSettings);
auto derivedKey = scrypt.deriveNewKey(32, "mypassword");
auto rederivedKey = scrypt.deriveExistingKey(32, "mypassword", derivedKey.kdfParameters);
EXPECT_TRUE(keyEquals(derivedKey.key, rederivedKey));
}
TEST_F(SCryptTest, GeneratedKeyIsReproductible_128) {
SCrypt scrypt(SCrypt::TestSettings);
auto derivedKey = scrypt.deriveNewKey(16, "mypassword");
auto rederivedKey = scrypt.deriveExistingKey(16, "mypassword", derivedKey.kdfParameters);
EXPECT_TRUE(keyEquals(derivedKey.key, rederivedKey));
}
TEST_F(SCryptTest, GeneratedKeyIsReproductible_DefaultSettings) {
SCrypt scrypt(SCrypt::TestSettings);
auto derivedKey = scrypt.deriveNewKey(16, "mypassword");
auto rederivedKey = scrypt.deriveExistingKey(16, "mypassword", derivedKey.kdfParameters);
EXPECT_TRUE(keyEquals(derivedKey.key, rederivedKey));
}
TEST_F(SCryptTest, DifferentPasswordResultsInDifferentKey) {
SCrypt scrypt(SCrypt::TestSettings);
auto derivedKey = scrypt.deriveNewKey(16, "mypassword");
auto rederivedKey = scrypt.deriveExistingKey(16, "mypassword2", derivedKey.kdfParameters);
EXPECT_FALSE(keyEquals(derivedKey.key, rederivedKey));
}
TEST_F(SCryptTest, UsesCorrectSettings) {
SCrypt scrypt(SCrypt::TestSettings);
auto derivedKey = scrypt.deriveNewKey(16, "mypassword");
auto parameters = SCryptParameters::deserialize(derivedKey.kdfParameters);
EXPECT_EQ(SCrypt::TestSettings.SALT_LEN, parameters.salt().size());
EXPECT_EQ(SCrypt::TestSettings.N, parameters.n());
EXPECT_EQ(SCrypt::TestSettings.r, parameters.r());
EXPECT_EQ(SCrypt::TestSettings.p, parameters.p());
}
TEST_F(SCryptTest, UsesCorrectDefaultSettings) {
SCrypt scrypt(SCrypt::DefaultSettings);
auto derivedKey = scrypt.deriveNewKey(16, "mypassword");
auto parameters = SCryptParameters::deserialize(derivedKey.kdfParameters);
EXPECT_EQ(SCrypt::DefaultSettings.SALT_LEN, parameters.salt().size());
EXPECT_EQ(SCrypt::DefaultSettings.N, parameters.n());
EXPECT_EQ(SCrypt::DefaultSettings.r, parameters.r());
EXPECT_EQ(SCrypt::DefaultSettings.p, parameters.p());
}

View File

@ -1,298 +0,0 @@
#include <gtest/gtest.h>
#include "cpp-utils/crypto/symmetric/Cipher.h"
#include "cpp-utils/crypto/symmetric/ciphers.h"
#include "cpp-utils/crypto/symmetric/testutils/FakeAuthenticatedCipher.h"
#include "cpp-utils/data/DataFixture.h"
#include "cpp-utils/data/Data.h"
#include <boost/optional/optional_io.hpp>
using namespace cpputils;
using std::string;
template<class Cipher>
class CipherTest: public ::testing::Test {
public:
BOOST_CONCEPT_ASSERT((CipherConcept<Cipher>));
typename Cipher::EncryptionKey encKey = createKeyFixture();
static typename Cipher::EncryptionKey createKeyFixture(int seed = 0) {
Data data = DataFixture::generate(Cipher::KEYSIZE, seed);
return Cipher::EncryptionKey::FromString(data.ToString());
}
void CheckEncryptThenDecryptIsIdentity(const Data &plaintext) {
Data ciphertext = Encrypt(plaintext);
Data decrypted = Decrypt(ciphertext);
EXPECT_EQ(plaintext, decrypted);
}
void CheckEncryptIsIndeterministic(const Data &plaintext) {
Data ciphertext = Encrypt(plaintext);
Data ciphertext2 = Encrypt(plaintext);
EXPECT_NE(ciphertext, ciphertext2);
}
void CheckEncryptedSize(const Data &plaintext) {
Data ciphertext = Encrypt(plaintext);
EXPECT_EQ(Cipher::ciphertextSize(plaintext.size()), ciphertext.size());
}
void ExpectDoesntDecrypt(const Data &ciphertext) {
auto decrypted = Cipher::decrypt(static_cast<const CryptoPP::byte*>(ciphertext.data()), ciphertext.size(), this->encKey);
EXPECT_FALSE(decrypted);
}
Data Encrypt(const Data &plaintext) {
return Cipher::encrypt(static_cast<const CryptoPP::byte*>(plaintext.data()), plaintext.size(), this->encKey);
}
Data Decrypt(const Data &ciphertext) {
return Cipher::decrypt(static_cast<const CryptoPP::byte*>(ciphertext.data()), ciphertext.size(), this->encKey).value();
}
static Data CreateZeroes(unsigned int size) {
return Data(size).FillWithZeroes();
}
static Data CreateData(unsigned int size, unsigned int seed = 0) {
return DataFixture::generate(size, seed);
}
};
TYPED_TEST_SUITE_P(CipherTest);
constexpr std::array<unsigned int, 7> SIZES = {{0, 1, 100, 1024, 5000, 1048576, 20971520}};
TYPED_TEST_P(CipherTest, Size) {
for (auto size: SIZES) {
EXPECT_EQ(size, TypeParam::ciphertextSize(TypeParam::plaintextSize(size)));
EXPECT_EQ(size, TypeParam::plaintextSize(TypeParam::ciphertextSize(size)));
}
}
TYPED_TEST_P(CipherTest, EncryptThenDecrypt_Zeroes) {
for (auto size: SIZES) {
Data plaintext = this->CreateZeroes(size);
this->CheckEncryptThenDecryptIsIdentity(plaintext);
}
}
TYPED_TEST_P(CipherTest, EncryptThenDecrypt_Data) {
for (auto size: SIZES) {
Data plaintext = this->CreateData(size);
this->CheckEncryptThenDecryptIsIdentity(plaintext);
}
}
TYPED_TEST_P(CipherTest, EncryptIsIndeterministic_Zeroes) {
for (auto size: SIZES) {
Data plaintext = this->CreateZeroes(size);
this->CheckEncryptIsIndeterministic(plaintext);
}
}
TYPED_TEST_P(CipherTest, EncryptIsIndeterministic_Data) {
for (auto size: SIZES) {
Data plaintext = this->CreateData(size);
this->CheckEncryptIsIndeterministic(plaintext);
}
}
TYPED_TEST_P(CipherTest, EncryptedSize) {
for (auto size: SIZES) {
Data plaintext = this->CreateData(size);
this->CheckEncryptedSize(plaintext);
}
}
TYPED_TEST_P(CipherTest, TryDecryptDataThatIsTooSmall) {
Data tooSmallCiphertext(TypeParam::ciphertextSize(0) - 1);
this->ExpectDoesntDecrypt(tooSmallCiphertext);
}
TYPED_TEST_P(CipherTest, TryDecryptDataThatIsMuchTooSmall_0) {
static_assert(TypeParam::ciphertextSize(0) > 0, "If this fails, the test case doesn't make sense.");
Data tooSmallCiphertext(0);
this->ExpectDoesntDecrypt(tooSmallCiphertext);
}
TYPED_TEST_P(CipherTest, TryDecryptDataThatIsMuchTooSmall_1) {
static_assert(TypeParam::ciphertextSize(0) > 1, "If this fails, the test case doesn't make sense.");
Data tooSmallCiphertext(1);
this->ExpectDoesntDecrypt(tooSmallCiphertext);
}
REGISTER_TYPED_TEST_SUITE_P(CipherTest,
Size,
EncryptThenDecrypt_Zeroes,
EncryptThenDecrypt_Data,
EncryptIsIndeterministic_Zeroes,
EncryptIsIndeterministic_Data,
EncryptedSize,
TryDecryptDataThatIsTooSmall,
TryDecryptDataThatIsMuchTooSmall_0,
TryDecryptDataThatIsMuchTooSmall_1
);
template<class Cipher>
class AuthenticatedCipherTest: public CipherTest<Cipher> {
public:
Data zeroes1 = CipherTest<Cipher>::CreateZeroes(1);
Data plaintext1 = CipherTest<Cipher>::CreateData(1);
Data zeroes2 = CipherTest<Cipher>::CreateZeroes(100 * 1024);
Data plaintext2 = CipherTest<Cipher>::CreateData(100 * 1024);
};
TYPED_TEST_SUITE_P(AuthenticatedCipherTest);
TYPED_TEST_P(AuthenticatedCipherTest, ModifyFirstByte_Zeroes_Size1) {
Data ciphertext = this->Encrypt(this->zeroes1);
void* firstByte = ciphertext.data();
serialize<CryptoPP::byte>(firstByte, deserialize<CryptoPP::byte>(firstByte) + 1);
this->ExpectDoesntDecrypt(ciphertext);
}
TYPED_TEST_P(AuthenticatedCipherTest, ModifyFirstByte_Data_Size1) {
Data ciphertext = this->Encrypt(this->plaintext1);
void* firstByte = ciphertext.data();
serialize<CryptoPP::byte>(firstByte, deserialize<CryptoPP::byte>(firstByte) + 1);
this->ExpectDoesntDecrypt(ciphertext);
}
TYPED_TEST_P(AuthenticatedCipherTest, ModifyFirstByte_Zeroes) {
Data ciphertext = this->Encrypt(this->zeroes2);
void* firstByte = ciphertext.data();
serialize<CryptoPP::byte>(firstByte, deserialize<CryptoPP::byte>(firstByte) + 1);
this->ExpectDoesntDecrypt(ciphertext);
}
TYPED_TEST_P(AuthenticatedCipherTest, ModifyFirstByte_Data) {
Data ciphertext = this->Encrypt(this->plaintext2);
void* firstByte = ciphertext.data();
serialize<CryptoPP::byte>(firstByte, deserialize<CryptoPP::byte>(firstByte) + 1);
this->ExpectDoesntDecrypt(ciphertext);
}
TYPED_TEST_P(AuthenticatedCipherTest, ModifyLastByte_Zeroes) {
Data ciphertext = this->Encrypt(this->zeroes2);
void* lastByte = ciphertext.dataOffset(ciphertext.size() - 1);
serialize<CryptoPP::byte>(lastByte, deserialize<CryptoPP::byte>(lastByte) + 1);
this->ExpectDoesntDecrypt(ciphertext);
}
TYPED_TEST_P(AuthenticatedCipherTest, ModifyLastByte_Data) {
Data ciphertext = this->Encrypt(this->plaintext2);
void* lastByte = ciphertext.dataOffset(ciphertext.size() - 1);
serialize<CryptoPP::byte>(lastByte, deserialize<CryptoPP::byte>(lastByte) + 1);
this->ExpectDoesntDecrypt(ciphertext);
}
TYPED_TEST_P(AuthenticatedCipherTest, ModifyMiddleByte_Zeroes) {
Data ciphertext = this->Encrypt(this->zeroes2);
void* middleByte = ciphertext.dataOffset(ciphertext.size()/2);
serialize<CryptoPP::byte>(middleByte, deserialize<CryptoPP::byte>(middleByte) + 1);
this->ExpectDoesntDecrypt(ciphertext);
}
TYPED_TEST_P(AuthenticatedCipherTest, ModifyMiddleByte_Data) {
Data ciphertext = this->Encrypt(this->plaintext2);
void* middleByte = ciphertext.dataOffset(ciphertext.size()/2);
serialize<CryptoPP::byte>(middleByte, deserialize<CryptoPP::byte>(middleByte) + 1);
this->ExpectDoesntDecrypt(ciphertext);
}
TYPED_TEST_P(AuthenticatedCipherTest, TryDecryptZeroesData) {
this->ExpectDoesntDecrypt(this->zeroes2);
}
TYPED_TEST_P(AuthenticatedCipherTest, TryDecryptRandomData) {
this->ExpectDoesntDecrypt(this->plaintext2);
}
REGISTER_TYPED_TEST_SUITE_P(AuthenticatedCipherTest,
ModifyFirstByte_Zeroes_Size1,
ModifyFirstByte_Zeroes,
ModifyFirstByte_Data_Size1,
ModifyFirstByte_Data,
ModifyLastByte_Zeroes,
ModifyLastByte_Data,
ModifyMiddleByte_Zeroes,
ModifyMiddleByte_Data,
TryDecryptZeroesData,
TryDecryptRandomData
);
INSTANTIATE_TYPED_TEST_SUITE_P(Fake, CipherTest, FakeAuthenticatedCipher);
INSTANTIATE_TYPED_TEST_SUITE_P(Fake, AuthenticatedCipherTest, FakeAuthenticatedCipher);
INSTANTIATE_TYPED_TEST_SUITE_P(XChaCha20Poly1305, CipherTest, XChaCha20Poly1305);
INSTANTIATE_TYPED_TEST_SUITE_P(XChaCha20Poly1305, AuthenticatedCipherTest, XChaCha20Poly1305);
INSTANTIATE_TYPED_TEST_SUITE_P(AES256_CFB, CipherTest, AES256_CFB); //CFB mode is not authenticated
INSTANTIATE_TYPED_TEST_SUITE_P(AES256_GCM, CipherTest, AES256_GCM);
INSTANTIATE_TYPED_TEST_SUITE_P(AES256_GCM, AuthenticatedCipherTest, AES256_GCM);
INSTANTIATE_TYPED_TEST_SUITE_P(AES128_CFB, CipherTest, AES128_CFB); //CFB mode is not authenticated
INSTANTIATE_TYPED_TEST_SUITE_P(AES128_GCM, CipherTest, AES128_GCM);
INSTANTIATE_TYPED_TEST_SUITE_P(AES128_GCM, AuthenticatedCipherTest, AES128_GCM);
INSTANTIATE_TYPED_TEST_SUITE_P(Twofish256_CFB, CipherTest, Twofish256_CFB); //CFB mode is not authenticated
INSTANTIATE_TYPED_TEST_SUITE_P(Twofish256_GCM, CipherTest, Twofish256_GCM);
INSTANTIATE_TYPED_TEST_SUITE_P(Twofish256_GCM, AuthenticatedCipherTest, Twofish256_GCM);
INSTANTIATE_TYPED_TEST_SUITE_P(Twofish128_CFB, CipherTest, Twofish128_CFB); //CFB mode is not authenticated
INSTANTIATE_TYPED_TEST_SUITE_P(Twofish128_GCM, CipherTest, Twofish128_GCM);
INSTANTIATE_TYPED_TEST_SUITE_P(Twofish128_GCM, AuthenticatedCipherTest, Twofish128_GCM);
INSTANTIATE_TYPED_TEST_SUITE_P(Serpent256_CFB, CipherTest, Serpent256_CFB); //CFB mode is not authenticated
INSTANTIATE_TYPED_TEST_SUITE_P(Serpent256_GCM, CipherTest, Serpent256_GCM);
INSTANTIATE_TYPED_TEST_SUITE_P(Serpent256_GCM, AuthenticatedCipherTest, Serpent256_GCM);
INSTANTIATE_TYPED_TEST_SUITE_P(Serpent128_CFB, CipherTest, Serpent128_CFB); //CFB mode is not authenticated
INSTANTIATE_TYPED_TEST_SUITE_P(Serpent128_GCM, CipherTest, Serpent128_GCM);
INSTANTIATE_TYPED_TEST_SUITE_P(Serpent128_GCM, AuthenticatedCipherTest, Serpent128_GCM);
INSTANTIATE_TYPED_TEST_SUITE_P(Cast256_CFB, CipherTest, Cast256_CFB); //CFB mode is not authenticated
INSTANTIATE_TYPED_TEST_SUITE_P(Cast256_GCM, CipherTest, Cast256_GCM);
INSTANTIATE_TYPED_TEST_SUITE_P(Cast256_GCM, AuthenticatedCipherTest, Cast256_GCM);
INSTANTIATE_TYPED_TEST_SUITE_P(Mars448_CFB, CipherTest, Mars448_CFB); //CFB mode is not authenticated
INSTANTIATE_TYPED_TEST_SUITE_P(Mars448_GCM, CipherTest, Mars448_GCM);
INSTANTIATE_TYPED_TEST_SUITE_P(Mars448_GCM, AuthenticatedCipherTest, Mars448_GCM);
INSTANTIATE_TYPED_TEST_SUITE_P(Mars256_CFB, CipherTest, Mars256_CFB); //CFB mode is not authenticated
INSTANTIATE_TYPED_TEST_SUITE_P(Mars256_GCM, CipherTest, Mars256_GCM);
INSTANTIATE_TYPED_TEST_SUITE_P(Mars256_GCM, AuthenticatedCipherTest, Mars256_GCM);
INSTANTIATE_TYPED_TEST_SUITE_P(Mars128_CFB, CipherTest, Mars128_CFB); //CFB mode is not authenticated
INSTANTIATE_TYPED_TEST_SUITE_P(Mars128_GCM, CipherTest, Mars128_GCM);
INSTANTIATE_TYPED_TEST_SUITE_P(Mars128_GCM, AuthenticatedCipherTest, Mars128_GCM);
// Test cipher names
TEST(CipherNameTest, TestCipherNames) {
EXPECT_EQ("xchacha20-poly1305", string(XChaCha20Poly1305::NAME));
EXPECT_EQ("aes-256-gcm", string(AES256_GCM::NAME));
EXPECT_EQ("aes-256-cfb", string(AES256_CFB::NAME));
EXPECT_EQ("aes-128-gcm", string(AES128_GCM::NAME));
EXPECT_EQ("aes-128-cfb", string(AES128_CFB::NAME));
EXPECT_EQ("twofish-256-gcm", string(Twofish256_GCM::NAME));
EXPECT_EQ("twofish-256-cfb", string(Twofish256_CFB::NAME));
EXPECT_EQ("twofish-128-gcm", string(Twofish128_GCM::NAME));
EXPECT_EQ("twofish-128-cfb", string(Twofish128_CFB::NAME));
EXPECT_EQ("serpent-256-gcm", string(Serpent256_GCM::NAME));
EXPECT_EQ("serpent-256-cfb", string(Serpent256_CFB::NAME));
EXPECT_EQ("serpent-128-gcm", string(Serpent128_GCM::NAME));
EXPECT_EQ("serpent-128-cfb", string(Serpent128_CFB::NAME));
EXPECT_EQ("cast-256-gcm", string(Cast256_GCM::NAME));
EXPECT_EQ("cast-256-cfb", string(Cast256_CFB::NAME));
EXPECT_EQ("mars-448-gcm", string(Mars448_GCM::NAME));
EXPECT_EQ("mars-448-cfb", string(Mars448_CFB::NAME));
EXPECT_EQ("mars-256-gcm", string(Mars256_GCM::NAME));
EXPECT_EQ("mars-256-cfb", string(Mars256_CFB::NAME));
EXPECT_EQ("mars-128-gcm", string(Mars128_GCM::NAME));
EXPECT_EQ("mars-128-cfb", string(Mars128_CFB::NAME));
}

View File

@ -1,3 +0,0 @@
#include "cpp-utils/data/DataFixture.h"
// Test the header can be included without needing additional dependencies

View File

@ -1,73 +0,0 @@
#include <gtest/gtest.h>
#include "cpp-utils/data/Data.h"
#include "cpp-utils/data/DataFixture.h"
using ::testing::Test;
using namespace cpputils;
class DataFixtureTest: public Test {
};
TEST_F(DataFixtureTest, CreateEmptyFixture) {
Data data = DataFixture::generate(0);
EXPECT_EQ(0u, data.size());
}
TEST_F(DataFixtureTest, CreateOneByteFixture) {
Data data = DataFixture::generate(1);
EXPECT_EQ(1u, data.size());
}
TEST_F(DataFixtureTest, CreateLargerFixture) {
Data data = DataFixture::generate(20 * 1024 * 1024);
EXPECT_EQ(20u * 1024u * 1024u, data.size());
}
TEST_F(DataFixtureTest, FixturesAreDeterministic_DefaultSeed) {
Data data1 = DataFixture::generate(1024 * 1024);
Data data2 = DataFixture::generate(1024 * 1024);
EXPECT_EQ(data1, data2);
}
TEST_F(DataFixtureTest, FixturesAreDeterministic_SeedIs5) {
Data data1 = DataFixture::generate(1024 * 1024, 5);
Data data2 = DataFixture::generate(1024 * 1024, 5);
EXPECT_EQ(data1, data2);
}
TEST_F(DataFixtureTest, DifferentSeedIsDifferentFixture) {
Data data1 = DataFixture::generate(1024 * 1024, 0);
Data data2 = DataFixture::generate(1024 * 1024, 1);
EXPECT_NE(data1, data2);
}
TEST_F(DataFixtureTest, FixturesAreDeterministic_DifferentSize_DefaultSeed_1) {
Data data1 = DataFixture::generate(1024);
Data data2 = DataFixture::generate(1);
EXPECT_EQ(0, std::memcmp(data1.data(), data2.data(), 1));
}
TEST_F(DataFixtureTest, FixturesAreDeterministic_DifferentSize_DefaultSeed_2) {
Data data1 = DataFixture::generate(1024);
Data data2 = DataFixture::generate(501); //Intentionally not 64bit-aligned, because the generate() function generates 64bit values for performance
EXPECT_EQ(0, std::memcmp(data1.data(), data2.data(), 501));
}
TEST_F(DataFixtureTest, FixturesAreDeterministic_DifferentSize_SeedIs5_1) {
Data data1 = DataFixture::generate(1024, 5);
Data data2 = DataFixture::generate(1, 5);
EXPECT_EQ(0, std::memcmp(data1.data(), data2.data(), 1));
}
TEST_F(DataFixtureTest, FixturesAreDeterministic_DifferentSize_SeedIs5_2) {
Data data1 = DataFixture::generate(1024, 5);
Data data2 = DataFixture::generate(501, 5); //Intentionally not 64bit-aligned, because the generate() function generates 64bit values for performance
EXPECT_EQ(0, std::memcmp(data1.data(), data2.data(), 501));
}

View File

@ -1,3 +0,0 @@
#include "cpp-utils/data/Data.h"
// Test the header can be included without needing additional dependencies

View File

@ -1,297 +0,0 @@
#include "cpp-utils/data/DataFixture.h"
#include "cpp-utils/data/Data.h"
#include "cpp-utils/data/SerializationHelper.h"
#include <gmock/gmock.h>
#include "cpp-utils/tempfile/TempFile.h"
#include <fstream>
using ::testing::Test;
using ::testing::WithParamInterface;
using ::testing::Values;
using ::testing::Return;
using cpputils::TempFile;
using std::ifstream;
using std::ofstream;
using std::string;
namespace bf = boost::filesystem;
using namespace cpputils;
class DataTest: public Test {
public:
bool DataIsZeroes(const Data &data) {
for (size_t i = 0; i != data.size(); ++ i) {
if (deserialize<uint8_t>(data.dataOffset(i)) != 0) {
return false;
}
}
return true;
}
};
class DataTestWithSizeParam: public DataTest, public WithParamInterface<size_t> {
public:
Data randomData;
DataTestWithSizeParam(): randomData(DataFixture::generate(GetParam())) {}
static void StoreData(const Data &data, const bf::path &filepath) {
ofstream file(filepath.string().c_str(), std::ios::binary | std::ios::trunc);
file.write(static_cast<const char*>(data.data()), data.size());
}
static void EXPECT_STORED_FILE_DATA_CORRECT(const Data &data, const bf::path &filepath) {
EXPECT_EQ(data.size(), bf::file_size(filepath));
ifstream file(filepath.string().c_str(), std::ios::binary);
char *read_data = new char[data.size()];
file.read(read_data, data.size());
EXPECT_EQ(0, std::memcmp(data.data(), read_data, data.size()));
delete[] read_data;
}
};
INSTANTIATE_TEST_SUITE_P(DataTestWithSizeParam, DataTestWithSizeParam, Values(0, 1, 2, 1024, 4096, 10*1024*1024));
TEST_P(DataTestWithSizeParam, ZeroInitializedDataIsDifferentToRandomData) {
if (GetParam() != 0) {
Data data(GetParam());
data.FillWithZeroes();
EXPECT_NE(randomData, data);
}
}
// Working on a large data area without a crash is a good indicator that we
// are actually working on memory that was validly allocated for us.
TEST_P(DataTestWithSizeParam, WriteAndCheck) {
Data data = randomData.copy();
EXPECT_EQ(randomData, data);
}
TEST_P(DataTestWithSizeParam, Size) {
Data data(GetParam());
EXPECT_EQ(GetParam(), data.size());
}
TEST_P(DataTestWithSizeParam, CheckStoredFile) {
TempFile file;
randomData.StoreToFile(file.path());
EXPECT_STORED_FILE_DATA_CORRECT(randomData, file.path());
}
TEST_P(DataTestWithSizeParam, CheckLoadedData) {
TempFile file;
StoreData(randomData, file.path());
Data data = Data::LoadFromFile(file.path()).value();
EXPECT_EQ(randomData, data);
}
TEST_P(DataTestWithSizeParam, StoreDoesntChangeData) {
Data data = randomData.copy();
TempFile file;
data.StoreToFile(file.path());
EXPECT_EQ(randomData, data);
}
TEST_P(DataTestWithSizeParam, StoreAndLoad) {
TempFile file;
randomData.StoreToFile(file.path());
Data loaded_data = Data::LoadFromFile(file.path()).value();
EXPECT_EQ(randomData, loaded_data);
}
TEST_P(DataTestWithSizeParam, Copy) {
Data copy = randomData.copy();
EXPECT_EQ(randomData, copy);
}
TEST_F(DataTest, ChangingCopyDoesntChangeOriginal) {
Data original = DataFixture::generate(1024);
Data copy = original.copy();
serialize<uint8_t>(copy.data(), deserialize<uint8_t>(copy.data()) + 1);
EXPECT_EQ(DataFixture::generate(1024), original);
EXPECT_NE(copy, original);
}
TEST_F(DataTest, InitializeWithZeroes) {
Data data(10*1024);
data.FillWithZeroes();
EXPECT_TRUE(DataIsZeroes(data));
}
TEST_F(DataTest, FillModifiedDataWithZeroes) {
Data data = DataFixture::generate(10*1024);
EXPECT_FALSE(DataIsZeroes(data));
data.FillWithZeroes();
EXPECT_TRUE(DataIsZeroes(data));
}
TEST_F(DataTest, MoveConstructor) {
Data original = DataFixture::generate(1024);
Data copy(std::move(original));
EXPECT_EQ(DataFixture::generate(1024), copy);
EXPECT_EQ(nullptr, original.data()); // NOLINT (intentional use-after-move)
EXPECT_EQ(0u, original.size()); // NOLINT (intentional use-after-move)
}
TEST_F(DataTest, MoveAssignment) {
Data original = DataFixture::generate(1024);
Data copy(0);
copy = std::move(original);
EXPECT_EQ(DataFixture::generate(1024), copy);
EXPECT_EQ(nullptr, original.data()); // NOLINT (intentional use-after-move)
EXPECT_EQ(0u, original.size()); // NOLINT (intentional use-after-move)
}
TEST_F(DataTest, Equality) {
Data data1 = DataFixture::generate(1024);
Data data2 = DataFixture::generate(1024);
EXPECT_TRUE(data1 == data2);
EXPECT_FALSE(data1 != data2);
}
TEST_F(DataTest, Inequality_DifferentSize) {
Data data1 = DataFixture::generate(1024);
Data data2 = DataFixture::generate(1023);
EXPECT_FALSE(data1 == data2);
EXPECT_TRUE(data1 != data2);
}
TEST_F(DataTest, Inequality_DifferentFirstByte) {
Data data1 = DataFixture::generate(1024);
Data data2 = DataFixture::generate(1024);
serialize<uint8_t>(data2.data(), deserialize<uint8_t>(data2.data()) + 1);
EXPECT_FALSE(data1 == data2);
EXPECT_TRUE(data1 != data2);
}
TEST_F(DataTest, Inequality_DifferentMiddleByte) {
Data data1 = DataFixture::generate(1024);
Data data2 = DataFixture::generate(1024);
serialize<uint8_t>(data2.dataOffset(500), deserialize<uint8_t>(data2.dataOffset(500)) + 1);
EXPECT_FALSE(data1 == data2);
EXPECT_TRUE(data1 != data2);
}
TEST_F(DataTest, Inequality_DifferentLastByte) {
Data data1 = DataFixture::generate(1024);
Data data2 = DataFixture::generate(1024);
serialize<uint8_t>(data2.dataOffset(1023), deserialize<uint8_t>(data2.dataOffset(1023)) + 1);
EXPECT_FALSE(data1 == data2);
EXPECT_TRUE(data1 != data2);
}
#ifdef __x86_64__
TEST_F(DataTest, LargesizeSize) {
//Needs 64bit for representation. This value isn't in the size param list, because the list is also used for read/write checks.
uint64_t size = static_cast<uint64_t>(4.5L*1024*1024*1024);
Data data(size);
EXPECT_EQ(size, data.size());
}
#else
#if defined(_MSC_VER)
#pragma message This is not a 64bit architecture. Large size data tests are disabled.
#else
#warning This is not a 64bit architecture. Large size data tests are disabled.
#endif
#endif
TEST_F(DataTest, LoadingNonexistingFile) {
TempFile file(false); // Pass false to constructor, so the tempfile is not created
EXPECT_FALSE(Data::LoadFromFile(file.path()));
}
class DataTestWithStringParam: public DataTest, public WithParamInterface<string> {};
INSTANTIATE_TEST_SUITE_P(DataTestWithStringParam, DataTestWithStringParam, Values("", "2898B4B8A13C0F0278CCE465DB", "6FFEBAD90C0DAA2B79628F0627CE9841"));
TEST_P(DataTestWithStringParam, FromAndToString) {
Data data = Data::FromString(GetParam());
EXPECT_EQ(GetParam(), data.ToString());
}
TEST_P(DataTestWithStringParam, ToAndFromString) {
Data data = Data::FromString(GetParam());
Data data2 = Data::FromString(data.ToString());
EXPECT_EQ(data, data2);
}
struct MockAllocator final : public Allocator {
MOCK_METHOD(void* , allocate, (size_t), (override));
MOCK_METHOD(void, free, (void*, size_t), (override));
};
class DataTestWithMockAllocator: public DataTest {
public:
char ptr_target{};
unique_ref<MockAllocator> allocator = make_unique_ref<MockAllocator>();
MockAllocator* allocator_ptr = allocator.get();
};
TEST_F(DataTestWithMockAllocator, whenCreatingNewData_thenTakesItFromAllocator) {
EXPECT_CALL(*allocator, allocate(5)).Times(1).WillOnce(Return(&ptr_target));
Data data(5, std::move(allocator));
EXPECT_EQ(&ptr_target, data.data());
}
TEST_F(DataTestWithMockAllocator, whenDestructingData_thenFreesItInAllocator) {
EXPECT_CALL(*allocator, allocate(5)).Times(1).WillOnce(Return(&ptr_target));
Data data(5, std::move(allocator));
EXPECT_CALL(*allocator_ptr, free(&ptr_target, 5)).Times(1);
}
TEST_F(DataTestWithMockAllocator, whenMoveConstructing_thenOnlyFreesOnce) {
EXPECT_CALL(*allocator, allocate(5)).Times(1).WillOnce(Return(&ptr_target));
Data data(5, std::move(allocator));
Data data2 = std::move(data);
EXPECT_CALL(*allocator_ptr, free(&ptr_target, 5)).Times(1);
}
TEST_F(DataTestWithMockAllocator, whenMoveAssigning_thenOnlyFreesOnce) {
EXPECT_CALL(*allocator, allocate(5)).Times(1).WillOnce(Return(&ptr_target));
Data data(5, std::move(allocator));
Data data2(3);
data2 = std::move(data);
EXPECT_CALL(*allocator_ptr, free(&ptr_target, 5)).Times(1);
}
TEST_F(DataTestWithMockAllocator, whenMoveConstructing_thenOnlyFreesWhenSecondIsDestructed) {
EXPECT_CALL(*allocator, allocate(5)).Times(1).WillOnce(Return(&ptr_target));
EXPECT_CALL(*allocator_ptr, free(testing::_, testing::_)).Times(0);
auto data = std::make_unique<Data>(5, std::move(allocator));
Data data2 = std::move(*data);
data.reset();
EXPECT_CALL(*allocator_ptr, free(&ptr_target, 5)).Times(1);
}
TEST_F(DataTestWithMockAllocator, whenMoveAssigning_thenOnlyFreesWhenSecondIsDestructed) {
EXPECT_CALL(*allocator, allocate(5)).Times(1).WillOnce(Return(&ptr_target));
EXPECT_CALL(*allocator_ptr, free(testing::_, testing::_)).Times(0);
auto data = std::make_unique<Data>(5, std::move(allocator));
Data data2(3);
data2 = std::move(*data);
data.reset();
EXPECT_CALL(*allocator_ptr, free(&ptr_target, 5)).Times(1);
}

View File

@ -1,3 +0,0 @@
#include "cpp-utils/data/FixedSizeData.h"
// Test the header can be included without needing additional dependencies

View File

@ -1,192 +0,0 @@
#include "cpp-utils/data/DataFixture.h"
#include "cpp-utils/data/FixedSizeData.h"
#include "cpp-utils/data/Data.h"
#include <gtest/gtest.h>
using ::testing::Test;
using ::testing::WithParamInterface;
using ::testing::Values;
using std::string;
using namespace cpputils;
class FixedSizeDataTest: public Test {
public:
static constexpr size_t SIZE = 16;
const string DATA1_AS_STRING = "1491BB4932A389EE14BC7090AC772972";
const string DATA2_AS_STRING = "272EE5517627CFA147A971A8E6E747E0";
const Data DATA3_AS_BINARY;
const Data DATA4_AS_BINARY;
FixedSizeDataTest() : DATA3_AS_BINARY(DataFixture::generate(SIZE, 1)), DATA4_AS_BINARY(DataFixture::generate(SIZE, 2)) {}
template<size_t SIZE>
void EXPECT_DATA_EQ(const Data &expected, const FixedSizeData<SIZE> &actual) {
EXPECT_EQ(expected.size(), SIZE);
EXPECT_EQ(0, std::memcmp(expected.data(), actual.data(), SIZE));
}
};
constexpr size_t FixedSizeDataTest::SIZE;
TEST_F(FixedSizeDataTest, EqualsTrue) {
FixedSizeData<SIZE> DATA1_1 = FixedSizeData<SIZE>::FromString(DATA1_AS_STRING);
FixedSizeData<SIZE> DATA1_2 = FixedSizeData<SIZE>::FromString(DATA1_AS_STRING);
EXPECT_TRUE(DATA1_1 == DATA1_2);
EXPECT_TRUE(DATA1_2 == DATA1_1);
}
TEST_F(FixedSizeDataTest, EqualsFalse) {
FixedSizeData<SIZE> DATA1_1 = FixedSizeData<SIZE>::FromString(DATA1_AS_STRING);
FixedSizeData<SIZE> DATA2_1 = FixedSizeData<SIZE>::FromString(DATA2_AS_STRING);
EXPECT_FALSE(DATA1_1 == DATA2_1);
EXPECT_FALSE(DATA2_1 == DATA1_1);
}
TEST_F(FixedSizeDataTest, NotEqualsFalse) {
FixedSizeData<SIZE> DATA1_1 = FixedSizeData<SIZE>::FromString(DATA1_AS_STRING);
FixedSizeData<SIZE> DATA1_2 = FixedSizeData<SIZE>::FromString(DATA1_AS_STRING);
EXPECT_FALSE(DATA1_1 != DATA1_2);
EXPECT_FALSE(DATA1_2 != DATA1_1);
}
TEST_F(FixedSizeDataTest, NotEqualsTrue) {
FixedSizeData<SIZE> DATA1_1 = FixedSizeData<SIZE>::FromString(DATA1_AS_STRING);
FixedSizeData<SIZE> DATA2_1 = FixedSizeData<SIZE>::FromString(DATA2_AS_STRING);
EXPECT_TRUE(DATA1_1 != DATA2_1);
EXPECT_TRUE(DATA2_1 != DATA1_1);
}
class FixedSizeDataTestWithStringParam: public FixedSizeDataTest, public WithParamInterface<string> {};
INSTANTIATE_TEST_SUITE_P(FixedSizeDataTestWithStringParam, FixedSizeDataTestWithStringParam, Values("2898B4B8A13CA63CBE0F0278CCE465DB", "6FFEBAD90C0DAA2B79628F0627CE9841"));
TEST_P(FixedSizeDataTestWithStringParam, FromAndToString) {
FixedSizeData<SIZE> data = FixedSizeData<SIZE>::FromString(GetParam());
EXPECT_EQ(GetParam(), data.ToString());
}
TEST_P(FixedSizeDataTestWithStringParam, ToAndFromString) {
FixedSizeData<SIZE> data = FixedSizeData<SIZE>::FromString(GetParam());
FixedSizeData<SIZE> data2 = FixedSizeData<SIZE>::FromString(data.ToString());
EXPECT_EQ(data, data2);
}
class FixedSizeDataTestWithBinaryParam: public FixedSizeDataTest, public WithParamInterface<const Data*> {
public:
static const Data VALUE1;
static const Data VALUE2;
};
const Data FixedSizeDataTestWithBinaryParam::VALUE1(DataFixture::generate(SIZE, 3));
const Data FixedSizeDataTestWithBinaryParam::VALUE2(DataFixture::generate(SIZE, 4));
INSTANTIATE_TEST_SUITE_P(FixedSizeDataTestWithBinaryParam, FixedSizeDataTestWithBinaryParam, Values(&FixedSizeDataTestWithBinaryParam::VALUE1, &FixedSizeDataTestWithBinaryParam::VALUE2));
TEST_P(FixedSizeDataTestWithBinaryParam, FromBinary) {
FixedSizeData<SIZE> data = FixedSizeData<SIZE>::FromBinary(GetParam()->data());
EXPECT_DATA_EQ(*GetParam(), data);
}
TEST_P(FixedSizeDataTestWithBinaryParam, FromAndToBinary) {
FixedSizeData<SIZE> data = FixedSizeData<SIZE>::FromBinary(GetParam()->data());
Data output(FixedSizeData<SIZE>::BINARY_LENGTH);
data.ToBinary(output.data());
EXPECT_EQ(*GetParam(), output);
}
TEST_P(FixedSizeDataTestWithBinaryParam, ToAndFromBinary) {
FixedSizeData<SIZE> data = FixedSizeData<SIZE>::FromBinary(GetParam()->data());
Data stored(FixedSizeData<SIZE>::BINARY_LENGTH);
data.ToBinary(stored.data());
FixedSizeData<SIZE> loaded = FixedSizeData<SIZE>::FromBinary(stored.data());
EXPECT_EQ(data, loaded);
}
class FixedSizeDataTestWithParam: public FixedSizeDataTest, public WithParamInterface<FixedSizeData<FixedSizeDataTest::SIZE>> {};
INSTANTIATE_TEST_SUITE_P(FixedSizeDataTestWithParam, FixedSizeDataTestWithParam, Values(FixedSizeData<FixedSizeDataTest::SIZE>::FromString("2898B4B8A13CA63CBE0F0278CCE465DB"), FixedSizeData<FixedSizeDataTest::SIZE>::FromString("6FFEBAD90C0DAA2B79628F0627CE9841")));
TEST_P(FixedSizeDataTestWithParam, CopyConstructor) {
FixedSizeData<SIZE> copy(GetParam());
EXPECT_EQ(GetParam(), copy);
}
TEST_P(FixedSizeDataTestWithParam, Take_Half) {
FixedSizeData<SIZE> source(GetParam());
FixedSizeData<SIZE/2> taken = source.take<SIZE/2>();
EXPECT_EQ(0, std::memcmp(source.data(), taken.data(), SIZE/2));
}
TEST_P(FixedSizeDataTestWithParam, Drop_Half) {
FixedSizeData<SIZE> source(GetParam());
FixedSizeData<SIZE/2> taken = source.drop<SIZE/2>();
EXPECT_EQ(0, std::memcmp(source.data() + SIZE/2, taken.data(), SIZE/2));
}
TEST_P(FixedSizeDataTestWithParam, Take_One) {
FixedSizeData<SIZE> source(GetParam());
FixedSizeData<1> taken = source.take<1>();
EXPECT_EQ(0, std::memcmp(source.data(), taken.data(), 1));
}
TEST_P(FixedSizeDataTestWithParam, Drop_One) {
FixedSizeData<SIZE> source(GetParam());
FixedSizeData<SIZE-1> taken = source.drop<1>();
EXPECT_EQ(0, std::memcmp(source.data() + 1, taken.data(), SIZE-1));
}
TEST_P(FixedSizeDataTestWithParam, Take_Nothing) {
FixedSizeData<SIZE> source(GetParam());
FixedSizeData<0> taken = source.take<0>();
(void)taken; // silence unused variable warning
}
TEST_P(FixedSizeDataTestWithParam, Drop_Nothing) {
FixedSizeData<SIZE> source(GetParam());
FixedSizeData<SIZE> taken = source.drop<0>();
EXPECT_EQ(0, std::memcmp(source.data(), taken.data(), SIZE));
}
TEST_P(FixedSizeDataTestWithParam, Take_All) {
FixedSizeData<SIZE> source(GetParam());
FixedSizeData<SIZE> taken = source.take<SIZE>();
EXPECT_EQ(0, std::memcmp(source.data(), taken.data(), SIZE));
}
TEST_P(FixedSizeDataTestWithParam, Drop_All) {
FixedSizeData<SIZE> source(GetParam());
FixedSizeData<0> taken = source.drop<SIZE>();
(void)taken; // silence unused variable warning
}
TEST_F(FixedSizeDataTest, CopyConstructorDoesntChangeSource) {
FixedSizeData<SIZE> data1 = FixedSizeData<SIZE>::FromString(DATA1_AS_STRING);
FixedSizeData<SIZE> data2(data1);
EXPECT_EQ(DATA1_AS_STRING, data1.ToString());
(void)data2; // silence unused variable warning
}
TEST_P(FixedSizeDataTestWithParam, IsEqualAfterAssignment1) {
FixedSizeData<SIZE> data2 = FixedSizeData<SIZE>::FromString(DATA2_AS_STRING);
EXPECT_NE(GetParam(), data2);
data2 = GetParam();
EXPECT_EQ(GetParam(), data2);
}
TEST_F(FixedSizeDataTest, AssignmentDoesntChangeSource) {
FixedSizeData<SIZE> data1 = FixedSizeData<SIZE>::FromString(DATA1_AS_STRING);
FixedSizeData<SIZE> data2 = FixedSizeData<SIZE>::FromString(DATA2_AS_STRING);
data2 = data1;
EXPECT_EQ(DATA1_AS_STRING, data1.ToString());
}
// This tests that a FixedSizeData object is very lightweight
// (it is meant to be kept on stack and passed around)
TEST_F(FixedSizeDataTest, IsLightweightObject) {
EXPECT_EQ(FixedSizeData<SIZE>::BINARY_LENGTH, sizeof(FixedSizeData<SIZE>));
}

View File

@ -1,205 +0,0 @@
#include <gtest/gtest.h>
#include <cpp-utils/data/SerializationHelper.h>
#include <cpp-utils/data/Data.h>
using cpputils::serialize;
using cpputils::deserialize;
using cpputils::deserializeWithOffset;
using cpputils::Data;
TEST(SerializationHelperTest, uint8) {
Data data(1);
serialize<uint8_t>(data.data(), 5u);
EXPECT_EQ(5u, deserialize<uint8_t>(data.data()));
}
TEST(SerializationHelperTest, int8_positive) {
Data data(1);
serialize<int8_t>(data.data(), 5);
EXPECT_EQ(5, deserialize<int8_t>(data.data()));
}
TEST(SerializationHelperTest, int8_negative) {
Data data(1);
serialize<int8_t>(data.data(), -5);
EXPECT_EQ(-5, deserialize<int8_t>(data.data()));
}
TEST(SerializationHelperTest, uint16_aligned) {
Data data(2);
serialize<uint16_t>(data.data(), 1000u);
EXPECT_EQ(1000u, deserialize<uint16_t>(data.data()));
}
TEST(SerializationHelperTest, uint16_unaligned) {
Data data(3);
serialize<uint16_t>(data.dataOffset(1), 1000u);
EXPECT_EQ(1000u, deserialize<uint16_t>(data.dataOffset(1)));
}
TEST(SerializationHelperTest, int16_postive_aligned) {
Data data(2);
serialize<int16_t>(data.data(), 1000);
EXPECT_EQ(1000, deserialize<int16_t>(data.data()));
}
TEST(SerializationHelperTest, int16_positive_unaligned) {
Data data(3);
serialize<int16_t>(data.dataOffset(1), 1000);
EXPECT_EQ(1000, deserialize<int16_t>(data.dataOffset(1)));
}
TEST(SerializationHelperTest, int16_negative_aligned) {
Data data(2);
serialize<int16_t>(data.data(), -1000);
EXPECT_EQ(-1000, deserialize<int16_t>(data.data()));
}
TEST(SerializationHelperTest, int16_negative_unaligned) {
Data data(3);
serialize<int16_t>(data.dataOffset(1), -1000);
EXPECT_EQ(-1000, deserialize<int16_t>(data.dataOffset(1)));
}
TEST(SerializationHelperTest, uint32_aligned) {
Data data(4);
serialize<uint32_t>(data.data(), 100000u);
EXPECT_EQ(100000u, deserialize<uint32_t>(data.data()));
}
TEST(SerializationHelperTest, uint32_unaligned) {
Data data(5);
serialize<uint32_t>(data.dataOffset(1), 100000u);
EXPECT_EQ(100000u, deserialize<uint32_t>(data.dataOffset(1)));
}
TEST(SerializationHelperTest, int32_positive_aligned) {
Data data(4);
serialize<int32_t>(data.data(), 100000);
EXPECT_EQ(100000, deserialize<int32_t>(data.data()));
}
TEST(SerializationHelperTest, int32_positive_unaligned) {
Data data(5);
serialize<int32_t>(data.dataOffset(1), 100000);
EXPECT_EQ(100000, deserialize<int32_t>(data.dataOffset(1)));
}
TEST(SerializationHelperTest, int32_negative_aligned) {
Data data(4);
serialize<int32_t>(data.data(), -100000);
EXPECT_EQ(-100000, deserialize<int32_t>(data.data()));
}
TEST(SerializationHelperTest, int32_negative_unaligned) {
Data data(5);
serialize<int32_t>(data.dataOffset(1), -100000);
EXPECT_EQ(-100000, deserialize<int32_t>(data.dataOffset(1)));
}
TEST(SerializationHelperTest, uint64_aligned) {
Data data(8);
serialize<uint64_t>(data.data(), 10000000000u);
EXPECT_EQ(10000000000u, deserialize<uint64_t>(data.data()));
}
TEST(SerializationHelperTest, uint64_unaligned) {
Data data(9);
serialize<uint64_t>(data.dataOffset(1), 10000000000u);
EXPECT_EQ(10000000000u, deserialize<uint64_t>(data.dataOffset(1)));
}
TEST(SerializationHelperTest, int64_positive_aligned) {
Data data(8);
serialize<int64_t>(data.data(), 10000000000);
EXPECT_EQ(10000000000, deserialize<int64_t>(data.data()));
}
TEST(SerializationHelperTest, int64_positive_unaligned) {
Data data(9);
serialize<int64_t>(data.dataOffset(1), 10000000000);
EXPECT_EQ(10000000000, deserialize<int64_t>(data.dataOffset(1)));
}
TEST(SerializationHelperTest, int64_negative_aligned) {
Data data(8);
serialize<int64_t>(data.data(), -10000000000);
EXPECT_EQ(-10000000000, deserialize<int64_t>(data.data()));
}
TEST(SerializationHelperTest, int64_negative_unaligned) {
Data data(9);
serialize<int64_t>(data.dataOffset(1), -10000000000);
EXPECT_EQ(-10000000000, deserialize<int64_t>(data.dataOffset(1)));
}
TEST(SerializationHelperTest, float_aligned) {
Data data(sizeof(float));
serialize<float>(data.data(), 3.1415f);
EXPECT_EQ(3.1415f, deserialize<float>(data.data()));
}
TEST(SerializationHelperTest, float_unaligned) {
Data data(sizeof(float) + 1);
serialize<float>(data.dataOffset(1), 3.1415f);
EXPECT_EQ(3.1415f, deserialize<float>(data.dataOffset(1)));
}
TEST(SerializationHelperTest, double_aligned) {
Data data(sizeof(double));
serialize<double>(data.data(), 3.1415);
EXPECT_EQ(3.1415, deserialize<double>(data.data()));
}
TEST(SerializationHelperTest, double_unaligned) {
Data data(sizeof(double) + 1);
serialize<double>(data.dataOffset(1), 3.1415);
EXPECT_EQ(3.1415, deserialize<double>(data.dataOffset(1)));
}
namespace {
struct DataStructure final {
uint64_t v1;
uint32_t v2;
uint16_t v3;
uint8_t v4;
};
bool operator==(const DataStructure &lhs, const DataStructure &rhs) {
return lhs.v1 == rhs.v1 && lhs.v2 == rhs.v2 && lhs.v3 == rhs.v3 && lhs.v4 == rhs.v4;
}
}
TEST(SerializationHelperTest, struct_aligned) {
Data data(sizeof(DataStructure));
const DataStructure fixture {10000000000u, 100000u, 1000u, 5u};
serialize<DataStructure>(data.data(), fixture);
EXPECT_EQ(fixture, deserialize<DataStructure>(data.data()));
}
TEST(SerializationHelperTest, struct_unaligned) {
Data data(sizeof(DataStructure) + 1);
const DataStructure fixture {10000000000u, 100000u, 1000u, 5u};
serialize<DataStructure>(data.dataOffset(1), fixture);
EXPECT_EQ(fixture, deserialize<DataStructure>(data.dataOffset(1)));
}
namespace {
struct OneByteStruct final {
uint8_t v;
};
static_assert(sizeof(OneByteStruct) == 1, "");
}
TEST(SerializationHelperTest, onebytestruct) {
Data data(1);
OneByteStruct fixture {5};
serialize<OneByteStruct>(data.data(), fixture);
EXPECT_EQ(fixture.v, deserialize<OneByteStruct>(data.data()).v);
}
TEST(SerializationHelperTest, deserializeWithOffset) {
Data data(5);
serialize<uint16_t>(data.dataOffset(1), 1000);
EXPECT_EQ(1000, deserializeWithOffset<uint16_t>(data.data(), 1));
}

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +0,0 @@
#include "cpp-utils/io/Console.h"
// Test the header can be included without needing additional dependencies

View File

@ -1,87 +0,0 @@
#pragma once
#ifndef MESSMER_CPPUTILS_TEST_IO_CONSOLETEST_H
#define MESSMER_CPPUTILS_TEST_IO_CONSOLETEST_H
#include <gtest/gtest.h>
#include "cpp-utils/io/IOStreamConsole.h"
#include <future>
#include <thread>
#include "cpp-utils/io/pipestream.h"
class ConsoleThread {
public:
ConsoleThread(std::ostream &ostr, std::istream &istr): _console(ostr, istr) {}
std::future<unsigned int> ask(const std::string &question, const std::vector<std::string> &options) {
return std::async(std::launch::async, [this, question, options]() {
return _console.ask(question, options);
});
}
std::future<bool> askYesNo(const std::string &question) {
return std::async(std::launch::async, [this, question]() {
return _console.askYesNo(question, true);
});
}
std::future<std::string> askPassword(const std::string &question) {
return std::async(std::launch::async, [this, question]() {
return _console.askPassword(question);
});
}
void print(const std::string &output) {
_console.print(output);
}
private:
cpputils::IOStreamConsole _console;
};
class ConsoleTest: public ::testing::Test {
public:
ConsoleTest(): _inputStr(), _outputStr(), _input(&_inputStr), _output(&_outputStr), _console(_output, _input) {}
void EXPECT_OUTPUT_LINES(std::initializer_list<std::string> lines) {
for (const std::string &line : lines) {
EXPECT_OUTPUT_LINE(line);
}
}
void EXPECT_OUTPUT_LINE(const std::string &expected, char delimiter = '\n', const std::string &expected_after_delimiter = "") {
std::string actual;
std::getline(_output, actual, delimiter);
EXPECT_EQ(expected, actual);
for (char expected_char : expected_after_delimiter) {
char actual_char = 0;
_output.get(actual_char);
EXPECT_EQ(expected_char, actual_char);
}
}
void sendInputLine(const std::string &line) {
_input << line << "\n" << std::flush;
}
std::future<unsigned int> ask(const std::string &question, const std::vector<std::string> &options) {
return _console.ask(question, options);
}
std::future<bool> askYesNo(const std::string &question) {
return _console.askYesNo(question);
}
std::future<std::string> askPassword(const std::string &question) {
return _console.askPassword(question);
}
void print(const std::string &output) {
_console.print(output);
}
private:
cpputils::pipestream _inputStr;
cpputils::pipestream _outputStr;
std::iostream _input;
std::iostream _output;
ConsoleThread _console;
};
#endif

View File

@ -1,185 +0,0 @@
#include "ConsoleTest.h"
using std::stringstream;
using std::string;
using std::istream;
using std::ostream;
class ConsoleTest_Ask: public ConsoleTest {};
TEST_F(ConsoleTest_Ask, CrashesWithoutOptions) {
EXPECT_THROW(
(ask("My Question?", {}).get()),
std::invalid_argument
);
}
TEST_F(ConsoleTest_Ask, OneOption) {
auto chosen = ask("My Question?", {"First Option"});
EXPECT_OUTPUT_LINES({
"My Question?",
" [1] First Option"
});
EXPECT_OUTPUT_LINE("Your choice [1-1]", ':', " ");
sendInputLine("1");
EXPECT_EQ(0u, chosen.get());
}
TEST_F(ConsoleTest_Ask, TwoOptions_ChooseFirst) {
auto chosen = ask("My Question?", {"First Option", "Second Option"});
EXPECT_OUTPUT_LINES({
"My Question?",
" [1] First Option",
" [2] Second Option"
});
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
sendInputLine("1");
EXPECT_EQ(0u, chosen.get());
}
TEST_F(ConsoleTest_Ask, TwoOptions_ChooseSecond) {
auto chosen = ask("My Question?", {"First Option", "Second Option"});
EXPECT_OUTPUT_LINES({
"My Question?",
" [1] First Option",
" [2] Second Option"
});
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
sendInputLine("2");
EXPECT_EQ(1u, chosen.get());
}
TEST_F(ConsoleTest_Ask, ThreeOptions_ChooseFirst) {
auto chosen = ask("My Other Question?", {"1st Option", "2nd Option", "3rd Option"});
EXPECT_OUTPUT_LINES({
"My Other Question?",
" [1] 1st Option",
" [2] 2nd Option",
" [3] 3rd Option"
});
EXPECT_OUTPUT_LINE("Your choice [1-3]", ':', " ");
sendInputLine("1");
EXPECT_EQ(0u, chosen.get());
}
TEST_F(ConsoleTest_Ask, ThreeOptions_ChooseSecond) {
auto chosen = ask("My Question?", {"1st Option", "2nd Option", "3rd Option"});
EXPECT_OUTPUT_LINES({
"My Question?",
" [1] 1st Option",
" [2] 2nd Option",
" [3] 3rd Option"
});
EXPECT_OUTPUT_LINE("Your choice [1-3]", ':', " ");
sendInputLine("2");
EXPECT_EQ(1u, chosen.get());
}
TEST_F(ConsoleTest_Ask, ThreeOptions_ChooseThird) {
auto chosen = ask("My Question?", {"1st Option", "2nd Option", "3rd Option"});
EXPECT_OUTPUT_LINES({
"My Question?",
" [1] 1st Option",
" [2] 2nd Option",
" [3] 3rd Option"
});
EXPECT_OUTPUT_LINE("Your choice [1-3]", ':', " ");
sendInputLine("3");
EXPECT_EQ(2u, chosen.get());
}
TEST_F(ConsoleTest_Ask, InputWithLeadingSpaces) {
auto chosen = ask("My Question?", {"First Option", "Second Option"});
EXPECT_OUTPUT_LINES({
"My Question?",
" [1] First Option",
" [2] Second Option"
});
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
sendInputLine(" 2");
EXPECT_EQ(1u, chosen.get());
}
TEST_F(ConsoleTest_Ask, InputWithFollowingSpaces) {
auto chosen = ask("My Question?", {"First Option", "Second Option"});
EXPECT_OUTPUT_LINES({
"My Question?",
" [1] First Option",
" [2] Second Option"
});
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
sendInputLine("2 ");
EXPECT_EQ(1u, chosen.get());
}
TEST_F(ConsoleTest_Ask, InputWithLeadingAndFollowingSpaces) {
auto chosen = ask("My Question?", {"First Option", "Second Option"});
EXPECT_OUTPUT_LINES({
"My Question?",
" [1] First Option",
" [2] Second Option"
});
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
sendInputLine(" 2 ");
EXPECT_EQ(1u, chosen.get());
}
TEST_F(ConsoleTest_Ask, InputEmptyLine) {
auto chosen = ask("My Question?", {"First Option", "Second Option"});
EXPECT_OUTPUT_LINES({
"My Question?",
" [1] First Option",
" [2] Second Option"
});
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
sendInputLine("");
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
sendInputLine(" "); // empty line with space
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
sendInputLine("2");
EXPECT_EQ(1u, chosen.get());
}
TEST_F(ConsoleTest_Ask, InputWrongNumbers) {
auto chosen = ask("My Question?", {"1st Option", "2nd Option"});
EXPECT_OUTPUT_LINES({
"My Question?",
" [1] 1st Option",
" [2] 2nd Option",
});
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
sendInputLine("0");
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
sendInputLine("-1");
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
sendInputLine("3");
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
sendInputLine("1.5");
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
sendInputLine("1,5");
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
sendInputLine("2");
EXPECT_EQ(1u, chosen.get());
}
TEST_F(ConsoleTest_Ask, InputNonNumbers) {
auto chosen = ask("My Question?", {"1st Option", "2nd Option"});
EXPECT_OUTPUT_LINES({
"My Question?",
" [1] 1st Option",
" [2] 2nd Option",
});
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
sendInputLine("abc");
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
sendInputLine("3a"); // Wrong number and string attached
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
sendInputLine("1a"); // Right number but string attached
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
sendInputLine("a3"); // Wrong number and string attached
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
sendInputLine("a1"); // Right number but string attached
EXPECT_OUTPUT_LINE("Your choice [1-2]", ':', " ");
sendInputLine("2");
EXPECT_EQ(1u, chosen.get());
}

View File

@ -1,22 +0,0 @@
#include "ConsoleTest.h"
using std::stringstream;
using std::string;
using std::istream;
using std::ostream;
class ConsoleTest_AskPassword: public ConsoleTest {};
TEST_F(ConsoleTest_AskPassword, InputSomePassword) {
auto chosen = askPassword("Please enter my password:");
EXPECT_OUTPUT_LINE("Please enter my password", ':');
sendInputLine("this is the password");
EXPECT_EQ("this is the password", chosen.get());
}
TEST_F(ConsoleTest_AskPassword, InputEmptyPassword) {
auto chosen = askPassword("Please enter my password:");
EXPECT_OUTPUT_LINE("Please enter my password", ':');
sendInputLine("");
EXPECT_EQ("", chosen.get());
}

View File

@ -1,108 +0,0 @@
#include "ConsoleTest.h"
using std::string;
class ConsoleTest_AskYesNo: public ConsoleTest {
public:
void EXPECT_TRUE_ON_INPUT(const string &input) {
EXPECT_RESULT_ON_INPUT(true, input);
}
void EXPECT_FALSE_ON_INPUT(const string &input) {
EXPECT_RESULT_ON_INPUT(false, input);
}
void EXPECT_RESULT_ON_INPUT(const bool expected, const string &input) {
auto chosen = askYesNo("Are you sure blablub?");
EXPECT_OUTPUT_LINES({"Are you sure blablub?"});
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
sendInputLine(input);
EXPECT_EQ(expected, chosen.get());
}
};
TEST_F(ConsoleTest_AskYesNo, Input_Yes) {
EXPECT_TRUE_ON_INPUT("Yes");
}
TEST_F(ConsoleTest_AskYesNo, Input_yes) {
EXPECT_TRUE_ON_INPUT("yes");
}
TEST_F(ConsoleTest_AskYesNo, Input_Y) {
EXPECT_TRUE_ON_INPUT("Y");
}
TEST_F(ConsoleTest_AskYesNo, Input_y) {
EXPECT_TRUE_ON_INPUT("y");
}
TEST_F(ConsoleTest_AskYesNo, Input_No) {
EXPECT_FALSE_ON_INPUT("No");
}
TEST_F(ConsoleTest_AskYesNo, Input_no) {
EXPECT_FALSE_ON_INPUT("no");
}
TEST_F(ConsoleTest_AskYesNo, Input_N) {
EXPECT_FALSE_ON_INPUT("N");
}
TEST_F(ConsoleTest_AskYesNo, Input_n) {
EXPECT_FALSE_ON_INPUT("n");
}
TEST_F(ConsoleTest_AskYesNo, InputWithLeadingSpaces) {
EXPECT_TRUE_ON_INPUT(" y");
}
TEST_F(ConsoleTest_AskYesNo, InputWithFollowingSpaces) {
EXPECT_TRUE_ON_INPUT("y ");
}
TEST_F(ConsoleTest_AskYesNo, InputWithLeadingAndFollowingSpaces) {
EXPECT_TRUE_ON_INPUT(" y ");
}
TEST_F(ConsoleTest_AskYesNo, InputEmptyLine) {
auto chosen = askYesNo("My Question?");
EXPECT_OUTPUT_LINES({"My Question?"});
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
sendInputLine("");
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
sendInputLine(" "); // empty line with space
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
sendInputLine("y");
EXPECT_EQ(true, chosen.get());
}
TEST_F(ConsoleTest_AskYesNo, WrongInput) {
auto chosen = askYesNo("My Question?");
EXPECT_OUTPUT_LINES({"My Question?"});
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
sendInputLine("0");
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
sendInputLine("1");
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
sendInputLine("bla");
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
sendInputLine("Y_andsomethingelse");
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
sendInputLine("y_andsomethingelse");
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
sendInputLine("N_andsomethingelse");
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
sendInputLine("n_andsomethingelse");
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
sendInputLine("Yes_andsomethingelse");
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
sendInputLine("yes_andsomethingelse");
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
sendInputLine("No_andsomethingelse");
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
sendInputLine("no_andsomethingelse");
EXPECT_OUTPUT_LINE("Your choice [y/n]", ':', " ");
sendInputLine("y");
EXPECT_EQ(true, chosen.get());
}

Some files were not shown because too many files have changed in this diff Show More