| // Copyright 2021 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <va/va.h> |
| |
| #include "media/gpu/vaapi/vaapi_wrapper.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using ::testing::_; |
| using ::testing::Invoke; |
| using ::testing::Return; |
| |
| namespace media { |
| namespace { |
| VaapiWrapper::VABufferDescriptor CreateVABufferDescriptor() { |
| constexpr static char kData[] = "vaBufferData"; |
| return VaapiWrapper::VABufferDescriptor{VAProcPipelineParameterBufferType, |
| sizeof(kData), kData}; |
| } |
| |
| class MockVaapiWrapper : public VaapiWrapper { |
| public: |
| MockVaapiWrapper() : VaapiWrapper(kVideoProcess) {} |
| MOCK_METHOD1(SubmitBuffer_Locked, bool(const VABufferDescriptor&)); |
| MOCK_METHOD0(DestroyPendingBuffers_Locked, void()); |
| |
| protected: |
| ~MockVaapiWrapper() override = default; |
| }; |
| } // namespace |
| |
| class VaapiWrapperTest : public testing::Test { |
| public: |
| VaapiWrapperTest() = default; |
| |
| void SetUp() override { |
| // Create a VaapiWrapper for testing. |
| mock_vaapi_wrapper_ = base::MakeRefCounted<MockVaapiWrapper>(); |
| ASSERT_TRUE(mock_vaapi_wrapper_); |
| |
| ON_CALL(*mock_vaapi_wrapper_, SubmitBuffer_Locked) |
| .WillByDefault( |
| Invoke(this, &VaapiWrapperTest::DefaultSubmitBuffer_Locked)); |
| ON_CALL(*mock_vaapi_wrapper_, DestroyPendingBuffers_Locked) |
| .WillByDefault(Invoke( |
| this, &VaapiWrapperTest::DefaultDestroyPendingBuffers_Locked)); |
| } |
| void TearDown() override { |
| // The VaapiWrapper destructor calls DestroyPendingBuffers_Locked(). Since |
| // MockVaapiWrapper is a derived class, |
| // MockVaapiWrapper::DestroyPendingBuffers_Locked() won't get called during |
| // destruction even though it's a virtual function. Instead, |
| // VaapiWrapper::DestroyPendingBuffers_Locked() will get called. Therefore, |
| // we need to clear |pending_va_buffers_| before this happens so that |
| // VaapiWrapper::DestroyPendingBuffers_Locked() doesn't call |
| // vaDestroyBuffer(). |
| mock_vaapi_wrapper_->pending_va_buffers_.clear(); |
| mock_vaapi_wrapper_.reset(); |
| } |
| |
| bool DefaultSubmitBuffer_Locked( |
| const VaapiWrapper::VABufferDescriptor& va_buffer) |
| EXCLUSIVE_LOCKS_REQUIRED(mock_vaapi_wrapper_->va_lock_) { |
| if (va_buffer.data) { |
| constexpr VABufferID kFakeBufferId = 1234; |
| mock_vaapi_wrapper_->pending_va_buffers_.push_back(kFakeBufferId); |
| return true; |
| } |
| // When |va_buffer|.data is null, the base method should return false and |
| // no libva calls should be made. |
| const bool submit_buffer_res = |
| (*mock_vaapi_wrapper_).VaapiWrapper::SubmitBuffer_Locked(va_buffer); |
| if (submit_buffer_res) |
| ADD_FAILURE(); |
| return false; |
| } |
| |
| void DefaultDestroyPendingBuffers_Locked() |
| EXCLUSIVE_LOCKS_REQUIRED(mock_vaapi_wrapper_->va_lock_) { |
| mock_vaapi_wrapper_->pending_va_buffers_.clear(); |
| } |
| |
| size_t GetPendingBuffersSize() const { |
| return mock_vaapi_wrapper_->pending_va_buffers_.size(); |
| } |
| |
| protected: |
| scoped_refptr<MockVaapiWrapper> mock_vaapi_wrapper_; |
| }; |
| |
| // This test ensures SubmitBuffer() calls SubmitBuffer_Locked(). |
| TEST_F(VaapiWrapperTest, SubmitBuffer) { |
| constexpr size_t kNumBuffers = 3; |
| auto va_buffer = CreateVABufferDescriptor(); |
| |
| EXPECT_CALL(*mock_vaapi_wrapper_, SubmitBuffer_Locked(_)).Times(kNumBuffers); |
| for (size_t i = 0; i < kNumBuffers; ++i) { |
| EXPECT_TRUE(mock_vaapi_wrapper_->SubmitBuffer( |
| va_buffer.type, va_buffer.size, va_buffer.data)); |
| } |
| EXPECT_EQ(GetPendingBuffersSize(), kNumBuffers); |
| } |
| |
| // This test ensures SubmitBuffers() calls SubmitBuffer_Locked() as many times |
| // as the number of passed buffers. |
| TEST_F(VaapiWrapperTest, SubmitBuffers) { |
| constexpr size_t kNumBuffers = 3; |
| auto va_buffer = CreateVABufferDescriptor(); |
| std::vector<VaapiWrapper::VABufferDescriptor> buffers(kNumBuffers, va_buffer); |
| |
| EXPECT_CALL(*mock_vaapi_wrapper_, SubmitBuffer_Locked(_)).Times(kNumBuffers); |
| EXPECT_TRUE(mock_vaapi_wrapper_->SubmitBuffers(buffers)); |
| EXPECT_EQ(GetPendingBuffersSize(), kNumBuffers); |
| } |
| |
| // This test ensures DestroyPendingBuffers_Locked() is executed on a failure of |
| // SubmitBuffer(). |
| TEST_F(VaapiWrapperTest, FailOnSubmitBuffer) { |
| auto va_buffer = CreateVABufferDescriptor(); |
| |
| ::testing::InSequence s; |
| EXPECT_CALL(*mock_vaapi_wrapper_, SubmitBuffer_Locked(_)).Times(2); |
| EXPECT_CALL(*mock_vaapi_wrapper_, DestroyPendingBuffers_Locked); |
| EXPECT_TRUE(mock_vaapi_wrapper_->SubmitBuffer(va_buffer.type, va_buffer.size, |
| va_buffer.data)); |
| EXPECT_FALSE(mock_vaapi_wrapper_->SubmitBuffer(va_buffer.type, va_buffer.size, |
| /*data=*/nullptr)); |
| EXPECT_EQ(GetPendingBuffersSize(), 0u); |
| } |
| |
| // This test ensures DestroyPendingBuffers_Locked() is executed on a failure of |
| // SubmitBuffers(). |
| TEST_F(VaapiWrapperTest, FailOnSubmitBuffers) { |
| constexpr size_t kNumBuffers = 3; |
| auto va_buffer = CreateVABufferDescriptor(); |
| std::vector<VaapiWrapper::VABufferDescriptor> buffers(kNumBuffers, va_buffer); |
| // Set data to nullptr so that VaapiWrapper::SubmitBuffer_Locked() fails. |
| buffers[1].data = nullptr; |
| |
| ::testing::InSequence s; |
| EXPECT_CALL(*mock_vaapi_wrapper_, SubmitBuffer_Locked(_)) |
| .Times(kNumBuffers - 1); |
| EXPECT_CALL(*mock_vaapi_wrapper_, DestroyPendingBuffers_Locked); |
| EXPECT_FALSE(mock_vaapi_wrapper_->SubmitBuffers(buffers)); |
| EXPECT_EQ(GetPendingBuffersSize(), 0u); |
| } |
| } // namespace media |