avs-device-sdk/CapabilityAgents/Alerts/test/Renderer/RendererTest.cpp

284 lines
8.6 KiB
C++
Raw Normal View History

Version 1.4.0 alexa-client-sdk Changes in this update: **Enhancements** * Added the Notifications Capability Agent. This allows a client to receive notification indicators from Alexa. * Added support for the `SoftwareInfo` event. This code is triggered in the `SampleApp` by providing a positive decimal integer as the "firmwareVersion" value in "sampleApp" object of the `AlexaClientSDKConfig.json`. The reported firmware version can be updated after starting the `SampleApp` by calling `SoftwareInfoSender::setFirmwareVersion()`. This code path can be exercised in the `SampleApp` with the new command: `f`. * Added unit tests for Alerts. * The GStreamer-based pipeline allows for the configuration of `MediaPlayer` output based on information provided in `Config`. * Playlist streaming now uses a `BLOCKING` writer, which improves streaming efficiency. **Bug Fixes** * Fixed bug where `SpeechSynthesizer` would not stop playback when a state change timeout was encountered. * Fixed the `SampleApplication` destructor to avoid segfaults if the object is not constructed correctly. * Fixed bug where `AudioPlayer` would erroneously call `executeStop()` in `cancelDirective()`. * [Issue 396](https://github.com/alexa/avs-device-sdk/issues/396) - Fixed bug for compilation error with GCC7 in `AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Audio/AlertsAudioFactoryInterface.h` * [Issue 384](https://github.com/alexa/avs-device-sdk/issues/384) - Fixed bug that caused `AuthServer.py` to crash. * Fixed bug where a long delay was encountered after pausing and resuming a large Audible chapter. * Fixed bug that caused named timers and reminders to loop for an additional `loopCount` . * Fixed memory corruption bug in `MessageInterpreter`. * Fixed illegal memory accesses in `MediaPlayer` logging. **Known Issues** * The `ACL` may encounter issues if audio attachments are received but not consumed. * Display Cards for Kindle don't render. * If using the GStreamer-based `MediaPlayer` implementation, after muting and un-muting an audio item, the next item in the queue will begin playing rather than continuing playback of the originally muted audio item. * `SpeechSynthesizerState` currently uses `GAINING_FOCUS` and `LOSING_FOCUS` as a workaround for handling intermediate state. These states may be removed in a future release. * Music playback doesn't immediately stop when a user barges-in on iHeartRadio.
2018-01-12 23:45:42 +00:00
/*
* Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0/
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <AVSCommon/Utils/MediaPlayer/MockMediaPlayer.h>
#include "Alerts/Renderer/Renderer.h"
namespace alexaClientSDK {
namespace capabilityAgents {
namespace alerts {
namespace renderer {
namespace test {
using namespace avsCommon::utils::mediaPlayer::test;
/// Amount of time that the renderer observer should wait for a task to finish.
static const std::chrono::milliseconds TEST_TIMEOUT{100};
/// Test source Id that exists for the tests
static const avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId TEST_SOURCE_ID_GOOD = 1234;
/// Test source Id that does not exist for the tests
static const avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId TEST_SOURCE_ID_BAD = 5678;
/// Test URLs for the renderer
static const std::string TEST_URL1 = "fake.url.one";
static const std::string TEST_URL2 = "fake.url.two";
/// Loop pause for the renderer.
static const std::chrono::milliseconds TEST_LOOP_PAUSE{100};
/// Loop count for the renderer.
static const int TEST_LOOP_COUNT = 2;
class MockRendererObserver : public RendererObserverInterface {
public:
bool waitFor(RendererObserverInterface::State newState) {
std::unique_lock<std::mutex> lock(m_mutex);
return m_conditionVariable.wait_for(lock, TEST_TIMEOUT, [this, newState] { return m_state == newState; });
}
void onRendererStateChange(RendererObserverInterface::State newState, const std::string& reason) {
std::lock_guard<std::mutex> lock(m_mutex);
m_state = newState;
m_conditionVariable.notify_all();
}
private:
std::mutex m_mutex;
std::condition_variable m_conditionVariable;
RendererObserverInterface::State m_state;
};
class TestMediaPlayer : public MockMediaPlayer {
public:
TestMediaPlayer() {
m_sourceIdRetVal = TEST_SOURCE_ID_GOOD;
m_playRetVal = true;
m_stopRetVal = true;
}
static std::shared_ptr<testing::NiceMock<TestMediaPlayer>> create() {
return std::make_shared<testing::NiceMock<TestMediaPlayer>>();
}
bool play(SourceId id) override {
return m_playRetVal;
}
bool stop(SourceId id) override {
return m_stopRetVal;
}
SourceId setSource(const std::string& url, std::chrono::milliseconds offset = std::chrono::milliseconds::zero())
override {
return m_sourceIdRetVal;
}
void setSourceRetVal(SourceId sourceRetVal) {
m_sourceIdRetVal = sourceRetVal;
}
void setPlayRetVal(bool playRetVal) {
m_playRetVal = playRetVal;
}
void setStopRetVal(bool stopRetVal) {
m_stopRetVal = stopRetVal;
}
private:
SourceId m_sourceIdRetVal;
bool m_playRetVal;
bool m_stopRetVal;
};
class RendererTest : public ::testing::Test {
public:
RendererTest();
~RendererTest();
void SetUpTest();
void TearDown() override;
protected:
std::shared_ptr<MockRendererObserver> m_observer;
std::shared_ptr<TestMediaPlayer> m_mediaPlayer;
std::shared_ptr<Renderer> m_renderer;
static std::unique_ptr<std::istream> audioFactoryFunc() {
Verison 1.6 of the avs-device-sdk Changes in this update: **Enhancements** * `rapidJson` is now included with "make install". * Updated the `TemplateRuntimeObserverInterface` to support clearing of `displayCards`. * Added Windows SDK support, along with an installation script (MinGW-w64). * Updated `ContextManager` to ignore context reported by a state provider. * The `SharedDataStream` object is now associated by playlist, rather than by URL. * Added the `RegistrationManager` component. Now, when a user logs out all persistent user-specific data is cleared from the SDK. The log out functionality can be exercised in the sample app with the new command: `k`. **Bug Fixes** * [Issue 400](https://github.com/alexa/avs-device-sdk/issues/400) Fixed a bug where the alert reminder did not iterate as intended after loss of network connection. * [Issue 477](https://github.com/alexa/avs-device-sdk/issues/477) Fixed a bug in which Alexa's weather response was being truncated. * Fixed an issue in which there were reports of instability related to the Sensory engine. To correct this, the `portAudio` [`suggestedLatency`](https://github.com/alexa/avs-device-sdk/blob/master/Integration/AlexaClientSDKConfig.json#L62) value can now be configured. **Known Issues** * The `ACL` may encounter issues if audio attachments are received but not consumed. * `SpeechSynthesizerState` currently uses `GAINING_FOCUS` and `LOSING_FOCUS` as a workaround for handling intermediate state. These states may be removed in a future release. * Music playback doesn't immediately stop when a user barges-in on iHeartRadio. * The Windows sample app sometimes hangs on exit. * GDP receives a `SIGPIPE` when troubleshooting the Windows sample app.
2018-03-09 00:55:39 +00:00
return std::unique_ptr<std::istream>(new std::stringstream());
Version 1.4.0 alexa-client-sdk Changes in this update: **Enhancements** * Added the Notifications Capability Agent. This allows a client to receive notification indicators from Alexa. * Added support for the `SoftwareInfo` event. This code is triggered in the `SampleApp` by providing a positive decimal integer as the "firmwareVersion" value in "sampleApp" object of the `AlexaClientSDKConfig.json`. The reported firmware version can be updated after starting the `SampleApp` by calling `SoftwareInfoSender::setFirmwareVersion()`. This code path can be exercised in the `SampleApp` with the new command: `f`. * Added unit tests for Alerts. * The GStreamer-based pipeline allows for the configuration of `MediaPlayer` output based on information provided in `Config`. * Playlist streaming now uses a `BLOCKING` writer, which improves streaming efficiency. **Bug Fixes** * Fixed bug where `SpeechSynthesizer` would not stop playback when a state change timeout was encountered. * Fixed the `SampleApplication` destructor to avoid segfaults if the object is not constructed correctly. * Fixed bug where `AudioPlayer` would erroneously call `executeStop()` in `cancelDirective()`. * [Issue 396](https://github.com/alexa/avs-device-sdk/issues/396) - Fixed bug for compilation error with GCC7 in `AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Audio/AlertsAudioFactoryInterface.h` * [Issue 384](https://github.com/alexa/avs-device-sdk/issues/384) - Fixed bug that caused `AuthServer.py` to crash. * Fixed bug where a long delay was encountered after pausing and resuming a large Audible chapter. * Fixed bug that caused named timers and reminders to loop for an additional `loopCount` . * Fixed memory corruption bug in `MessageInterpreter`. * Fixed illegal memory accesses in `MediaPlayer` logging. **Known Issues** * The `ACL` may encounter issues if audio attachments are received but not consumed. * Display Cards for Kindle don't render. * If using the GStreamer-based `MediaPlayer` implementation, after muting and un-muting an audio item, the next item in the queue will begin playing rather than continuing playback of the originally muted audio item. * `SpeechSynthesizerState` currently uses `GAINING_FOCUS` and `LOSING_FOCUS` as a workaround for handling intermediate state. These states may be removed in a future release. * Music playback doesn't immediately stop when a user barges-in on iHeartRadio.
2018-01-12 23:45:42 +00:00
}
};
RendererTest::RendererTest() :
m_observer{std::make_shared<MockRendererObserver>()},
m_mediaPlayer{TestMediaPlayer::create()},
m_renderer{Renderer::create(m_mediaPlayer)} {
m_renderer->setObserver(m_observer);
}
RendererTest::~RendererTest() {
m_mediaPlayer->setObserver(nullptr);
m_mediaPlayer.reset();
}
void RendererTest::SetUpTest() {
std::function<std::unique_ptr<std::istream>()> audioFactory = RendererTest::audioFactoryFunc;
std::vector<std::string> urls = {TEST_URL1, TEST_URL2};
m_renderer->setObserver(m_observer);
m_renderer->start(audioFactory, urls, TEST_LOOP_COUNT, TEST_LOOP_PAUSE);
}
void RendererTest::TearDown() {
m_mediaPlayer->setSourceRetVal(TEST_SOURCE_ID_GOOD);
m_mediaPlayer->setPlayRetVal(true);
m_mediaPlayer->setStopRetVal(true);
}
/**
* Test if the Renderer class creates an object appropriately and fails when it must
*/
TEST_F(RendererTest, create) {
/// m_renderer was created using create() in the constructor. Check if not null
ASSERT_NE(m_renderer, nullptr);
/// confirm we return a nullptr if a nullptr was passed in
ASSERT_EQ(Renderer::create(nullptr), nullptr);
}
/**
* Test if the Renderer starts
*/
TEST_F(RendererTest, start) {
SetUpTest();
ASSERT_TRUE(m_observer->waitFor(RendererObserverInterface::State::UNSET));
}
/**
* Test if the Renderer stops
*/
TEST_F(RendererTest, stop) {
SetUpTest();
m_renderer->stop();
ASSERT_FALSE(m_observer->waitFor(RendererObserverInterface::State::ERROR));
}
/**
* Test if the Renderer errors out when it cant stop
*/
TEST_F(RendererTest, stopError) {
SetUpTest();
m_mediaPlayer->setStopRetVal(false);
m_renderer->stop();
ASSERT_TRUE(m_observer->waitFor(RendererObserverInterface::State::ERROR));
}
/**
* Test if the Renderer correctly handles Playback starting
*/
TEST_F(RendererTest, onPlaybackStarted) {
SetUpTest();
/// shouldn't start if the source is bad
m_renderer->onPlaybackStarted(TEST_SOURCE_ID_BAD);
ASSERT_FALSE(m_observer->waitFor(RendererObserverInterface::State::STARTED));
/// should start if the source is good
m_renderer->onPlaybackStarted(TEST_SOURCE_ID_GOOD);
ASSERT_TRUE(m_observer->waitFor(RendererObserverInterface::State::STARTED));
}
/**
* Test if the Renderer correctly handles Playback stopping
*/
TEST_F(RendererTest, onPlaybackStopped) {
SetUpTest();
/// shouldn't stop if the source is bad
m_renderer->onPlaybackStopped(TEST_SOURCE_ID_BAD);
ASSERT_FALSE(m_observer->waitFor(RendererObserverInterface::State::STOPPED));
/// should stop if the source is good
m_renderer->onPlaybackStopped(TEST_SOURCE_ID_GOOD);
ASSERT_TRUE(m_observer->waitFor(RendererObserverInterface::State::STOPPED));
}
/**
* Test if the Renderer correctly handles Playback finishing
*/
TEST_F(RendererTest, onPlaybackFinished) {
avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId blankSourceId{};
/// shouldn't finish if the source is bad
m_renderer->onPlaybackFinished(TEST_SOURCE_ID_BAD);
ASSERT_FALSE(m_observer->waitFor(RendererObserverInterface::State::STOPPED));
/// should finish if the source is good
m_renderer->onPlaybackFinished(blankSourceId);
ASSERT_TRUE(m_observer->waitFor(RendererObserverInterface::State::STOPPED));
}
/**
* Test if the Renderer gracefully handles errors when Playback finishing
*/
TEST_F(RendererTest, onPlaybackFinishedError) {
SetUpTest();
/// shouldn't finish even if the source is good, if the media player is errored out
m_mediaPlayer->setSourceRetVal(avsCommon::utils::mediaPlayer::MediaPlayerInterface::ERROR);
m_renderer->onPlaybackFinished(TEST_SOURCE_ID_GOOD);
ASSERT_FALSE(m_observer->waitFor(RendererObserverInterface::State::STOPPED));
/// shouldn't finish even if the source is good, if the media player can't play it
m_mediaPlayer->setSourceRetVal(TEST_SOURCE_ID_GOOD);
m_mediaPlayer->setPlayRetVal(false);
m_renderer->onPlaybackFinished(TEST_SOURCE_ID_GOOD);
ASSERT_FALSE(m_observer->waitFor(RendererObserverInterface::State::STOPPED));
}
/**
* Test if the Renderer correctly handles Playback erroring out
*/
TEST_F(RendererTest, onPlaybackError) {
const avsCommon::utils::mediaPlayer::ErrorType& errorType =
avsCommon::utils::mediaPlayer::ErrorType::MEDIA_ERROR_INVALID_REQUEST;
std::string errorMsg = "testError";
SetUpTest();
/// shouldn't respond with errors if the source is bad
m_renderer->onPlaybackError(TEST_SOURCE_ID_BAD, errorType, errorMsg);
ASSERT_FALSE(m_observer->waitFor(RendererObserverInterface::State::ERROR));
/// shouldn't respond with errors if the source is good
m_renderer->onPlaybackError(TEST_SOURCE_ID_GOOD, errorType, errorMsg);
ASSERT_TRUE(m_observer->waitFor(RendererObserverInterface::State::ERROR));
}
} // namespace test
} // namespace renderer
} // namespace alerts
} // namespace capabilityAgents
} // namespace alexaClientSDK