avs-device-sdk/CapabilityAgents/SpeechSynthesizer/test/SpeechSynthesizerTest.cpp

722 lines
31 KiB
C++

/*
* SpeechSynthesizerTest.cpp
*
* Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0/
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
#include <chrono>
#include <future>
#include <memory>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <AVSCommon/MockExceptionEncounteredSender.h>
#include <AVSCommon/AVS/Attachment/AttachmentManagerInterface.h>
#include <AVSCommon/SDKInterfaces/MockContextManager.h>
#include <AVSCommon/SDKInterfaces/MockFocusManager.h>
#include <AVSCommon/SDKInterfaces/MockMessageSender.h>
#include <AVSCommon/SDKInterfaces/MockDirectiveSequencer.h>
#include <AVSCommon/SDKInterfaces/MockDirectiveHandlerResult.h>
#include <AVSCommon/AVS/Attachment/AttachmentManager.h>
#include "SpeechSynthesizer/SpeechSynthesizer.h"
namespace alexaClientSDK {
namespace capabilityAgents {
namespace speechSynthesizer {
namespace test {
using namespace avsUtils;
using namespace avsCommon;
using namespace avsCommon::avs;
using namespace avsCommon::avs::attachment;
using namespace avsCommon::sdkInterfaces;
using namespace avsCommon::sdkInterfaces::test;
using namespace avsCommon::test;
using namespace avsCommon::utils::mediaPlayer;
using namespace ::testing;
/// Plenty of time for a test to complete.
static std::chrono::milliseconds WAIT_TIMEOUT(1000);
/// The name of the @c FocusManager channel used by the @c SpeechSynthesizer.
static const std::string CHANNEL_NAME("Dialog");
/// The activity Id used with the @c FocusManager by @c SpeechSynthesizer.
static const std::string FOCUS_MANAGER_ACTIVITY_ID("SpeechSynthesizer.Speak");
/// Namespace for SpeechSynthesizer.
static const std::string NAMESPACE_SPEECH_SYNTHESIZER("SpeechSynthesizer");
/// Name for SpeechSynthesizer directive.
static const std::string NAME_SPEAK("Speak");
/// Wrong name for testing.
static const std::string NAME_RECOGNIZE("Recognize");
/// The @c NamespaceAndName to send to the @c ContextManager.
static const NamespaceAndName NAMESPACE_AND_NAME_SPEECH_STATE{NAMESPACE_SPEECH_SYNTHESIZER, "SpeechState"};
/// Message Id for testing.
static const std::string MESSAGE_ID_TEST("MessageId_Test");
/// DialogRequestId for testing.
static const std::string DIALOG_REQUEST_ID_TEST("DialogRequestId_Test");
/// Token for testing.
static const std::string TOKEN_TEST("Token_Test");
/// Format of the audio.
static const std::string FORMAT_TEST("AUDIO_MPEG");
/// URL for testing.
static const std::string URL_TEST("cid:Test");
/// Context ID for testing
static const std::string CONTEXT_ID_TEST("ContextId_Test");
/// A payload for testing
static const std::string PAYLOAD_TEST =
"{"
"\"url\":\"" + URL_TEST + "\","
"\"format\":\"" + FORMAT_TEST + "\","
"\"token\":\""+ TOKEN_TEST + "\""
"}";
/// The @c FINISHED state of the @c SpeechSynthesizer.
static const std::string FINISHED_STATE("FINISHED");
/// The @c PLAYING state of the @c SpeechSynthesizer
static const std::string PLAYING_STATE{"PLAYING"};
/// The offset in milliseconds returned by the mock media player.
static const long OFFSET_IN_MILLISECONDS_TEST{100};
/// The expected state when the @c SpeechSynthesizer is in @c PLAYING state.
static const std::string PLAYING_STATE_TEST =
"{"
"\"token\":\"" + TOKEN_TEST + "\","
"\"offsetInMilliseconds\":" + std::to_string(OFFSET_IN_MILLISECONDS_TEST) + ","
"\"playerActivity\":\"" + PLAYING_STATE + "\""
"}";
/// The expected state when the @c SpeechSynthesizer is in @c FINISHED state.
static const std::string FINISHED_STATE_TEST =
"{"
"\"token\":\"" + TOKEN_TEST + "\","
"\"offsetInMilliseconds\":" + std::to_string(0) + ","
"\"playerActivity\":\"" + FINISHED_STATE + "\""
"}";
/// The expected state when the @c SpeechSynthesizer is not handling any directive.
static const std::string IDLE_STATE_TEST =
"{"
"\"token\":\"\","
"\"offsetInMilliseconds\":" + std::to_string(0) + ","
"\"playerActivity\":\"" + FINISHED_STATE + "\""
"}";
/// Provide State Token for testing.
static const unsigned int PROVIDE_STATE_TOKEN_TEST{1};
/**
* MockAttachmentManager
*/
class MockAttachmentManager : public AttachmentManagerInterface {
public:
MOCK_CONST_METHOD2(generateAttachmentId, std::string(const std::string&, const std::string&));
MOCK_METHOD1(setAttachmentTimeoutMinutes, bool(std::chrono::minutes timeoutMinutes));
MOCK_METHOD1(createWriter, std::unique_ptr<AttachmentWriter>(const std::string & attachmentId));
MOCK_METHOD2(createReader, std::unique_ptr<AttachmentReader>(
const std::string & attachmentId, AttachmentReader::Policy policy));
};
/**
* gmock does not fully support C++11's move only semantics. Replaces the use of unique_ptr in
* @c MediaPlayerInterface with shared_ptr so that methods using unique_ptr can be mocked.
*/
class MediaPlayerMockAdapter : public MediaPlayerInterface {
public:
virtual ~MediaPlayerMockAdapter() = default;
MediaPlayerStatus setSource(
std::unique_ptr<avsCommon::avs::attachment::AttachmentReader> attachmentReader) override;
/**
* Variant of setSource taking a shared_ptr instead of a unique_ptr.
*
* @param attachmentReader The audioAttachment to read.
* @return @c SUCCESS if the the source was set successfully else @c FAILURE. If setSource is called when audio is
* currently playing, the playing audio will be stopped and the source set to the new value. If there is an error
* stopping the player, this will return @c FAILURE.
*/
virtual void setSource(
std::shared_ptr<avsCommon::avs::attachment::AttachmentReader> attachmentReader) = 0;
};
MediaPlayerStatus MediaPlayerMockAdapter::setSource(
std::unique_ptr<avsCommon::avs::attachment::AttachmentReader> attachmentReader) {
std::shared_ptr<avsCommon::avs::attachment::AttachmentReader> temp(std::move(attachmentReader));
setSource(temp);
return MediaPlayerStatus::SUCCESS;
}
class MockMediaPlayer : public MediaPlayerMockAdapter {
public:
/// Constructor.
MockMediaPlayer();
/// Destructor.
~MockMediaPlayer();
/**
* Creates an instance of the @c MockMediaPlayer.
*
* @return An instance of the @c MockMediaPlayer.
*/
static std::shared_ptr<NiceMock<MockMediaPlayer>> create();
// 'override' commented out to avoid needless warnings generated because MOCK_METHOD* does not use it.
void setObserver(
std::shared_ptr<avsCommon::utils::mediaPlayer::MediaPlayerObserverInterface> playerObserver) /*override*/;
MOCK_METHOD1(setSource, void(std::shared_ptr<avsCommon::avs::attachment::AttachmentReader> attachmentReader));
MOCK_METHOD0(play, MediaPlayerStatus());
MOCK_METHOD0(stop, MediaPlayerStatus());
MOCK_METHOD0(getOffsetInMilliseconds, int64_t());
/**
* This is a mock method which will signal to @c waitForPlay to send the play started notification to the observer.
*
* @return @c SUCCESS.
*/
MediaPlayerStatus mockPlay();
/**
* This is a mock method which will signal to @c waitForStop to send the play finished notification to the observer.
*
* @return @c SUCCESS.
*/
MediaPlayerStatus mockStop();
/**
* Waits for play to be called. It notifies the observer that play has started.
*
* @param duration Time to wait for a play to be called before notifying observer that an error occurred.
* @return @c true if play was called within the timeout duration else @c false.
*/
bool waitForPlay(const std::chrono::milliseconds duration = std::chrono::milliseconds(200));
/**
* Waits for stop to be called. It notifies the observer that play has finished.
*
* @param duration Time to wait for a stop to be called before notifying observer that an error occurred.
* @return @c true if stop was called within the timeout duration else @c false.
*/
bool waitForStop(const std::chrono::milliseconds duration = std::chrono::milliseconds(200));
/**
* Waits for the promise @c m_wakePlayPromise to be fulfilled and the future to be notified of call to @c play.
*
* @param timeout The duration to wait for the future to be ready.
* @return @c true if @c play was called within the @c timeout else @c false.
*/
bool waitUntilPlaybackStarted(std::chrono::milliseconds timeout = std::chrono::milliseconds(200));
/**
* Waits for the promise @c m_wakeStopPromise to be fulfilled and the future to be notified of call to @c stop.
*
* @param timeout The duration to wait for the future to be ready.
* @return @c true if @c stop was called within the @c timeout else @c false.
*/
bool waitUntilPlaybackFinished(std::chrono::milliseconds timeout = std::chrono::milliseconds(200));
/// Condition variable to wake the @ waitForPlay.
std::condition_variable m_wakeTriggerPlay;
/// Condition variable to wake the @ waitForStop.
std::condition_variable m_wakeTriggerStop;
/// mutex to protect @c m_play, @c m_stop and @c m_shutdown.
std::mutex m_mutex;
/// Flag to indicate @c play was called.
bool m_play;
/// Flag to indicate @c stop was called.
bool m_stop;
/// Flag to indicate when MockMediaPlayer is shutting down.
bool m_shutdown;
/// Thread to run @c waitForPlay asynchronously.
std::thread m_playThread;
/// Thread to run @c waitForStop asynchronously.
std::thread m_stopThread;
/// Promise to be fulfilled when @c play is called.
std::promise<void> m_wakePlayPromise;
/// Future to notify when @c play is called.
std::future<void> m_wakePlayFuture;
/// Promise to be fulfilled when @c stop is called.
std::promise<void> m_wakeStopPromise;
/// Future to notify when @c stop is called.
std::future<void> m_wakeStopFuture;
/// The player observer to be notified of the media player state changes.
std::shared_ptr<avsCommon::utils::mediaPlayer::MediaPlayerObserverInterface> m_playerObserver;
};
std::shared_ptr<NiceMock<MockMediaPlayer>> MockMediaPlayer::create() {
auto result = std::make_shared<NiceMock<MockMediaPlayer>>();
ON_CALL(*result.get(), play()).WillByDefault(Invoke(result.get(), &MockMediaPlayer::mockPlay));
ON_CALL(*result.get(), stop()).WillByDefault(Invoke(result.get(), &MockMediaPlayer::mockStop));
return result;
}
MockMediaPlayer::MockMediaPlayer(): m_play{false}, m_stop{false}, m_shutdown{false}, m_wakePlayPromise{},
m_wakePlayFuture{m_wakePlayPromise.get_future()}, m_wakeStopPromise{},
m_wakeStopFuture{m_wakeStopPromise.get_future()}, m_playerObserver{nullptr} {
}
MockMediaPlayer::~MockMediaPlayer() {
{
std::lock_guard<std::mutex> lock(m_mutex);
m_shutdown = true;
}
m_wakeTriggerPlay.notify_all();
m_wakeTriggerStop.notify_all();
if (m_playThread.joinable()) {
m_playThread.join();
}
if(m_stopThread.joinable()) {
m_stopThread.join();
}
}
void MockMediaPlayer::setObserver(
std::shared_ptr<avsCommon::utils::mediaPlayer::MediaPlayerObserverInterface> playerObserver) {
m_playerObserver = playerObserver;
}
MediaPlayerStatus MockMediaPlayer::mockPlay() {
std::unique_lock<std::mutex> lock(m_mutex);
m_playThread = std::thread(&MockMediaPlayer::waitForPlay, this, std::chrono::milliseconds(50));
m_play = true;
m_wakeTriggerPlay.notify_one();
return MediaPlayerStatus::SUCCESS;
}
MediaPlayerStatus MockMediaPlayer::mockStop() {
std::unique_lock<std::mutex> lock(m_mutex);
m_stopThread = std::thread(&MockMediaPlayer::waitForStop, this, std::chrono::milliseconds(50));
m_stop = true;
m_wakeTriggerStop.notify_one();
return MediaPlayerStatus::SUCCESS;
}
bool MockMediaPlayer::waitForPlay(const std::chrono::milliseconds duration) {
std::unique_lock<std::mutex> lock(m_mutex);
if (!m_wakeTriggerPlay.wait_for(lock, duration, [this]() { return (m_play || m_shutdown); }))
{ if (m_playerObserver) {
m_playerObserver->onPlaybackError("waitForPlay timed out");
}
return false;
}
m_wakePlayPromise.set_value();
if (m_playerObserver) {
m_playerObserver->onPlaybackStarted();
}
return true;
}
bool MockMediaPlayer::waitForStop(const std::chrono::milliseconds duration) {
std::unique_lock<std::mutex> lock(m_mutex);
if (!m_wakeTriggerPlay.wait_for(lock, duration, [this]() { return (m_stop || m_shutdown); }))
{
if (m_playerObserver) {
m_playerObserver->onPlaybackError("waitForStop timed out");
}
return false;
}
m_wakeStopPromise.set_value();
if (m_playerObserver) {
m_playerObserver->onPlaybackFinished();
}
return true;
}
bool MockMediaPlayer::waitUntilPlaybackStarted(std::chrono::milliseconds timeout) {
return m_wakePlayFuture.wait_for(timeout) == std::future_status::ready;
}
bool MockMediaPlayer::waitUntilPlaybackFinished(std::chrono::milliseconds timeout) {
return m_wakeStopFuture.wait_for(timeout) == std::future_status::ready;
}
class SpeechSynthesizerTest : public ::testing::Test {
public:
SpeechSynthesizerTest();
void SetUp() override;
/// @c SpeechSynthesizer to test
std::shared_ptr<SpeechSynthesizer> m_speechSynthesizer;
/// Player to send the audio to.
std::shared_ptr<MockMediaPlayer> m_mockSpeechPlayer;
/// @c ContextManager to provide state and update state.
std::shared_ptr<MockContextManager> m_mockContextManager;
/**
* Fulfills the @c m_wakeSetStatePromise. This is invoked in response to a @c setState call.
*
* @return @c SUCCESS.
*/
SetStateResult wakeOnSetState();
/// Promise to be fulfilled when @c setState is called.
std::promise<void> m_wakeSetStatePromise;
/// Future to notify when @c setState is called.
std::future<void> m_wakeSetStateFuture;
/// @c FocusManager to request focus to the DIALOG channel.
std::shared_ptr<MockFocusManager> m_mockFocusManager;
/**
* Fulfills the @c m_wakeAcquireChannelPromise. This is invoked in response to a @c acquireChannel call.
*
* @return @c true
*/
bool wakeOnAcquireChannel();
/// Promise to be fulfilled when @c acquireChannel is called.
std::promise<void> m_wakeAcquireChannelPromise;
/// Future to notify when @c acquireChannel is called.
std::future<void> m_wakeAcquireChannelFuture;
/**
* Fulfills the @c m_wakeReleaseChannelPromise. This is invoked in response to a @c releaseChannel call.
*
* @return @c true
*/
std::future<bool> wakeOnReleaseChannel();
/// Promise to be fulfilled when @c releaseChannel is called.
std::promise<void> m_wakeReleaseChannelPromise;
/// Future to notify when @c releaseChannel is called.
std::future<void> m_wakeReleaseChannelFuture;
/// A directive handler result to send the result to.
std::unique_ptr<MockDirectiveHandlerResult> m_mockDirHandlerResult;
/**
* Fulfills the @c m_wakeSetCompletedPromise. This is invoked in response to a @c setCompleted call.
*/
void wakeOnSetCompleted();
/// Promise to be fulfilled when @c setCompleted is called.
std::promise<void> m_wakeSetCompletedPromise;
/// Future to notify when @c setCompleted is called.
std::future<void> m_wakeSetCompletedFuture;
/// A message sender used to send events to AVS.
std::shared_ptr<MockMessageSender> m_mockMessageSender;
/**
* Fulfills the @c m_wakeSendMessagePromise. This is invoked in response to a @c sendMessage call.
*/
void wakeOnSendMessage();
/// Promise to be fulfilled when @c sendMessage is called.
std::promise<void> m_wakeSendMessagePromise;
/// Future to notify when @c sendMessage is called.
std::future<void> m_wakeSendMessageFuture;
/// An exception sender used to send exception encountered events to AVS.
std::shared_ptr<MockExceptionEncounteredSender> m_mockExceptionSender;
/// Attachment manager used to create a reader.
std::shared_ptr<AttachmentManager> m_attachmentManager;
};
SpeechSynthesizerTest::SpeechSynthesizerTest() :
m_wakeSetStatePromise{},
m_wakeSetStateFuture{m_wakeSetStatePromise.get_future()},
m_wakeAcquireChannelPromise{},
m_wakeAcquireChannelFuture{m_wakeAcquireChannelPromise.get_future()},
m_wakeReleaseChannelPromise{},
m_wakeReleaseChannelFuture{m_wakeReleaseChannelPromise.get_future()},
m_wakeSetCompletedPromise{},
m_wakeSetCompletedFuture{m_wakeSetCompletedPromise.get_future()},
m_wakeSendMessagePromise{},
m_wakeSendMessageFuture{m_wakeSendMessagePromise.get_future()} {
}
void SpeechSynthesizerTest::SetUp() {
m_mockContextManager = std::make_shared<NiceMock<MockContextManager>>();
m_mockFocusManager = std::make_shared<NiceMock<MockFocusManager>>();
m_mockMessageSender = std::make_shared<NiceMock<MockMessageSender>>();
m_mockExceptionSender = std::make_shared<NiceMock<MockExceptionEncounteredSender>>();
m_attachmentManager = std::make_shared<AttachmentManager>(AttachmentManager::AttachmentType::IN_PROCESS);
m_mockSpeechPlayer = MockMediaPlayer::create();
m_speechSynthesizer = SpeechSynthesizer::create(
m_mockSpeechPlayer,
m_mockMessageSender,
m_mockFocusManager,
m_mockContextManager,
m_attachmentManager,
m_mockExceptionSender);
m_mockDirHandlerResult.reset(new MockDirectiveHandlerResult);
ASSERT_TRUE(m_speechSynthesizer);
}
SetStateResult SpeechSynthesizerTest::wakeOnSetState() {
m_wakeSetStatePromise.set_value();
return SetStateResult::SUCCESS;
}
bool SpeechSynthesizerTest::wakeOnAcquireChannel() {
m_wakeAcquireChannelPromise.set_value();
return true;
}
std::future<bool> SpeechSynthesizerTest::wakeOnReleaseChannel() {
std::promise<bool> releaseChannelSuccess;
std::future<bool> returnValue = releaseChannelSuccess.get_future();
releaseChannelSuccess.set_value(true);
m_wakeReleaseChannelPromise.set_value();
return returnValue;
}
void SpeechSynthesizerTest::wakeOnSetCompleted() {
m_wakeSetCompletedPromise.set_value();
}
void SpeechSynthesizerTest::wakeOnSendMessage() {
m_wakeSendMessagePromise.set_value();
}
/*
* Test call to handleDirectiveImmediately.
* Expected result is that @c acquireChannel is called with the correct channel. On focus changed @c FOREGROUND, audio
* should play. Expect the @c ContextManager @c setState is called when state changes to @c PLAYING.
*/
TEST_F(SpeechSynthesizerTest, testCallingHandleImmediately) {
auto avsMessageHeader = std::make_shared<AVSMessageHeader>(
NAMESPACE_SPEECH_SYNTHESIZER, NAME_SPEAK, MESSAGE_ID_TEST, DIALOG_REQUEST_ID_TEST);
std::shared_ptr<AVSDirective> directive = AVSDirective::create(
"", avsMessageHeader, PAYLOAD_TEST, m_attachmentManager, CONTEXT_ID_TEST);
EXPECT_CALL(*(m_mockFocusManager.get()), acquireChannel(CHANNEL_NAME, _, FOCUS_MANAGER_ACTIVITY_ID)).Times(1).
WillOnce(InvokeWithoutArgs(this, &SpeechSynthesizerTest::wakeOnAcquireChannel));
EXPECT_CALL(*(m_mockSpeechPlayer.get()), setSource(_)).Times(AtLeast(1));
EXPECT_CALL(*(m_mockSpeechPlayer.get()), play()).Times(AtLeast(1));
EXPECT_CALL(*(m_mockSpeechPlayer.get()), getOffsetInMilliseconds()).Times(1).WillOnce(Return(100));
EXPECT_CALL(*(m_mockContextManager.get()), setState(
NAMESPACE_AND_NAME_SPEECH_STATE, PLAYING_STATE_TEST, StateRefreshPolicy::ALWAYS, 0)).Times(AtLeast(1)).
WillOnce(InvokeWithoutArgs(this, &SpeechSynthesizerTest::wakeOnSetState));
EXPECT_CALL(*(m_mockMessageSender.get()), sendMessage(_)).Times(AtLeast(1)).WillRepeatedly(
InvokeWithoutArgs(this, &SpeechSynthesizerTest::wakeOnSendMessage));
m_speechSynthesizer->handleDirectiveImmediately(directive);
ASSERT_TRUE(std::future_status::ready == m_wakeAcquireChannelFuture.wait_for(WAIT_TIMEOUT));
m_speechSynthesizer->onFocusChanged(FocusState::FOREGROUND);
ASSERT_TRUE(m_mockSpeechPlayer->waitUntilPlaybackStarted());
ASSERT_TRUE(std::future_status::ready == m_wakeSetStateFuture.wait_for(WAIT_TIMEOUT));
ASSERT_TRUE(std::future_status::ready == m_wakeSendMessageFuture.wait_for(WAIT_TIMEOUT));
}
/*
* Tests preHandleDirective and HandleDirective
* Call preHandle with a valid SPEAK directive. Then call handleDirective. Expected result is that @c acquireChannel
* is called with the correct channel. On focus changed @c FOREGROUND, audio should play. Expect the @c ContextManager
* @c setState is called when state changes to @c PLAYING.
*/
TEST_F(SpeechSynthesizerTest, testCallingHandle) {
auto avsMessageHeader = std::make_shared<AVSMessageHeader>(
NAMESPACE_SPEECH_SYNTHESIZER, NAME_SPEAK, MESSAGE_ID_TEST, DIALOG_REQUEST_ID_TEST);
std::shared_ptr<AVSDirective> directive = AVSDirective::create(
"", avsMessageHeader, PAYLOAD_TEST, m_attachmentManager, CONTEXT_ID_TEST);
EXPECT_CALL(*(m_mockFocusManager.get()), acquireChannel(CHANNEL_NAME, _, FOCUS_MANAGER_ACTIVITY_ID)).Times(1).
WillOnce(InvokeWithoutArgs(this, &SpeechSynthesizerTest::wakeOnAcquireChannel));
EXPECT_CALL(*(m_mockSpeechPlayer.get()), setSource(_)).Times(AtLeast(1));
EXPECT_CALL(*(m_mockSpeechPlayer.get()), play()).Times(AtLeast(1));
EXPECT_CALL(*(m_mockSpeechPlayer.get()), getOffsetInMilliseconds()).Times(1).WillOnce(Return(100));
EXPECT_CALL(*(m_mockContextManager.get()), setState(
NAMESPACE_AND_NAME_SPEECH_STATE, PLAYING_STATE_TEST, StateRefreshPolicy::ALWAYS, 0)).Times(AtLeast(1)).
WillOnce(InvokeWithoutArgs(this, &SpeechSynthesizerTest::wakeOnSetState));
EXPECT_CALL(*(m_mockMessageSender.get()), sendMessage(_)).Times(AtLeast(1)).WillRepeatedly(
InvokeWithoutArgs(this, &SpeechSynthesizerTest::wakeOnSendMessage));
m_speechSynthesizer->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirHandlerResult));
m_speechSynthesizer->CapabilityAgent::handleDirective(MESSAGE_ID_TEST);
ASSERT_TRUE(std::future_status::ready == m_wakeAcquireChannelFuture.wait_for(WAIT_TIMEOUT));
m_speechSynthesizer->onFocusChanged(FocusState::FOREGROUND);
ASSERT_TRUE(m_mockSpeechPlayer->waitUntilPlaybackStarted());
ASSERT_TRUE(std::future_status::ready == m_wakeSetStateFuture.wait_for(WAIT_TIMEOUT));
ASSERT_TRUE(std::future_status::ready == m_wakeSendMessageFuture.wait_for(WAIT_TIMEOUT));
}
/**
* Tests cancelDirective.
* Call preHandle with a valid SPEAK directive. Then call cancelDirective. Expect that neither @c setState nor
* @c sendMessage are called since handle was never called to start playing audio.
*/
TEST_F(SpeechSynthesizerTest, testCallingCancel) {
auto avsMessageHeader = std::make_shared<AVSMessageHeader>(
NAMESPACE_SPEECH_SYNTHESIZER, NAME_SPEAK, MESSAGE_ID_TEST, DIALOG_REQUEST_ID_TEST);
std::shared_ptr<AVSDirective> directive = AVSDirective::create(
"", avsMessageHeader, PAYLOAD_TEST, m_attachmentManager, CONTEXT_ID_TEST);
EXPECT_CALL(*(m_mockContextManager.get()), setState(_,_,_,_)).Times(0);
EXPECT_CALL(*(m_mockMessageSender.get()), sendMessage(_)).Times(0);
m_speechSynthesizer->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirHandlerResult));
m_speechSynthesizer->CapabilityAgent::cancelDirective(MESSAGE_ID_TEST);
}
/**
* Testing cancelDirective after calling
* Call preHandle with a valid SPEAK directive. Then call handleDirective. Expected result is that @c acquireChannel
* is called once. On Focus Changed to foreground, audio should play. Call cancel directive. Expect the
* @c ContextManager @c setState is called when the state changes to @c PLAYING and then to @c FINISHED.
* Expect @c sendMessage is called only once. On cancel, message should not be sent to AVS.
*/
TEST_F(SpeechSynthesizerTest, testCallingCancelAfterHandle) {
auto avsMessageHeader = std::make_shared<AVSMessageHeader>(
NAMESPACE_SPEECH_SYNTHESIZER, NAME_SPEAK, MESSAGE_ID_TEST, DIALOG_REQUEST_ID_TEST);
std::shared_ptr<AVSDirective> directive = AVSDirective::create(
"", avsMessageHeader, PAYLOAD_TEST, m_attachmentManager, CONTEXT_ID_TEST);
EXPECT_CALL(*(m_mockFocusManager.get()), acquireChannel(CHANNEL_NAME, _, FOCUS_MANAGER_ACTIVITY_ID)).Times(1).
WillOnce(InvokeWithoutArgs(this, &SpeechSynthesizerTest::wakeOnAcquireChannel));
EXPECT_CALL(*(m_mockSpeechPlayer.get()), setSource(_)).Times(AtLeast(1));
EXPECT_CALL(*(m_mockSpeechPlayer.get()), play()).Times(AtLeast(1));
EXPECT_CALL(*(m_mockSpeechPlayer.get()), getOffsetInMilliseconds()).Times(1).WillOnce(Return(100));
EXPECT_CALL(*(m_mockContextManager.get()), setState(
NAMESPACE_AND_NAME_SPEECH_STATE, PLAYING_STATE_TEST, StateRefreshPolicy::ALWAYS, 0)).Times(AtLeast(1)).
WillRepeatedly(InvokeWithoutArgs(this, &SpeechSynthesizerTest::wakeOnSetState));
EXPECT_CALL(*(m_mockContextManager.get()), setState(
NAMESPACE_AND_NAME_SPEECH_STATE, FINISHED_STATE_TEST, StateRefreshPolicy::NEVER, 0)).Times(AtLeast(1)).
WillRepeatedly(InvokeWithoutArgs(this, &SpeechSynthesizerTest::wakeOnSetState));
EXPECT_CALL(*(m_mockMessageSender.get()), sendMessage(_)).Times(1).WillOnce(
InvokeWithoutArgs(this, &SpeechSynthesizerTest::wakeOnSendMessage));
EXPECT_CALL(*(m_mockFocusManager.get()), releaseChannel(CHANNEL_NAME,_)).Times(1).WillOnce(
InvokeWithoutArgs(this, &SpeechSynthesizerTest::wakeOnReleaseChannel));
EXPECT_CALL(*(m_mockDirHandlerResult.get()), setCompleted()).Times(1).WillOnce(
InvokeWithoutArgs(this, &SpeechSynthesizerTest::wakeOnSetCompleted));
m_speechSynthesizer->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirHandlerResult));
m_speechSynthesizer->CapabilityAgent::handleDirective(MESSAGE_ID_TEST);
ASSERT_TRUE(std::future_status::ready == m_wakeAcquireChannelFuture.wait_for(WAIT_TIMEOUT));
m_speechSynthesizer->onFocusChanged(FocusState::FOREGROUND);
ASSERT_TRUE(m_mockSpeechPlayer->waitUntilPlaybackStarted());
ASSERT_TRUE(std::future_status::ready == m_wakeSetStateFuture.wait_for(WAIT_TIMEOUT));
m_wakeSetStatePromise = std::promise<void>();
m_wakeSetStateFuture = m_wakeSetStatePromise.get_future();
ASSERT_TRUE(std::future_status::ready == m_wakeSendMessageFuture.wait_for(WAIT_TIMEOUT));
m_speechSynthesizer->CapabilityAgent::cancelDirective(MESSAGE_ID_TEST);
ASSERT_TRUE(m_mockSpeechPlayer->waitUntilPlaybackFinished());
ASSERT_TRUE(std::future_status::ready == m_wakeSetStateFuture.wait_for(WAIT_TIMEOUT));
ASSERT_TRUE(std::future_status::ready == m_wakeReleaseChannelFuture.wait_for(WAIT_TIMEOUT));
ASSERT_TRUE(std::future_status::ready == m_wakeSetCompletedFuture.wait_for(WAIT_TIMEOUT));
}
/**
* Testing provideState.
* Call @c provideState and expect that setState is called.
*/
TEST_F(SpeechSynthesizerTest, testCallingProvideStateWhenNotPlaying) {
EXPECT_CALL(*(m_mockSpeechPlayer.get()), getOffsetInMilliseconds()).Times(0);
EXPECT_CALL(
*(m_mockContextManager.get()), setState(
NAMESPACE_AND_NAME_SPEECH_STATE, IDLE_STATE_TEST, StateRefreshPolicy::NEVER, PROVIDE_STATE_TOKEN_TEST))
.Times(1).WillOnce(
InvokeWithoutArgs(this, &SpeechSynthesizerTest::wakeOnSetState));
m_speechSynthesizer->provideState(PROVIDE_STATE_TOKEN_TEST);
ASSERT_TRUE(std::future_status::ready == m_wakeSetStateFuture.wait_for(WAIT_TIMEOUT));
}
/**
* Testing provideState when playing.
* Call @c provideState after the state of the @c SpeechSynthesizer has changed to @c PLAYING.
* Expect @c getOffsetInMilliseconds is called. Expect @c setState is called when state changes and when state is
* requested via @c provideState.
*/
TEST_F(SpeechSynthesizerTest, testCallingProvideStateWhenPlaying) {
auto avsMessageHeader = std::make_shared<AVSMessageHeader>(
NAMESPACE_SPEECH_SYNTHESIZER, NAME_SPEAK, MESSAGE_ID_TEST, DIALOG_REQUEST_ID_TEST);
std::shared_ptr<AVSDirective> directive = AVSDirective::create(
"", avsMessageHeader, PAYLOAD_TEST, m_attachmentManager, CONTEXT_ID_TEST);
EXPECT_CALL(*(m_mockFocusManager.get()), acquireChannel(CHANNEL_NAME, _, FOCUS_MANAGER_ACTIVITY_ID)).Times(1).
WillOnce(InvokeWithoutArgs(this, &SpeechSynthesizerTest::wakeOnAcquireChannel));
EXPECT_CALL(*(m_mockSpeechPlayer.get()), setSource(_)).Times(AtLeast(1));
EXPECT_CALL(*(m_mockSpeechPlayer.get()), play()).Times(AtLeast(1));
EXPECT_CALL(*(m_mockSpeechPlayer.get()), getOffsetInMilliseconds()).Times(AtLeast(1)).WillRepeatedly(Return(100));
EXPECT_CALL(*(m_mockContextManager.get()), setState(
NAMESPACE_AND_NAME_SPEECH_STATE, PLAYING_STATE_TEST, StateRefreshPolicy::ALWAYS, 0)).Times(1).
WillOnce(InvokeWithoutArgs(this, &SpeechSynthesizerTest::wakeOnSetState));
EXPECT_CALL(*(m_mockContextManager.get()), setState(
NAMESPACE_AND_NAME_SPEECH_STATE, PLAYING_STATE_TEST, StateRefreshPolicy::ALWAYS, PROVIDE_STATE_TOKEN_TEST))
.Times(1).WillOnce(InvokeWithoutArgs(this, &SpeechSynthesizerTest::wakeOnSetState));
EXPECT_CALL(*(m_mockContextManager.get()), setState(
NAMESPACE_AND_NAME_SPEECH_STATE, FINISHED_STATE_TEST, StateRefreshPolicy::NEVER, 0)).Times(1).
WillOnce(Return(SetStateResult::SUCCESS));
EXPECT_CALL(*(m_mockMessageSender.get()), sendMessage(_)).Times(AtLeast(1)).WillRepeatedly(
InvokeWithoutArgs(this, &SpeechSynthesizerTest::wakeOnSendMessage));
EXPECT_CALL(*(m_mockFocusManager.get()), releaseChannel(CHANNEL_NAME,_)).Times(1).WillOnce(
InvokeWithoutArgs(this, &SpeechSynthesizerTest::wakeOnReleaseChannel));
EXPECT_CALL(*(m_mockDirHandlerResult.get()), setCompleted()).Times(1).WillOnce(
InvokeWithoutArgs(this, &SpeechSynthesizerTest::wakeOnSetCompleted));
m_speechSynthesizer->CapabilityAgent::preHandleDirective(directive, std::move(m_mockDirHandlerResult));
m_speechSynthesizer->CapabilityAgent::handleDirective(MESSAGE_ID_TEST);
ASSERT_TRUE(std::future_status::ready == m_wakeAcquireChannelFuture.wait_for(WAIT_TIMEOUT));
m_speechSynthesizer->onFocusChanged(FocusState::FOREGROUND);
ASSERT_TRUE(m_mockSpeechPlayer->waitUntilPlaybackStarted());
ASSERT_TRUE(std::future_status::ready == m_wakeSetStateFuture.wait_for(WAIT_TIMEOUT));
m_wakeSetStatePromise = std::promise<void>();
m_wakeSetStateFuture = m_wakeSetStatePromise.get_future();
ASSERT_TRUE(std::future_status::ready == m_wakeSendMessageFuture.wait_for(WAIT_TIMEOUT));
m_speechSynthesizer->provideState(PROVIDE_STATE_TOKEN_TEST);
ASSERT_TRUE(std::future_status::ready == m_wakeSetStateFuture.wait_for(WAIT_TIMEOUT));
m_speechSynthesizer->CapabilityAgent::cancelDirective(MESSAGE_ID_TEST);
ASSERT_TRUE(m_mockSpeechPlayer->waitUntilPlaybackFinished());
ASSERT_TRUE(std::future_status::ready == m_wakeReleaseChannelFuture.wait_for(WAIT_TIMEOUT));
ASSERT_TRUE(std::future_status::ready == m_wakeSetCompletedFuture.wait_for(WAIT_TIMEOUT));
}
} // namespace test
} // namespace speechSynthesizer
} // namespace capabilityAgents
} // namespace alexaClientSDK