795 lines
30 KiB
C++
795 lines
30 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 <AVSCommon/Utils/Logger/Logger.h>
|
||
|
#include <gmock/gmock.h>
|
||
|
#include <gtest/gtest.h>
|
||
|
|
||
|
#include "ContextManager/ContextManager.h"
|
||
|
|
||
|
using namespace testing;
|
||
|
|
||
|
namespace alexaClientSDK {
|
||
|
namespace contextManager {
|
||
|
namespace test {
|
||
|
|
||
|
using namespace avsCommon;
|
||
|
using namespace avsCommon::avs;
|
||
|
using namespace avsCommon::sdkInterfaces;
|
||
|
|
||
|
/// String to identify log entries originating from this file.
|
||
|
static const std::string TAG("ContextManagerTest");
|
||
|
|
||
|
/**
|
||
|
* Create a LogEntry using this file's TAG and the specified event string.
|
||
|
*
|
||
|
* @param The event string for this @c LogEntry.
|
||
|
*/
|
||
|
#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event)
|
||
|
|
||
|
/// Payload for SpeechSynthesizer state when it is playing back audio.
|
||
|
static const std::string SPEECH_SYNTHESIZER_PAYLOAD_PLAYING =
|
||
|
"{"
|
||
|
"\"playerActivity\":\"PLAYING\","
|
||
|
"\"offsetInMilliseconds\":5,"
|
||
|
"\"token\":\"\""
|
||
|
"}";
|
||
|
/// Payload for SpeechSynthesizer state when playback has finished.
|
||
|
static const std::string SPEECH_SYNTHESIZER_PAYLOAD_FINISHED =
|
||
|
"{"
|
||
|
"\"playerActivity\":\"FINISHED\","
|
||
|
"\"offsetInMilliseconds\":0,"
|
||
|
"\"token\":\"\""
|
||
|
"}";
|
||
|
|
||
|
/// Payload for AudioPlayer.
|
||
|
static const std::string AUDIO_PLAYER_PAYLOAD =
|
||
|
"{"
|
||
|
"\"playerActivity\":\"FINISHED\","
|
||
|
"\"offsetInMilliseconds\":0,"
|
||
|
"\"token\":\"\""
|
||
|
"}";
|
||
|
|
||
|
/// Payload for Alerts.
|
||
|
static const std::string ALERTS_PAYLOAD =
|
||
|
"{"
|
||
|
"\"allAlerts\": ["
|
||
|
"{"
|
||
|
"\"token\": \"\","
|
||
|
"\"type\": \"TIMER\","
|
||
|
"\"scheduledTime\": \"\""
|
||
|
"}"
|
||
|
"],"
|
||
|
"\"activeAlerts\": ["
|
||
|
"{"
|
||
|
"\"token\": \"\","
|
||
|
"\"type\": \"TIMER\","
|
||
|
"\"scheduledTime\": \"\""
|
||
|
"}"
|
||
|
"]"
|
||
|
"}";
|
||
|
|
||
|
/// Context used for testing.
|
||
|
static const std::string CONTEXT_TEST =
|
||
|
"{"
|
||
|
"\"context\":["
|
||
|
"{"
|
||
|
"\"header\":{"
|
||
|
"\"namespace\":\"AudioPlayer\","
|
||
|
"\"name\":\"PlaybackState\""
|
||
|
"},"
|
||
|
"\"payload\":{"
|
||
|
"\"playerActivity\":\"FINISHED\","
|
||
|
"\"offsetInMilliseconds\":0,"
|
||
|
"\"token\":\"\""
|
||
|
"}"
|
||
|
"},"
|
||
|
"{"
|
||
|
"\"header\":{"
|
||
|
"\"namespace\":\"SpeechSynthesizer\","
|
||
|
"\"name\":\"SpeechState\""
|
||
|
"},"
|
||
|
"\"payload\":{"
|
||
|
"\"playerActivity\":\"FINISHED\","
|
||
|
"\"offsetInMilliseconds\":0,"
|
||
|
"\"token\":\"\""
|
||
|
"}"
|
||
|
"}"
|
||
|
"]"
|
||
|
"}";
|
||
|
|
||
|
/// Default time @c doProvide sleeps before it calls @c setState.
|
||
|
static const std::chrono::milliseconds DEFAULT_SLEEP_TIME = std::chrono::milliseconds(10);
|
||
|
|
||
|
/// Time @c doProvide sleeps before it calls @c setState to induce a timeout.
|
||
|
static const std::chrono::milliseconds TIMEOUT_SLEEP_TIME = std::chrono::milliseconds(100);
|
||
|
|
||
|
/**
|
||
|
* Default timeout for the @c ContextRequester to get the context.This needs to be modified if the
|
||
|
* @c TIMEOUT_SLEEP_TIME is modified. The value should be less than the @c TIMEOUT_SLEEP_TIME.
|
||
|
*/
|
||
|
static const std::chrono::milliseconds DEFAULT_TIMEOUT = std::chrono::milliseconds(50);
|
||
|
|
||
|
/// Timeout for the @c ContextRequester to get the failure.
|
||
|
static const std::chrono::milliseconds FAILURE_TIMEOUT = std::chrono::milliseconds(220);
|
||
|
|
||
|
/// Namespace for SpeechSynthesizer.
|
||
|
static const std::string NAMESPACE_SPEECH_SYNTHESIZER("SpeechSynthesizer");
|
||
|
|
||
|
/// Namespace for AudioPlayer.
|
||
|
static const std::string NAMESPACE_AUDIO_PLAYER("AudioPlayer");
|
||
|
|
||
|
/// Namespace for Alerts.
|
||
|
static const std::string NAMESPACE_ALERTS("Alerts");
|
||
|
|
||
|
/// Name for SpeechSynthesizer state.
|
||
|
static const std::string NAME_SPEECH_STATE("SpeechState");
|
||
|
|
||
|
/// Name for AudioPlayer state.
|
||
|
static const std::string NAME_PLAYBACK_STATE("PlaybackState");
|
||
|
|
||
|
/// Name for Alerts state.
|
||
|
static const std::string NAME_ALERTS_STATE("AlertsState");
|
||
|
|
||
|
/// SpeechSynthesizer namespace and name
|
||
|
static const NamespaceAndName SPEECH_SYNTHESIZER(NAMESPACE_SPEECH_SYNTHESIZER, NAME_SPEECH_STATE);
|
||
|
|
||
|
/// AudioPlayer namespace and name
|
||
|
static const NamespaceAndName AUDIO_PLAYER(NAMESPACE_AUDIO_PLAYER, NAME_PLAYBACK_STATE);
|
||
|
|
||
|
/// Alerts namespace and name
|
||
|
static const NamespaceAndName ALERTS(NAMESPACE_ALERTS, NAME_ALERTS_STATE);
|
||
|
|
||
|
/// Dummy provider namespace and name
|
||
|
static const NamespaceAndName DUMMY_PROVIDER("Dummy", "DummyName");
|
||
|
|
||
|
/**
|
||
|
* @c MockContextRequester used to verify @c ContextManager behavior.
|
||
|
*/
|
||
|
class MockContextRequester : public ContextRequesterInterface {
|
||
|
public:
|
||
|
/**
|
||
|
* Creates an instance of the @c MockContextRequester.
|
||
|
*
|
||
|
* @param contextManager Pointer to an instance of the @c ContextManager.
|
||
|
* @return A shared pointer to an instance of the @c MockContextRequester.
|
||
|
*/
|
||
|
static std::shared_ptr<MockContextRequester> create(std::shared_ptr<ContextManager> contextManager);
|
||
|
|
||
|
/**
|
||
|
* Constructor
|
||
|
*
|
||
|
* @param contextManager Pointer to an instance of the @c ContextManager.
|
||
|
*/
|
||
|
MockContextRequester(std::shared_ptr<ContextManager> contextManager);
|
||
|
|
||
|
~MockContextRequester();
|
||
|
|
||
|
void onContextAvailable(const std::string& context) override;
|
||
|
|
||
|
void onContextFailure(const ContextRequestError error) override;
|
||
|
|
||
|
/**
|
||
|
* Waits for a specified time for the context to be available @c getContext.
|
||
|
*
|
||
|
* @param duration Number of milliseconds to wait before giving up.
|
||
|
* @return @c true if a context or failure was received within the specified duration, else @c false.
|
||
|
*/
|
||
|
bool waitForContext(const std::chrono::milliseconds duration = std::chrono::milliseconds(200));
|
||
|
|
||
|
/**
|
||
|
* Waits for a specified time for the context request to fail on a @c getContext.
|
||
|
*
|
||
|
* @param duration Number of milliseconds to wait before giving up.
|
||
|
* @return @c true if a failure was received within the specified duration, else @c false.
|
||
|
*/
|
||
|
bool waitForFailure(const std::chrono::milliseconds duration = std::chrono::milliseconds(200));
|
||
|
|
||
|
/**
|
||
|
* Function to read the stored context.
|
||
|
*
|
||
|
* @return The context.
|
||
|
*/
|
||
|
std::string& getContextString();
|
||
|
|
||
|
/**
|
||
|
* Function to read check context string JSON against the reference context JSON with unordered states.
|
||
|
*
|
||
|
* @return @c true if both JSON context strings are equivalent, else @c false.
|
||
|
*/
|
||
|
bool checkContextString(const std::string& a, const std::string& b);
|
||
|
|
||
|
private:
|
||
|
/// Instance of @ ContextManager
|
||
|
std::shared_ptr<ContextManager> m_contextManager;
|
||
|
|
||
|
/// Condition variable to wake the @ waitForContext.
|
||
|
std::condition_variable m_wakeTriggerSuccess;
|
||
|
|
||
|
/// Condition variable to wake the @ waitForFailure.
|
||
|
std::condition_variable m_wakeTriggerFailure;
|
||
|
|
||
|
/// mutex to protect @c m_contextAvailable.
|
||
|
std::mutex m_mutexSuccess;
|
||
|
|
||
|
/// mutex to protect @c m_contextFailure.
|
||
|
std::mutex m_mutexFailure;
|
||
|
|
||
|
/// Flag to indicate context is available.
|
||
|
bool m_contextAvailable;
|
||
|
|
||
|
/// Flag to indicate there was a failure during @c getContext.
|
||
|
bool m_contextFailure;
|
||
|
|
||
|
/// String to hold the context returned by the @c ContextManager.
|
||
|
std::string m_context;
|
||
|
|
||
|
/**
|
||
|
* Function to convert a JSON object to string.
|
||
|
*
|
||
|
*@param documentNode the JSON object.
|
||
|
*@return the serialized JSON string
|
||
|
*/
|
||
|
std::string serializeJSONObjectToString(const rapidjson::Value& documentNode);
|
||
|
};
|
||
|
|
||
|
std::shared_ptr<MockContextRequester> MockContextRequester::create(std::shared_ptr<ContextManager> contextManager) {
|
||
|
auto result = std::make_shared<MockContextRequester>(contextManager);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
MockContextRequester::MockContextRequester(std::shared_ptr<ContextManager> contextManager) :
|
||
|
m_contextManager{contextManager},
|
||
|
m_contextAvailable{false},
|
||
|
m_contextFailure{false} {
|
||
|
}
|
||
|
|
||
|
MockContextRequester::~MockContextRequester() {
|
||
|
}
|
||
|
|
||
|
void MockContextRequester::onContextAvailable(const std::string& context) {
|
||
|
std::unique_lock<std::mutex> lock(m_mutexSuccess);
|
||
|
m_context = context;
|
||
|
m_contextAvailable = true;
|
||
|
m_wakeTriggerSuccess.notify_one();
|
||
|
}
|
||
|
|
||
|
void MockContextRequester::onContextFailure(const ContextRequestError error) {
|
||
|
std::unique_lock<std::mutex> lock(m_mutexFailure);
|
||
|
m_contextFailure = true;
|
||
|
m_wakeTriggerFailure.notify_one();
|
||
|
}
|
||
|
|
||
|
bool MockContextRequester::waitForContext(const std::chrono::milliseconds duration) {
|
||
|
std::unique_lock<std::mutex> lock(m_mutexSuccess);
|
||
|
if (!m_wakeTriggerSuccess.wait_for(lock, duration, [this]() { return (m_contextAvailable || m_contextFailure); })) {
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool MockContextRequester::waitForFailure(const std::chrono::milliseconds duration) {
|
||
|
std::unique_lock<std::mutex> lock(m_mutexFailure);
|
||
|
if (!m_wakeTriggerFailure.wait_for(lock, duration, [this]() { return (m_contextAvailable || m_contextFailure); })) {
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
std::string& MockContextRequester::getContextString() {
|
||
|
return m_context;
|
||
|
}
|
||
|
|
||
|
std::string MockContextRequester::serializeJSONObjectToString(const rapidjson::Value& documentNode) {
|
||
|
if (!documentNode.IsObject()) {
|
||
|
ACSDK_ERROR(LX("serializeJSONObjectToStringFailed")
|
||
|
.d("reason", "invalidType")
|
||
|
.d("expectedType", rapidjson::Type::kObjectType)
|
||
|
.d("type", documentNode.GetType()));
|
||
|
return "";
|
||
|
}
|
||
|
|
||
|
rapidjson::StringBuffer stringBuffer;
|
||
|
rapidjson::Writer<rapidjson::StringBuffer> writer(stringBuffer);
|
||
|
|
||
|
if (!documentNode.Accept(writer)) {
|
||
|
ACSDK_ERROR(LX("serializeJSONObjectToStringFailed").d("reason", "acceptFailed").d("handler", "Writer"));
|
||
|
return "";
|
||
|
}
|
||
|
|
||
|
return stringBuffer.GetString();
|
||
|
}
|
||
|
bool MockContextRequester::checkContextString(const std::string& a, const std::string& b) {
|
||
|
rapidjson::Document ajson;
|
||
|
rapidjson::Document bjson;
|
||
|
const char contextstr[] = "context";
|
||
|
|
||
|
ajson.Parse(a.c_str());
|
||
|
if (!ajson.HasMember(contextstr)) {
|
||
|
ACSDK_ERROR(LX("checkContextStringFailed").d("reason", "invalidContextString").d("json", a));
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bjson.Parse(b.c_str());
|
||
|
if (!bjson.HasMember(contextstr)) {
|
||
|
ACSDK_ERROR(LX("checkContextStringFailed").d("reason", "invalidContextString").d("json", b));
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
const rapidjson::Value& acontext = ajson[contextstr];
|
||
|
const rapidjson::Value& bcontext = bjson[contextstr];
|
||
|
|
||
|
if (!acontext.IsArray()) {
|
||
|
ACSDK_ERROR(LX("checkContextStringFailed").d("reason", "invalidContextString").d("json", a));
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (!bcontext.IsArray()) {
|
||
|
ACSDK_ERROR(LX("checkContextStringFailed").d("reason", "invalidContextString").d("json", b));
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (acontext.Size() != bcontext.Size()) {
|
||
|
ACSDK_ERROR(LX("checkContextStringFailed").d("reason", "contextStringNotMatched").d("value", a).d("ref", b));
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
for (rapidjson::Value::ConstValueIterator itr = acontext.Begin(); itr != acontext.End(); ++itr) {
|
||
|
bool entryMatch = false;
|
||
|
std::string aconstring;
|
||
|
aconstring = serializeJSONObjectToString(*itr);
|
||
|
|
||
|
for (rapidjson::Value::ConstValueIterator itr2 = bcontext.Begin(); itr2 != bcontext.End(); ++itr2) {
|
||
|
std::string bconstring;
|
||
|
|
||
|
bconstring = serializeJSONObjectToString(*itr);
|
||
|
if (aconstring == bconstring) {
|
||
|
entryMatch = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (!entryMatch) {
|
||
|
ACSDK_ERROR(
|
||
|
LX("checkContextStringFailed").d("reason", "contextStringNotMatched").d("value", a).d("ref", b));
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
/**
|
||
|
* @c MockStateProvider used to verify @c ContextManager behavior.
|
||
|
*/
|
||
|
class MockStateProvider : public StateProviderInterface {
|
||
|
public:
|
||
|
/**
|
||
|
* Creates an instance of the @c MockStateProvider.
|
||
|
*
|
||
|
* @param contextManager Pointer to an instance of the @c ContextManager.
|
||
|
* @param namespaceAndName The namespace and name of the @c StateProvider.
|
||
|
* @param jsonState The state of the @c StateProvider set in response to the @c provideState.
|
||
|
* @param refreshPolicy The refresh policy for the state set in response to the @c provideState.
|
||
|
* @param delayTime The time @c m_doProvideThread sleeps before updating state via @c setState.
|
||
|
* @return A pointer to an instance of the @c MockStateProvider.
|
||
|
*/
|
||
|
static std::shared_ptr<MockStateProvider> create(
|
||
|
std::shared_ptr<ContextManager> contextManager,
|
||
|
const NamespaceAndName& namespaceAndName,
|
||
|
const std::string& state,
|
||
|
const StateRefreshPolicy& refreshPolicy,
|
||
|
const std::chrono::milliseconds delayTime);
|
||
|
|
||
|
/**
|
||
|
* Constructor. It sets up the state and refresh policy that needs to be sent in response to a @c provideContext
|
||
|
* request.
|
||
|
*
|
||
|
* @param contextManager Pointer to an instance of the @c ContextManager.
|
||
|
* @param namespaceAndName The namespace and name of the @c StateProvider.
|
||
|
* @param jsonState The state of the @c StateProvider set in response to the @c provideState.
|
||
|
* @param refreshPolicy The refresh policy for the state set in response to the @c provideState.
|
||
|
* @param delayTime The time @c m_doProvideThread sleeps before updating state via @c setState.
|
||
|
* @return A pointer to an instance of the @c MockStateProvider.
|
||
|
*/
|
||
|
MockStateProvider(
|
||
|
std::shared_ptr<ContextManager> contextManager,
|
||
|
const NamespaceAndName& namespaceAndName,
|
||
|
const std::string& state,
|
||
|
const StateRefreshPolicy& refreshPolicy,
|
||
|
const std::chrono::milliseconds delayTime);
|
||
|
|
||
|
~MockStateProvider();
|
||
|
|
||
|
void provideState(const NamespaceAndName& stateProviderName, unsigned int currentstateRequestToken) override;
|
||
|
|
||
|
/**
|
||
|
* Method for m_doProvideThread. It waits for @c m_delayTime and then calls @c setState with the state @c m_state
|
||
|
* and the refresh policy @c m_refreshPolicy the @c stateProviderInterface was initialized with.
|
||
|
*/
|
||
|
void doProvideState();
|
||
|
|
||
|
unsigned int getCurrentstateRequestToken();
|
||
|
|
||
|
private:
|
||
|
/// Pointer to an instance of the @c ContextManager
|
||
|
std::shared_ptr<ContextManager> m_contextManager;
|
||
|
|
||
|
/// The namespace and name of the @c stateProviderInterface
|
||
|
NamespaceAndName m_namespaceAndName;
|
||
|
|
||
|
/// The state provided to the @c ContextManager via @c setState.
|
||
|
std::string m_state;
|
||
|
|
||
|
/// The refresh policy for the state provided to the @c ContextManager via @c setState.
|
||
|
StateRefreshPolicy m_refreshPolicy;
|
||
|
|
||
|
/// The token provided by the @c ContextManager.
|
||
|
unsigned int m_stateRequestToken;
|
||
|
|
||
|
/// Thread to execute the doProvide.
|
||
|
std::thread m_doProvideThread;
|
||
|
|
||
|
/// Mutex to protect the @c m_provideState.
|
||
|
std::mutex m_providerMutex;
|
||
|
|
||
|
/// The condition variable used to wake up @c m_doProvideThread when the @c ContextManager requests for state.
|
||
|
std::condition_variable m_providerWakeTrigger;
|
||
|
|
||
|
/// The time the @c m_doProvideThread sleeps before providing state.
|
||
|
std::chrono::milliseconds m_delayTime;
|
||
|
|
||
|
/// Flag to indicate when a @c provideState has been called.
|
||
|
bool m_provideState;
|
||
|
|
||
|
/// Flag to indicate when the @c MockStateProvider is shutting down. When set, @c m_doProvideThread needs to return.
|
||
|
bool m_stateProviderShutdown;
|
||
|
};
|
||
|
|
||
|
std::shared_ptr<MockStateProvider> MockStateProvider::create(
|
||
|
std::shared_ptr<ContextManager> contextManager,
|
||
|
const NamespaceAndName& stateProviderName,
|
||
|
const std::string& state,
|
||
|
const StateRefreshPolicy& refreshPolicy,
|
||
|
const std::chrono::milliseconds delayTime) {
|
||
|
auto result =
|
||
|
std::make_shared<MockStateProvider>(contextManager, stateProviderName, state, refreshPolicy, delayTime);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
MockStateProvider::MockStateProvider(
|
||
|
std::shared_ptr<ContextManager> contextManager,
|
||
|
const NamespaceAndName& stateProviderName,
|
||
|
const std::string& state,
|
||
|
const StateRefreshPolicy& refreshPolicy,
|
||
|
const std::chrono::milliseconds delayTime) :
|
||
|
m_contextManager{contextManager},
|
||
|
m_namespaceAndName{stateProviderName},
|
||
|
m_state{state},
|
||
|
m_refreshPolicy{refreshPolicy},
|
||
|
m_stateRequestToken{0},
|
||
|
m_delayTime{delayTime},
|
||
|
m_provideState{false},
|
||
|
m_stateProviderShutdown{false} {
|
||
|
m_doProvideThread = std::thread(&MockStateProvider::doProvideState, this);
|
||
|
}
|
||
|
|
||
|
MockStateProvider::~MockStateProvider() {
|
||
|
std::unique_lock<std::mutex> lock(m_providerMutex);
|
||
|
m_stateProviderShutdown = true;
|
||
|
m_providerWakeTrigger.notify_one();
|
||
|
lock.unlock();
|
||
|
if (m_doProvideThread.joinable()) {
|
||
|
m_doProvideThread.join();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void MockStateProvider::provideState(const NamespaceAndName& stateProviderName, unsigned int stateRequestToken) {
|
||
|
m_stateRequestToken = stateRequestToken;
|
||
|
std::lock_guard<std::mutex> lock(m_providerMutex);
|
||
|
m_provideState = true;
|
||
|
m_providerWakeTrigger.notify_one();
|
||
|
}
|
||
|
|
||
|
void MockStateProvider::doProvideState() {
|
||
|
while (true) {
|
||
|
std::unique_lock<std::mutex> lock(m_providerMutex);
|
||
|
m_providerWakeTrigger.wait(lock, [this]() { return (m_provideState || m_stateProviderShutdown); });
|
||
|
if (m_stateProviderShutdown) {
|
||
|
lock.unlock();
|
||
|
return;
|
||
|
}
|
||
|
std::this_thread::sleep_for(m_delayTime);
|
||
|
EXPECT_EQ(
|
||
|
SetStateResult::SUCCESS,
|
||
|
m_contextManager->setState(m_namespaceAndName, m_state, m_refreshPolicy, m_stateRequestToken));
|
||
|
m_provideState = false;
|
||
|
lock.unlock();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
unsigned int MockStateProvider::getCurrentstateRequestToken() {
|
||
|
return m_stateRequestToken;
|
||
|
}
|
||
|
|
||
|
/// Context Manager Test
|
||
|
class ContextManagerTest : public ::testing::Test {
|
||
|
public:
|
||
|
/**
|
||
|
* Setup the @c ContextManagerTest. Creates a @c ContextManager. Creates a couple of @c MockStateProviders and
|
||
|
* a @c MockContextRequester.
|
||
|
*/
|
||
|
void SetUp() override;
|
||
|
|
||
|
/// @c ContextManager to test
|
||
|
std::shared_ptr<ContextManager> m_contextManager;
|
||
|
|
||
|
/// @c MockStateProviders and @c MockContextRequesters
|
||
|
std::shared_ptr<MockStateProvider> m_speechSynthesizer;
|
||
|
std::shared_ptr<MockStateProvider> m_audioPlayer;
|
||
|
std::shared_ptr<MockStateProvider> m_alerts;
|
||
|
std::shared_ptr<MockContextRequester> m_contextRequester;
|
||
|
std::shared_ptr<MockContextRequester> m_contextRequester2;
|
||
|
};
|
||
|
|
||
|
void ContextManagerTest::SetUp() {
|
||
|
m_contextManager = ContextManager::create();
|
||
|
m_speechSynthesizer = MockStateProvider::create(
|
||
|
m_contextManager,
|
||
|
SPEECH_SYNTHESIZER,
|
||
|
SPEECH_SYNTHESIZER_PAYLOAD_FINISHED,
|
||
|
StateRefreshPolicy::NEVER,
|
||
|
DEFAULT_SLEEP_TIME);
|
||
|
m_audioPlayer = MockStateProvider::create(
|
||
|
m_contextManager, AUDIO_PLAYER, AUDIO_PLAYER_PAYLOAD, StateRefreshPolicy::NEVER, DEFAULT_SLEEP_TIME);
|
||
|
m_contextManager->setStateProvider(SPEECH_SYNTHESIZER, m_speechSynthesizer);
|
||
|
m_contextManager->setStateProvider(AUDIO_PLAYER, m_audioPlayer);
|
||
|
m_contextRequester = MockContextRequester::create(m_contextManager);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the state with a @c StateRefreshPolicy @c ALWAYS for a @c StateProviderInterface that is registered with the
|
||
|
* @c ContextManager. Expect @c SetStateResult @c SUCCESS is returned.
|
||
|
*/
|
||
|
TEST_F(ContextManagerTest, testSetStateForRegisteredProvider) {
|
||
|
ASSERT_EQ(
|
||
|
SetStateResult::SUCCESS,
|
||
|
m_contextManager->setState(
|
||
|
SPEECH_SYNTHESIZER,
|
||
|
SPEECH_SYNTHESIZER_PAYLOAD_PLAYING,
|
||
|
StateRefreshPolicy::ALWAYS,
|
||
|
m_speechSynthesizer->getCurrentstateRequestToken()));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the state with a @c StateRefreshPolicy @c NEVER for a @c StateProviderInterface that is not registered with the
|
||
|
* @c ContextManager. Expect @c SetStateResult @c SUCCESS is returned.
|
||
|
*/
|
||
|
TEST_F(ContextManagerTest, testSetStateForUnregisteredProvider) {
|
||
|
ASSERT_EQ(SetStateResult::SUCCESS, m_contextManager->setState(ALERTS, ALERTS_PAYLOAD, StateRefreshPolicy::NEVER));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the state with a @c StateRefreshPolicy @c ALWAYS for a @c StateProviderInterface that is not registered with
|
||
|
* the @c ContextManager. Expect @c SetStateResult @c STATE_PROVIDER_NOT_REGISTERED is returned.
|
||
|
*/
|
||
|
TEST_F(ContextManagerTest, testSetStateForUnregisteredProviderWithRefreshPolicyAlways) {
|
||
|
m_alerts = MockStateProvider::create(
|
||
|
m_contextManager, ALERTS, ALERTS_PAYLOAD, StateRefreshPolicy::NEVER, DEFAULT_SLEEP_TIME);
|
||
|
ASSERT_EQ(
|
||
|
SetStateResult::STATE_PROVIDER_NOT_REGISTERED,
|
||
|
m_contextManager->setState(
|
||
|
ALERTS, ALERTS_PAYLOAD, StateRefreshPolicy::ALWAYS, m_alerts->getCurrentstateRequestToken()));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the states with a @c StateRefreshPolicy @c ALWAYS for @c StateProviderInterfaces that are registered with the
|
||
|
* @c ContextManager. Request for context by calling @c getContext. Expect that the context is returned within the
|
||
|
* timeout period. Check the context that is returned by the @c ContextManager. Expect it should match the test value.
|
||
|
*/
|
||
|
TEST_F(ContextManagerTest, testGetContext) {
|
||
|
ASSERT_EQ(
|
||
|
SetStateResult::SUCCESS,
|
||
|
m_contextManager->setState(
|
||
|
SPEECH_SYNTHESIZER,
|
||
|
SPEECH_SYNTHESIZER_PAYLOAD_PLAYING,
|
||
|
StateRefreshPolicy::ALWAYS,
|
||
|
m_speechSynthesizer->getCurrentstateRequestToken()));
|
||
|
ASSERT_EQ(
|
||
|
SetStateResult::SUCCESS,
|
||
|
m_contextManager->setState(
|
||
|
AUDIO_PLAYER,
|
||
|
AUDIO_PLAYER_PAYLOAD,
|
||
|
StateRefreshPolicy::ALWAYS,
|
||
|
m_audioPlayer->getCurrentstateRequestToken()));
|
||
|
m_contextManager->getContext(m_contextRequester);
|
||
|
ASSERT_TRUE(m_contextRequester->waitForContext(DEFAULT_TIMEOUT));
|
||
|
ASSERT_EQ(CONTEXT_TEST, m_contextRequester->getContextString());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the states with a @c StateRefreshPolicy @c ALWAYS for @c StateProviderInterfaces that are registered with the
|
||
|
* @c ContextManager. Request for context by calling @c getContext by multiple requesters. Expect that the context is
|
||
|
* returned to each of the requesters within the timeout period.
|
||
|
*/
|
||
|
TEST_F(ContextManagerTest, testMultipleGetContextRequests) {
|
||
|
ASSERT_EQ(
|
||
|
SetStateResult::SUCCESS,
|
||
|
m_contextManager->setState(
|
||
|
SPEECH_SYNTHESIZER,
|
||
|
SPEECH_SYNTHESIZER_PAYLOAD_PLAYING,
|
||
|
StateRefreshPolicy::ALWAYS,
|
||
|
m_speechSynthesizer->getCurrentstateRequestToken()));
|
||
|
ASSERT_EQ(
|
||
|
SetStateResult::SUCCESS,
|
||
|
m_contextManager->setState(
|
||
|
AUDIO_PLAYER,
|
||
|
AUDIO_PLAYER_PAYLOAD,
|
||
|
StateRefreshPolicy::ALWAYS,
|
||
|
m_audioPlayer->getCurrentstateRequestToken()));
|
||
|
m_contextRequester2 = MockContextRequester::create(m_contextManager);
|
||
|
m_contextManager->getContext(m_contextRequester);
|
||
|
m_contextManager->getContext(m_contextRequester2);
|
||
|
ASSERT_TRUE(m_contextRequester->waitForContext(DEFAULT_TIMEOUT));
|
||
|
ASSERT_EQ(CONTEXT_TEST, m_contextRequester->getContextString());
|
||
|
ASSERT_TRUE(m_contextRequester2->waitForContext(DEFAULT_TIMEOUT));
|
||
|
ASSERT_EQ(CONTEXT_TEST, m_contextRequester2->getContextString());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the states with a @c StateRefreshPolicy @c ALWAYS for @c StateProviderInterfaces that are registered with the
|
||
|
* @c ContextManager. Request for context by calling @c getContext. Expect that the context is returned within the
|
||
|
* timeout period. Check the context that is returned by the @c ContextManager. Expect it should match the test value.
|
||
|
* Register a registered @c StateProviderInterface again with the @c ContextManager. Then set the state for the same
|
||
|
* @c StateProviderInterface with a @c StateRefreshPolicy @c ALWAYS. Expect @c SetStateResult @c SUCCESS is returned.
|
||
|
* Request for context by calling @c getContext. Expect that the context is returned within the
|
||
|
* timeout period. Check the context that is returned by the @c ContextManager matches the test context.
|
||
|
*/
|
||
|
TEST_F(ContextManagerTest, testSetProviderTwice) {
|
||
|
ASSERT_EQ(
|
||
|
SetStateResult::SUCCESS,
|
||
|
m_contextManager->setState(
|
||
|
SPEECH_SYNTHESIZER,
|
||
|
SPEECH_SYNTHESIZER_PAYLOAD_PLAYING,
|
||
|
StateRefreshPolicy::ALWAYS,
|
||
|
m_speechSynthesizer->getCurrentstateRequestToken()));
|
||
|
ASSERT_EQ(
|
||
|
SetStateResult::SUCCESS,
|
||
|
m_contextManager->setState(
|
||
|
AUDIO_PLAYER,
|
||
|
AUDIO_PLAYER_PAYLOAD,
|
||
|
StateRefreshPolicy::ALWAYS,
|
||
|
m_audioPlayer->getCurrentstateRequestToken()));
|
||
|
m_contextManager->getContext(m_contextRequester);
|
||
|
ASSERT_TRUE(m_contextRequester->waitForContext(DEFAULT_TIMEOUT));
|
||
|
ASSERT_EQ(CONTEXT_TEST, m_contextRequester->getContextString());
|
||
|
// Call setStateProvider for a registered StateProviderInterface.
|
||
|
m_contextManager->setStateProvider(SPEECH_SYNTHESIZER, m_speechSynthesizer);
|
||
|
ASSERT_EQ(
|
||
|
SetStateResult::SUCCESS,
|
||
|
m_contextManager->setState(
|
||
|
SPEECH_SYNTHESIZER,
|
||
|
SPEECH_SYNTHESIZER_PAYLOAD_PLAYING,
|
||
|
StateRefreshPolicy::ALWAYS,
|
||
|
m_speechSynthesizer->getCurrentstateRequestToken()));
|
||
|
m_contextManager->getContext(m_contextRequester);
|
||
|
ASSERT_TRUE(m_contextRequester->waitForContext(DEFAULT_TIMEOUT));
|
||
|
ASSERT_EQ(CONTEXT_TEST, m_contextRequester->getContextString());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Register a @c StateProviderInterfaces with the @c ContextManager which responds slowly to @provideState requests.
|
||
|
* Set the states with a @c StateRefreshPolicy @c ALWAYS for @c StateProviderInterfaces. Request for context by calling
|
||
|
* @c getContext. Expect that failure occurs due to timeout.
|
||
|
*/
|
||
|
TEST_F(ContextManagerTest, testProvideStateTimeout) {
|
||
|
m_alerts = MockStateProvider::create(
|
||
|
m_contextManager, ALERTS, ALERTS_PAYLOAD, StateRefreshPolicy::NEVER, TIMEOUT_SLEEP_TIME);
|
||
|
m_contextManager->setStateProvider(ALERTS, m_alerts);
|
||
|
ASSERT_EQ(
|
||
|
SetStateResult::SUCCESS,
|
||
|
m_contextManager->setState(
|
||
|
SPEECH_SYNTHESIZER,
|
||
|
SPEECH_SYNTHESIZER_PAYLOAD_PLAYING,
|
||
|
StateRefreshPolicy::ALWAYS,
|
||
|
m_speechSynthesizer->getCurrentstateRequestToken()));
|
||
|
ASSERT_EQ(
|
||
|
SetStateResult::SUCCESS,
|
||
|
m_contextManager->setState(
|
||
|
AUDIO_PLAYER,
|
||
|
AUDIO_PLAYER_PAYLOAD,
|
||
|
StateRefreshPolicy::ALWAYS,
|
||
|
m_audioPlayer->getCurrentstateRequestToken()));
|
||
|
ASSERT_EQ(
|
||
|
SetStateResult::SUCCESS,
|
||
|
m_contextManager->setState(
|
||
|
ALERTS, ALERTS_PAYLOAD, StateRefreshPolicy::ALWAYS, m_alerts->getCurrentstateRequestToken()));
|
||
|
m_contextManager->getContext(m_contextRequester);
|
||
|
ASSERT_TRUE(m_contextRequester->waitForFailure(FAILURE_TIMEOUT));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Remove a @c StateProviderInterface that was registered with the @c ContextManager. Set the state with a
|
||
|
* @c StateRefreshPolicy @c ALWAYS for the @c StateProviderInterface that was unregistered. Expect @c SetStateResult
|
||
|
* @c STATE_PROVIDER_NOT_REGISTERED is returned.
|
||
|
*/
|
||
|
TEST_F(ContextManagerTest, testRemoveProvider) {
|
||
|
m_contextManager->setStateProvider(SPEECH_SYNTHESIZER, nullptr);
|
||
|
ASSERT_EQ(
|
||
|
SetStateResult::STATE_PROVIDER_NOT_REGISTERED,
|
||
|
m_contextManager->setState(
|
||
|
SPEECH_SYNTHESIZER,
|
||
|
SPEECH_SYNTHESIZER_PAYLOAD_PLAYING,
|
||
|
StateRefreshPolicy::ALWAYS,
|
||
|
m_speechSynthesizer->getCurrentstateRequestToken()));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Request for context by calling @c getContextSet. Wait for context. Set the state for a @c StateProviderInterface
|
||
|
* that is registered with the @c ContextManager with a wrong token value. Expect that
|
||
|
* @c SetStateResult @c STATE_TOKEN_OUTDATED is returned.
|
||
|
*/
|
||
|
TEST_F(ContextManagerTest, testIncorrectToken) {
|
||
|
m_contextManager->getContext(m_contextRequester);
|
||
|
ASSERT_TRUE(m_contextRequester->waitForContext(DEFAULT_TIMEOUT));
|
||
|
ASSERT_EQ(
|
||
|
SetStateResult::STATE_TOKEN_OUTDATED,
|
||
|
m_contextManager->setState(
|
||
|
SPEECH_SYNTHESIZER,
|
||
|
SPEECH_SYNTHESIZER_PAYLOAD_PLAYING,
|
||
|
StateRefreshPolicy::ALWAYS,
|
||
|
m_speechSynthesizer->getCurrentstateRequestToken() + 1));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the states with a @c StateRefreshPolicy @c ALWAYS for @c StateProviderInterfaces that are registered with the
|
||
|
* @c ContextManager. Request for context by calling @c getContext. Expect that the context is returned within the
|
||
|
* timeout period.
|
||
|
*
|
||
|
* There's a dummyProvider with StateRefreshPolicy @c SOMETIMES that returns an empty context. Check ContextManager is
|
||
|
* okay with it and would include the context provided by the dummyProvider.
|
||
|
*
|
||
|
* Check the context that is returned by the @c ContextManager. Expect it should match the test value.
|
||
|
*/
|
||
|
TEST_F(ContextManagerTest, testEmptyProvider) {
|
||
|
auto dummyProvider = MockStateProvider::create(
|
||
|
m_contextManager, DUMMY_PROVIDER, "", StateRefreshPolicy::SOMETIMES, DEFAULT_SLEEP_TIME);
|
||
|
m_contextManager->setStateProvider(DUMMY_PROVIDER, dummyProvider);
|
||
|
|
||
|
ASSERT_EQ(
|
||
|
SetStateResult::SUCCESS,
|
||
|
m_contextManager->setState(
|
||
|
SPEECH_SYNTHESIZER,
|
||
|
SPEECH_SYNTHESIZER_PAYLOAD_PLAYING,
|
||
|
StateRefreshPolicy::ALWAYS,
|
||
|
m_speechSynthesizer->getCurrentstateRequestToken()));
|
||
|
ASSERT_EQ(
|
||
|
SetStateResult::SUCCESS,
|
||
|
m_contextManager->setState(
|
||
|
AUDIO_PLAYER,
|
||
|
AUDIO_PLAYER_PAYLOAD,
|
||
|
StateRefreshPolicy::ALWAYS,
|
||
|
m_audioPlayer->getCurrentstateRequestToken()));
|
||
|
ASSERT_EQ(
|
||
|
SetStateResult::SUCCESS,
|
||
|
m_contextManager->setState(
|
||
|
DUMMY_PROVIDER, "", StateRefreshPolicy::ALWAYS, dummyProvider->getCurrentstateRequestToken()));
|
||
|
m_contextManager->getContext(m_contextRequester);
|
||
|
ASSERT_TRUE(m_contextRequester->waitForContext(DEFAULT_TIMEOUT));
|
||
|
ASSERT_TRUE(m_contextRequester->checkContextString(CONTEXT_TEST, m_contextRequester->getContextString()));
|
||
|
}
|
||
|
|
||
|
} // namespace test
|
||
|
} // namespace contextManager
|
||
|
} // namespace alexaClientSDK
|