avs-device-sdk/Settings/test/SettingEventSenderTest.cpp

327 lines
11 KiB
C++

/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0/
*
* or in the "license" file accompanying this file. This file 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 <chrono>
#include <memory>
#include <string>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <Settings/SettingEventMetadata.h>
#include <Settings/SettingEventSender.h>
#include <AVSCommon/SDKInterfaces/MockAVSConnectionManager.h>
#include <AVSCommon/SDKInterfaces/MockMessageSender.h>
#include <AVSCommon/Utils/PromiseFuturePair.h>
namespace alexaClientSDK {
namespace settings {
namespace test {
using namespace avsCommon::avs;
using namespace avsCommon::sdkInterfaces;
using namespace avsCommon::sdkInterfaces::test;
using namespace avsCommon::utils;
using namespace settings;
using namespace ::testing;
using ::testing::MatchesRegex;
/// The metadata for the event messages used in this test.
static const SettingEventMetadata METADATA = {"NAMESPACE", "CHANGEDEVENT", "REPORTEVENT", "SETTING"};
/// The expected changed event format.
// clang-format off
static const std::string EXPECTED_CHANGED_EVENT = R"(\{"event":\{"header":\{"namespace":"NAMESPACE","name":"CHANGEDEVENT","messageId":".*-.*-.*-.*-.*"\},"payload":\{"SETTING":true\}\}\})";
// clang-format on
/// The expected report event format.
// clang-format off
static const std::string EXPECTED_REPORT_EVENT = R"(\{"event":\{"header":\{"namespace":"NAMESPACE","name":"REPORTEVENT","messageId":".*-.*-.*-.*-.*"\},"payload":\{"SETTING":true\}\}\})";
// clang-format on
/// Table with the retry times on subsequent retries.
static const std::vector<int> RETRY_TABLE = {
500, // Retry 1: 0.5s
1000, // Retry 2: 1s
1500 // Retry 3: 1.5s
};
///
static const auto MAX_RETRY_WAIT = std::chrono::seconds(4);
/**
* Test harness for @c SettingEventSender class.
*/
class SettingEventSenderTest : public Test {
public:
/// Set up the test harness for running a test.
void SetUp() override;
protected:
/// An instance of the @c SettingEventSender to be tested.
std::shared_ptr<SettingEventSender> m_sender;
/// Mock of @c MessageSenderInterface.
std::shared_ptr<MockMessageSender> m_mockMessageSender;
/// Mock of @c AVSConnectionManagerInterface.
std::shared_ptr<MockAVSConnectionManager> m_mockAVSConnectionManager;
};
void SettingEventSenderTest::SetUp() {
m_mockMessageSender = std::make_shared<NiceMock<MockMessageSender>>();
m_mockAVSConnectionManager = std::make_shared<NiceMock<MockAVSConnectionManager>>();
m_sender = SettingEventSender::create(METADATA, m_mockMessageSender, RETRY_TABLE);
ASSERT_NE(m_sender, nullptr);
}
/**
* Test create validation.
*/
TEST_F(SettingEventSenderTest, test_createValidation) {
m_sender = SettingEventSender::create(METADATA, nullptr);
ASSERT_EQ(m_sender, nullptr);
}
/**
* Test to verify if the changed event is sent as expected.
*/
TEST_F(SettingEventSenderTest, test_sendChangedEvent) {
PromiseFuturePair<std::string> setValuePromise;
EXPECT_CALL(*m_mockMessageSender, sendMessage(_))
.WillOnce(Invoke([&setValuePromise](std::shared_ptr<MessageRequest> request) {
setValuePromise.setValue(request->getJsonContent());
request->sendCompleted(avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::SUCCESS);
}));
m_sender->sendChangedEvent("true");
ASSERT_TRUE(setValuePromise.waitFor(std::chrono::seconds(1)));
EXPECT_THAT(setValuePromise.getValue(), MatchesRegex(EXPECTED_CHANGED_EVENT));
}
/**
* Test to verify if the report event is sent as expected.
*/
TEST_F(SettingEventSenderTest, test_sendReportEvent) {
PromiseFuturePair<std::string> setValuePromise;
EXPECT_CALL(*m_mockMessageSender, sendMessage(_))
.WillOnce(Invoke([&setValuePromise](std::shared_ptr<MessageRequest> request) {
setValuePromise.setValue(request->getJsonContent());
request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS);
}));
m_sender->sendReportEvent("true");
ASSERT_TRUE(setValuePromise.waitFor(std::chrono::seconds(1)));
EXPECT_THAT(setValuePromise.getValue(), MatchesRegex(EXPECTED_REPORT_EVENT));
}
/**
* Test validation for incorrect JSON value passed.
*/
TEST_F(SettingEventSenderTest, test_invalidJSONValue) {
const std::string invalidJSONValue = "TRUE";
EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(0);
ASSERT_FALSE(m_sender->sendReportEvent(invalidJSONValue).get());
}
/**
* Test sending of events will block until a response is received
*/
TEST_F(SettingEventSenderTest, testSlow_blockingSend) {
PromiseFuturePair<std::shared_ptr<MessageRequest>> firstRequest;
PromiseFuturePair<std::shared_ptr<MessageRequest>> secondRequest;
{
InSequence dummy;
EXPECT_CALL(*m_mockMessageSender, sendMessage(_))
.WillOnce(
Invoke([&firstRequest](std::shared_ptr<MessageRequest> request) { firstRequest.setValue(request); }));
EXPECT_CALL(*m_mockMessageSender, sendMessage(_))
.WillOnce(Invoke([&secondRequest](std::shared_ptr<MessageRequest> request) {
secondRequest.setValue(request);
request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS);
}));
}
std::thread sendThread([this]() {
m_sender->sendReportEvent("true");
m_sender->sendReportEvent("false");
});
ASSERT_TRUE(firstRequest.waitFor(std::chrono::seconds(1)));
EXPECT_THAT(firstRequest.getValue()->getJsonContent(), MatchesRegex(EXPECTED_REPORT_EVENT));
// There is no HTTP response on the first request, second request should not be sent out.
ASSERT_FALSE(secondRequest.waitFor(std::chrono::seconds(1)));
// Respond to the first request
firstRequest.getValue()->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS);
sendThread.join();
}
/**
* Test retries will give up after N attempts
*/
TEST_F(SettingEventSenderTest, testSlow_maxRetries) {
std::atomic<unsigned long> attempts;
unsigned long maxAttempts = RETRY_TABLE.size();
PromiseFuturePair<void> retryDone;
attempts = 0;
EXPECT_CALL(*m_mockMessageSender, sendMessage(_))
.WillRepeatedly(Invoke([&attempts, &retryDone, maxAttempts](std::shared_ptr<MessageRequest> request) {
request->sendCompleted(MessageRequestObserverInterface::Status::THROTTLED);
attempts++;
if (maxAttempts == attempts) {
retryDone.setValue();
}
}));
ASSERT_FALSE(m_sender->sendReportEvent("true").get());
ASSERT_TRUE(retryDone.waitFor(MAX_RETRY_WAIT));
}
/**
* Test retry on server internal error HTTP response
*/
TEST_F(SettingEventSenderTest, testSlow_retryOnInternalError) {
std::atomic<unsigned long> attempts;
unsigned long maxAttempts = RETRY_TABLE.size();
PromiseFuturePair<void> retryDone;
attempts = 0;
EXPECT_CALL(*m_mockMessageSender, sendMessage(_))
.WillRepeatedly(Invoke([&attempts, &retryDone, maxAttempts](std::shared_ptr<MessageRequest> request) {
request->sendCompleted(MessageRequestObserverInterface::Status::SERVER_INTERNAL_ERROR_V2);
attempts++;
if (maxAttempts == attempts) {
retryDone.setValue();
}
}));
ASSERT_FALSE(m_sender->sendReportEvent("true").get());
ASSERT_TRUE(retryDone.waitFor(MAX_RETRY_WAIT));
}
/**
* Test retry on server internal error HTTP response and will stop after success
*/
TEST_F(SettingEventSenderTest, testSlow_retryStopAfterSuccess) {
std::atomic<unsigned long> attempts;
unsigned long maxAttempts = RETRY_TABLE.size();
PromiseFuturePair<void> retryDone;
attempts = 0;
auto handleSendMessageError = [&attempts, &retryDone, maxAttempts](std::shared_ptr<MessageRequest> request) {
request->sendCompleted(MessageRequestObserverInterface::Status::SERVER_INTERNAL_ERROR_V2);
attempts++;
if (maxAttempts == attempts) {
retryDone.setValue();
}
};
auto handleSendMessageSuccess = [&attempts, &retryDone, maxAttempts](std::shared_ptr<MessageRequest> request) {
request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS);
attempts++;
if (maxAttempts == attempts) {
retryDone.setValue();
}
};
EXPECT_CALL(*m_mockMessageSender, sendMessage(_))
.WillOnce(Invoke(handleSendMessageError))
.WillOnce(Invoke(handleSendMessageSuccess))
.WillRepeatedly(Invoke(handleSendMessageSuccess));
ASSERT_TRUE(m_sender->sendReportEvent("true").get());
ASSERT_FALSE(retryDone.waitFor(MAX_RETRY_WAIT));
ASSERT_LE(attempts, maxAttempts);
}
/**
* Test no retry on non-connected HTTP response
*/
TEST_F(SettingEventSenderTest, testSlow_noRetryOnNonConnected) {
std::atomic<unsigned long> attempts;
unsigned long maxAttempts = RETRY_TABLE.size();
PromiseFuturePair<void> retryDone;
attempts = 0;
EXPECT_CALL(*m_mockMessageSender, sendMessage(_))
.WillRepeatedly(Invoke([&attempts, &retryDone, maxAttempts](std::shared_ptr<MessageRequest> request) {
request->sendCompleted(MessageRequestObserverInterface::Status::NOT_CONNECTED);
attempts++;
if (maxAttempts == attempts) {
retryDone.setValue();
}
}));
ASSERT_FALSE(m_sender->sendReportEvent("true").get());
ASSERT_FALSE(retryDone.waitFor(MAX_RETRY_WAIT));
}
/**
* Test cancellation of retries
*/
TEST_F(SettingEventSenderTest, testSlow_cancelRetry) {
std::atomic<unsigned long> attempts;
unsigned long maxAttempts = RETRY_TABLE.size();
PromiseFuturePair<void> retryDone;
attempts = 0;
EXPECT_CALL(*m_mockMessageSender, sendMessage(_))
.WillRepeatedly(Invoke([this, &attempts, &retryDone, maxAttempts](std::shared_ptr<MessageRequest> request) {
// On the second attempt, cancel the send.
if (attempts == 1) {
m_sender->cancel();
}
request->sendCompleted(MessageRequestObserverInterface::Status::THROTTLED);
attempts++;
if (maxAttempts == attempts) {
retryDone.setValue();
}
}));
ASSERT_FALSE(m_sender->sendReportEvent("true").get());
ASSERT_FALSE(retryDone.waitFor(MAX_RETRY_WAIT));
// Verify that retry attempts is only 2 since it's canceled.
ASSERT_EQ(attempts, 2u);
}
} // namespace test
} // namespace settings
} // namespace alexaClientSDK