SmartAudio/package/avs/avs-sdk/files/avs-device-sdk/AVSCommon/AVS/test/ExceptionEncounteredSenderT...

251 lines
10 KiB
C++

/*
* Copyright 2017-2018 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 <memory>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <rapidjson/document.h>
#include <rapidjson/error/en.h>
#include <rapidjson/stringbuffer.h>
#include <AVSCommon/AVS/ExceptionEncounteredSender.h>
#include <AVSCommon/SDKInterfaces/MockMessageSender.h>
#include <AVSCommon/Utils/JSON/JSONUtils.h>
#include <AVSCommon/Utils/UUIDGeneration/UUIDGeneration.h>
using namespace testing;
namespace alexaClientSDK {
namespace avsCommon {
namespace test {
using namespace utils::json::jsonUtils;
/// The namespace for this event
static const std::string NAMESPACE = "System";
/// JSON key for the context section of a message.
static const std::string MESSAGE_CONTEXT_KEY = "context";
/// JSON key for the event section of a message.
static const std::string MESSAGE_EVENT_KEY = "event";
/// JSON key for the header section of a message.
static const std::string MESSAGE_HEADER_KEY = "header";
/// JSON key for the namespace field of a message header.
static const std::string MESSAGE_NAMESPACE_KEY = "namespace";
/// JSON key for the name field of a message header.
static const std::string MESSAGE_NAME_KEY = "name";
/// JSON key for the message ID field of a message header.
static const std::string MESSAGE_MESSAGE_ID_KEY = "messageId";
/// JSON key for the payload section of an message.
static const std::string MESSAGE_PAYLOAD_KEY = "payload";
/// JSON value for a ExceptionEncountered event's name.
static const std::string EXCEPTION_ENCOUNTERED_EVENT_NAME = "ExceptionEncountered";
/// JSON key for the Unparsed Directive field of an ExceptionEncountered event.
static const std::string UNPARSED_DIRECTIVE_KEY = "unparsedDirective";
/// JSON key for the error field of an ExceptionEncountered event.
static const std::string EXCEPTION_ENCOUNTERED_ERROR_KEY = "error";
/// JSON key for the type field of a ExceptionEncountered event's error.
static const std::string ERROR_TYPE_KEY = "type";
/// JSON key for the message field of a ExceptionEncountered event's error description.
static const std::string ERROR_MESSAGE_KEY = "message";
/// String to send unparsed Directive in @c testExceptionEncounteredSucceeds
static const std::string UNPARSED_DIRECTIVE_JSON_STRING = "unparsedDirective Json String";
/**
* Utility class which captures parameters to a ExceptionEncountered event,
* and provides functions to send and verify the event
* using those parameters.
*/
class ExceptionEncounteredEvent {
public:
/**
* Constructs an object which captures the parameters to send in a ExceptionEncountered Event.
* Parameters are passed through
* directly to @c ExceptionEncounteredSender::sendExceptionEncountered().
*/
ExceptionEncounteredEvent(
const std::string& unparsedDirective,
avs::ExceptionErrorType error,
const std::string& errorDescription);
/**
* This function sends a ExceptionEncountered event using the provided @c exceptionencounteredsender.
*
* @param exceptionencounteredsender The @c ExceptionEncounteredSender to call on
* @c ExceptionEncounteredSender::sendExceptionEncountered().
*/
void send(std::shared_ptr<avs::ExceptionEncounteredSender> exceptionencounteredsender);
/**
* This function verifies that JSON content of a ExceptionEncountered event @c MessageRequest is correct.
* This function signature matches that of @c MessageSenderInterface::sendMessage() so that an
* @c EXPECT_CALL() can @c Invoke() this function directly.
*
* @param request The @c MessageRequest to verify.
*/
void verifyMessage(std::shared_ptr<avsCommon::avs::MessageRequest> request);
private:
/// The Unparsed Directive string to use for this ExceptionEncountered event
std::string m_unparsedDirective;
/// The Error type to use for this ExceptionEncountered event
avs::ExceptionErrorType m_error;
/// The Error Description message to use for this ExceptionEncountered event
std::string m_errorDescription;
};
ExceptionEncounteredEvent::ExceptionEncounteredEvent(
const std::string& unparsedDirective,
avs::ExceptionErrorType error,
const std::string& errorDescription) :
m_unparsedDirective{unparsedDirective},
m_error{error},
m_errorDescription{errorDescription} {
}
void ExceptionEncounteredEvent::send(std::shared_ptr<avs::ExceptionEncounteredSender> exceptionEncounteredSender) {
exceptionEncounteredSender->sendExceptionEncountered(m_unparsedDirective, m_error, m_errorDescription);
}
void ExceptionEncounteredEvent::verifyMessage(std::shared_ptr<avsCommon::avs::MessageRequest> request) {
rapidjson::Document document;
document.Parse(request->getJsonContent().c_str());
EXPECT_FALSE(document.HasParseError())
<< "rapidjson detected a parsing error at offset:" << std::to_string(document.GetErrorOffset())
<< ", error message: " << GetParseError_En(document.GetParseError());
auto context = document.FindMember(MESSAGE_CONTEXT_KEY);
ASSERT_NE(context, document.MemberEnd());
auto event = document.FindMember(MESSAGE_EVENT_KEY);
ASSERT_NE(event, document.MemberEnd());
auto header = event->value.FindMember(MESSAGE_HEADER_KEY);
ASSERT_NE(header, event->value.MemberEnd());
auto payload = event->value.FindMember(MESSAGE_PAYLOAD_KEY);
ASSERT_NE(payload, event->value.MemberEnd());
std::string temp_string = "";
ASSERT_TRUE(retrieveValue(header->value, MESSAGE_NAMESPACE_KEY, &temp_string));
EXPECT_EQ(temp_string, NAMESPACE);
ASSERT_TRUE(retrieveValue(header->value, MESSAGE_NAME_KEY, &temp_string));
EXPECT_EQ(temp_string, EXCEPTION_ENCOUNTERED_EVENT_NAME);
ASSERT_TRUE(retrieveValue(header->value, MESSAGE_MESSAGE_ID_KEY, &temp_string));
ASSERT_NE(temp_string, "");
ASSERT_TRUE(retrieveValue(payload->value, UNPARSED_DIRECTIVE_KEY, &temp_string));
EXPECT_EQ(temp_string, m_unparsedDirective);
auto error = payload->value.FindMember(EXCEPTION_ENCOUNTERED_ERROR_KEY);
ASSERT_NE(error, payload->value.MemberEnd());
std::ostringstream errorType;
errorType << m_error;
ASSERT_TRUE(retrieveValue(error->value, ERROR_TYPE_KEY, &temp_string));
EXPECT_EQ(temp_string, errorType.str());
ASSERT_TRUE(retrieveValue(error->value, ERROR_MESSAGE_KEY, &temp_string));
EXPECT_EQ(temp_string, m_errorDescription);
}
/// Test harness for @c ExceptionEncounteredSender class.
class ExceptionEncounteredSenderTest : public ::testing::Test {
public:
/// Set up the test harness for running a test.
void SetUp() override;
protected:
/**
* Function to send a exceptionencountered event and verify that it succeeds. Parameters are passed through
* to @c ExceptionEncounteredEvent::ExceptionEncounteredEvent().
*
* @return @c true if the exceptionencountered event sent correctly, else @c false.
*/
bool testExceptionEncounteredSucceeds(
const std::string& unparsedDirective,
avs::ExceptionErrorType error,
const std::string& errorDescription);
/// The mock @c MessageSenderInterface.
std::shared_ptr<avsCommon::sdkInterfaces::test::MockMessageSender> m_mockMessageSender;
/// The @c ExceptionEncounteredSender to test
std::shared_ptr<avs::ExceptionEncounteredSender> m_exceptionEncounteredSender;
/// The @c ExceptionEncounteredEvent from the @c testExceptionEncounteredSucceeds() call
std::shared_ptr<ExceptionEncounteredEvent> m_exceptionEncounteredEvent;
};
void ExceptionEncounteredSenderTest::SetUp() {
m_mockMessageSender = std::make_shared<avsCommon::sdkInterfaces::test::MockMessageSender>();
m_exceptionEncounteredSender = avs::ExceptionEncounteredSender::create(m_mockMessageSender);
EXPECT_NE(m_exceptionEncounteredSender, nullptr);
}
bool ExceptionEncounteredSenderTest::testExceptionEncounteredSucceeds(
const std::string& unparsedDirective,
avs::ExceptionErrorType error,
const std::string& errorDescription) {
bool done = false;
m_exceptionEncounteredEvent =
std::make_shared<ExceptionEncounteredEvent>(unparsedDirective, error, errorDescription);
EXPECT_CALL(*m_mockMessageSender, sendMessage(_))
.WillOnce(Invoke(m_exceptionEncounteredEvent.get(), &ExceptionEncounteredEvent::verifyMessage));
m_exceptionEncounteredEvent->send(m_exceptionEncounteredSender);
done = true;
return done;
}
/*
* This function sends @c ExceptionErrorType::UNEXPECTED_INFORMATION_RECEIVED and verifies that
* @c ExceptionEncounteredSender::sendExceptionEncountered send the event.
*/
TEST_F(ExceptionEncounteredSenderTest, errorTypeUnexpectedInformationReceived) {
ASSERT_TRUE(testExceptionEncounteredSucceeds(
UNPARSED_DIRECTIVE_JSON_STRING,
avs::ExceptionErrorType::UNEXPECTED_INFORMATION_RECEIVED,
"The directive sent was malformed"));
}
/*
* This function sends @c ExceptionErrorType::UNSUPPORTED_OPERATION and verifies that
* @c ExceptionEncounteredSender::sendExceptionEncountered send the event.
*/
TEST_F(ExceptionEncounteredSenderTest, errorTypeUnexpectedOperation) {
ASSERT_TRUE(testExceptionEncounteredSucceeds(
UNPARSED_DIRECTIVE_JSON_STRING, avs::ExceptionErrorType::UNSUPPORTED_OPERATION, "Operation not supported"));
}
/*
* This function sends @c ExceptionErrorType::INTERNAL_ERROR and verifies that
* @c ExceptionEncounteredSender::sendExceptionEncountered send the event.
*/
TEST_F(ExceptionEncounteredSenderTest, errorTypeInternalError) {
ASSERT_TRUE(testExceptionEncounteredSucceeds(
UNPARSED_DIRECTIVE_JSON_STRING, avs::ExceptionErrorType::INTERNAL_ERROR, "An error occurred with the device"));
}
} // namespace test
} // namespace avsCommon
} // namespace alexaClientSDK