/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License 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. */ // Unit Test for MediaSampleQueue // #define LOG_NDEBUG 0 #define LOG_TAG "MediaSampleQueueTests" #include #include #include #include namespace android { /** Duration to use when delaying threads to order operations. */ static constexpr int64_t kThreadDelayDurationMs = 100; class MediaSampleQueueTests : public ::testing::Test { public: MediaSampleQueueTests() { LOG(DEBUG) << "MediaSampleQueueTests created"; } ~MediaSampleQueueTests() { LOG(DEBUG) << "MediaSampleQueueTests destroyed"; } }; static std::shared_ptr newSample(uint32_t id) { return MediaSample::createWithReleaseCallback(nullptr /* buffer */, 0 /* offset */, id, nullptr /* callback */); } TEST_F(MediaSampleQueueTests, TestSequentialDequeueOrder) { LOG(DEBUG) << "TestSequentialDequeueOrder Starts"; static constexpr int kNumSamples = 4; MediaSampleQueue sampleQueue; EXPECT_TRUE(sampleQueue.isEmpty()); // Enqueue loop. for (int i = 0; i < kNumSamples; ++i) { sampleQueue.enqueue(newSample(i)); EXPECT_FALSE(sampleQueue.isEmpty()); } // Dequeue loop. for (int i = 0; i < kNumSamples; ++i) { std::shared_ptr sample; bool aborted = sampleQueue.dequeue(&sample); EXPECT_NE(sample, nullptr); EXPECT_EQ(sample->bufferId, i); EXPECT_FALSE(aborted); } EXPECT_TRUE(sampleQueue.isEmpty()); } TEST_F(MediaSampleQueueTests, TestInterleavedDequeueOrder) { LOG(DEBUG) << "TestInterleavedDequeueOrder Starts"; static constexpr int kNumSamples = 4; MediaSampleQueue sampleQueue; // Enqueue and dequeue. for (int i = 0; i < kNumSamples; ++i) { sampleQueue.enqueue(newSample(i)); EXPECT_FALSE(sampleQueue.isEmpty()); std::shared_ptr sample; bool aborted = sampleQueue.dequeue(&sample); EXPECT_NE(sample, nullptr); EXPECT_EQ(sample->bufferId, i); EXPECT_FALSE(aborted); EXPECT_TRUE(sampleQueue.isEmpty()); } } TEST_F(MediaSampleQueueTests, TestBlockingDequeue) { LOG(DEBUG) << "TestBlockingDequeue Starts"; MediaSampleQueue sampleQueue; std::thread enqueueThread([&sampleQueue] { // Note: This implementation is a bit racy. Any amount of sleep will not guarantee that the // main thread will be blocked on the sample queue by the time this thread calls enqueue. // But we can say with high confidence that it will and the test will not fail regardless. std::this_thread::sleep_for(std::chrono::milliseconds(kThreadDelayDurationMs)); sampleQueue.enqueue(newSample(1)); }); std::shared_ptr sample; bool aborted = sampleQueue.dequeue(&sample); EXPECT_NE(sample, nullptr); EXPECT_EQ(sample->bufferId, 1); EXPECT_FALSE(aborted); EXPECT_TRUE(sampleQueue.isEmpty()); enqueueThread.join(); } TEST_F(MediaSampleQueueTests, TestDequeueBufferRelease) { LOG(DEBUG) << "TestDequeueBufferRelease Starts"; static constexpr int kNumSamples = 4; std::vector bufferReleased(kNumSamples, false); MediaSample::OnSampleReleasedCallback callback = [&bufferReleased](MediaSample* sample) { bufferReleased[sample->bufferId] = true; }; MediaSampleQueue sampleQueue; for (int i = 0; i < kNumSamples; ++i) { bool aborted = sampleQueue.enqueue( MediaSample::createWithReleaseCallback(nullptr, 0, i, callback)); EXPECT_FALSE(aborted); } for (int i = 0; i < kNumSamples; ++i) { EXPECT_FALSE(bufferReleased[i]); } for (int i = 0; i < kNumSamples; ++i) { { std::shared_ptr sample; bool aborted = sampleQueue.dequeue(&sample); EXPECT_NE(sample, nullptr); EXPECT_EQ(sample->bufferId, i); EXPECT_FALSE(bufferReleased[i]); EXPECT_FALSE(aborted); } for (int j = 0; j < kNumSamples; ++j) { EXPECT_EQ(bufferReleased[j], j <= i); } } } TEST_F(MediaSampleQueueTests, TestAbortBufferRelease) { LOG(DEBUG) << "TestAbortBufferRelease Starts"; static constexpr int kNumSamples = 4; std::vector bufferReleased(kNumSamples, false); MediaSample::OnSampleReleasedCallback callback = [&bufferReleased](MediaSample* sample) { bufferReleased[sample->bufferId] = true; }; MediaSampleQueue sampleQueue; for (int i = 0; i < kNumSamples; ++i) { bool aborted = sampleQueue.enqueue( MediaSample::createWithReleaseCallback(nullptr, 0, i, callback)); EXPECT_FALSE(aborted); } for (int i = 0; i < kNumSamples; ++i) { EXPECT_FALSE(bufferReleased[i]); } EXPECT_FALSE(sampleQueue.isEmpty()); sampleQueue.abort(); EXPECT_TRUE(sampleQueue.isEmpty()); for (int i = 0; i < kNumSamples; ++i) { EXPECT_TRUE(bufferReleased[i]); } } TEST_F(MediaSampleQueueTests, TestNonEmptyAbort) { LOG(DEBUG) << "TestNonEmptyAbort Starts"; MediaSampleQueue sampleQueue; bool aborted = sampleQueue.enqueue(newSample(1)); EXPECT_FALSE(aborted); sampleQueue.abort(); std::shared_ptr sample; aborted = sampleQueue.dequeue(&sample); EXPECT_TRUE(aborted); EXPECT_EQ(sample, nullptr); aborted = sampleQueue.enqueue(sample); EXPECT_TRUE(aborted); } TEST_F(MediaSampleQueueTests, TestEmptyAbort) { LOG(DEBUG) << "TestEmptyAbort Starts"; MediaSampleQueue sampleQueue; sampleQueue.abort(); std::shared_ptr sample; bool aborted = sampleQueue.dequeue(&sample); EXPECT_TRUE(aborted); EXPECT_EQ(sample, nullptr); aborted = sampleQueue.enqueue(sample); EXPECT_TRUE(aborted); } TEST_F(MediaSampleQueueTests, TestBlockingAbort) { LOG(DEBUG) << "TestBlockingAbort Starts"; MediaSampleQueue sampleQueue; std::thread abortingThread([&sampleQueue] { // Note: This implementation is a bit racy. Any amount of sleep will not guarantee that the // main thread will be blocked on the sample queue by the time this thread calls abort. // But we can say with high confidence that it will and the test will not fail regardless. std::this_thread::sleep_for(std::chrono::milliseconds(kThreadDelayDurationMs)); sampleQueue.abort(); }); std::shared_ptr sample; bool aborted = sampleQueue.dequeue(&sample); EXPECT_TRUE(aborted); EXPECT_EQ(sample, nullptr); abortingThread.join(); } } // namespace android int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }