Add test cases for BlockStore2 and fix existing BlockStore2 implementations

This commit is contained in:
Sebastian Messmer 2017-02-21 22:27:46 +00:00
parent 70bfc47a2f
commit dbf54b9563
15 changed files with 612 additions and 60 deletions

View File

@ -9,6 +9,7 @@ set(SOURCES
implementations/testfake/FakeBlock.cpp implementations/testfake/FakeBlock.cpp
implementations/inmemory/InMemoryBlock.cpp implementations/inmemory/InMemoryBlock.cpp
implementations/inmemory/InMemoryBlockStore.cpp implementations/inmemory/InMemoryBlockStore.cpp
implementations/inmemory/InMemoryBlockStore2.cpp
implementations/parallelaccess/ParallelAccessBlockStore.cpp implementations/parallelaccess/ParallelAccessBlockStore.cpp
implementations/parallelaccess/BlockRef.cpp implementations/parallelaccess/BlockRef.cpp
implementations/parallelaccess/ParallelAccessBlockStoreAdapter.cpp implementations/parallelaccess/ParallelAccessBlockStoreAdapter.cpp

View File

@ -21,7 +21,7 @@ public:
} }
boost::future<bool> tryCreate(const Key &key, const cpputils::Data &data) override { boost::future<bool> tryCreate(const Key &key, const cpputils::Data &data) override {
cpputils::Data encrypted = _encrypt(data); cpputils::Data encrypted = _encrypt(key, data);
return _baseBlockStore->tryCreate(key, encrypted); return _baseBlockStore->tryCreate(key, encrypted);
} }
@ -31,34 +31,43 @@ public:
boost::future<boost::optional<cpputils::Data>> load(const Key &key) const override { boost::future<boost::optional<cpputils::Data>> load(const Key &key) const override {
auto loaded = _baseBlockStore->load(key); auto loaded = _baseBlockStore->load(key);
return loaded.then([this] (boost::future<boost::optional<cpputils::Data>> data_) { return loaded.then([this, key] (boost::future<boost::optional<cpputils::Data>> data_) {
auto data = data_.get(); auto data = data_.get();
if (boost::none == data) { if (boost::none == data) {
return boost::optional<cpputils::Data>(boost::none); return boost::optional<cpputils::Data>(boost::none);
} }
return _tryDecrypt(data); return _tryDecrypt(key, *data);
}); });
} }
boost::future<void> store(const Key &key, const cpputils::Data &data) override { boost::future<void> store(const Key &key, const cpputils::Data &data) override {
cpputils::Data encrypted = _encrypt(data); cpputils::Data encrypted = _encrypt(key, data);
return _baseBlockStore->store(key, encrypted); return _baseBlockStore->store(key, encrypted);
} }
private: private:
cpputils::Data _encrypt(const Key &key, const cpputils::Data &data) { // This header is prepended to blocks to allow future versions to have compatibility.
static constexpr uint16_t FORMAT_VERSION_HEADER = 0;
cpputils::Data _encrypt(const Key &key, const cpputils::Data &data) const {
cpputils::Data plaintextWithHeader = _prependKeyHeaderToData(key, data); cpputils::Data plaintextWithHeader = _prependKeyHeaderToData(key, data);
return Cipher::encrypt((byte*)plaintextWithHeader.data(), plaintextWithHeader.size(), _encKey); cpputils::Data encrypted = Cipher::encrypt((byte*)plaintextWithHeader.data(), plaintextWithHeader.size(), _encKey);
return _prependFormatHeaderToData(encrypted);
} }
boost::optional<cpputils::Data> _tryDecrypt(const Key &key, const cpputils::Data &data) { boost::optional<cpputils::Data> _tryDecrypt(const Key &key, const cpputils::Data &data) const {
boost::optional<cpputils::Data> decrypted = Cipher::decrypt((byte*)data.data(), data.size(), _encKey); auto ciphertext = _checkAndRemoveFormatHeader(data);
if (boost::none != decrypted && !_keyHeaderIsCorrect(key, *decrypted)) { boost::optional<cpputils::Data> decrypted = Cipher::decrypt((byte*)ciphertext.data(), ciphertext.size(), _encKey);
if (boost::none == decrypted) {
// TODO Warning // TODO Warning
return boost::none; return boost::none;
} }
return decrypted; if (!_keyHeaderIsCorrect(key, *decrypted)) {
// TODO Warning
return boost::none;
}
return _removeKeyHeader(*decrypted);
} }
static cpputils::Data _prependKeyHeaderToData(const Key &key, const cpputils::Data &data) { static cpputils::Data _prependKeyHeaderToData(const Key &key, const cpputils::Data &data) {
@ -72,6 +81,24 @@ private:
return 0 == std::memcmp(key.data(), data.data(), Key::BINARY_LENGTH); return 0 == std::memcmp(key.data(), data.data(), Key::BINARY_LENGTH);
} }
static cpputils::Data _prependFormatHeaderToData(const cpputils::Data &data) {
cpputils::Data dataWithHeader(sizeof(FORMAT_VERSION_HEADER) + data.size());
std::memcpy(dataWithHeader.dataOffset(0), &FORMAT_VERSION_HEADER, sizeof(FORMAT_VERSION_HEADER));
std::memcpy(dataWithHeader.dataOffset(sizeof(FORMAT_VERSION_HEADER)), data.data(), data.size());
return dataWithHeader;
}
static cpputils::Data _removeKeyHeader(const cpputils::Data &data) {
return data.copyAndRemovePrefix(Key::BINARY_LENGTH);
}
static cpputils::Data _checkAndRemoveFormatHeader(const cpputils::Data &data) {
if (*reinterpret_cast<decltype(FORMAT_VERSION_HEADER)*>(data.data()) != FORMAT_VERSION_HEADER) {
throw std::runtime_error("The encrypted block has the wrong format. Was it created with a newer version of CryFS?");
}
return data.copyAndRemovePrefix(sizeof(FORMAT_VERSION_HEADER));
}
cpputils::unique_ref<BlockStore2> _baseBlockStore; cpputils::unique_ref<BlockStore2> _baseBlockStore;
typename Cipher::EncryptionKey _encKey; typename Cipher::EncryptionKey _encKey;

