From b6abca2edcb36c0c0848d1cd8dc291f23293aa80 Mon Sep 17 00:00:00 2001 From: Naveen Bobbili Date: Mon, 12 Nov 2018 16:12:38 -0800 Subject: 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 --- src/plugins/voiceagents/test/VoiceAgentTest.cpp | 94 +++++++ .../test/VoiceAgentsDataManagerTest.cpp | 294 +++++++++++++++++++++ src/plugins/voiceagents/test/VoiceAgentsTestData.h | 67 +++++ 3 files changed, 455 insertions(+) create mode 100644 src/plugins/voiceagents/test/VoiceAgentTest.cpp create mode 100644 src/plugins/voiceagents/test/VoiceAgentsDataManagerTest.cpp create mode 100644 src/plugins/voiceagents/test/VoiceAgentsTestData.h (limited to 'src/plugins/voiceagents/test') 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 + +#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(); + + 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 mConsoleLogger; + std::shared_ptr 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 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 + +#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(); + mAfbApi = std::make_shared<::testing::NiceMock>(); + mVADataManager = VoiceAgentsDataManager::create(mConsoleLogger, mAfbApi); + + mAgentsChangeObserver = std::make_shared< + ::testing::StrictMock>(); + 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 + findVoiceAgent(std::set> &voiceAgents, + std::string &vaId) { + for (auto va : voiceAgents) { + if (va->getId() == vaId) + return va; + } + + return nullptr; + } + + std::shared_ptr 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> mAfbApi; + + // It is a StrictMock because we want to fail the test for all non interesting + // calls. + std::shared_ptr<::testing::StrictMock> + mAgentsChangeObserver; + + std::vector mVoiceAgentsData; + std::unique_ptr 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 +#include +#include +#include + +namespace vshl { +namespace test { + +typedef std::shared_ptr> 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 getVoiceAgentsTestData() { + std::vector 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>( + new std::unordered_set{"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>( + new std::unordered_set{"Ford Prefect", "Zaphod Beeblebrox"}) // Wake Words + }, + }; + + return voiceAgentsTestData; +} + +} // namespace test +} // namespace vshl -- cgit 1.2.3-korg