avs-device-sdk/srcs/Captions/Implementation/test/CaptionManagerTest.cpp

371 lines
14 KiB
C++

/*
* Copyright 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.
*/
/// @file CaptionManagerTest.cpp
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <AVSCommon/Utils/Logger/Logger.h>
#include <AVSCommon/Utils/MediaPlayer/MockMediaPlayer.h>
#include <Captions/CaptionManager.h>
#include <Captions/CaptionLine.h>
#include "MockCaptionParser.h"
#include "MockCaptionPresenter.h"
#include "TestTimingAdapterFactory.cpp"
namespace alexaClientSDK {
namespace captions {
namespace test {
using namespace ::testing;
using namespace avsCommon;
using namespace avsCommon::avs;
using namespace avsCommon::utils;
using namespace avsCommon::utils::mediaPlayer;
using namespace avsCommon::utils::mediaPlayer::test;
/**
* Test rig.
*/
class CaptionManagerTest : public ::testing::Test {
public:
void SetUp() override;
void TearDown() override;
/// The system under test.
std::shared_ptr<CaptionManager> caption_manager;
/// Mock player with which to exercise CaptionManager.
std::shared_ptr<MockMediaPlayer> m_player;
/// Mock parser with which to exercise CaptionManager.
std::shared_ptr<MockCaptionParser> m_parser;
/// Mock presenter with which to exercise CaptionManager.
std::shared_ptr<NiceMock<MockCaptionPresenter>> m_presenter;
/// An implementation of the timing adapter factory with which to exercise CaptionManager, which returns mocks.
std::shared_ptr<TestTimingAdapterFactory> m_timingFactory;
};
void CaptionManagerTest::SetUp() {
avsCommon::utils::logger::getConsoleLogger()->setLevel(avsCommon::utils::logger::Level::DEBUG9);
m_player = MockMediaPlayer::create();
m_parser = std::make_shared<NiceMock<MockCaptionParser>>();
m_presenter = std::make_shared<NiceMock<MockCaptionPresenter>>();
m_timingFactory = std::make_shared<TestTimingAdapterFactory>();
caption_manager = CaptionManager::create(m_parser, m_timingFactory);
caption_manager->addMediaPlayer(m_player);
caption_manager->setCaptionPresenter(m_presenter);
}
void CaptionManagerTest::TearDown() {
if (m_player) {
m_player->shutdown();
}
if (caption_manager) {
caption_manager->shutdown();
}
}
/**
* Sanity test to ensure the TestTimingAdapterFactory is returning the expected result.
*/
TEST_F(CaptionManagerTest, test_testTestTimingAdapterFactory) {
auto mockTimingAdapter = m_timingFactory->getMockTimingAdapter();
ASSERT_EQ(mockTimingAdapter, m_timingFactory->getTimingAdapter(nullptr));
}
/**
* Test that CaptionManager::setMediaPlayers() adds a media player that is bound to media events.
*/
TEST_F(CaptionManagerTest, test_testSetMediaPlayerBindsMediaPlayer) {
auto mockTimingAdapter = m_timingFactory->getMockTimingAdapter();
auto sourceID1 = m_player->setSource("http://fake.url", std::chrono::milliseconds(0));
// CaptionManager::setMediaPlayers() has already been called in CaptionManagerTest::SetUp().
// in order to check this, the timing adapter needs to be pre-loaded.
caption_manager->onParsed(CaptionFrame(sourceID1));
EXPECT_CALL(*mockTimingAdapter, pause()).Times(1);
// trigger the finished event from the media player
m_player->mockPause(sourceID1);
}
/**
* Test that create fails with null arguments
*/
TEST_F(CaptionManagerTest, test_createWithNullArgs) {
auto manager = CaptionManager::create(nullptr, nullptr);
ASSERT_FALSE(manager);
}
/**
* Test that create succeeds with a null TimingAdapterFactory.
*/
TEST_F(CaptionManagerTest, test_createWithNullTimingAdapterFactory) {
auto manager = CaptionManager::create(m_parser, nullptr);
ASSERT_NE(nullptr, manager);
}
/**
* Test that create fails with a null Parser.
*/
TEST_F(CaptionManagerTest, test_createWithNullParser) {
auto manager = CaptionManager::create(nullptr, m_timingFactory);
ASSERT_FALSE(manager);
}
/**
* Test that the source ID is maintained from onParsed() to queueForDisplay().
*/
TEST_F(CaptionManagerTest, test_sourceIdDoesNotChange) {
auto mockTimingAdapter = m_timingFactory->getMockTimingAdapter();
int sourceID1 = 1;
auto expectedCaptionFrame = CaptionFrame(sourceID1);
EXPECT_CALL(*mockTimingAdapter.get(), queueForDisplay(expectedCaptionFrame, _)).Times(1);
caption_manager->onParsed(CaptionFrame(sourceID1));
}
/**
* Test the media focus behavior for a single media player instance.
*/
TEST_F(CaptionManagerTest, test_singleMediaPlayerPause) {
auto mockTimingAdapter = m_timingFactory->getMockTimingAdapter();
std::vector<CaptionLine> expectedLines;
CaptionLine expectedLine1 = CaptionLine("The time is 2:17 PM.", {TextStyle()});
expectedLines.emplace_back(expectedLine1);
auto expectedCaptionFrame =
CaptionFrame(1, std::chrono::milliseconds(1), std::chrono::milliseconds(0), expectedLines);
EXPECT_CALL(*mockTimingAdapter.get(), queueForDisplay(expectedCaptionFrame, _)).Times(1);
EXPECT_CALL(*m_presenter, getWrapIndex(_)).Times(1).WillOnce(Return(std::pair<bool, uint32_t>(false, 0)));
std::vector<CaptionLine> lines;
CaptionLine line = CaptionLine("The time is 2:17 PM.", {});
lines.emplace_back(line);
auto captionFrame = CaptionFrame(1, std::chrono::milliseconds(1), std::chrono::milliseconds(0), lines);
caption_manager->onParsed(captionFrame);
}
/**
* Tests the splitting behavior when the caption is all spaces.
*/
TEST_F(CaptionManagerTest, test_splitCaptionFrameWhitespaceOnly) {
auto mockTimingAdapter = m_timingFactory->getMockTimingAdapter();
std::vector<CaptionLine> expectedLines;
CaptionLine expectedLine1 = CaptionLine(" ", {TextStyle()});
expectedLines.emplace_back(expectedLine1);
auto expectedCaptionFrame =
CaptionFrame(1, std::chrono::milliseconds(1), std::chrono::milliseconds(0), expectedLines);
EXPECT_CALL(*mockTimingAdapter, queueForDisplay(expectedCaptionFrame, _)).Times(1);
EXPECT_CALL(*m_presenter, getWrapIndex(_)).Times(1).WillOnce(Return(std::pair<bool, uint32_t>(false, 0)));
std::vector<CaptionLine> lines;
CaptionLine line = CaptionLine(" ", {});
lines.emplace_back(line);
auto captionFrame = CaptionFrame(1, std::chrono::milliseconds(1), std::chrono::milliseconds(0), lines);
caption_manager->onParsed(captionFrame);
}
/**
* Tests the splitting behavior when the caption is all spaces after a line wrap.
*/
TEST_F(CaptionManagerTest, test_splitCaptionFrameWhitespaceAfterLineWrap) {
auto mockTimingAdapter = m_timingFactory->getMockTimingAdapter();
std::vector<CaptionLine> expectedLines;
CaptionLine expectedLine1 = CaptionLine("The time is 2:17 PM.", {TextStyle()});
expectedLines.emplace_back(expectedLine1);
auto expectedCaptionFrame =
CaptionFrame(1, std::chrono::milliseconds(1), std::chrono::milliseconds(0), expectedLines);
EXPECT_CALL(*mockTimingAdapter, queueForDisplay(expectedCaptionFrame, _)).Times(1);
EXPECT_CALL(*m_presenter, getWrapIndex(_))
.Times(2)
.WillOnce(Return(std::pair<bool, uint32_t>(true, 20)))
.WillOnce(Return(std::pair<bool, uint32_t>(false, 0)));
std::vector<CaptionLine> lines;
CaptionLine line = CaptionLine("The time is 2:17 PM. ", {});
lines.emplace_back(line);
auto captionFrame = CaptionFrame(1, std::chrono::milliseconds(1), std::chrono::milliseconds(0), lines);
caption_manager->onParsed(captionFrame);
}
/**
* Tests the splitting behavior when the a no whitespace is present at before the requested wrap index.
*/
TEST_F(CaptionManagerTest, test_splitCaptionFrameNoWhitespaceBeforeWrapIndex) {
auto mockTimingAdapter = m_timingFactory->getMockTimingAdapter();
std::vector<CaptionLine> expectedLines;
CaptionLine expectedLine1 = CaptionLine("Thiscapti", {TextStyle()});
CaptionLine expectedLine2 = CaptionLine("onhasnosp", {TextStyle()});
CaptionLine expectedLine3 = CaptionLine("aces", {TextStyle()});
expectedLines.emplace_back(expectedLine1);
expectedLines.emplace_back(expectedLine2);
expectedLines.emplace_back(expectedLine3);
auto expectedCaptionFrame =
CaptionFrame(1, std::chrono::milliseconds(1), std::chrono::milliseconds(0), expectedLines);
EXPECT_CALL(*mockTimingAdapter, queueForDisplay(expectedCaptionFrame, _)).Times(1);
EXPECT_CALL(*m_presenter, getWrapIndex(_))
.Times(3)
.WillOnce(Return(std::pair<bool, uint32_t>(true, 9)))
.WillOnce(Return(std::pair<bool, uint32_t>(true, 9)))
.WillOnce(Return(std::pair<bool, uint32_t>(false, 0)));
std::vector<CaptionLine> lines;
CaptionLine line = CaptionLine("Thiscaptionhasnospaces", {});
lines.emplace_back(line);
auto captionFrame = CaptionFrame(1, std::chrono::milliseconds(1), std::chrono::milliseconds(0), lines);
caption_manager->onParsed(captionFrame);
}
/**
* Tests the splitting behavior when no split is needed.
*/
TEST_F(CaptionManagerTest, test_splitCaptionFrameFalseWillNotSplitLine) {
auto mockTimingAdapter = m_timingFactory->getMockTimingAdapter();
std::vector<CaptionLine> expectedLines;
CaptionLine expectedLine1 = CaptionLine("The time is 2:17 PM.", {TextStyle()});
expectedLines.emplace_back(expectedLine1);
auto expectedCaptionFrame =
CaptionFrame(1, std::chrono::milliseconds(1), std::chrono::milliseconds(0), expectedLines);
EXPECT_CALL(*mockTimingAdapter, queueForDisplay(expectedCaptionFrame, _)).Times(1);
EXPECT_CALL(*m_presenter, getWrapIndex(_)).Times(1).WillOnce(Return(std::pair<bool, uint32_t>(false, 0)));
std::vector<CaptionLine> lines;
CaptionLine line = CaptionLine("The time is 2:17 PM.", {});
lines.emplace_back(line);
auto captionFrame = CaptionFrame(1, std::chrono::milliseconds(1), std::chrono::milliseconds(0), lines);
caption_manager->onParsed(captionFrame);
}
/**
* Tests the splitting of a caption frame at a character index that happens to fall on a space character ('0x20').
*/
TEST_F(CaptionManagerTest, test_splitCaptionFrameAtSpaceIndex) {
auto mockTimingAdapter = m_timingFactory->getMockTimingAdapter();
std::vector<CaptionLine> expectedLines;
CaptionLine expectedLine1 = CaptionLine("The time is", {TextStyle()});
CaptionLine expectedLine2 = CaptionLine("2:17 PM.", {TextStyle()});
expectedLines.emplace_back(expectedLine1);
expectedLines.emplace_back(expectedLine2);
auto expectedCaptionFrame =
CaptionFrame(1, std::chrono::milliseconds(1), std::chrono::milliseconds(0), expectedLines);
EXPECT_CALL(*m_presenter, getWrapIndex(_))
.Times(2)
.WillOnce(Return(std::pair<bool, uint32_t>(true, 12)))
.WillOnce(Return(std::pair<bool, uint32_t>(false, 0)));
EXPECT_CALL(*mockTimingAdapter, queueForDisplay(expectedCaptionFrame, _)).Times(1);
std::vector<CaptionLine> lines;
CaptionLine line1 = CaptionLine("The time is 2:17 PM.", {});
lines.emplace_back(line1);
auto captionFrame = CaptionFrame(1, std::chrono::milliseconds(1), std::chrono::milliseconds(0), lines);
caption_manager->onParsed(captionFrame);
}
/**
* Test that CaptionManager::addMediaPlayer() does not add the same media player twice.
*/
TEST_F(CaptionManagerTest, test_testAddDuplicateMediaPlayerFails) {
auto mockTimingAdapter = m_timingFactory->getMockTimingAdapter();
EXPECT_CALL(*mockTimingAdapter, pause()).Times(1);
// Re-add the player added in Setup() (a duplicate add).
caption_manager->addMediaPlayer(m_player);
// test that the media player is still bound
auto sourceID2 = m_player->setSource("http://fake.url.com", std::chrono::milliseconds(0));
caption_manager->onParsed(CaptionFrame(sourceID2));
// trigger the finished event from the media player
m_player->mockPause(sourceID2);
}
/**
* Test that CaptionManager::addMediaPlayer() adds a media player that is bound to media events.
*/
TEST_F(CaptionManagerTest, test_testAddMediaPlayerBindsMediaPlayer) {
auto playerToAdd = MockMediaPlayer::create();
auto mockTimingAdapter = m_timingFactory->getMockTimingAdapter();
auto sourceID1 = playerToAdd->setSource("http://fake.url", std::chrono::milliseconds(0));
EXPECT_CALL(*mockTimingAdapter, pause()).Times(2);
caption_manager->addMediaPlayer(playerToAdd);
// in order to check this, the timing adapter needs to be pre-loaded.
caption_manager->onParsed(CaptionFrame(sourceID1));
// trigger the finished event from the media player
playerToAdd->mockPause(sourceID1);
// test that the other media players are also still bound
auto sourceID2 = m_player->setSource("http://fake.url.com", std::chrono::milliseconds(0));
caption_manager->onParsed(CaptionFrame(sourceID2));
// trigger the finished event from the media player
m_player->mockPause(sourceID2);
}
/**
* Test that CaptionManager::removeMediaPlayer() removes a media player bound to media events.
*/
TEST_F(CaptionManagerTest, test_testRemoveMediaPlayerUnbindsMediaPlayer) {
auto mockTimingAdapter = m_timingFactory->getMockTimingAdapter();
auto sourceID1 = m_player->setSource("http://fake.url", std::chrono::milliseconds(0));
caption_manager->removeMediaPlayer(m_player);
// in order to check this, the timing adapter needs to be pre-loaded.
caption_manager->onParsed(CaptionFrame(sourceID1));
// no calls expected now that the media player was removed
EXPECT_CALL(*mockTimingAdapter, pause()).Times(0);
// trigger the finished event from the media player
m_player->mockPause(sourceID1);
}
/**
* Tests that the caption manager is enabled after construction when
* captions are enabled.
*/
TEST_F(CaptionManagerTest, test_isEnabled) {
#ifdef ENABLE_CAPTIONS
ASSERT_TRUE(caption_manager->isEnabled());
#else
ASSERT_FALSE(caption_manager->isEnabled());
#endif
}
} // namespace test
} // namespace captions
} // namespace alexaClientSDK