View File

@ -0,0 +1,65 @@
#include "InMemoryBlockStore2.h"
#include <memory>
#include <cpp-utils/assert/assert.h>
#include <cpp-utils/system/get_total_memory.h>
using std::make_unique;
using std::string;
using std::mutex;
using std::lock_guard;
using std::piecewise_construct;
using std::make_tuple;
using std::make_pair;
using cpputils::Data;
using cpputils::unique_ref;
using cpputils::make_unique_ref;
using boost::optional;
using boost::none;
using boost::future;
using boost::make_ready_future;
namespace blockstore {
namespace inmemory {
InMemoryBlockStore2::InMemoryBlockStore2()
: _blocks() {}
future<bool> InMemoryBlockStore2::tryCreate(const Key &key, const Data &data) {
auto result = _blocks.insert(make_pair(key, data.copy()));
return make_ready_future(result.second); // Return if insertion was successful (i.e. key didn't exist yet)
}
future<bool> InMemoryBlockStore2::remove(const Key &key) {
auto found = _blocks.find(key);
if (found == _blocks.end()) {
// Key not found
return make_ready_future(false);
}
_blocks.erase(found);
return make_ready_future(true);
}
future<optional<Data>> InMemoryBlockStore2::load(const Key &key) const {
auto found = _blocks.find(key);
if (found == _blocks.end()) {
return make_ready_future(optional<Data>(none));
}
return make_ready_future(optional<Data>(found->second.copy()));
}
future<void> InMemoryBlockStore2::store(const Key &key, const Data &data) {
auto found = _blocks.find(key);
if (found == _blocks.end()) {
return tryCreate(key, data).then([] (future<bool> success) {
if (!success.get()) {
throw std::runtime_error("Could neither save nor create the block in InMemoryBlockStore::store()");
}
});
}
found->second = data.copy();
return make_ready_future();
}
}
}

View File

@ -0,0 +1,30 @@
#pragma once
#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_INMEMORY_INMEMORYBLOCKSTORE2_H_
#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_INMEMORY_INMEMORYBLOCKSTORE2_H_
#include "../../interface/BlockStore2.h"
#include <cpp-utils/macros.h>
#include <unordered_map>
namespace blockstore {
namespace inmemory {
class InMemoryBlockStore2 final: public BlockStore2 {
public:
InMemoryBlockStore2();
boost::future<bool> tryCreate(const Key &key, const cpputils::Data &data) override;
boost::future<bool> remove(const Key &key) override;
boost::future<boost::optional<cpputils::Data>> load(const Key &key) const override;
boost::future<void> store(const Key &key, const cpputils::Data &data) override;
private:
std::unordered_map<Key, cpputils::Data> _blocks;
DISALLOW_COPY_AND_ASSIGN(InMemoryBlockStore2);
};
}
}
#endif

View File

@ -1,7 +1,12 @@
#include "OnDiskBlockStore2.h" #include "OnDiskBlockStore2.h"
using std::string;
namespace blockstore { namespace blockstore {
namespace ondisk { namespace ondisk {
const string OnDiskBlockStore2::FORMAT_VERSION_HEADER_PREFIX = "cryfs;block;";
const string OnDiskBlockStore2::FORMAT_VERSION_HEADER = OnDiskBlockStore2::FORMAT_VERSION_HEADER_PREFIX + "0";
} }
} }

View File

