summaryrefslogtreecommitdiffstats
path: root/src/plugins/voiceagents
diff options
context:
space:
mode:
authorNaveen Bobbili <nbobbili@amazon.com>2018-11-12 16:12:38 -0800
committerNaveen Bobbili <nbobbili@amazon.com>2018-11-13 15:05:41 -0800
commitb6abca2edcb36c0c0848d1cd8dc291f23293aa80 (patch)
treea838812e0b66f0695cb6cf0f8bebfa38315ce8b8 /src/plugins/voiceagents
parentbe70712f89eacd20dca413bcce46e4aa26b5709e (diff)
SPEC-1924: AGL Speech Framework's Voice Service High Level 1.0 Release.
Details: 1) Control plugin implementation for VSHL 1.0 2) Exposed APIs that are documented in the confluence page https://confluence.automotivelinux.org/display/SPE/Speech+EG+Architecture 3) Implemented 39 unit tests based on GTest framework to test all the low level components of VSHL binding. 4) Implemented a HTML5 based VSHL API tester application to test VSHL APIs. API specification: https://confluence.automotivelinux.org/display/SPE/Speech+EG+Architecture#SpeechEGArchitecture-HighLevelVoiceService Test performed: 1) Tested AGL service running Alexa Auto SDK https://github.com/alexa/aac-sdk on Ubuntu 16.04 and Renesas R-Car M3 board. License: Apache 2.0 Developers/Owners: Naveen Bobbili (nbobbili@amazon.com) Prakash Buddhiraja (buddhip@amazon.com) Shotaro Uchida (shotaru@amazon.co.jp) Change-Id: I3370f4ad65aff030f24f4ad571fb02d525bbfbca Signed-off-by: Naveen Bobbili <nbobbili@amazon.com>
Diffstat (limited to 'src/plugins/voiceagents')
-rw-r--r--src/plugins/voiceagents/VoiceAgentEventNames.h38
-rw-r--r--src/plugins/voiceagents/VoiceAgentsDataManager.h135
-rw-r--r--src/plugins/voiceagents/VoiceAgentsDataManagerImpl.cpp272
-rw-r--r--src/plugins/voiceagents/include/VoiceAgent.h95
-rw-r--r--src/plugins/voiceagents/include/VoiceAgentEventsHandler.h95
-rw-r--r--src/plugins/voiceagents/src/VoiceAgentEventsHandler.cpp139
-rw-r--r--src/plugins/voiceagents/src/VoiceAgentImpl.cpp126
-rw-r--r--src/plugins/voiceagents/test/VoiceAgentTest.cpp94
-rw-r--r--src/plugins/voiceagents/test/VoiceAgentsDataManagerTest.cpp294
-rw-r--r--src/plugins/voiceagents/test/VoiceAgentsTestData.h67
10 files changed, 1355 insertions, 0 deletions
diff --git a/src/plugins/voiceagents/VoiceAgentEventNames.h b/src/plugins/voiceagents/VoiceAgentEventNames.h
new file mode 100644
index 0000000..4575528
--- /dev/null
+++ b/src/plugins/voiceagents/VoiceAgentEventNames.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2018-2019 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.
+ */
+#ifndef VSHL_VOICEAGENTS_INCLUDE_VOICEAGENTEVENTNAMES_H_
+#define VSHL_VOICEAGENTS_INCLUDE_VOICEAGENTEVENTNAMES_H_
+
+#include <list>
+#include <string>
+
+using namespace std;
+
+namespace vshl {
+namespace voiceagents {
+
+static string VSHL_EVENT_AUTH_STATE_EVENT = "voice_authstate_event";
+static string VSHL_EVENT_CONNECTION_STATE_EVENT = "voice_connectionstate_event";
+static string VSHL_EVENT_DIALOG_STATE_EVENT = "voice_dialogstate_event";
+
+static list<string> VSHL_EVENTS = {
+ VSHL_EVENT_AUTH_STATE_EVENT, VSHL_EVENT_CONNECTION_STATE_EVENT,
+ VSHL_EVENT_DIALOG_STATE_EVENT,
+};
+
+} // namespace voiceagents
+} // namespace vshl
+
+#endif // VSHL_VOICEAGENTS_INCLUDE_VOICEAGENTEVENTNAMES_H_
diff --git a/src/plugins/voiceagents/VoiceAgentsDataManager.h b/src/plugins/voiceagents/VoiceAgentsDataManager.h
new file mode 100644
index 0000000..a4c9143
--- /dev/null
+++ b/src/plugins/voiceagents/VoiceAgentsDataManager.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2018-2019 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.
+ */
+#ifndef VSHL_VOICEAGENTS_INCLUDE_VOICEAGENTS_H_
+#define VSHL_VOICEAGENTS_INCLUDE_VOICEAGENTS_H_
+
+#include <memory>
+#include <set>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "interfaces/afb/IAFBApi.h"
+#include "interfaces/utilities/events/IEventFilter.h"
+#include "interfaces/utilities/logging/ILogger.h"
+#include "interfaces/voiceagents/IVoiceAgent.h"
+#include "interfaces/voiceagents/IVoiceAgentsChangeObserver.h"
+#include "voiceagents/include/VoiceAgent.h"
+#include "voiceagents/include/VoiceAgentEventsHandler.h"
+
+namespace vshl {
+namespace voiceagents {
+/*
+ * This class implements the data model for voiceagents.
+ * Supports add, remove and query operations on voiceagent data.
+ * Notifies the observers of the changes in the voiceagents data model.
+ */
+class VoiceAgentsDataManager {
+public:
+ // Create a VoiceAgentsDataManager.
+ static std::unique_ptr<VoiceAgentsDataManager> create(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi);
+
+ /**
+ * Activates the list of voiceagents.
+ *
+ * @return Number of activated agents
+ */
+ uint32_t activateVoiceAgents(const unordered_set<string>& activeVoiceAgentIds);
+
+ /**
+ * Deactivates the list of voiceagents.
+ *
+ * @return Number of de-activated agents
+ */
+ uint32_t deactivateVoiceAgents(const unordered_set<string>& inactiveVoiceAgentIds);
+
+ // Sets the default voiceagent.
+ bool setDefaultVoiceAgent(const string& voiceAgentId);
+
+ // Sets the default voiceagent.
+ std::string getDefaultVoiceAgent();
+
+ // Sets the active wakeword for the voiceagent.
+ bool setActiveWakeWord(const string& voiceAgentId, const string& wakeword);
+
+ // Adds a new voiceagent to the cache and also persists the information in a
+ // database.
+ // This call would notify all the observers about the new voiceagent addition.
+ bool addNewVoiceAgent(
+ const string& id,
+ const string& name,
+ const string& description,
+ const string& api,
+ const string& vendor,
+ const string& activeWakeword,
+ const bool isActive,
+ const shared_ptr<unordered_set<string>> wakewords);
+
+ // Removes the voiceagent from thecache and also from persistent database.
+ // This call would notify all the observers about the removal of the
+ // voiceagent.
+ bool removeVoiceAgent(const string& voiceAgentId);
+
+ // Returns the set of all voice agents in @c VoiceAgentsDataManger cache
+ std::set<std::shared_ptr<vshl::common::interfaces::IVoiceAgent>> getAllVoiceAgents();
+
+ // Returns the event filter that belongs to the core module.
+ shared_ptr<vshl::common::interfaces::IEventFilter> getEventFilter() const;
+
+ // Subscribe to an event coming from the voiceagent.
+ bool subscribeToVshlEventFromVoiceAgent(
+ vshl::common::interfaces::IAFBRequest& request,
+ const string eventName,
+ const string voiceagentId);
+
+ // Adds a new voiceagent change observer.
+ bool addVoiceAgentsChangeObserver(shared_ptr<vshl::common::interfaces::IVoiceAgentsChangeObserver> observer);
+
+ // Removes the voiceagent change observer from the list.
+ bool removeVoiceAgentsChangeObserver(shared_ptr<vshl::common::interfaces::IVoiceAgentsChangeObserver> observer);
+
+ // Destructor
+ ~VoiceAgentsDataManager();
+
+private:
+ // Constructor
+ VoiceAgentsDataManager(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi);
+
+ // Binding API reference
+ shared_ptr<vshl::common::interfaces::IAFBApi> mAfbApi;
+
+ // A list of all the voiceagent change observers
+ unordered_set<shared_ptr<vshl::common::interfaces::IVoiceAgentsChangeObserver>> mVoiceAgentChangeObservers;
+
+ // A map of voiceagents grouped by ID
+ unordered_map<string, shared_ptr<VoiceAgent>> mVoiceAgents;
+
+ // Voiceagent event handler.
+ shared_ptr<VoiceAgentEventsHandler> mVoiceAgentEventsHandler;
+
+ // Default voiceagent
+ string mDefaultVoiceAgentId;
+
+ // Logger
+ shared_ptr<vshl::common::interfaces::ILogger> mLogger;
+};
+
+} // namespace voiceagents
+} // namespace vshl
+
+#endif // VSHL_VOICEAGENTS_INCLUDE_VOICEAGENTS_H_
diff --git a/src/plugins/voiceagents/VoiceAgentsDataManagerImpl.cpp b/src/plugins/voiceagents/VoiceAgentsDataManagerImpl.cpp
new file mode 100644
index 0000000..626a7fc
--- /dev/null
+++ b/src/plugins/voiceagents/VoiceAgentsDataManagerImpl.cpp
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2018-2019 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 "voiceagents/VoiceAgentsDataManager.h"
+
+#include "voiceagents/include/VoiceAgentEventsHandler.h"
+
+static string TAG = "vshl::voiceagents::VoiceAgentsDataManager";
+
+/**
+ * Specifies the severity level of a log message
+ */
+using Level = vshl::common::interfaces::ILogger::Level;
+
+namespace vshl {
+namespace voiceagents {
+
+std::unique_ptr<VoiceAgentsDataManager> VoiceAgentsDataManager::create(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi) {
+ return std::unique_ptr<VoiceAgentsDataManager>(new VoiceAgentsDataManager(logger, afbApi));
+}
+
+// Constructor
+VoiceAgentsDataManager::VoiceAgentsDataManager(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi) :
+ mLogger(logger),
+ mAfbApi(afbApi) {
+ mVoiceAgentEventsHandler = VoiceAgentEventsHandler::create(mLogger, mAfbApi);
+}
+
+// Destructor
+VoiceAgentsDataManager::~VoiceAgentsDataManager() {
+ // Clear the observers
+ mVoiceAgentChangeObservers.clear();
+ // Clear the voiceagents
+ mVoiceAgents.clear();
+}
+
+uint32_t VoiceAgentsDataManager::activateVoiceAgents(const unordered_set<string>& activeVoiceAgentIds) {
+ if (activeVoiceAgentIds.empty() || mVoiceAgents.empty()) {
+ mLogger->log(Level::ERROR, TAG, "Failed to activate voiceagents");
+ return 0;
+ }
+
+ uint32_t agentsActivated = 0;
+ for (auto voiceAgentId : activeVoiceAgentIds) {
+ auto voiceAgentIt = mVoiceAgents.find(voiceAgentId);
+ if (voiceAgentIt != mVoiceAgents.end()) {
+ // activate the voiceagent
+ ++agentsActivated;
+ if (!voiceAgentIt->second->isActive()) {
+ voiceAgentIt->second->setIsActive(true);
+ // Notify observers
+ for (auto observer : mVoiceAgentChangeObservers) {
+ observer->OnVoiceAgentActivated(voiceAgentIt->second);
+ }
+ }
+ }
+ }
+ return agentsActivated;
+}
+
+uint32_t VoiceAgentsDataManager::deactivateVoiceAgents(const unordered_set<string>& inactiveVoiceAgentIds) {
+ if (inactiveVoiceAgentIds.empty() || mVoiceAgents.empty()) {
+ mLogger->log(Level::ERROR, TAG, "Failed to deactivate voiceagents");
+ return 0;
+ }
+
+ uint32_t agentsDeactivated = 0;
+ for (auto voiceAgentId : inactiveVoiceAgentIds) {
+ auto voiceAgentIt = mVoiceAgents.find(voiceAgentId);
+ if (voiceAgentIt != mVoiceAgents.end()) {
+ ++agentsDeactivated;
+ if (voiceAgentIt->second->isActive()) {
+ // deactivate the voiceagent
+ voiceAgentIt->second->setIsActive(false);
+ // Notify observers
+ for (auto observer : mVoiceAgentChangeObservers) {
+ observer->OnVoiceAgentDeactivated(voiceAgentIt->second);
+ }
+ }
+ }
+ }
+
+ return agentsDeactivated;
+}
+
+bool VoiceAgentsDataManager::setDefaultVoiceAgent(const string& voiceAgentId) {
+ if (mVoiceAgents.empty() || voiceAgentId.empty()) {
+ string message = string("Failed to set default voiceagent id: ") + voiceAgentId;
+ mLogger->log(Level::ERROR, TAG, message);
+ return false;
+ }
+
+ auto defaultVoiceAgentIt = mVoiceAgents.find(voiceAgentId);
+
+ if (defaultVoiceAgentIt != mVoiceAgents.end()) {
+ if (mDefaultVoiceAgentId != voiceAgentId) {
+ // Notify observers
+ for (auto observer : mVoiceAgentChangeObservers) {
+ observer->OnDefaultVoiceAgentChanged(defaultVoiceAgentIt->second);
+ }
+ }
+ mDefaultVoiceAgentId = voiceAgentId;
+ } else {
+ mLogger->log(Level::ERROR, TAG, "Can't set default agent. Invalid voice agent id:" + voiceAgentId);
+ return false;
+ }
+
+ return true;
+}
+
+std::string VoiceAgentsDataManager::getDefaultVoiceAgent() {
+ return mDefaultVoiceAgentId;
+}
+
+bool VoiceAgentsDataManager::setActiveWakeWord(const string& voiceAgentId, const string& wakeword) {
+ if (mVoiceAgents.empty() || wakeword.empty()) {
+ string message =
+ string("Failed to set active wakeword: ") + wakeword + string(" for voiceagent id: ") + voiceAgentId;
+ mLogger->log(Level::ERROR, TAG, message);
+ return false;
+ }
+
+ auto voiceAgentIt = mVoiceAgents.find(voiceAgentId);
+ if (voiceAgentIt == mVoiceAgents.end()) {
+ return false;
+ }
+
+ string oldWakeWord = voiceAgentIt->second->getActiveWakeword();
+ if (oldWakeWord != wakeword) {
+ voiceAgentIt->second->setActiveWakeWord(wakeword);
+ // Notify observers
+ for (auto observer : mVoiceAgentChangeObservers) {
+ observer->OnVoiceAgentActiveWakeWordChanged(voiceAgentIt->second);
+ }
+ }
+
+ return true;
+}
+
+bool VoiceAgentsDataManager::addNewVoiceAgent(
+ const string& id,
+ const string& name,
+ const string& description,
+ const string& api,
+ const string& vendor,
+ const string& activeWakeword,
+ const bool isActive,
+ const shared_ptr<unordered_set<string>> wakewords) {
+ shared_ptr<VoiceAgent> voiceAgent =
+ VoiceAgent::create(mLogger, id, name, description, api, vendor, activeWakeword, isActive, wakewords);
+
+ if (voiceAgent.get() == nullptr || voiceAgent->getId().empty()) {
+ string message = string("Invalid Arguments: Failed to add new voiceagent");
+ mLogger->log(Level::ERROR, TAG, message);
+ return false;
+ }
+
+ if (!mVoiceAgents.empty() && mVoiceAgents.find(voiceAgent->getId()) != mVoiceAgents.end()) {
+ string message =
+ string("Failed to add new voiceagent. Voiceagent: ") + voiceAgent->getId() + string(" already exists.");
+ mLogger->log(Level::ERROR, TAG, message);
+ return false;
+ }
+
+ mVoiceAgents.insert(make_pair(voiceAgent->getId(), voiceAgent));
+
+ // Notify the observers
+ for (auto observer : mVoiceAgentChangeObservers) {
+ observer->OnVoiceAgentAdded(voiceAgent);
+ }
+
+ // Create all vshl events for the voiceagent.
+ mVoiceAgentEventsHandler->createVshlEventsForVoiceAgent(voiceAgent->getId());
+
+ return true;
+}
+
+bool VoiceAgentsDataManager::removeVoiceAgent(const string& voiceAgentId) {
+ if (mVoiceAgents.empty()) {
+ string message = string("Failed to remove voiceagent: ") + voiceAgentId + string(". Voiceagents data empty.");
+ mLogger->log(Level::ERROR, TAG, message);
+ return false;
+ }
+
+ auto voiceAgentIt = mVoiceAgents.find(voiceAgentId);
+ if (voiceAgentIt == mVoiceAgents.end()) {
+ string message = string("Failed to remove voiceagent: ") + voiceAgentId + string(". Doesn't exist.");
+ mLogger->log(Level::ERROR, TAG, message);
+ return false;
+ }
+
+ auto voiceAgent = voiceAgentIt->second;
+ // Remove from the map
+ mVoiceAgents.erase(voiceAgentId);
+ // Notify the observers
+ for (auto observer : mVoiceAgentChangeObservers) {
+ observer->OnVoiceAgentRemoved(voiceAgent);
+ }
+
+ // Remove all vshl events for the voiceagent.
+ mVoiceAgentEventsHandler->removeVshlEventsForVoiceAgent(voiceAgent->getId());
+
+ return true;
+}
+
+std::set<std::shared_ptr<vshl::common::interfaces::IVoiceAgent>> VoiceAgentsDataManager::getAllVoiceAgents() {
+ std::set<std::shared_ptr<vshl::common::interfaces::IVoiceAgent>> voiceAgentsSet;
+ for (auto element : mVoiceAgents) {
+ voiceAgentsSet.insert(element.second);
+ }
+
+ return voiceAgentsSet;
+}
+
+// Returns the event filter that belongs to the core module.
+shared_ptr<vshl::common::interfaces::IEventFilter> VoiceAgentsDataManager::getEventFilter() const {
+ return mVoiceAgentEventsHandler;
+}
+
+bool VoiceAgentsDataManager::subscribeToVshlEventFromVoiceAgent(
+ vshl::common::interfaces::IAFBRequest& request,
+ const string eventName,
+ const string voiceAgentId) {
+ auto voiceAgentIt = mVoiceAgents.find(voiceAgentId);
+ if (voiceAgentIt == mVoiceAgents.end()) {
+ mLogger->log(
+ Level::ERROR,
+ TAG,
+ "\
+ Failed to subscribe to VSHL events from voiceagent. VoiceAgent: " +
+ voiceAgentId + " doesn't exist.");
+ return false;
+ }
+ return mVoiceAgentEventsHandler->subscribeToVshlEventFromVoiceAgent(request, eventName, voiceAgentIt->second);
+}
+
+bool VoiceAgentsDataManager::addVoiceAgentsChangeObserver(
+ shared_ptr<vshl::common::interfaces::IVoiceAgentsChangeObserver> observer) {
+ if (!observer) {
+ return false;
+ }
+
+ mVoiceAgentChangeObservers.insert(observer);
+ return true;
+}
+
+bool VoiceAgentsDataManager::removeVoiceAgentsChangeObserver(
+ shared_ptr<vshl::common::interfaces::IVoiceAgentsChangeObserver> observer) {
+ if (!observer) {
+ return false;
+ }
+
+ mVoiceAgentChangeObservers.erase(observer);
+ return true;
+}
+} // namespace voiceagents
+} // namespace vshl
diff --git a/src/plugins/voiceagents/include/VoiceAgent.h b/src/plugins/voiceagents/include/VoiceAgent.h
new file mode 100644
index 0000000..4dd55d4
--- /dev/null
+++ b/src/plugins/voiceagents/include/VoiceAgent.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2018-2019 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.
+ */
+#ifndef VSHL_VOICEAGENTS_INCLUDE_VOICEAGENT_H_
+#define VSHL_VOICEAGENTS_INCLUDE_VOICEAGENT_H_
+
+#include <memory>
+#include <unordered_set>
+
+#include "interfaces/utilities/logging/ILogger.h"
+#include "interfaces/voiceagents/IVoiceAgent.h"
+
+using namespace std;
+
+namespace vshl {
+namespace voiceagents {
+/*
+ * Default implementation of IVoiceAgent interface.
+ */
+class VoiceAgent : public vshl::common::interfaces::IVoiceAgent {
+public:
+ // Creates @c VoiceAgent instance
+ static shared_ptr<VoiceAgent>
+ create(shared_ptr<vshl::common::interfaces::ILogger> logger, const string &id,
+ const string &name, const string &description, const string &api,
+ const string &vendor, const string &activeWakeword,
+ const bool isActive,
+ const shared_ptr<unordered_set<string>> wakewords);
+
+ // Destructor
+ ~VoiceAgent();
+
+ // IVoiceAgent overriden methods
+ bool setActiveWakeWord(const string &wakeword) override;
+ void setIsActive(bool active) override;
+ string getId() const override;
+ string getName() const override;
+ string getDescription() const override;
+ string getApi() const override;
+ string getVendor() const override;
+ shared_ptr<unordered_set<string>> getWakeWords() const override;
+ bool isActive() const override;
+ string getActiveWakeword() const override;
+
+private:
+ // Constructor
+ VoiceAgent(shared_ptr<vshl::common::interfaces::ILogger> logger,
+ const string &id, const string &name, const string &description,
+ const string &api, const string &vendor,
+ const string &activeWakeword, const bool isActive,
+ const shared_ptr<unordered_set<string>> wakewords);
+
+ // Logger
+ shared_ptr<vshl::common::interfaces::ILogger> mLogger;
+
+ // Id
+ string mId;
+
+ // Name
+ string mName;
+
+ // Description
+ string mDescription;
+
+ // API
+ string mApi;
+
+ // Vendor
+ string mVendor;
+
+ // Active wakeword
+ string mActiveWakeword;
+
+ // Active ??
+ bool mIsActive;
+
+ // Wakewords
+ shared_ptr<unordered_set<string>> mWakewords;
+};
+
+} // namespace voiceagents
+} // namespace vshl
+
+#endif // VSHL_VOICEAGENTS_INCLUDE_VOICEAGENT_H_
diff --git a/src/plugins/voiceagents/include/VoiceAgentEventsHandler.h b/src/plugins/voiceagents/include/VoiceAgentEventsHandler.h
new file mode 100644
index 0000000..3c1ca6c
--- /dev/null
+++ b/src/plugins/voiceagents/include/VoiceAgentEventsHandler.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2018-2019 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.
+ */
+#ifndef VSHL_VOICEAGENTS_INCLUDE_VOICEAGENTSTATE_EVENT_HANDLER_H_
+#define VSHL_VOICEAGENTS_INCLUDE_VOICEAGENTSTATE_EVENT_HANDLER_H_
+
+#include <algorithm>
+#include <memory>
+#include <unordered_map>
+
+#include "interfaces/afb/IAFBApi.h"
+#include "interfaces/utilities/events/IEventFilter.h"
+#include "interfaces/utilities/logging/ILogger.h"
+#include "voiceagents/VoiceAgentEventNames.h"
+#include "voiceagents/include/VoiceAgent.h"
+
+using namespace std;
+
+namespace vshl {
+namespace voiceagents {
+/*
+ * This class is reponsible for handling agent specific events
+ * subscription and delivery on behalf of the high level voice service.
+ * This class also listen to the incoming events from voice agents
+ * and implements propagation to application layer.
+ */
+class VoiceAgentEventsHandler : public vshl::common::interfaces::IEventFilter {
+public:
+ // Create a VREventFilter.
+ static shared_ptr<VoiceAgentEventsHandler> create(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi);
+
+ // Creates all the vshl events for a specific voiceagent id.
+ // For e.g if voiceagent is VA-001 then a new vshl event
+ // voice_authstate_event#VA-001 for auth state will be created.
+ // Please see VoiceAgentEventNames.h for all the event names.
+ void createVshlEventsForVoiceAgent(const string voiceAgentId);
+
+ // Removes the events from its bookkeeping.
+ void removeVshlEventsForVoiceAgent(const string voiceAgentId);
+
+ // Subscribe to a vshl event corresponding to a voiceagent.
+ bool subscribeToVshlEventFromVoiceAgent(
+ vshl::common::interfaces::IAFBRequest& request,
+ const string eventName,
+ const shared_ptr<VoiceAgent> voiceAgent);
+
+ ~VoiceAgentEventsHandler();
+
+protected:
+ string getName() override;
+
+ // IEventFilter override
+ bool onIncomingEvent(const string eventName, const string voiceAgentId, const string payload) override;
+
+private:
+ // Constructor
+ VoiceAgentEventsHandler(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi);
+
+ // Helper method to generate the event name with voiceagent Id
+ // concatenated.
+ string createEventNameWithVAId(string eventName, string voiceAgentId);
+
+ // call subscribe verb on the voiceagent. True if subscription successful.
+ // False otherwise.
+ bool callSubscribeVerb(const shared_ptr<VoiceAgent> voiceAgent);
+
+ // Binding API reference
+ shared_ptr<vshl::common::interfaces::IAFBApi> mAfbApi;
+
+ // A map of VSHL event ID to its Event object
+ unordered_map<string, shared_ptr<common::interfaces::IAFBApi::IAFBEvent>> mEventsMap;
+
+ // Logger
+ shared_ptr<vshl::common::interfaces::ILogger> mLogger;
+};
+
+} // namespace voiceagents
+} // namespace vshl
+
+#endif // VSHL_VOICEAGENTS_INCLUDE_VOICEAGENTSTATE_EVENT_HANDLER_H_
diff --git a/src/plugins/voiceagents/src/VoiceAgentEventsHandler.cpp b/src/plugins/voiceagents/src/VoiceAgentEventsHandler.cpp
new file mode 100644
index 0000000..4952721
--- /dev/null
+++ b/src/plugins/voiceagents/src/VoiceAgentEventsHandler.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2018-2019 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 "voiceagents/include/VoiceAgentEventsHandler.h"
+
+static string TAG = "vshl::voiceagents::VoiceAgentEventsHandler";
+static string VA_VERB_SUBSCRIBE = "subscribe";
+
+using Level = vshl::common::interfaces::ILogger::Level;
+using namespace vshl::common::interfaces;
+
+namespace vshl {
+namespace voiceagents {
+
+shared_ptr<VoiceAgentEventsHandler> VoiceAgentEventsHandler::create(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi) {
+ auto eventFilter = std::shared_ptr<VoiceAgentEventsHandler>(new VoiceAgentEventsHandler(logger, afbApi));
+ return eventFilter;
+}
+
+VoiceAgentEventsHandler::VoiceAgentEventsHandler(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ shared_ptr<vshl::common::interfaces::IAFBApi> afbApi) :
+ mAfbApi(afbApi),
+ mLogger(logger) {
+}
+
+VoiceAgentEventsHandler::~VoiceAgentEventsHandler() {
+ mEventsMap.clear();
+}
+
+string VoiceAgentEventsHandler::getName() {
+ return TAG;
+}
+
+void VoiceAgentEventsHandler::createVshlEventsForVoiceAgent(const string voiceAgentId) {
+ // Update the events map with all the VSHL Events.
+ for (auto eventName : VSHL_EVENTS) {
+ string eventNameWithVAId = createEventNameWithVAId(eventName, voiceAgentId);
+ auto it = mEventsMap.find(eventNameWithVAId);
+ if (it == mEventsMap.end() && mAfbApi) {
+ // create a new event and add it to the map.
+ shared_ptr<IAFBApi::IAFBEvent> event = mAfbApi->createEvent(eventNameWithVAId);
+ mEventsMap.insert(make_pair(eventNameWithVAId, event));
+ }
+ }
+}
+
+void VoiceAgentEventsHandler::removeVshlEventsForVoiceAgent(const string voiceAgentId) {
+ // Update the events map with all the VSHL Events.
+ for (auto eventName : VSHL_EVENTS) {
+ string eventNameWithVAId = createEventNameWithVAId(eventName, voiceAgentId);
+ auto it = mEventsMap.find(eventNameWithVAId);
+ if (it != mEventsMap.end()) {
+ mEventsMap.erase(it);
+ }
+ }
+}
+
+bool VoiceAgentEventsHandler::subscribeToVshlEventFromVoiceAgent(
+ vshl::common::interfaces::IAFBRequest& request,
+ const string eventName,
+ const shared_ptr<VoiceAgent> voiceAgent) {
+ auto supportedEventsIt = find(VSHL_EVENTS.begin(), VSHL_EVENTS.end(), eventName);
+ if (supportedEventsIt == VSHL_EVENTS.end()) {
+ mLogger->log(Level::ERROR, TAG, "Event: " + eventName + " not a known event.");
+ return false;
+ }
+
+ // Check if the entry for the voiceagent is present in the
+ // events map. If not then return false because the responsibility
+ // of adding to the map lies in the hands of AddVoiceAgent method.
+ string eventNameWithVAId = createEventNameWithVAId(eventName, voiceAgent->getId());
+ auto createdEventsIt = mEventsMap.find(eventNameWithVAId);
+ if (createdEventsIt == mEventsMap.end()) {
+ mLogger->log(Level::ERROR, TAG, "Not able to subscribe. Event doesn't exist, " + eventNameWithVAId);
+ return false;
+ }
+ createdEventsIt->second->subscribe(request);
+
+ if (!callSubscribeVerb(voiceAgent)) {
+ mLogger->log(Level::WARNING, TAG, "Failed to subscribe to voiceagent: " + voiceAgent->getId());
+ }
+
+ return true;
+}
+
+// IEventFilter override.
+bool VoiceAgentEventsHandler::onIncomingEvent(const string eventName, const string voiceAgentId, const string payload) {
+ string eventNameWithVAId = createEventNameWithVAId(eventName, voiceAgentId);
+ auto it = mEventsMap.find(eventNameWithVAId);
+ if (it != mEventsMap.end()) {
+ return it->second->publishEvent(json_object_new_string(payload.c_str()));
+ }
+
+ return true;
+}
+
+string VoiceAgentEventsHandler::createEventNameWithVAId(string eventName, string voiceAgentId) {
+ return eventName + "#" + voiceAgentId;
+}
+
+bool VoiceAgentEventsHandler::callSubscribeVerb(const shared_ptr<VoiceAgent> voiceAgent) {
+ if (!voiceAgent) {
+ mLogger->log(Level::ERROR, TAG, "Failed to callSubscribeVerb. Invalid input parameter.");
+ return false;
+ }
+
+ if (!mAfbApi) {
+ mLogger->log(
+ Level::ERROR, TAG, "Failed to callSubscribeVerb on voicegent: " + voiceAgent->getId() + ", No API.");
+ return false;
+ }
+
+ // TODO(Naveen): Move to utilities.
+ json_object* object = NULL;
+ std::string error, info;
+ int rc = mAfbApi->callSync(voiceAgent->getApi(), VA_VERB_SUBSCRIBE, NULL, &object, error, info);
+
+ if (object) {
+ free(object);
+ }
+
+ return true;
+}
+} // namespace voiceagents
+} // namespace vshl
diff --git a/src/plugins/voiceagents/src/VoiceAgentImpl.cpp b/src/plugins/voiceagents/src/VoiceAgentImpl.cpp
new file mode 100644
index 0000000..f2ef8a1
--- /dev/null
+++ b/src/plugins/voiceagents/src/VoiceAgentImpl.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2018-2019 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 <sstream>
+
+#include "voiceagents/include/VoiceAgent.h"
+
+static string TAG = "vshl::voiceagents::VoiceAgent";
+
+/**
+ * Specifies the severity level of a log message
+ */
+using Level = vshl::common::interfaces::ILogger::Level;
+
+namespace vshl {
+namespace voiceagents {
+// Creates @c VoiceAgent instance
+shared_ptr<VoiceAgent> VoiceAgent::create(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ const string& id,
+ const string& name,
+ const string& description,
+ const string& api,
+ const string& vendor,
+ const string& activeWakeword,
+ const bool isActive,
+ const shared_ptr<unordered_set<string>> wakewords) {
+ if (wakewords == nullptr) {
+ logger->log(Level::ERROR, TAG, "Wakeword list null");
+ return nullptr;
+ }
+
+ auto voiceAgent = std::unique_ptr<VoiceAgent>(
+ new VoiceAgent(logger, id, name, description, api, vendor, activeWakeword, isActive, wakewords));
+ if (!voiceAgent->setActiveWakeWord(activeWakeword)) {
+ return nullptr;
+ }
+
+ return voiceAgent;
+}
+
+VoiceAgent::VoiceAgent(
+ shared_ptr<vshl::common::interfaces::ILogger> logger,
+ const string& id,
+ const string& name,
+ const string& description,
+ const string& api,
+ const string& vendor,
+ const string& activeWakeword,
+ const bool isActive,
+ const shared_ptr<unordered_set<string>> wakewords) :
+ mLogger(logger),
+ mId(id),
+ mName(name),
+ mDescription(description),
+ mApi(api),
+ mVendor(vendor),
+ mActiveWakeword(activeWakeword),
+ mIsActive(isActive),
+ mWakewords(wakewords) {
+}
+
+// Destructor
+VoiceAgent::~VoiceAgent() {
+}
+
+// Set the active wakeword for this voiceagent
+bool VoiceAgent::setActiveWakeWord(const string& wakeword) {
+ if (mWakewords->find(wakeword) != mWakewords->end()) {
+ mActiveWakeword = wakeword;
+ return true;
+ }
+
+ mLogger->log(Level::ERROR, TAG, "Wakeword: " + wakeword + " doesn't exist in wakeword list");
+ return false;
+}
+
+// Sets the activation state of this voiceagent
+void VoiceAgent::setIsActive(bool active) {
+ mIsActive = active;
+}
+
+string VoiceAgent::getId() const {
+ return mId;
+}
+
+string VoiceAgent::getName() const {
+ return mName;
+}
+
+string VoiceAgent::getDescription() const {
+ return mDescription;
+}
+
+string VoiceAgent::getApi() const {
+ return mApi;
+}
+
+string VoiceAgent::getVendor() const {
+ return mVendor;
+}
+
+shared_ptr<unordered_set<string>> VoiceAgent::getWakeWords() const {
+ return mWakewords;
+}
+
+bool VoiceAgent::isActive() const {
+ return mIsActive;
+}
+
+string VoiceAgent::getActiveWakeword() const {
+ return mActiveWakeword;
+}
+} // namespace voiceagents
+} // namespace vshl
diff --git a/src/plugins/voiceagents/test/VoiceAgentTest.cpp b/src/plugins/voiceagents/test/VoiceAgentTest.cpp
new file mode 100644
index 0000000..e5ad15e
--- /dev/null
+++ b/src/plugins/voiceagents/test/VoiceAgentTest.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2018-2019 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 <gtest/gtest.h>
+
+#include "voiceagents/include/VoiceAgent.h"
+
+#include "voiceagents/test/VoiceAgentsTestData.h"
+#include "test/common/ConsoleLogger.h"
+
+using namespace vshl::voiceagents;
+using namespace vshl::test::common;
+
+namespace vshl {
+namespace test {
+
+class VoiceAgentTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ mConsoleLogger = std::make_shared<ConsoleLogger>();
+
+ mVoiceAgentData = *(getVoiceAgentsTestData().begin());
+ mVoiceAgent = VoiceAgent::create(
+ mConsoleLogger,
+ mVoiceAgentData.id,
+ mVoiceAgentData.name,
+ mVoiceAgentData.description,
+ mVoiceAgentData.api,
+ mVoiceAgentData.vendor,
+ mVoiceAgentData.activeWakeword,
+ mVoiceAgentData.isActive,
+ mVoiceAgentData.wakewords);
+ }
+
+ std::shared_ptr<ConsoleLogger> mConsoleLogger;
+ std::shared_ptr<VoiceAgent> mVoiceAgent;
+ VoiceAgentTestData mVoiceAgentData;
+};
+
+TEST_F(VoiceAgentTest, InitializesCorrectly) {
+ ASSERT_NE(mVoiceAgent, nullptr);
+ ASSERT_EQ(mVoiceAgent->getId(), mVoiceAgentData.id);
+ ASSERT_EQ(mVoiceAgent->getName(), mVoiceAgentData.name);
+ ASSERT_EQ(mVoiceAgent->getDescription(), mVoiceAgentData.description);
+ ASSERT_EQ(mVoiceAgent->getApi(), mVoiceAgentData.api);
+ ASSERT_EQ(mVoiceAgent->getVendor(), mVoiceAgentData.vendor);
+ ASSERT_EQ(mVoiceAgent->getActiveWakeword(), mVoiceAgentData.activeWakeword);
+ ASSERT_EQ(mVoiceAgent->isActive(), mVoiceAgentData.isActive);
+
+ std::unordered_set<std::string> wakeWords = *mVoiceAgentData.wakewords;
+ ASSERT_EQ(*(mVoiceAgent->getWakeWords()), wakeWords);
+}
+
+TEST_F(VoiceAgentTest, FailsCreationOnNonExistentWakeword) {
+ std::string nonExistentWW = "non-existent";
+ auto voiceAgent = VoiceAgent::create(
+ mConsoleLogger,
+ mVoiceAgentData.id,
+ mVoiceAgentData.name,
+ mVoiceAgentData.description,
+ mVoiceAgentData.api,
+ mVoiceAgentData.vendor,
+ nonExistentWW,
+ mVoiceAgentData.isActive,
+ mVoiceAgentData.wakewords);
+ ASSERT_EQ(voiceAgent, nullptr);
+}
+
+TEST_F(VoiceAgentTest, SetsWakewordCorrectly) {
+ std::string wakeword = *(mVoiceAgentData.wakewords->begin());
+ ASSERT_TRUE(mVoiceAgent->setActiveWakeWord(wakeword));
+ ASSERT_EQ(mVoiceAgent->getActiveWakeword(), wakeword);
+}
+
+TEST_F(VoiceAgentTest, FailsToSetNonExistentWakeword) {
+ std::string nonExistentWW = "non-existent";
+ ASSERT_FALSE(mVoiceAgent->setActiveWakeWord(nonExistentWW));
+ ASSERT_EQ(mVoiceAgent->getActiveWakeword(), mVoiceAgentData.activeWakeword);
+}
+
+} // namespace test
+} // namespace vshl \ No newline at end of file
diff --git a/src/plugins/voiceagents/test/VoiceAgentsDataManagerTest.cpp b/src/plugins/voiceagents/test/VoiceAgentsDataManagerTest.cpp
new file mode 100644
index 0000000..58c62ed
--- /dev/null
+++ b/src/plugins/voiceagents/test/VoiceAgentsDataManagerTest.cpp
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2018-2019 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 <gtest/gtest.h>
+
+#include "voiceagents/VoiceAgentsDataManager.h"
+
+#include "test/common/ConsoleLogger.h"
+#include "test/mocks/AFBApiMock.h"
+#include "test/mocks/VoiceAgentsChangeObserverMock.h"
+#include "voiceagents/test/VoiceAgentsTestData.h"
+
+using namespace vshl::common::interfaces;
+using namespace vshl::voiceagents;
+
+using namespace vshl::test::common;
+
+namespace vshl {
+namespace test {
+
+class VoiceAgentDataManagerTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ mConsoleLogger = std::make_shared<ConsoleLogger>();
+ mAfbApi = std::make_shared<::testing::NiceMock<AFBApiMock>>();
+ mVADataManager = VoiceAgentsDataManager::create(mConsoleLogger, mAfbApi);
+
+ mAgentsChangeObserver = std::make_shared<
+ ::testing::StrictMock<VoiceAgentsChangeObserverMock>>();
+ mVADataManager->addVoiceAgentsChangeObserver(mAgentsChangeObserver);
+
+ mVoiceAgentsData = getVoiceAgentsTestData();
+ }
+
+ void TearDown() override {
+ mVADataManager->removeVoiceAgentsChangeObserver(mAgentsChangeObserver);
+ }
+
+ static bool addVoiceAgent(VoiceAgentsDataManager &mgr,
+ VoiceAgentTestData &data) {
+ return mgr.addNewVoiceAgent(data.id, data.name, data.description, data.api,
+ data.vendor, data.activeWakeword, data.isActive,
+ data.wakewords);
+ }
+
+ static bool isEqual(const VoiceAgentTestData &lhs, const IVoiceAgent &rhs) {
+ return lhs.id == rhs.getId() && lhs.name == rhs.getName() &&
+ lhs.description == rhs.getDescription() && lhs.api == rhs.getApi() &&
+ lhs.vendor == rhs.getVendor() &&
+ lhs.activeWakeword == rhs.getActiveWakeword() &&
+ lhs.isActive == rhs.isActive() &&
+ *lhs.wakewords == *rhs.getWakeWords();
+ }
+
+ static std::shared_ptr<IVoiceAgent>
+ findVoiceAgent(std::set<std::shared_ptr<IVoiceAgent>> &voiceAgents,
+ std::string &vaId) {
+ for (auto va : voiceAgents) {
+ if (va->getId() == vaId)
+ return va;
+ }
+
+ return nullptr;
+ }
+
+ std::shared_ptr<ConsoleLogger> mConsoleLogger;
+
+ // It is a NiceMock because we don't want gtest to produce warnings about non
+ // interesting calls.
+ // The non interesting calls like createEvent is internal implementation
+ // detail for many of the
+ // tests in this class, and hence suppression of these warnings with NiceMock.
+ std::shared_ptr<::testing::NiceMock<AFBApiMock>> mAfbApi;
+
+ // It is a StrictMock because we want to fail the test for all non interesting
+ // calls.
+ std::shared_ptr<::testing::StrictMock<VoiceAgentsChangeObserverMock>>
+ mAgentsChangeObserver;
+
+ std::vector<VoiceAgentTestData> mVoiceAgentsData;
+ std::unique_ptr<VoiceAgentsDataManager> mVADataManager;
+};
+
+TEST_F(VoiceAgentDataManagerTest, InitializesCorrectly) {
+ ASSERT_NE(mVADataManager, nullptr);
+ ASSERT_EQ(mVADataManager->getAllVoiceAgents().size(), 0);
+ ASSERT_EQ(mVADataManager->getDefaultVoiceAgent(), std::string());
+}
+
+TEST_F(VoiceAgentDataManagerTest, addingVoiceAgentWithSameIdFails) {
+ EXPECT_CALL(*mAgentsChangeObserver, OnVoiceAgentAdded(::testing::_)).Times(1);
+
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[0]));
+ ASSERT_FALSE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[0]));
+
+ auto allVoiceAgents = mVADataManager->getAllVoiceAgents();
+ ASSERT_EQ(allVoiceAgents.size(), 1);
+}
+
+TEST_F(VoiceAgentDataManagerTest,
+ addingVoiceAgentWithNonExistentActiveWakewordFails) {
+ auto voiceAgetData = mVoiceAgentsData[0];
+ voiceAgetData.activeWakeword = "non-existent";
+ ASSERT_FALSE(addVoiceAgent(*mVADataManager, voiceAgetData));
+
+ auto allVoiceAgents = mVADataManager->getAllVoiceAgents();
+ ASSERT_EQ(allVoiceAgents.size(), 0);
+}
+
+TEST_F(VoiceAgentDataManagerTest, canAddNewVoiceAgents) {
+ EXPECT_CALL(*mAgentsChangeObserver, OnVoiceAgentAdded(::testing::_)).Times(2);
+
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[0]));
+
+ auto allVoiceAgents = mVADataManager->getAllVoiceAgents();
+ ASSERT_EQ(allVoiceAgents.size(), 1);
+
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[1]));
+
+ allVoiceAgents = mVADataManager->getAllVoiceAgents();
+ ASSERT_EQ(allVoiceAgents.size(), 2);
+
+ for (auto va : allVoiceAgents) {
+ bool voiceAgentFound = false;
+ for (auto vaData : mVoiceAgentsData) {
+ if (isEqual(vaData, *va)) {
+ voiceAgentFound = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(voiceAgentFound);
+ }
+}
+
+TEST_F(VoiceAgentDataManagerTest, removingUnknonwVoiceAgentFails) {
+ EXPECT_CALL(*mAgentsChangeObserver, OnVoiceAgentAdded(::testing::_)).Times(1);
+
+ ASSERT_FALSE(mVADataManager->removeVoiceAgent("non-existent-vaid"));
+
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[0]));
+ ASSERT_FALSE(mVADataManager->removeVoiceAgent("non-existent-vaid"));
+}
+
+TEST_F(VoiceAgentDataManagerTest, canRemoveVoiceAgents) {
+ EXPECT_CALL(*mAgentsChangeObserver, OnVoiceAgentAdded(::testing::_)).Times(2);
+ EXPECT_CALL(*mAgentsChangeObserver, OnVoiceAgentRemoved(::testing::_))
+ .Times(2);
+
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[0]));
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[1]));
+
+ auto allVoiceAgents = mVADataManager->getAllVoiceAgents();
+ ASSERT_EQ(allVoiceAgents.size(), 2);
+
+ ASSERT_TRUE(mVADataManager->removeVoiceAgent(mVoiceAgentsData[0].id));
+
+ allVoiceAgents = mVADataManager->getAllVoiceAgents();
+ ASSERT_EQ(allVoiceAgents.size(), 1);
+
+ ASSERT_TRUE(mVADataManager->removeVoiceAgent(mVoiceAgentsData[1].id));
+
+ allVoiceAgents = mVADataManager->getAllVoiceAgents();
+ ASSERT_EQ(allVoiceAgents.size(), 0);
+}
+
+TEST_F(VoiceAgentDataManagerTest, activatingNonExistentVoiceAgentsFails) {
+ uint32_t result = mVADataManager->activateVoiceAgents({"non", "existent"});
+ ASSERT_EQ(result, 0);
+
+ EXPECT_CALL(*mAgentsChangeObserver, OnVoiceAgentAdded(::testing::_)).Times(2);
+
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[0]));
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[1]));
+
+ result = mVADataManager->activateVoiceAgents({"non", "existent"});
+ ASSERT_EQ(result, 0);
+}
+
+TEST_F(VoiceAgentDataManagerTest, deactivatingNonExistentVoiceAgentsFails) {
+ uint32_t result = mVADataManager->deactivateVoiceAgents({"non", "existent"});
+ ASSERT_EQ(result, 0);
+
+ EXPECT_CALL(*mAgentsChangeObserver, OnVoiceAgentAdded(::testing::_)).Times(2);
+
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[0]));
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[1]));
+
+ result = mVADataManager->deactivateVoiceAgents({"non", "existent"});
+ ASSERT_EQ(result, 0);
+}
+
+TEST_F(VoiceAgentDataManagerTest, canActivateDeactivateVoiceAgents) {
+ {
+ ::testing::InSequence dummy;
+
+ EXPECT_CALL(*mAgentsChangeObserver, OnVoiceAgentAdded(::testing::_))
+ .Times(2);
+ EXPECT_CALL(*mAgentsChangeObserver, OnVoiceAgentDeactivated(::testing::_))
+ .Times(1);
+ EXPECT_CALL(*mAgentsChangeObserver, OnVoiceAgentActivated(::testing::_))
+ .Times(1);
+ }
+
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[0]));
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[1]));
+
+ std::string vaId = mVoiceAgentsData[0].id;
+
+ auto allVA = mVADataManager->getAllVoiceAgents();
+ auto voiceAgent = findVoiceAgent(allVA, vaId);
+ ASSERT_NE(voiceAgent, nullptr);
+ ASSERT_TRUE(voiceAgent->isActive());
+
+ uint32_t result =
+ mVADataManager->deactivateVoiceAgents({"non-existent", vaId});
+ ASSERT_EQ(result, 1);
+ ASSERT_FALSE(voiceAgent->isActive());
+
+ // Try de-activating already de-activated agent
+ result = mVADataManager->deactivateVoiceAgents({vaId});
+ ASSERT_EQ(result, 1);
+ ASSERT_FALSE(voiceAgent->isActive());
+
+ result = mVADataManager->activateVoiceAgents({"non-existent", vaId});
+ ASSERT_EQ(result, 1);
+ ASSERT_TRUE(voiceAgent->isActive());
+
+ // Try activating already activated agent
+ result = mVADataManager->activateVoiceAgents({vaId});
+ ASSERT_EQ(result, 1);
+ ASSERT_TRUE(voiceAgent->isActive());
+}
+
+TEST_F(VoiceAgentDataManagerTest,
+ NoDefaultAgentIsReturnedWhenNoDefaultAgentIsSet) {
+ EXPECT_CALL(*mAgentsChangeObserver, OnVoiceAgentAdded(::testing::_)).Times(2);
+
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[0]));
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[1]));
+
+ std::string defaultAgentId = mVADataManager->getDefaultVoiceAgent();
+ ASSERT_EQ(defaultAgentId, "");
+}
+
+TEST_F(VoiceAgentDataManagerTest, DefaultAgentCanBeSet) {
+ EXPECT_CALL(*mAgentsChangeObserver, OnVoiceAgentAdded(::testing::_)).Times(2);
+
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[0]));
+ ASSERT_TRUE(addVoiceAgent(*mVADataManager, mVoiceAgentsData[1]));
+
+ auto allAgents = mVADataManager->getAllVoiceAgents();
+ std::string vaId1 = mVoiceAgentsData[1].id;
+ std::string vaId2 = mVoiceAgentsData[0].id;
+ auto va1 = findVoiceAgent(allAgents, vaId1);
+ auto va2 = findVoiceAgent(allAgents, vaId2);
+
+ {
+ ::testing::InSequence dummy;
+
+ EXPECT_CALL(*mAgentsChangeObserver, OnDefaultVoiceAgentChanged(va1))
+ .Times(1);
+ EXPECT_CALL(*mAgentsChangeObserver, OnDefaultVoiceAgentChanged(va2))
+ .Times(1);
+ }
+
+ ASSERT_TRUE(mVADataManager->setDefaultVoiceAgent(vaId1));
+ ASSERT_EQ(mVADataManager->getDefaultVoiceAgent(), vaId1);
+
+ ASSERT_TRUE(mVADataManager->setDefaultVoiceAgent(vaId2));
+ ASSERT_EQ(mVADataManager->getDefaultVoiceAgent(), vaId2);
+
+ ASSERT_FALSE(mVADataManager->setDefaultVoiceAgent("non-existent"));
+ ASSERT_EQ(mVADataManager->getDefaultVoiceAgent(), vaId2);
+
+ // Setting default agent to already default agent shouldn't result in extra
+ // callback to OnDefaultVoiceAgentChanged
+ ASSERT_TRUE(mVADataManager->setDefaultVoiceAgent(vaId2));
+ ASSERT_EQ(mVADataManager->getDefaultVoiceAgent(), vaId2);
+}
+
+} // namespace test
+} // namespace vshl \ No newline at end of file
diff --git a/src/plugins/voiceagents/test/VoiceAgentsTestData.h b/src/plugins/voiceagents/test/VoiceAgentsTestData.h
new file mode 100644
index 0000000..ced068f
--- /dev/null
+++ b/src/plugins/voiceagents/test/VoiceAgentsTestData.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2018-2019 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 <string>
+#include <unordered_set>
+#include <vector>
+
+namespace vshl {
+namespace test {
+
+typedef std::shared_ptr<std::unordered_set<std::string>> WakeWords;
+
+struct VoiceAgentTestData {
+ std::string id;
+ std::string name;
+ std::string description;
+ std::string api;
+ std::string vendor;
+ std::string activeWakeword;
+ bool isActive;
+ WakeWords wakewords;
+};
+
+static std::vector<VoiceAgentTestData> getVoiceAgentsTestData() {
+ std::vector<VoiceAgentTestData> voiceAgentsTestData{
+ {
+ "VA-001", // Id
+ "Foundation", // Name
+ "Voice Agent For Galactic Empire", // Description
+ "api-1", // API
+ "Asimov", // Vendor
+ "Hari Seldon", // Active Wakeword
+ true, // Is Active
+ std::shared_ptr<std::unordered_set<std::string>>(
+ new std::unordered_set<std::string>{"Hari Seldon", "Cleon I ", "Eto Demerzel"}) // Wake Words
+ },
+ {
+ "VA-002", // Id
+ "Betelgeuse", // Name
+ "Voice Agent For Galaxy hopper", // Description
+ "api-2", // API
+ "Douglas Adams", // Vendor
+ "Ford Prefect", // Active Wakeword
+ true, // Is Active
+ std::shared_ptr<std::unordered_set<std::string>>(
+ new std::unordered_set<std::string>{"Ford Prefect", "Zaphod Beeblebrox"}) // Wake Words
+ },
+ };
+
+ return voiceAgentsTestData;
+}
+
+} // namespace test
+} // namespace vshl