/* * Copyright 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 #include #include #include #include #include #include #include #include #include #include #include "AndroidSLESMediaPlayer/AndroidSLESMediaQueue.h" /// String to identify log entries originating from this file. static const std::string TAG{"AndroidSLESMediaQueueTest"}; /** * Create a LogEntry using this file's TAG and the specified event string. * * @param The event string for this @c LogEntry. */ #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) namespace alexaClientSDK { namespace mediaPlayer { namespace android { namespace test { using namespace ::testing; using namespace applicationUtilities::androidUtilities; using namespace applicationUtilities::androidUtilities::test; using namespace avsCommon::avs; using MockSlSimpleBufferQueue = MockInterfaceImpl; /// The path to the input Dir containing the test audio files. static std::string inputsDirPath; /// MP3 test file path. static const std::string MP3_FILE_PATH("/fox_dog.mp3"); /// Timeout for wait callback static const std::chrono::milliseconds TIMEOUT{100}; /// Number of buffers used by the buffer queue. static const auto NUMBER_OF_BUFFERS = AndroidSLESMediaQueue::NUMBER_OF_BUFFERS; /// Mock buffer count. static std::atomic counter; /// Mock buffer index. static int index; /// Mutex used to synchronize the enqueueBuffer and onBufferFree static std::mutex bufferMutex; /// Condition variable used to guarantee that onBufferFree only happens after counter > 0; static std::condition_variable bufferCondition; /// Mock decoder. class MockDecoder : public DecoderInterface { public: /// Mock read call. MOCK_METHOD2(read, std::pair(int16_t*, size_t)); /// Mock abort call. MOCK_METHOD0(abort, void()); }; /// Mock status callback. class MockCallback { public: /// Method that expectations should be based on. void callback(AndroidSLESMediaQueue::QueueEvent event, const std::string& reason); /// Method used to wait for the callback call. It returns the status it received. bool waitCallback(const AndroidSLESMediaQueue::QueueEvent& expectedEvent); private: /// Mutex to synchronize calls. std::mutex m_mutex; /// Condition variable used to wake-up callback. std::condition_variable m_condition; /// The queue status received; AndroidSLESMediaQueue::QueueEvent m_event{AndroidSLESMediaQueue::QueueEvent::ERROR}; }; void MockCallback::callback(AndroidSLESMediaQueue::QueueEvent event, const std::string& reason) { std::lock_guard lock{m_mutex}; m_event = event; m_condition.notify_one(); } bool MockCallback::waitCallback(const AndroidSLESMediaQueue::QueueEvent& expectedEvent) { std::unique_lock lock{m_mutex}; return m_condition.wait_for(lock, TIMEOUT, [this, &expectedEvent] { return m_event == expectedEvent; }); } /// Mock register callback succeeds. static SLresult mockRegisterCallbackOk( SLAndroidSimpleBufferQueueItf self, slAndroidSimpleBufferQueueCallback callback, void* pCallbackContext) { return SL_RESULT_SUCCESS; } /// Mock register callback fails. static SLresult mockRegisterCallbackFailed( SLAndroidSimpleBufferQueueItf self, slAndroidSimpleBufferQueueCallback callback, void* pCallbackContext) { return SL_RESULT_INTERNAL_ERROR; } /// Mock enqueue call succeeds. static SLresult mockEnqueueOk(SLAndroidSimpleBufferQueueItf self, const void* pBuffer, SLuint32 size) { std::lock_guard lock{bufferMutex}; bufferCondition.notify_one(); counter++; return SL_RESULT_SUCCESS; } /// Mock enqueue call fails. SLresult mockEnqueueFailed(SLAndroidSimpleBufferQueueItf self, const void* pBuffer, SLuint32 size) { return SL_RESULT_INTERNAL_ERROR; } /// Mock get state. SLresult mockGetState(SLAndroidSimpleBufferQueueItf self, SLAndroidSimpleBufferQueueState* pState) { pState->count = counter; pState->index = index; return SL_RESULT_SUCCESS; } /// Mock clear call succeeds. static SLresult mockClearOk(SLAndroidSimpleBufferQueueItf self) { return SL_RESULT_SUCCESS; } /// Mock buffer free call. Increment index and decrement counter. static void bufferFree(AndroidSLESMediaQueue& mediaQueue) { if (!counter) { std::unique_lock lock{bufferMutex}; bufferCondition.wait_for(lock, TIMEOUT); } counter--; index++; mediaQueue.onBufferFree(); } class AndroidSLESMediaQueueTest : public Test { protected: void SetUp() override; void TearDown() override; /// Mock the OpenSL ES buffer queue object wrapper. std::shared_ptr m_objectMock; /// Mock the actuall OpenSL ES buffer queue object. std::shared_ptr m_slObject; /// Mock the OpenSL ES buffer queue interface. std::shared_ptr m_queueMock; /// The status callback mock. MockCallback m_callback; }; void AndroidSLESMediaQueueTest::SetUp() { counter = 0; index = 0; m_objectMock = std::make_shared(); m_queueMock = std::make_shared(); m_slObject = AndroidSLESObject::create(m_objectMock->getObject()); m_objectMock->mockGetInterface(SL_IID_ANDROIDSIMPLEBUFFERQUEUE, m_queueMock); m_queueMock->get().RegisterCallback = mockRegisterCallbackOk; m_queueMock->get().Enqueue = mockEnqueueOk; m_queueMock->get().GetState = mockGetState; m_queueMock->get().Clear = mockClearOk; } void AndroidSLESMediaQueueTest::TearDown() { m_queueMock.reset(); m_objectMock.reset(); } /// Test buffer queue create. TEST_F(AndroidSLESMediaQueueTest, testCreateSucceed) { std::pair done = {DecoderInterface::Status::DONE, 0}; auto decoder = avsCommon::utils::memory::make_unique>(); EXPECT_CALL(*decoder, read(_, _)).WillOnce(Return(done)); auto mediaQueue = AndroidSLESMediaQueue::create( m_slObject, std::move(decoder), [this](AndroidSLESMediaQueue::QueueEvent queueStatus, const std::string& reason) { m_callback.callback(queueStatus, reason); }); EXPECT_NE(mediaQueue, nullptr); } /// Check that create fail if we are missing the OpenSL ES object. TEST_F(AndroidSLESMediaQueueTest, testCreateFailMissingSlObject) { auto decoder = avsCommon::utils::memory::make_unique>(); auto mediaQueue = AndroidSLESMediaQueue::create( nullptr, std::move(decoder), [this](AndroidSLESMediaQueue::QueueEvent queueStatus, const std::string& reason) { m_callback.callback(queueStatus, reason); }); EXPECT_EQ(mediaQueue, nullptr); } /// Check that create fail if the @c SL_IID_ANDROIDSIMPLEBUFFERQUEUE is missing. TEST_F(AndroidSLESMediaQueueTest, testCreateFailMissingInterface) { m_objectMock->mockGetInterface(SL_IID_ANDROIDSIMPLEBUFFERQUEUE, nullptr); auto decoder = avsCommon::utils::memory::make_unique>(); auto mediaQueue = AndroidSLESMediaQueue::create( m_slObject, std::move(decoder), [this](AndroidSLESMediaQueue::QueueEvent queueStatus, const std::string& reason) { m_callback.callback(queueStatus, reason); }); EXPECT_EQ(mediaQueue, nullptr); } /// Check that create fail if the decoder is missing. TEST_F(AndroidSLESMediaQueueTest, testCreateFailMissingDecoder) { auto mediaQueue = AndroidSLESMediaQueue::create( m_slObject, nullptr, [this](AndroidSLESMediaQueue::QueueEvent queueStatus, const std::string& reason) { m_callback.callback(queueStatus, reason); }); EXPECT_EQ(mediaQueue, nullptr); } /// Check that create fail if the callback function is missing. TEST_F(AndroidSLESMediaQueueTest, testCreateFailMissingCallback) { auto decoder = avsCommon::utils::memory::make_unique>(); auto mediaQueue = AndroidSLESMediaQueue::create(m_slObject, std::move(decoder), nullptr); EXPECT_EQ(mediaQueue, nullptr); } /// Check that create fail if the callback function cannot be registered. TEST_F(AndroidSLESMediaQueueTest, testCreateFailRegisterCallback) { m_queueMock->get().RegisterCallback = mockRegisterCallbackFailed; auto decoder = avsCommon::utils::memory::make_unique>(); auto mediaQueue = AndroidSLESMediaQueue::create(m_slObject, std::move(decoder), nullptr); EXPECT_EQ(mediaQueue, nullptr); } /// Test buffer queue events when the media queue succeeds to read data from the decoder. TEST_F(AndroidSLESMediaQueueTest, testOnBufferQueueSucceed) { // Always return valid read. constexpr size_t wordsRead{1000}; // Arbitrary number of words that is > 0. constexpr size_t wordSize{2}; // Media queue uses int16_t which is 2 bytes long. std::pair ok = {DecoderInterface::Status::OK, wordsRead}; auto decoder = avsCommon::utils::memory::make_unique>(); EXPECT_CALL(*decoder, read(_, _)).WillRepeatedly(Return(ok)); auto mediaQueue = AndroidSLESMediaQueue::create( m_slObject, std::move(decoder), [this](AndroidSLESMediaQueue::QueueEvent queueStatus, const std::string& reason) { m_callback.callback(queueStatus, reason); }); EXPECT_NE(mediaQueue, nullptr); bufferFree(*mediaQueue); size_t buffersPlayed = 1; EXPECT_FALSE(m_callback.waitCallback(AndroidSLESMediaQueue::QueueEvent::FINISHED_PLAYING)); EXPECT_EQ(counter, static_cast(NUMBER_OF_BUFFERS)); EXPECT_EQ(mediaQueue->getNumBytesBuffered(), NUMBER_OF_BUFFERS * wordsRead * wordSize); EXPECT_EQ(mediaQueue->getNumBytesPlayed(), buffersPlayed * wordsRead * wordSize); } /// Test buffer queue events when the media queue succeeds to read data from the decoder till the end of the stream. TEST_F(AndroidSLESMediaQueueTest, testEnqueueTillDone) { constexpr size_t wordsRead{1000}; // Arbitrary number of words that is > 0. constexpr size_t wordSize{2}; // Media queue uses int16_t which is 2 bytes long. std::pair ok = {DecoderInterface::Status::OK, wordsRead}; std::pair done = {DecoderInterface::Status::DONE, 0}; // Return valid read for NUMBER_OF_BUFFERS times, then return done. auto decoder = avsCommon::utils::memory::make_unique>(); EXPECT_CALL(*decoder, read(_, _)).WillOnce(Return(done)); EXPECT_CALL(*decoder, read(_, _)) .Times(AndroidSLESMediaQueue::NUMBER_OF_BUFFERS) .WillRepeatedly(Return(ok)) .RetiresOnSaturation(); auto mediaQueue = AndroidSLESMediaQueue::create( m_slObject, std::move(decoder), [this](AndroidSLESMediaQueue::QueueEvent queueStatus, const std::string& reason) { m_callback.callback(queueStatus, reason); }); EXPECT_NE(mediaQueue, nullptr); for (size_t i = 0; i <= AndroidSLESMediaQueue::NUMBER_OF_BUFFERS; ++i) { bufferFree(*mediaQueue); } EXPECT_TRUE(m_callback.waitCallback(AndroidSLESMediaQueue::QueueEvent::FINISHED_PLAYING)); EXPECT_EQ(mediaQueue->getNumBytesPlayed(), AndroidSLESMediaQueue::NUMBER_OF_BUFFERS * wordsRead * wordSize); EXPECT_EQ(mediaQueue->getNumBytesBuffered(), 0u); } /// Test that the buffer queue emits an error event when the decoder fails. TEST_F(AndroidSLESMediaQueueTest, testDecoderFailure) { std::pair error = {DecoderInterface::Status::ERROR, 0}; auto decoder = avsCommon::utils::memory::make_unique>(); EXPECT_CALL(*decoder, read(_, _)).WillOnce(Return(error)); auto mediaQueue = AndroidSLESMediaQueue::create( m_slObject, std::move(decoder), [this](AndroidSLESMediaQueue::QueueEvent queueStatus, const std::string& reason) { m_callback.callback(queueStatus, reason); }); EXPECT_NE(mediaQueue, nullptr); EXPECT_TRUE(m_callback.waitCallback(AndroidSLESMediaQueue::QueueEvent::ERROR)); } /// Test buffer queue events emits an error event when it fails to enqueue a buffer TEST_F(AndroidSLESMediaQueueTest, testEnqueueFailure) { // Always return valid read. constexpr size_t wordsRead{1000}; // Arbitrary number of words that is > 0. std::pair ok = {DecoderInterface::Status::OK, wordsRead}; auto decoder = avsCommon::utils::memory::make_unique>(); EXPECT_CALL(*decoder, read(_, _)).WillRepeatedly(Return(ok)); auto mediaQueue = AndroidSLESMediaQueue::create( m_slObject, std::move(decoder), [this](AndroidSLESMediaQueue::QueueEvent queueStatus, const std::string& reason) { m_callback.callback(queueStatus, reason); }); EXPECT_NE(mediaQueue, nullptr); m_queueMock->get().Enqueue = mockEnqueueFailed; bufferFree(*mediaQueue); EXPECT_TRUE(m_callback.waitCallback(AndroidSLESMediaQueue::QueueEvent::ERROR)); } } // namespace test } // namespace android } // namespace mediaPlayer } // namespace alexaClientSDK