@ -7,48 +7,95 @@
#include <cpp-utils/macros.h> #include <cpp-utils/macros.h>
#include <cpp-utils/pointer/unique_ref.h> #include <cpp-utils/pointer/unique_ref.h>
#include "OnDiskBlockStore.h" #include "OnDiskBlockStore.h"
#include <cpp-utils/logging/logging.h>
namespace blockstore { namespace blockstore {
namespace ondisk { namespace ondisk {
//TODO Implement without basing on OnDiskBlockStore
class OnDiskBlockStore2 final: public BlockStore2 { class OnDiskBlockStore2 final: public BlockStore2 {
public: public:
explicit OnDiskBlockStore2(cpputils::unique_ref<OnDiskBlockStore> base) explicit OnDiskBlockStore2(const boost::filesystem::path& path)
: _base(std::move(base)) {} : _rootDir(path) {}
boost::future<bool> tryCreate(const Key &key, const cpputils::Data &data) override { boost::future<bool> tryCreate(const Key &key, const cpputils::Data &data) override {
auto created = _base->tryCreate(key, data.copy()); auto filepath = _getFilepath(key);
if (boost::none == created) { if (boost::filesystem::exists(filepath)) {
return boost::make_ready_future(false); return boost::make_ready_future(false);
} else {
return boost::make_ready_future(true);
} }
store(key, data).wait();
return boost::make_ready_future(true);
} }
boost::future<bool> remove(const Key &key) override { boost::future<bool> remove(const Key &key) override {
_base->remove(key); auto filepath = _getFilepath(key);
if (!boost::filesystem::is_regular_file(filepath)) { // TODO Is this branch necessary?
return boost::make_ready_future(false);
}
bool retval = boost::filesystem::remove(filepath);
if (!retval) {
cpputils::logging::LOG(cpputils::logging::ERROR, "Couldn't find block {} to remove", key.ToString());
return boost::make_ready_future(false);
}
if (boost::filesystem::is_empty(filepath.parent_path())) {
boost::filesystem::remove(filepath.parent_path());
}
return boost::make_ready_future(true); return boost::make_ready_future(true);
} }
boost::future<boost::optional<cpputils::Data>> load(const Key &key) const override { boost::future<boost::optional<cpputils::Data>> load(const Key &key) const override {
auto block = _base->load(key); auto fileContent = cpputils::Data::LoadFromFile(_getFilepath(key));
if (boost::none == block) { if (fileContent == boost::none) {
return boost::make_ready_future<boost::optional<cpputils::Data>>(boost::none); return boost::make_ready_future(boost::optional<cpputils::Data>(boost::none));
} }
cpputils::Data data((*block)->size()); return boost::make_ready_future(boost::optional<cpputils::Data>(_checkAndRemoveHeader(std::move(*fileContent))));
std::memcpy(data.data(), (*block)->data(), data.size());
return boost::make_ready_future<boost::optional<cpputils::Data>>(std::move(data));
} }
boost::future<void> store(const Key &key, const cpputils::Data &data) override { boost::future<void> store(const Key &key, const cpputils::Data &data) override {
_base->overwrite(key, data.copy()); cpputils::Data fileContent(formatVersionHeaderSize() + data.size());
std::memcpy(fileContent.data(), FORMAT_VERSION_HEADER.c_str(), formatVersionHeaderSize());
std::memcpy(fileContent.dataOffset(formatVersionHeaderSize()), data.data(), data.size());
auto filepath = _getFilepath(key);
boost::filesystem::create_directory(filepath.parent_path()); // TODO Instead create all of them once at fs creation time?
fileContent.StoreToFile(filepath);
return boost::make_ready_future(); return boost::make_ready_future();
} }
private: private:
cpputils::unique_ref<OnDiskBlockStore> _base; boost::filesystem::path _rootDir;
static const std::string FORMAT_VERSION_HEADER_PREFIX;
static const std::string FORMAT_VERSION_HEADER;
boost::filesystem::path _getFilepath(const Key &key) const {
std::string keyStr = key.ToString();
return _rootDir / keyStr.substr(0,3) / keyStr.substr(3);
}
static cpputils::Data _checkAndRemoveHeader(const cpputils::Data &data) {
if (!_isAcceptedCryfsHeader(data)) {
if (_isOtherCryfsHeader(data)) {
throw std::runtime_error("This block is not supported yet. Maybe it was created with a newer version of CryFS?");
} else {
throw std::runtime_error("This is not a valid block.");
}
}
cpputils::Data result(data.size() - formatVersionHeaderSize());
std::memcpy(result.data(), data.dataOffset(formatVersionHeaderSize()), result.size());
return result;
}
static bool _isAcceptedCryfsHeader(const cpputils::Data &data) {
return 0 == std::memcmp(data.data(), FORMAT_VERSION_HEADER.c_str(), formatVersionHeaderSize());
}
static bool _isOtherCryfsHeader(const cpputils::Data &data) {
return 0 == std::memcmp(data.data(), FORMAT_VERSION_HEADER_PREFIX.c_str(), FORMAT_VERSION_HEADER_PREFIX.size());
}
static unsigned int formatVersionHeaderSize() {
return FORMAT_VERSION_HEADER.size() + 1; // +1 because of the null byte
}
DISALLOW_COPY_AND_ASSIGN(OnDiskBlockStore2); DISALLOW_COPY_AND_ASSIGN(OnDiskBlockStore2);
}; };

View File

@ -23,6 +23,7 @@ namespace blockstore {
: _reason("Integrity violation: " + reason) { : _reason("Integrity violation: " + reason) {
} }
friend class VersionCountingBlockStore; friend class VersionCountingBlockStore;
friend class VersionCountingBlockStore2;
std::string _reason; std::string _reason;
}; };

View File

@ -5,6 +5,7 @@
#include "../../interface/BlockStore2.h" #include "../../interface/BlockStore2.h"
#include <cpp-utils/macros.h> #include <cpp-utils/macros.h>
#include "KnownBlockVersions.h" #include "KnownBlockVersions.h"
#include "IntegrityViolationError.h"
namespace blockstore { namespace blockstore {
namespace versioncounting { namespace versioncounting {
@ -19,42 +20,57 @@ public:
boost::future<bool> tryCreate(const Key &key, const cpputils::Data &data) override { boost::future<bool> tryCreate(const Key &key, const cpputils::Data &data) override {
_checkNoPastIntegrityViolations(); _checkNoPastIntegrityViolations();
uint64_t version = blockStore->knownBlockVersions()->incrementVersion(key); uint64_t version = _knownBlockVersions.incrementVersion(key);
cpputils::Data dataWithHeader = _prependHeaderToData(blockStore->knownBlockVersions()->myClientId(), version, data); cpputils::Data dataWithHeader = _prependHeaderToData(_knownBlockVersions.myClientId(), version, data);
return baseBlockStore_->tryCreate(key, dataWithHeader); return _baseBlockStore->tryCreate(key, dataWithHeader);
} }
boost::future<bool> remove(const Key &key) override { boost::future<bool> remove(const Key &key) override {
_checkNoPastIntegrityViolations(); _checkNoPastIntegrityViolations();
_knownBlockVersions.markBlockAsDeleted(key); _knownBlockVersions.markBlockAsDeleted(key);
return baseBlockStore->remove(key); return _baseBlockStore->remove(key);
} }
boost::future<boost::optional<cpputils::Data>> load(const Key &key) const override { boost::future<boost::optional<cpputils::Data>> load(const Key &key) const override {
_checkNoPastIntegrityViolations(); _checkNoPastIntegrityViolations();
auto loaded = baseBlockStore_->load(key); return _baseBlockStore->load(key).then([this, key] (boost::future<boost::optional<cpputils::Data>> loaded_) {
loaded.then([this, key] (boost::future<boost::optional<cpputils::Data>> loaded_) {
auto loaded = loaded_.get(); auto loaded = loaded_.get();
if (boost::none == loaded) { if (boost::none == loaded) {
if (_missingBlockIsIntegrityViolation && _knownBlockVersions.blockShouldExist(key)) { if (_missingBlockIsIntegrityViolation && _knownBlockVersions.blockShouldExist(key)) {
integrityViolationDetected("A block that should exist wasn't found. Did an attacker delete it?"); integrityViolationDetected("A block that should exist wasn't found. Did an attacker delete it?");
} }
return boost::none; return boost::optional<cpputils::Data>(boost::none);
} }
if (!_checkHeader(key, *loaded)) { _checkHeader(key, *loaded);
return boost::none; return boost::optional<cpputils::Data>(_removeHeader(*loaded));
}
return *loaded;
}); });
} }
boost::future<void> store(const Key &key, const cpputils::Data &data) override { boost::future<void> store(const Key &key, const cpputils::Data &data) override {
_checkNoPastIntegrityViolations(); _checkNoPastIntegrityViolations();
TODO Need to load first so it can see if the version number changed by another //TODO There's a bug in the next branch in override(). They have to load and read the old version number too.
THIS BUG IS ALSO IN THE NEXT BRANCH (i.e. without these changes here) return load(key).then([this, key, data = data.copy()] (boost::future<boost::optional<cpputils::Data>> loaded_) {
//... auto loaded = loaded_.get();
if (boost::none == loaded) {
return tryCreate(key, data).then([] (boost::future<bool> success) {
if (!success.get()) {
throw std::runtime_error("Could neither store nor create the block in VersionCountingBlockStore::store");
}
});
}
// Loading the block already read the newest version number into _knownBlockVersions, now we only have to increment it
// TODO Check that (with a caching blockstore below) this doesn't impact performance
uint64_t version = _knownBlockVersions.incrementVersion(key);
cpputils::Data dataWithHeader = _prependHeaderToData(_knownBlockVersions.myClientId(), version, data);
return _baseBlockStore->store(key, dataWithHeader);
});
} }
private:
// This header is prepended to blocks to allow future versions to have compatibility.
static constexpr uint16_t FORMAT_VERSION_HEADER = 0;
public:
static constexpr uint64_t VERSION_ZERO = 0; static constexpr uint64_t VERSION_ZERO = 0;
static constexpr unsigned int CLIENTID_HEADER_OFFSET = sizeof(FORMAT_VERSION_HEADER); static constexpr unsigned int CLIENTID_HEADER_OFFSET = sizeof(FORMAT_VERSION_HEADER);
static constexpr unsigned int VERSION_HEADER_OFFSET = sizeof(FORMAT_VERSION_HEADER) + sizeof(uint32_t); static constexpr unsigned int VERSION_HEADER_OFFSET = sizeof(FORMAT_VERSION_HEADER) + sizeof(uint32_t);
@ -62,10 +78,7 @@ public:
private: private:
// This header is prepended to blocks to allow future versions to have compatibility. cpputils::Data _prependHeaderToData(uint32_t myClientId, uint64_t version, const cpputils::Data &data) {
static constexpr uint16_t FORMAT_VERSION_HEADER = 0;
cpputils::Data VersionCountingBlock::_prependHeaderToData(uint32_t myClientId, uint64_t version, const cpputils::Data &data) {
static_assert(HEADER_LENGTH == sizeof(FORMAT_VERSION_HEADER) + sizeof(myClientId) + sizeof(version), "Wrong header length"); static_assert(HEADER_LENGTH == sizeof(FORMAT_VERSION_HEADER) + sizeof(myClientId) + sizeof(version), "Wrong header length");
cpputils::Data result(data.size() + HEADER_LENGTH); cpputils::Data result(data.size() + HEADER_LENGTH);
std::memcpy(result.dataOffset(0), &FORMAT_VERSION_HEADER, sizeof(FORMAT_VERSION_HEADER)); std::memcpy(result.dataOffset(0), &FORMAT_VERSION_HEADER, sizeof(FORMAT_VERSION_HEADER));
@ -75,32 +88,45 @@ private:
return result; return result;
} }
void _checkHeader(const Key &key, const cpputils::Data &data) { void _checkHeader(const Key &key, const cpputils::Data &data) const {
_checkFormatHeader(data); _checkFormatHeader(data);
_checkVersionHeader(key, data); _checkVersionHeader(key, data);
} }
void _checkFormatHeader(const cpputils::Data &data) { void _checkFormatHeader(const cpputils::Data &data) const {
if (*reinterpret_cast<decltype(FORMAT_VERSION_HEADER)*>(data.data()) != FORMAT_VERSION_HEADER) { if (*reinterpret_cast<decltype(FORMAT_VERSION_HEADER)*>(data.data()) != FORMAT_VERSION_HEADER) {
throw std::runtime_error("The versioned block has the wrong format. Was it created with a newer version of CryFS?"); throw std::runtime_error("The versioned block has the wrong format. Was it created with a newer version of CryFS?");
} }
} }
void _checkVersionHeader(const Key &key, const cpputils::Data &data) { void _checkVersionHeader(const Key &key, const cpputils::Data &data) const {
uint32_t clientId; uint32_t clientId = _readClientId(data);
std::memcpy(&clientId, _dataWithHeader.dataOffset(CLIENTID_HEADER_OFFSET), sizeof(clientId)); uint64_t version = _readVersion(data);
uint64_t version; if(!_knownBlockVersions.checkAndUpdateVersion(clientId, key, version)) {
std::memcpy(&version, _dataWithHeader.dataOffset(VERSION_HEADER_OFFSET), sizeof(version));
if(!_knownBlockVersions.checkAndUpdateVersion(lastClientId, key, version)) {
integrityViolationDetected("The block version number is too low. Did an attacker try to roll back the block or to re-introduce a deleted block?"); integrityViolationDetected("The block version number is too low. Did an attacker try to roll back the block or to re-introduce a deleted block?");
} }
} }
void _checkNoPastIntegrityViolations() { static uint32_t _readClientId(const cpputils::Data &data) {
uint32_t clientId;
std::memcpy(&clientId, data.dataOffset(CLIENTID_HEADER_OFFSET), sizeof(clientId));
return clientId;
}
static uint64_t _readVersion(const cpputils::Data &data) {
uint64_t version;
std::memcpy(&version, data.dataOffset(VERSION_HEADER_OFFSET), sizeof(version));
return version;
}
cpputils::Data _removeHeader(const cpputils::Data &data) const {
return data.copyAndRemovePrefix(HEADER_LENGTH);
}
void _checkNoPastIntegrityViolations() const {
if (_integrityViolationDetected) { if (_integrityViolationDetected) {
throw std::runtime_error(string() + throw std::runtime_error(std::string() +
"There was an integrity violation detected. Preventing any further access to the file system. " + "There was an integrity violation detected. Preventing any further access to the file system. " +
"If you want to reset the integrity data (i.e. accept changes made by a potential attacker), " + "If you want to reset the integrity data (i.e. accept changes made by a potential attacker), " +
"please unmount the file system and delete the following file before re-mounting it: " + "please unmount the file system and delete the following file before re-mounting it: " +
@ -108,13 +134,13 @@ private:
} }
} }
void integrityViolationDetected(const string &reason) const { void integrityViolationDetected(const std::string &reason) const {
_integrityViolationDetected = true; _integrityViolationDetected = true;
throw IntegrityViolationError(reason); throw IntegrityViolationError(reason);
} }
cpputils::unique_ref<BlockStore2> _baseBlockStore; cpputils::unique_ref<BlockStore2> _baseBlockStore;
KnownBlockVersions _knownBlockVersions; mutable KnownBlockVersions _knownBlockVersions;
const bool _missingBlockIsIntegrityViolation; const bool _missingBlockIsIntegrityViolation;
mutable bool _integrityViolationDetected; mutable bool _integrityViolationDetected;

View File

@ -10,6 +10,8 @@
#include <boost/thread/future.hpp> #include <boost/thread/future.hpp>
#include <cpp-utils/random/Random.h> #include <cpp-utils/random/Random.h>
// TODO warn_unused_result for all boost::future interfaces
namespace blockstore { namespace blockstore {
class BlockStore2 { class BlockStore2 {

View File

@ -8,6 +8,7 @@
#include "../macros.h" #include "../macros.h"
#include <memory> #include <memory>
#include <fstream> #include <fstream>
#include "../assert/assert.h"
namespace cpputils { namespace cpputils {
@ -21,6 +22,9 @@ public:
Data copy() const; Data copy() const;
//TODO Test copyAndRemovePrefix
Data copyAndRemovePrefix(size_t prefixSize) const;
void *data(); void *data();
const void *data() const; const void *data() const;
@ -94,6 +98,13 @@ inline Data Data::copy() const {
return copy; return copy;
} }
inline Data Data::copyAndRemovePrefix(size_t prefixSize) const {
ASSERT(prefixSize <= _size, "Can't remove more than there is");
Data copy(_size - prefixSize);
std::memcpy(copy.data(), dataOffset(prefixSize), copy.size());
return copy;
}
inline void *Data::data() { inline void *Data::data() {
return const_cast<void*>(const_cast<const Data*>(this)->data()); return const_cast<void*>(const_cast<const Data*>(this)->data());
} }

View File

@ -1,8 +1,11 @@
#include <cpp-utils/crypto/symmetric/ciphers.h> #include <cpp-utils/crypto/symmetric/ciphers.h>
#include <cpp-utils/crypto/symmetric/Cipher.h> #include <cpp-utils/crypto/symmetric/Cipher.h>
#include "blockstore/implementations/encrypted/EncryptedBlockStore.h" #include "blockstore/implementations/encrypted/EncryptedBlockStore.h"
#include "blockstore/implementations/encrypted/EncryptedBlockStore2.h"
#include "blockstore/implementations/testfake/FakeBlockStore.h" #include "blockstore/implementations/testfake/FakeBlockStore.h"
#include "blockstore/implementations/inmemory/InMemoryBlockStore2.h"
#include "../../testutils/BlockStoreTest.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. //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 "../../../cpp-utils/crypto/symmetric/testutils/FakeAuthenticatedCipher.h"
#include <gtest/gtest.h> #include <gtest/gtest.h>
@ -10,8 +13,11 @@
using ::testing::Test; using ::testing::Test;
using blockstore::BlockStore; using blockstore::BlockStore;
using blockstore::BlockStore2;
using blockstore::encrypted::EncryptedBlockStore; using blockstore::encrypted::EncryptedBlockStore;
using blockstore::encrypted::EncryptedBlockStore2;
using blockstore::testfake::FakeBlockStore; using blockstore::testfake::FakeBlockStore;
using blockstore::inmemory::InMemoryBlockStore2;
using cpputils::AES256_GCM; using cpputils::AES256_GCM;
using cpputils::AES256_CFB; using cpputils::AES256_CFB;
using cpputils::FakeAuthenticatedCipher; using cpputils::FakeAuthenticatedCipher;
@ -38,3 +44,21 @@ private:
INSTANTIATE_TYPED_TEST_CASE_P(Encrypted_FakeCipher, BlockStoreTest, EncryptedBlockStoreTestFixture<FakeAuthenticatedCipher>); INSTANTIATE_TYPED_TEST_CASE_P(Encrypted_FakeCipher, BlockStoreTest, EncryptedBlockStoreTestFixture<FakeAuthenticatedCipher>);
INSTANTIATE_TYPED_TEST_CASE_P(Encrypted_AES256_GCM, BlockStoreTest, EncryptedBlockStoreTestFixture<AES256_GCM>); INSTANTIATE_TYPED_TEST_CASE_P(Encrypted_AES256_GCM, BlockStoreTest, EncryptedBlockStoreTestFixture<AES256_GCM>);
INSTANTIATE_TYPED_TEST_CASE_P(Encrypted_AES256_CFB, BlockStoreTest, EncryptedBlockStoreTestFixture<AES256_CFB>); INSTANTIATE_TYPED_TEST_CASE_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) {
Data data = DataFixture::generate(Cipher::EncryptionKey::BINARY_LENGTH, seed);
return Cipher::EncryptionKey::FromBinary(data.data());
}
};
INSTANTIATE_TYPED_TEST_CASE_P(Encrypted_FakeCipher, BlockStore2Test, EncryptedBlockStore2TestFixture<FakeAuthenticatedCipher>);
INSTANTIATE_TYPED_TEST_CASE_P(Encrypted_AES256_GCM, BlockStore2Test, EncryptedBlockStore2TestFixture<AES256_GCM>);
INSTANTIATE_TYPED_TEST_CASE_P(Encrypted_AES256_CFB, BlockStore2Test, EncryptedBlockStore2TestFixture<AES256_CFB>);

View File

@ -1,14 +1,18 @@
#include "blockstore/implementations/inmemory/InMemoryBlock.h" #include "blockstore/implementations/inmemory/InMemoryBlock.h"
#include "blockstore/implementations/inmemory/InMemoryBlockStore.h" #include "blockstore/implementations/inmemory/InMemoryBlockStore.h"
#include "blockstore/implementations/inmemory/InMemoryBlockStore2.h"
#include "../../testutils/BlockStoreTest.h" #include "../../testutils/BlockStoreTest.h"
#include "../../testutils/BlockStore2Test.h"
#include "../../testutils/BlockStoreWithRandomKeysTest.h" #include "../../testutils/BlockStoreWithRandomKeysTest.h"
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround.h> #include <cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround.h>
using blockstore::BlockStore; using blockstore::BlockStore;
using blockstore::BlockStore2;
using blockstore::BlockStoreWithRandomKeys; using blockstore::BlockStoreWithRandomKeys;
using blockstore::inmemory::InMemoryBlockStore; using blockstore::inmemory::InMemoryBlockStore;
using blockstore::inmemory::InMemoryBlockStore2;
using cpputils::unique_ref; using cpputils::unique_ref;
using cpputils::make_unique_ref; using cpputils::make_unique_ref;
@ -29,3 +33,12 @@ public:
}; };
INSTANTIATE_TYPED_TEST_CASE_P(InMemory, BlockStoreWithRandomKeysTest, InMemoryBlockStoreWithRandomKeysTestFixture); INSTANTIATE_TYPED_TEST_CASE_P(InMemory, BlockStoreWithRandomKeysTest, InMemoryBlockStoreWithRandomKeysTestFixture);
class InMemoryBlockStore2TestFixture: public BlockStore2TestFixture {
public:
unique_ref<BlockStore2> createBlockStore() override {
return make_unique_ref<InMemoryBlockStore2>();
}
};
INSTANTIATE_TYPED_TEST_CASE_P(InMemory, BlockStore2Test, InMemoryBlockStore2TestFixture);

View File

@ -1,6 +1,8 @@
#include "blockstore/implementations/ondisk/OnDiskBlock.h" #include "blockstore/implementations/ondisk/OnDiskBlock.h"
#include "blockstore/implementations/ondisk/OnDiskBlockStore.h" #include "blockstore/implementations/ondisk/OnDiskBlockStore.h"
#include "blockstore/implementations/ondisk/OnDiskBlockStore2.h"
#include "../../testutils/BlockStoreTest.h" #include "../../testutils/BlockStoreTest.h"
#include "../../testutils/BlockStore2Test.h"
#include "../../testutils/BlockStoreWithRandomKeysTest.h" #include "../../testutils/BlockStoreWithRandomKeysTest.h"
#include <gtest/gtest.h> #include <gtest/gtest.h>
@ -10,6 +12,8 @@
using blockstore::BlockStore; using blockstore::BlockStore;
using blockstore::BlockStoreWithRandomKeys; using blockstore::BlockStoreWithRandomKeys;
using blockstore::ondisk::OnDiskBlockStore; using blockstore::ondisk::OnDiskBlockStore;
using blockstore::BlockStore2;
using blockstore::ondisk::OnDiskBlockStore2;
using cpputils::TempDir; using cpputils::TempDir;
using cpputils::unique_ref; using cpputils::unique_ref;
@ -40,3 +44,16 @@ private:
}; };
INSTANTIATE_TYPED_TEST_CASE_P(OnDisk, BlockStoreWithRandomKeysTest, OnDiskBlockStoreWithRandomKeysTestFixture); INSTANTIATE_TYPED_TEST_CASE_P(OnDisk, BlockStoreWithRandomKeysTest, OnDiskBlockStoreWithRandomKeysTestFixture);
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_CASE_P(OnDisk, BlockStore2Test, OnDiskBlockStore2TestFixture);

View File

@ -1,15 +1,21 @@
#include "blockstore/implementations/versioncounting/VersionCountingBlockStore.h" #include "blockstore/implementations/versioncounting/VersionCountingBlockStore.h"
#include "blockstore/implementations/versioncounting/VersionCountingBlockStore2.h"
#include "blockstore/implementations/testfake/FakeBlockStore.h" #include "blockstore/implementations/testfake/FakeBlockStore.h"
#include "blockstore/implementations/inmemory/InMemoryBlockStore2.h"
#include "../../testutils/BlockStoreTest.h" #include "../../testutils/BlockStoreTest.h"
#include "../../testutils/BlockStore2Test.h"
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <cpp-utils/tempfile/TempFile.h> #include <cpp-utils/tempfile/TempFile.h>
using ::testing::Test; using ::testing::Test;
using blockstore::BlockStore; using blockstore::BlockStore;
using blockstore::BlockStore2;
using blockstore::versioncounting::VersionCountingBlockStore; using blockstore::versioncounting::VersionCountingBlockStore;
using blockstore::versioncounting::VersionCountingBlockStore2;
using blockstore::versioncounting::KnownBlockVersions; using blockstore::versioncounting::KnownBlockVersions;
using blockstore::testfake::FakeBlockStore; using blockstore::testfake::FakeBlockStore;
using blockstore::inmemory::InMemoryBlockStore2;
using cpputils::Data; using cpputils::Data;
using cpputils::DataFixture; using cpputils::DataFixture;
@ -17,16 +23,30 @@ using cpputils::make_unique_ref;
using cpputils::unique_ref; using cpputils::unique_ref;
using cpputils::TempFile; using cpputils::TempFile;
template<bool SINGLECLIENT> template<bool MissingBlockIsIntegrityViolation>
class VersionCountingBlockStoreTestFixture: public BlockStoreTestFixture { class VersionCountingBlockStoreTestFixture: public BlockStoreTestFixture {
public: public:
VersionCountingBlockStoreTestFixture() :stateFile(false) {} VersionCountingBlockStoreTestFixture() :stateFile(false) {}
TempFile stateFile; TempFile stateFile;
unique_ref<BlockStore> createBlockStore() override { unique_ref<BlockStore> createBlockStore() override {
return make_unique_ref<VersionCountingBlockStore>(make_unique_ref<FakeBlockStore>(), stateFile.path(), 0x12345678, SINGLECLIENT); return make_unique_ref<VersionCountingBlockStore>(make_unique_ref<FakeBlockStore>(), stateFile.path(), 0x12345678, MissingBlockIsIntegrityViolation);
} }
}; };
INSTANTIATE_TYPED_TEST_CASE_P(VersionCounting_multiclient, BlockStoreTest, VersionCountingBlockStoreTestFixture<false>); INSTANTIATE_TYPED_TEST_CASE_P(VersionCounting_multiclient, BlockStoreTest, VersionCountingBlockStoreTestFixture<false>);
INSTANTIATE_TYPED_TEST_CASE_P(VersionCounting_singleclient, BlockStoreTest, VersionCountingBlockStoreTestFixture<true>); INSTANTIATE_TYPED_TEST_CASE_P(VersionCounting_singleclient, BlockStoreTest, VersionCountingBlockStoreTestFixture<true>);
template<bool MissingBlockIsIntegrityViolation>
class VersionCountingBlockStore2TestFixture: public BlockStore2TestFixture {
public:
VersionCountingBlockStore2TestFixture() :stateFile(false) {}
TempFile stateFile;
unique_ref<BlockStore2> createBlockStore() override {
return make_unique_ref<VersionCountingBlockStore2>(make_unique_ref<InMemoryBlockStore2>(), stateFile.path(), 0x12345678, MissingBlockIsIntegrityViolation);
}
};
INSTANTIATE_TYPED_TEST_CASE_P(VersionCounting_multiclient, BlockStore2Test, VersionCountingBlockStore2TestFixture<false>);
INSTANTIATE_TYPED_TEST_CASE_P(VersionCounting_singleclient, BlockStore2Test, VersionCountingBlockStore2TestFixture<true>);

View File

@ -0,0 +1,263 @@
#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;
};
TYPED_TEST_CASE_P(BlockStore2Test);
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenCallingTryCreateOnExistingBlock_thenFails) {
blockstore::Key key = this->blockStore->create(cpputils::Data(1024)).get();
EXPECT_FALSE(this->blockStore->tryCreate(key, cpputils::Data(1024)).get());
}
TYPED_TEST_P(BlockStore2Test, givenEmptyBlockStore_whenCallingTryCreateOnNonExistingBlock_thenSucceeds) {
blockstore::Key key = blockstore::Key::FromString("1491BB4932A389EE14BC7090AC772972");
EXPECT_TRUE(this->blockStore->tryCreate(key, cpputils::Data(1024)).get());
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenCallingTryCreateOnNonExistingBlock_thenSucceeds) {
this->blockStore->create(cpputils::Data(512));
blockstore::Key key = blockstore::Key::FromString("1491BB4932A389EE14BC7090AC772972");
EXPECT_TRUE(this->blockStore->tryCreate(key, cpputils::Data(1024)).get());
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenLoadExistingBlock_thenSucceeds) {
blockstore::Key key = this->blockStore->create(cpputils::Data(1024)).get();
EXPECT_NE(boost::none, this->blockStore->load(key).get());
}
TYPED_TEST_P(BlockStore2Test, givenEmptyBlockStore_whenLoadNonexistingBlock_thenFails) {
const blockstore::Key key = blockstore::Key::FromString("1491BB4932A389EE14BC7090AC772972");
EXPECT_EQ(boost::none, this->blockStore->load(key).get());
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenLoadNonexistingBlock_thenFails) {
this->blockStore->create(cpputils::Data(512));
const blockstore::Key key = blockstore::Key::FromString("1491BB4932A389EE14BC7090AC772972");
EXPECT_EQ(boost::none, this->blockStore->load(key).get());
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenStoringExistingBlock_thenSucceeds) {
blockstore::Key key = this->blockStore->create(cpputils::Data(1024)).get();
this->blockStore->store(key, cpputils::Data(1024)).wait();
}
TYPED_TEST_P(BlockStore2Test, givenEmptyBlockStore_whenStoringNonexistingBlock_thenSucceeds) {
const blockstore::Key key = blockstore::Key::FromString("1491BB4932A389EE14BC7090AC772972");
this->blockStore->store(key, cpputils::Data(1024)).wait();
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenStoringNonexistingBlock_thenSucceeds) {
this->blockStore->create(cpputils::Data(512));
const blockstore::Key key = blockstore::Key::FromString("1491BB4932A389EE14BC7090AC772972");
this->blockStore->store(key, cpputils::Data(1024)).wait();
}
TYPED_TEST_P(BlockStore2Test, givenEmptyBlockStore_whenCreatingTwoBlocks_thenTheyGetDifferentKeys) {
blockstore::Key key1 = this->blockStore->create(cpputils::Data(1024)).get();
blockstore::Key key2 = this->blockStore->create(cpputils::Data(1024)).get();
EXPECT_NE(key1, key2);
}
TYPED_TEST_P(BlockStore2Test, givenOtherwiseEmptyBlockStore_whenRemovingBlock_thenBlockIsNotLoadableAnymore) {
blockstore::Key key = this->blockStore->create(cpputils::Data(1024)).get();
EXPECT_NE(boost::none, this->blockStore->load(key).get());
this->blockStore->remove(key).get();
EXPECT_EQ(boost::none, this->blockStore->load(key).get());
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenRemovingBlock_thenBlockIsNotLoadableAnymore) {
blockstore::Key key = this->blockStore->create(cpputils::Data(1024)).get();
this->blockStore->create(cpputils::Data(512));
EXPECT_NE(boost::none, this->blockStore->load(key).get());
this->blockStore->remove(key).get();
EXPECT_EQ(boost::none, this->blockStore->load(key).get());
}
TYPED_TEST_P(BlockStore2Test, givenOtherwiseEmptyBlockStore_whenRemovingExistingBlock_thenSucceeds) {
blockstore::Key key = this->blockStore->create(cpputils::Data(1024)).get();
EXPECT_EQ(true, this->blockStore->remove(key).get());
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenRemovingExistingBlock_thenSucceeds) {
blockstore::Key key = this->blockStore->create(cpputils::Data(1024)).get();
this->blockStore->create(cpputils::Data(512));
EXPECT_EQ(true, this->blockStore->remove(key).get());
}
TYPED_TEST_P(BlockStore2Test, givenEmptyBlockStore_whenRemovingNonexistingBlock_thenFails) {
const blockstore::Key key = blockstore::Key::FromString("1491BB4932A389EE14BC7090AC772972");
auto result = this->blockStore->remove(key).get();
EXPECT_EQ(false, result);
}
TYPED_TEST_P(BlockStore2Test, givenEmptyBlockStore_whenCreatingAndLoadingEmptyBlock_thenCorrectBlockLoads) {
auto key = this->blockStore->create(cpputils::Data(0)).get();
auto loaded = this->blockStore->load(key).get().value();
EXPECT_EQ(0u, loaded.size());
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenCreatingAndLoadingEmptyBlock_thenCorrectBlockLoads) {
this->blockStore->create(cpputils::Data(512));
auto key = this->blockStore->create(cpputils::Data(0)).get();
auto loaded = this->blockStore->load(key).get().value();
EXPECT_EQ(0u, loaded.size());
}
TYPED_TEST_P(BlockStore2Test, givenEmptyBlockStore_whenCreatingAndLoadingNonEmptyBlock_thenCorrectBlockLoads) {
cpputils::Data data = cpputils::DataFixture::generate(1024);
auto key = this->blockStore->create(data.copy()).get();
auto loaded = this->blockStore->load(key).get().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 key = this->blockStore->create(data.copy()).get();
auto loaded = this->blockStore->load(key).get().value();
EXPECT_EQ(loaded, data);
}
TYPED_TEST_P(BlockStore2Test, givenEmptyBlockStore_whenStoringAndLoadingNonExistingEmptyBlock_thenCorrectBlockLoads) {
const blockstore::Key key = blockstore::Key::FromString("1491BB4932A389EE14BC7090AC772972");
this->blockStore->store(key, cpputils::Data(0)).wait();
auto loaded = this->blockStore->load(key).get().value();
EXPECT_EQ(0u, loaded.size());
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenStoringAndLoadingNonExistingEmptyBlock_thenCorrectBlockLoads) {
this->blockStore->create(cpputils::Data(512));
const blockstore::Key key = blockstore::Key::FromString("1491BB4932A389EE14BC7090AC772972");
this->blockStore->store(key, cpputils::Data(0)).wait();
auto loaded = this->blockStore->load(key).get().value();
EXPECT_EQ(0u, loaded.size());
}
TYPED_TEST_P(BlockStore2Test, givenEmptyBlockStore_whenStoringAndLoadingNonExistingNonEmptyBlock_thenCorrectBlockLoads) {
const blockstore::Key key = blockstore::Key::FromString("1491BB4932A389EE14BC7090AC772972");
cpputils::Data data = cpputils::DataFixture::generate(1024);
this->blockStore->store(key, data.copy()).wait();
auto loaded = this->blockStore->load(key).get().value();
EXPECT_EQ(data, loaded);
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenStoringAndLoadingNonExistingNonEmptyBlock_thenCorrectBlockLoads) {
this->blockStore->create(cpputils::Data(512));
const blockstore::Key key = blockstore::Key::FromString("1491BB4932A389EE14BC7090AC772972");
cpputils::Data data = cpputils::DataFixture::generate(1024);
this->blockStore->store(key, data.copy()).wait();
auto loaded = this->blockStore->load(key).get().value();
EXPECT_EQ(data, loaded);
}
TYPED_TEST_P(BlockStore2Test, givenEmptyBlockStore_whenStoringAndLoadingExistingEmptyBlock_thenCorrectBlockLoads) {
auto key = this->blockStore->create(cpputils::Data(512)).get();
this->blockStore->store(key, cpputils::Data(0)).wait();
auto loaded = this->blockStore->load(key).get().value();
EXPECT_EQ(0u, loaded.size());
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenStoringAndLoadingExistingEmptyBlock_thenCorrectBlockLoads) {
this->blockStore->create(cpputils::Data(512)).get();
auto key = this->blockStore->create(cpputils::Data(512)).get();
this->blockStore->store(key, cpputils::Data(0)).wait();
auto loaded = this->blockStore->load(key).get().value();
EXPECT_EQ(0u, loaded.size());
}
TYPED_TEST_P(BlockStore2Test, givenEmptyBlockStore_whenStoringAndLoadingExistingNonEmptyBlock_thenCorrectBlockLoads) {
auto key = this->blockStore->create(cpputils::Data(512)).get();
cpputils::Data data = cpputils::DataFixture::generate(1024);
this->blockStore->store(key, data.copy()).wait();
auto loaded = this->blockStore->load(key).get().value();
EXPECT_EQ(data, loaded);
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenStoringAndLoadingExistingNonEmptyBlock_thenCorrectBlockLoads) {
this->blockStore->create(cpputils::Data(512)).get();
auto key = this->blockStore->create(cpputils::Data(512)).get();
cpputils::Data data = cpputils::DataFixture::generate(1024);
this->blockStore->store(key, data.copy()).wait();
auto loaded = this->blockStore->load(key).get().value();
EXPECT_EQ(data, loaded);
}
TYPED_TEST_P(BlockStore2Test, givenEmptyBlockStore_whenLoadingNonExistingBlock_thenFails) {
const blockstore::Key key = blockstore::Key::FromString("1491BB4932A389EE14BC7090AC772972");
EXPECT_EQ(boost::none, this->blockStore->load(key).get());
}
TYPED_TEST_P(BlockStore2Test, givenNonEmptyBlockStore_whenLoadingNonExistingBlock_thenFails) {
this->blockStore->create(cpputils::Data(512)).get();
const blockstore::Key key = blockstore::Key::FromString("1491BB4932A389EE14BC7090AC772972");
EXPECT_EQ(boost::none, this->blockStore->load(key).get());
}
REGISTER_TYPED_TEST_CASE_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,
givenEmptyBlockStore_whenCreatingAndLoadingEmptyBlock_thenCorrectBlockLoads,
givenNonEmptyBlockStore_whenCreatingAndLoadingEmptyBlock_thenCorrectBlockLoads,
givenEmptyBlockStore_whenCreatingAndLoadingNonEmptyBlock_thenCorrectBlockLoads,
givenNonEmptyBlockStore_whenCreatingAndLoadingNonEmptyBlock_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
);
#endif