Create a FakeBlockStore and use it instead of InMemoryBlockStore in tests
This commit is contained in:
parent
1924c936a4
commit
15b10feeaf
@ -1,2 +1,3 @@
|
|||||||
add_subdirectory(ondisk)
|
add_subdirectory(ondisk)
|
||||||
add_subdirectory(inmemory)
|
add_subdirectory(inmemory)
|
||||||
|
add_subdirectory(testfake)
|
@ -20,12 +20,12 @@ unique_ptr<Block> InMemoryBlockStore::create(const Key &key, size_t size) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Return a copy of the stored InMemoryBlock
|
//Return a pointer to the stored InMemoryBlock
|
||||||
return make_unique<InMemoryBlock>(insert_result.first->second);
|
return make_unique<InMemoryBlock>(insert_result.first->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
unique_ptr<Block> InMemoryBlockStore::load(const Key &key) {
|
unique_ptr<Block> InMemoryBlockStore::load(const Key &key) {
|
||||||
//Return a copy of the stored InMemoryBlock
|
//Return a pointer to the stored InMemoryBlock
|
||||||
try {
|
try {
|
||||||
return make_unique<InMemoryBlock>(_blocks.at(key.AsString()));
|
return make_unique<InMemoryBlock>(_blocks.at(key.AsString()));
|
||||||
} catch (const std::out_of_range &e) {
|
} catch (const std::out_of_range &e) {
|
||||||
|
3
src/blockstore/implementations/testfake/CMakeLists.txt
Normal file
3
src/blockstore/implementations/testfake/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
add_library(blockstore_testfake FakeBlock.cpp FakeBlockStore.cpp)
|
||||||
|
|
||||||
|
target_link_libraries(blockstore_testfake blockstore_interface blockstore_utils)
|
42
src/blockstore/implementations/testfake/FakeBlock.cpp
Normal file
42
src/blockstore/implementations/testfake/FakeBlock.cpp
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#include "FakeBlock.h"
|
||||||
|
#include "FakeBlockStore.h"
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
using std::unique_ptr;
|
||||||
|
using std::shared_ptr;
|
||||||
|
using std::istream;
|
||||||
|
using std::ostream;
|
||||||
|
using std::ifstream;
|
||||||
|
using std::ofstream;
|
||||||
|
using std::ios;
|
||||||
|
using std::string;
|
||||||
|
|
||||||
|
namespace blockstore {
|
||||||
|
namespace testfake {
|
||||||
|
|
||||||
|
FakeBlock::FakeBlock(FakeBlockStore *store, const string &key, shared_ptr<Data> data)
|
||||||
|
: _store(store), _key(key), _data(data) {
|
||||||
|
}
|
||||||
|
|
||||||
|
FakeBlock::~FakeBlock() {
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void *FakeBlock::data() {
|
||||||
|
return _data->data();
|
||||||
|
}
|
||||||
|
|
||||||
|
const void *FakeBlock::data() const {
|
||||||
|
return _data->data();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t FakeBlock::size() const {
|
||||||
|
return _data->size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FakeBlock::flush() {
|
||||||
|
_store->updateData(_key, *_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
37
src/blockstore/implementations/testfake/FakeBlock.h
Normal file
37
src/blockstore/implementations/testfake/FakeBlock.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
#ifndef BLOCKSTORE_IMPLEMENTATIONS_INMEMORY_INMEMORYBLOCK_H_
|
||||||
|
#define BLOCKSTORE_IMPLEMENTATIONS_INMEMORY_INMEMORYBLOCK_H_
|
||||||
|
|
||||||
|
#include <blockstore/interface/Block.h>
|
||||||
|
#include <blockstore/utils/Data.h>
|
||||||
|
|
||||||
|
#include "fspp/utils/macros.h"
|
||||||
|
|
||||||
|
namespace blockstore {
|
||||||
|
namespace testfake {
|
||||||
|
class FakeBlockStore;
|
||||||
|
|
||||||
|
class FakeBlock: public Block {
|
||||||
|
public:
|
||||||
|
FakeBlock(FakeBlockStore *store, const std::string &key, std::shared_ptr<Data> data);
|
||||||
|
virtual ~FakeBlock();
|
||||||
|
|
||||||
|
void *data() override;
|
||||||
|
const void *data() const override;
|
||||||
|
|
||||||
|
void flush() override;
|
||||||
|
|
||||||
|
size_t size() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
FakeBlockStore *_store;
|
||||||
|
const std::string _key;
|
||||||
|
std::shared_ptr<Data> _data;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(FakeBlock);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
52
src/blockstore/implementations/testfake/FakeBlockStore.cpp
Normal file
52
src/blockstore/implementations/testfake/FakeBlockStore.cpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#include "FakeBlockStore.h"
|
||||||
|
#include "FakeBlock.h"
|
||||||
|
|
||||||
|
using std::unique_ptr;
|
||||||
|
using std::make_unique;
|
||||||
|
using std::make_shared;
|
||||||
|
using std::string;
|
||||||
|
using std::mutex;
|
||||||
|
using std::lock_guard;
|
||||||
|
|
||||||
|
namespace blockstore {
|
||||||
|
namespace testfake {
|
||||||
|
|
||||||
|
FakeBlockStore::FakeBlockStore()
|
||||||
|
: _blocks(), _used_dataregions_for_blocks() {}
|
||||||
|
|
||||||
|
unique_ptr<Block> FakeBlockStore::create(const Key &key, size_t size) {
|
||||||
|
string key_string = key.AsString();
|
||||||
|
auto insert_result = _blocks.emplace(key_string, size);
|
||||||
|
insert_result.first->second.FillWithZeroes();
|
||||||
|
|
||||||
|
if (!insert_result.second) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Return a copy of the stored data
|
||||||
|
_used_dataregions_for_blocks.push_back(make_shared<Data>(size));
|
||||||
|
std::memcpy(_used_dataregions_for_blocks.back()->data(), insert_result.first->second.data(), size);
|
||||||
|
return make_unique<FakeBlock>(this, key_string, _used_dataregions_for_blocks.back());
|
||||||
|
}
|
||||||
|
|
||||||
|
unique_ptr<Block> FakeBlockStore::load(const Key &key) {
|
||||||
|
//Return a copy of the stored data
|
||||||
|
string key_string = key.AsString();
|
||||||
|
try {
|
||||||
|
const Data &data = _blocks.at(key_string);
|
||||||
|
_used_dataregions_for_blocks.push_back(make_shared<Data>(data.size()));
|
||||||
|
std::memcpy(_used_dataregions_for_blocks.back()->data(), data.data(), data.size());
|
||||||
|
return make_unique<FakeBlock>(this, key_string, _used_dataregions_for_blocks.back());
|
||||||
|
} catch (const std::out_of_range &e) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FakeBlockStore::updateData(const std::string &key, const Data &data) {
|
||||||
|
Data &stored_data = _blocks.at(key);
|
||||||
|
assert(data.size() == stored_data.size());
|
||||||
|
std::memcpy(stored_data.data(), data.data(), data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
55
src/blockstore/implementations/testfake/FakeBlockStore.h
Normal file
55
src/blockstore/implementations/testfake/FakeBlockStore.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#pragma once
|
||||||
|
#ifndef BLOCKSTORE_IMPLEMENTATIONS_INMEMORY_INMEMORYBLOCKSTORE_H_
|
||||||
|
#define BLOCKSTORE_IMPLEMENTATIONS_INMEMORY_INMEMORYBLOCKSTORE_H_
|
||||||
|
|
||||||
|
#include <blockstore/interface/helpers/BlockStoreWithRandomKeys.h>
|
||||||
|
#include "blockstore/utils/Data.h"
|
||||||
|
#include "fspp/utils/macros.h"
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace blockstore {
|
||||||
|
namespace testfake {
|
||||||
|
class FakeBlock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This blockstore is meant to be used for unit tests when the module under test needs a blockstore to work with.
|
||||||
|
* It basically is the same as the InMemoryBlockStore, but much less forgiving for programming mistakes.
|
||||||
|
*
|
||||||
|
* InMemoryBlockStore for example simply ignores flushing and gives you access to the same data region each time
|
||||||
|
* you request a block. This is very performant, but also forgiving to mistakes. Say you write over the boundaries
|
||||||
|
* of a block, then you wouldn't notice, since the next time you access the block, the overflow data is (probably)
|
||||||
|
* still there. Or say an application is relying on flushing the block store in the right moment. Since flushing
|
||||||
|
* is a no-op in InMemoryBlockStore, you wouldn't notice either.
|
||||||
|
*
|
||||||
|
* So this FakeBlockStore has a background copy of each block. When you request a block, you will get a copy of
|
||||||
|
* the data (instead of a direct pointer as InMemoryBlockStore does) and flushing will copy the data back to the
|
||||||
|
* background. This way, tests are more likely to fail if they use the blockstore wrongly.
|
||||||
|
*/
|
||||||
|
class FakeBlockStore: public BlockStoreWithRandomKeys {
|
||||||
|
public:
|
||||||
|
FakeBlockStore();
|
||||||
|
|
||||||
|
std::unique_ptr<Block> create(const Key &key, size_t size) override;
|
||||||
|
std::unique_ptr<Block> load(const Key &key) override;
|
||||||
|
|
||||||
|
void updateData(const std::string &key, const Data &data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::map<std::string, Data> _blocks;
|
||||||
|
|
||||||
|
//This vector keeps a handle of the data regions for all created FakeBlock objects.
|
||||||
|
//This way, it is ensured that no two created FakeBlock objects will work on the
|
||||||
|
//same data region. Without this, it could happen that a test case creates a FakeBlock,
|
||||||
|
//destructs it, creates another one, and the new one gets the same memory region.
|
||||||
|
//We want to avoid this for the reasons mentioned above (overflow data).
|
||||||
|
std::vector<std::shared_ptr<Data>> _used_dataregions_for_blocks;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(FakeBlockStore);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,31 @@
|
|||||||
|
#include <blockstore/implementations/testfake/FakeBlock.h>
|
||||||
|
#include <blockstore/implementations/testfake/FakeBlockStore.h>
|
||||||
|
#include <test/blockstore/testutils/BlockStoreWithRandomKeysTest.h>
|
||||||
|
#include <test/blockstore/testutils/BlockStoreTest.h>
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
|
||||||
|
using blockstore::BlockStore;
|
||||||
|
using blockstore::BlockStoreWithRandomKeys;
|
||||||
|
using blockstore::testfake::FakeBlockStore;
|
||||||
|
|
||||||
|
using std::unique_ptr;
|
||||||
|
using std::make_unique;
|
||||||
|
|
||||||
|
class FakeBlockStoreTestFixture: public BlockStoreTestFixture {
|
||||||
|
public:
|
||||||
|
unique_ptr<BlockStore> createBlockStore() override {
|
||||||
|
return make_unique<FakeBlockStore>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(TestFake, BlockStoreTest, FakeBlockStoreTestFixture);
|
||||||
|
|
||||||
|
class FakeBlockStoreWithRandomKeysTestFixture: public BlockStoreWithRandomKeysTestFixture {
|
||||||
|
public:
|
||||||
|
unique_ptr<BlockStoreWithRandomKeys> createBlockStore() override {
|
||||||
|
return make_unique<FakeBlockStore>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(TestFake, BlockStoreWithRandomKeysTest, FakeBlockStoreWithRandomKeysTestFixture);
|
@ -20,6 +20,12 @@ void DataBlockFixture::fillFileWithRandomData(long long int IV) {
|
|||||||
val += 1442695040888963407;
|
val += 1442695040888963407;
|
||||||
reinterpret_cast<long long int*>(_fileData)[i] = val;
|
reinterpret_cast<long long int*>(_fileData)[i] = val;
|
||||||
}
|
}
|
||||||
|
//Fill remaining bytes
|
||||||
|
for(size_t i=(_size/sizeof(long long int))*sizeof(long long int); i<_size; ++i) {
|
||||||
|
val *= 6364136223846793005L;
|
||||||
|
val += 1442695040888963407;
|
||||||
|
reinterpret_cast<char*>(_fileData)[i] = val;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *DataBlockFixture::data() const {
|
const char *DataBlockFixture::data() const {
|
||||||
|
Loading…
Reference in New Issue
Block a user