blob: 7eab41043e189c901187ac73e51281ae765f4dea [file] [log] [blame]
/*
Copyright (c) 2018-2019 Nokia.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <gtest/gtest.h>
#include "private/error.hpp"
#include "private/redis/asyncredisstorage.hpp"
#include "private/syncstorageimpl.hpp"
#include "private/tst/asyncstoragemock.hpp"
#include "private/tst/systemmock.hpp"
#include <sdl/backenderror.hpp>
#include <sdl/invalidnamespace.hpp>
#include <sdl/notconnected.hpp>
#include <sdl/operationinterrupted.hpp>
#include <sdl/rejectedbybackend.hpp>
using namespace shareddatalayer;
using namespace shareddatalayer::redis;
using namespace shareddatalayer::tst;
using namespace testing;
namespace
{
class SyncStorageImplTest: public testing::Test
{
public:
std::unique_ptr<SyncStorageImpl> syncStorage;
/* AsyncStorageMock ownership will be passed to implementation. To be able to do verification
* with the mock object also here after its ownership is passed we take raw pointer to
* AsyncStorageMock before passing it to implementation. Works fine, as implementation will
* not release injected mock object before test case execution finishes
*/
std::unique_ptr<StrictMock<AsyncStorageMock>> asyncStorageMockPassedToImplementation;
StrictMock<AsyncStorageMock>* asyncStorageMockRawPtr;
StrictMock<SystemMock> systemMock;
AsyncStorage::ModifyAck savedModifyAck;
AsyncStorage::ModifyIfAck savedModifyIfAck;
AsyncStorage::GetAck savedGetAck;
AsyncStorage::FindKeysAck savedFindKeysAck;
AsyncStorage::ReadyAck savedReadyAck;
int pFd;
SyncStorage::DataMap dataMap;
SyncStorage::Keys keys;
const SyncStorage::Namespace ns;
SyncStorageImplTest():
asyncStorageMockPassedToImplementation(new StrictMock<AsyncStorageMock>()),
asyncStorageMockRawPtr(asyncStorageMockPassedToImplementation.get()),
pFd(10),
dataMap({{ "key1", { 0x0a, 0x0b, 0x0c } }, { "key2", { 0x0d, 0x0e, 0x0f, 0xff } }}),
keys({ "key1", "key2" }),
ns("someKnownNamespace")
{
expectConstructorCalls();
syncStorage.reset(new SyncStorageImpl(std::move(asyncStorageMockPassedToImplementation), systemMock));
}
void expectConstructorCalls()
{
InSequence dummy;
EXPECT_CALL(*asyncStorageMockRawPtr, fd())
.Times(1)
.WillOnce(Return(pFd));
}
void expectSdlReadinessCheck()
{
InSequence dummy;
expectWaitReadyAsync();
expectPollWait();
expectHandleEvents();
}
void expectPollWait()
{
EXPECT_CALL(systemMock, poll( _, 1, -1))
.Times(1)
.WillOnce(Invoke([](struct pollfd *fds, nfds_t, int)
{
fds->revents = POLLIN;
return 1;
}));
}
void expectPollError()
{
EXPECT_CALL(systemMock, poll( _, 1, -1))
.Times(1)
.WillOnce(Invoke([](struct pollfd *fds, nfds_t, int)
{
fds->revents = POLLIN;
return -1;
}));
}
void expectPollExceptionalCondition()
{
EXPECT_CALL(systemMock, poll( _, 1, -1))
.Times(1)
.WillOnce(Invoke([](struct pollfd *fds, nfds_t, int)
{
fds->revents = POLLPRI;
return 1;
}));
}
void expectHandleEvents()
{
EXPECT_CALL(*asyncStorageMockRawPtr, handleEvents())
.Times(1)
.WillOnce(Invoke([this]()
{
savedReadyAck(std::error_code());
}));
}
void expectWaitReadyAsync()
{
EXPECT_CALL(*asyncStorageMockRawPtr, waitReadyAsync(ns,_))
.Times(1)
.WillOnce(SaveArg<1>(&savedReadyAck));
}
void expectModifyAckWithError()
{
EXPECT_CALL(*asyncStorageMockRawPtr, handleEvents())
.Times(1)
.WillOnce(Invoke([this]()
{
savedModifyAck(AsyncRedisCommandDispatcherErrorCode::OUT_OF_MEMORY);
}));
}
void expectModifyIfAck(const std::error_code& error, bool status)
{
EXPECT_CALL(*asyncStorageMockRawPtr, handleEvents())
.Times(1)
.WillOnce(Invoke([this, error, status]()
{
savedModifyIfAck(error, status);
}));
}
void expectGetAckWithError()
{
EXPECT_CALL(*asyncStorageMockRawPtr, handleEvents())
.Times(1)
.WillOnce(Invoke([this]()
{
savedGetAck(AsyncRedisCommandDispatcherErrorCode::OUT_OF_MEMORY, dataMap);
}));
}
void expectGetAck()
{
EXPECT_CALL(*asyncStorageMockRawPtr, handleEvents())
.Times(1)
.WillOnce(Invoke([this]()
{
savedGetAck(std::error_code(), dataMap);
}));
}
void expectFindKeysAck()
{
EXPECT_CALL(*asyncStorageMockRawPtr, handleEvents())
.Times(1)
.WillOnce(Invoke([this]()
{
savedFindKeysAck(std::error_code(), keys);
}));
}
void expectFindKeysAckWithError()
{
EXPECT_CALL(*asyncStorageMockRawPtr, handleEvents())
.Times(1)
.WillOnce(Invoke([this]()
{
savedFindKeysAck(AsyncRedisCommandDispatcherErrorCode::OUT_OF_MEMORY, keys);
}));
}
void expectSetAsync(const SyncStorage::DataMap& dataMap)
{
EXPECT_CALL(*asyncStorageMockRawPtr, setAsync(ns, dataMap, _))
.Times(1)
.WillOnce(SaveArg<2>(&savedModifyAck));
}
void expectSetIfAsync(const SyncStorage::Key& key, const SyncStorage::Data& oldData, const SyncStorage::Data& newData)
{
EXPECT_CALL(*asyncStorageMockRawPtr, setIfAsync(ns, key, oldData, newData, _))
.Times(1)
.WillOnce(SaveArg<4>(&savedModifyIfAck));
}
void expectGetAsync(const SyncStorage::Keys& keys)
{
EXPECT_CALL(*asyncStorageMockRawPtr, getAsync(ns, keys, _))
.Times(1)
.WillOnce(SaveArg<2>(&savedGetAck));
}
void expectFindKeysAsync()
{
EXPECT_CALL(*asyncStorageMockRawPtr, findKeysAsync(ns, _, _))
.Times(1)
.WillOnce(SaveArg<2>(&savedFindKeysAck));
}
void expectRemoveAsync(const SyncStorage::Keys& keys)
{
EXPECT_CALL(*asyncStorageMockRawPtr, removeAsync(ns, keys, _))
.Times(1)
.WillOnce(SaveArg<2>(&savedModifyAck));
}
void expectRemoveIfAsync(const SyncStorage::Key& key, const SyncStorage::Data& data)
{
EXPECT_CALL(*asyncStorageMockRawPtr, removeIfAsync(ns, key, data, _))
.Times(1)
.WillOnce(SaveArg<3>(&savedModifyIfAck));
}
void expectRemoveAllAsync()
{
EXPECT_CALL(*asyncStorageMockRawPtr, removeAllAsync(ns, _))
.Times(1)
.WillOnce(SaveArg<1>(&savedModifyAck));
}
void expectSetIfNotExistsAsync(const SyncStorage::Key& key, const SyncStorage::Data& data)
{
EXPECT_CALL(*asyncStorageMockRawPtr, setIfNotExistsAsync(ns, key, data, _))
.Times(1)
.WillOnce(SaveArg<3>(&savedModifyIfAck));
}
};
}
TEST_F(SyncStorageImplTest, IsNotCopyable)
{
InSequence dummy;
EXPECT_FALSE(std::is_copy_constructible<SyncStorageImpl>::value);
EXPECT_FALSE(std::is_copy_assignable<SyncStorageImpl>::value);
}
TEST_F(SyncStorageImplTest, ImplementssyncStorage)
{
InSequence dummy;
EXPECT_TRUE((std::is_base_of<SyncStorage, SyncStorageImpl>::value));
}
TEST_F(SyncStorageImplTest, EventsAreNotHandledWhenPollReturnsError)
{
InSequence dummy;
expectSdlReadinessCheck();
expectSetAsync(dataMap);
expectPollError();
expectPollWait();
expectHandleEvents();
syncStorage->set(ns, dataMap);
}
TEST_F(SyncStorageImplTest, EventsAreNotHandledWhenThereIsAnExceptionalConditionOnTheFd)
{
InSequence dummy;
expectSdlReadinessCheck();
expectSetAsync(dataMap);
expectPollExceptionalCondition();
expectPollWait();
expectHandleEvents();
syncStorage->set(ns, dataMap);
}
TEST_F(SyncStorageImplTest, SetSuccessfully)
{
InSequence dummy;
expectSdlReadinessCheck();
expectSetAsync(dataMap);
expectPollWait();
expectHandleEvents();
syncStorage->set(ns, dataMap);
}
TEST_F(SyncStorageImplTest, SetCanThrowBackendError)
{
InSequence dummy;
expectSdlReadinessCheck();
expectSetAsync(dataMap);
expectPollWait();
expectModifyAckWithError();
EXPECT_THROW(syncStorage->set(ns, dataMap), BackendError);
}
TEST_F(SyncStorageImplTest, SetIfSuccessfully)
{
InSequence dummy;
expectSdlReadinessCheck();
expectSetAsync(dataMap);
expectPollWait();
expectHandleEvents();
syncStorage->set(ns, dataMap);
expectSdlReadinessCheck();
expectSetIfAsync("key1", { 0x0a, 0x0b, 0x0c }, { 0x0d, 0x0e, 0x0f });
expectPollWait();
expectHandleEvents();
syncStorage->setIf(ns, "key1", { 0x0a, 0x0b, 0x0c }, { 0x0d, 0x0e, 0x0f });
}
TEST_F(SyncStorageImplTest, SetIfCanThrowBackendError)
{
InSequence dummy;
expectSdlReadinessCheck();
expectSetAsync(dataMap);
expectPollWait();
expectHandleEvents();
syncStorage->set(ns, dataMap);
expectSdlReadinessCheck();
expectSetIfAsync("key1", { 0x0a, 0x0b, 0x0c }, { 0x0d, 0x0e, 0x0f });
expectPollWait();
expectModifyIfAck(AsyncRedisCommandDispatcherErrorCode::OUT_OF_MEMORY, false);
EXPECT_THROW(syncStorage->setIf(ns, "key1", { 0x0a, 0x0b, 0x0c }, { 0x0d, 0x0e, 0x0f }), BackendError);
}
TEST_F(SyncStorageImplTest, SetIfNotExistsSuccessfully)
{
InSequence dummy;
expectSdlReadinessCheck();
expectSetIfNotExistsAsync("key1", { 0x0a, 0x0b, 0x0c });
expectPollWait();
expectModifyIfAck(std::error_code(), true);
EXPECT_TRUE(syncStorage->setIfNotExists(ns, "key1", { 0x0a, 0x0b, 0x0c }));
}
TEST_F(SyncStorageImplTest, SetIfNotExistsReturnsFalseIfKeyAlreadyExists)
{
InSequence dummy;
expectSdlReadinessCheck();
expectSetIfNotExistsAsync("key1", { 0x0a, 0x0b, 0x0c });
expectPollWait();
expectModifyIfAck(std::error_code(), false);
EXPECT_FALSE(syncStorage->setIfNotExists(ns, "key1", { 0x0a, 0x0b, 0x0c }));
}
TEST_F(SyncStorageImplTest, SetIfNotExistsCanThrowBackendError)
{
InSequence dummy;
expectSdlReadinessCheck();
expectSetIfNotExistsAsync("key1", { 0x0a, 0x0b, 0x0c });
expectPollWait();
expectModifyIfAck(AsyncRedisCommandDispatcherErrorCode::OUT_OF_MEMORY, false);
EXPECT_THROW(syncStorage->setIfNotExists(ns, "key1", { 0x0a, 0x0b, 0x0c }), BackendError);
}
TEST_F(SyncStorageImplTest, GetSuccessfully)
{
InSequence dummy;
expectSdlReadinessCheck();
expectGetAsync(keys);
expectPollWait();
expectGetAck();
auto map(syncStorage->get(ns, keys));
EXPECT_EQ(map, dataMap);
}
TEST_F(SyncStorageImplTest, GetCanThrowBackendError)
{
InSequence dummy;
expectSdlReadinessCheck();
expectGetAsync(keys);
expectPollWait();
expectGetAckWithError();
EXPECT_THROW(syncStorage->get(ns, keys), BackendError);
}
TEST_F(SyncStorageImplTest, RemoveSuccessfully)
{
InSequence dummy;
expectSdlReadinessCheck();
expectRemoveAsync(keys);
expectPollWait();
expectHandleEvents();
syncStorage->remove(ns, keys);
}
TEST_F(SyncStorageImplTest, RemoveCanThrowBackendError)
{
InSequence dummy;
expectSdlReadinessCheck();
expectRemoveAsync(keys);
expectPollWait();
expectModifyAckWithError();
EXPECT_THROW(syncStorage->remove(ns, keys), BackendError);
}
TEST_F(SyncStorageImplTest, RemoveIfSuccessfully)
{
InSequence dummy;
expectSdlReadinessCheck();
expectRemoveIfAsync("key1", { 0x0a, 0x0b, 0x0c });
expectPollWait();
expectModifyIfAck(std::error_code(), true);
EXPECT_TRUE(syncStorage->removeIf(ns, "key1", { 0x0a, 0x0b, 0x0c }));
}
TEST_F(SyncStorageImplTest, RemoveIfReturnsFalseIfKeyDoesnotMatch)
{
InSequence dummy;
expectSdlReadinessCheck();
expectRemoveIfAsync("key1", { 0x0a, 0x0b, 0x0c });
expectPollWait();
expectModifyIfAck(std::error_code(), false);
EXPECT_FALSE(syncStorage->removeIf(ns, "key1", { 0x0a, 0x0b, 0x0c }));
}
TEST_F(SyncStorageImplTest, RemoveIfCanThrowBackendError)
{
InSequence dummy;
expectSdlReadinessCheck();
expectRemoveIfAsync("key1", { 0x0a, 0x0b, 0x0c });
expectPollWait();
expectModifyIfAck(AsyncRedisCommandDispatcherErrorCode::OUT_OF_MEMORY, false);
EXPECT_THROW(syncStorage->removeIf(ns, "key1", { 0x0a, 0x0b, 0x0c }), BackendError);
}
TEST_F(SyncStorageImplTest, FindKeysSuccessfully)
{
InSequence dummy;
expectSdlReadinessCheck();
expectFindKeysAsync();
expectPollWait();
expectFindKeysAck();
auto ids(syncStorage->findKeys(ns, "*"));
EXPECT_EQ(ids, keys);
}
TEST_F(SyncStorageImplTest, FindKeysAckCanThrowBackendError)
{
InSequence dummy;
expectSdlReadinessCheck();
expectFindKeysAsync();
expectPollWait();
expectFindKeysAckWithError();
EXPECT_THROW(syncStorage->findKeys(ns, "*"), BackendError);
}
TEST_F(SyncStorageImplTest, RemoveAllSuccessfully)
{
InSequence dummy;
expectSdlReadinessCheck();
expectRemoveAllAsync();
expectPollWait();
expectHandleEvents();
syncStorage->removeAll(ns);
}
TEST_F(SyncStorageImplTest, RemoveAllCanThrowBackendError)
{
InSequence dummy;
expectSdlReadinessCheck();
expectRemoveAllAsync();
expectPollWait();
expectModifyAckWithError();
EXPECT_THROW(syncStorage->removeAll(ns), BackendError);
}
TEST_F(SyncStorageImplTest, AllAsyncRedisStorageErrorCodesThrowCorrectException)
{
InSequence dummy;
std::error_code ec;
for (AsyncRedisStorage::ErrorCode arsec = AsyncRedisStorage::ErrorCode::SUCCESS; arsec < AsyncRedisStorage::ErrorCode::END_MARKER; ++arsec)
{
if (arsec != AsyncRedisStorage::ErrorCode::SUCCESS)
{
expectSdlReadinessCheck();
expectSetIfNotExistsAsync("key1", { 0x0a, 0x0b, 0x0c });
expectPollWait();
}
switch (arsec)
{
case AsyncRedisStorage::ErrorCode::SUCCESS:
break;
case AsyncRedisStorage::ErrorCode::INVALID_NAMESPACE:
expectModifyIfAck(arsec, false);
EXPECT_THROW(syncStorage->setIfNotExists(ns, "key1", { 0x0a, 0x0b, 0x0c }), InvalidNamespace);
break;
case AsyncRedisStorage::ErrorCode::REDIS_NOT_YET_DISCOVERED:
expectModifyIfAck(arsec, false);
EXPECT_THROW(syncStorage->setIfNotExists(ns, "key1", { 0x0a, 0x0b, 0x0c }), NotConnected);
break;
default:
FAIL() << "No mapping for AsyncRedisStorage::ErrorCode value: " << arsec;
break;
}
}
}
TEST_F(SyncStorageImplTest, AllDispatcherErrorCodesThrowCorrectException)
{
InSequence dummy;
std::error_code ec;
for (AsyncRedisCommandDispatcherErrorCode aec = AsyncRedisCommandDispatcherErrorCode::SUCCESS; aec < AsyncRedisCommandDispatcherErrorCode::END_MARKER; ++aec)
{
if (aec != AsyncRedisCommandDispatcherErrorCode::SUCCESS)
{
expectSdlReadinessCheck();
expectSetIfNotExistsAsync("key1", { 0x0a, 0x0b, 0x0c });
expectPollWait();
}
switch (aec)
{
case AsyncRedisCommandDispatcherErrorCode::SUCCESS:
break;
case AsyncRedisCommandDispatcherErrorCode::UNKNOWN_ERROR:
expectModifyIfAck(aec, false);
EXPECT_THROW(syncStorage->setIfNotExists(ns, "key1", { 0x0a, 0x0b, 0x0c }), BackendError);
break;
case AsyncRedisCommandDispatcherErrorCode::CONNECTION_LOST:
expectModifyIfAck(aec, false);
EXPECT_THROW(syncStorage->setIfNotExists(ns, "key1", { 0x0a, 0x0b, 0x0c }), OperationInterrupted);
break;
case AsyncRedisCommandDispatcherErrorCode::PROTOCOL_ERROR:
expectModifyIfAck(aec, false);
EXPECT_THROW(syncStorage->setIfNotExists(ns, "key1", { 0x0a, 0x0b, 0x0c }), RejectedByBackend);
break;
case AsyncRedisCommandDispatcherErrorCode::OUT_OF_MEMORY:
expectModifyIfAck(aec, false);
EXPECT_THROW(syncStorage->setIfNotExists(ns, "key1", { 0x0a, 0x0b, 0x0c }), BackendError);
break;
case AsyncRedisCommandDispatcherErrorCode::DATASET_LOADING:
expectModifyIfAck(aec, false);
EXPECT_THROW(syncStorage->setIfNotExists(ns, "key1", { 0x0a, 0x0b, 0x0c }), NotConnected);
break;
case AsyncRedisCommandDispatcherErrorCode::NOT_CONNECTED:
expectModifyIfAck(aec, false);
EXPECT_THROW(syncStorage->setIfNotExists(ns, "key1", { 0x0a, 0x0b, 0x0c }), NotConnected);
break;
case AsyncRedisCommandDispatcherErrorCode::IO_ERROR:
expectModifyIfAck(aec, false);
EXPECT_THROW(syncStorage->setIfNotExists(ns, "key1", { 0x0a, 0x0b, 0x0c }), BackendError);
break;
default:
FAIL() << "No mapping for AsyncRedisCommandDispatcherErrorCode value: " << aec;
break;
}
}
}
TEST_F(SyncStorageImplTest, CanThrowStdExceptionIfDispatcherErrorCodeCannotBeMappedToSdlException)
{
InSequence dummy;
expectSdlReadinessCheck();
expectSetIfNotExistsAsync("key1", { 0x0a, 0x0b, 0x0c });
expectPollWait();
expectModifyIfAck(std::error_code(1, std::system_category()), false);
EXPECT_THROW(syncStorage->setIfNotExists(ns, "key1", { 0x0a, 0x0b, 0x0c }), std::range_error);
}