| /* |
| * Copyright 2018 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. |
| */ |
| |
| #define LOG_TAG "VtsHalGraphicsMapperV3_0TargetTest" |
| |
| #include <chrono> |
| #include <thread> |
| #include <vector> |
| |
| #include <VtsHalHidlTargetTestBase.h> |
| #include <android-base/logging.h> |
| #include <mapper-vts/3.0/MapperVts.h> |
| |
| namespace android { |
| namespace hardware { |
| namespace graphics { |
| namespace mapper { |
| namespace V3_0 { |
| namespace vts { |
| namespace { |
| |
| using android::hardware::graphics::common::V1_2::BufferUsage; |
| using android::hardware::graphics::common::V1_2::PixelFormat; |
| |
| // Test environment for graphics.mapper. |
| class GraphicsMapperHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase { |
| public: |
| // get the test environment singleton |
| static GraphicsMapperHidlEnvironment* Instance() { |
| static GraphicsMapperHidlEnvironment* instance = new GraphicsMapperHidlEnvironment; |
| return instance; |
| } |
| |
| virtual void registerTestServices() override { |
| registerTestService<IAllocator>(); |
| registerTestService<IMapper>(); |
| } |
| }; |
| |
| class GraphicsMapperHidlTest : public ::testing::VtsHalHidlTargetTestBase { |
| protected: |
| void SetUp() override { |
| ASSERT_NO_FATAL_FAILURE( |
| mGralloc = std::make_unique<Gralloc>( |
| GraphicsMapperHidlEnvironment::Instance()->getServiceName<IAllocator>(), |
| GraphicsMapperHidlEnvironment::Instance()->getServiceName<IMapper>())); |
| |
| mDummyDescriptorInfo.width = 64; |
| mDummyDescriptorInfo.height = 64; |
| mDummyDescriptorInfo.layerCount = 1; |
| mDummyDescriptorInfo.format = PixelFormat::RGBA_8888; |
| mDummyDescriptorInfo.usage = |
| static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN); |
| } |
| |
| void TearDown() override {} |
| |
| std::unique_ptr<Gralloc> mGralloc; |
| IMapper::BufferDescriptorInfo mDummyDescriptorInfo{}; |
| }; |
| |
| /** |
| * Test IAllocator::dumpDebugInfo by calling it. |
| */ |
| TEST_F(GraphicsMapperHidlTest, AllocatorDumpDebugInfo) { |
| mGralloc->dumpDebugInfo(); |
| } |
| |
| /** |
| * Test IAllocator::allocate with valid buffer descriptors. |
| */ |
| TEST_F(GraphicsMapperHidlTest, AllocatorAllocate) { |
| BufferDescriptor descriptor; |
| ASSERT_NO_FATAL_FAILURE(descriptor = mGralloc->createDescriptor(mDummyDescriptorInfo)); |
| |
| for (uint32_t count = 0; count < 5; count++) { |
| std::vector<const native_handle_t*> bufferHandles; |
| uint32_t stride; |
| ASSERT_NO_FATAL_FAILURE(bufferHandles = |
| mGralloc->allocate(descriptor, count, false, &stride)); |
| |
| if (count >= 1) { |
| EXPECT_LE(mDummyDescriptorInfo.width, stride) << "invalid buffer stride"; |
| } |
| |
| for (auto bufferHandle : bufferHandles) { |
| mGralloc->freeBuffer(bufferHandle); |
| } |
| } |
| } |
| |
| /** |
| * Test IAllocator::allocate with invalid buffer descriptors. |
| */ |
| TEST_F(GraphicsMapperHidlTest, AllocatorAllocateNegative) { |
| // this assumes any valid descriptor is non-empty |
| BufferDescriptor descriptor; |
| mGralloc->getAllocator()->allocate(descriptor, 1, |
| [&](const auto& tmpError, const auto&, const auto&) { |
| EXPECT_EQ(Error::BAD_DESCRIPTOR, tmpError); |
| }); |
| } |
| |
| /** |
| * Test IAllocator::allocate does not leak. |
| */ |
| TEST_F(GraphicsMapperHidlTest, AllocatorAllocateNoLeak) { |
| auto info = mDummyDescriptorInfo; |
| info.width = 1024; |
| info.height = 1024; |
| |
| for (int i = 0; i < 2048; i++) { |
| auto bufferHandle = mGralloc->allocate(info, false); |
| mGralloc->freeBuffer(bufferHandle); |
| } |
| } |
| |
| /** |
| * Test that IAllocator::allocate is thread-safe. |
| */ |
| TEST_F(GraphicsMapperHidlTest, AllocatorAllocateThreaded) { |
| BufferDescriptor descriptor; |
| ASSERT_NO_FATAL_FAILURE(descriptor = mGralloc->createDescriptor(mDummyDescriptorInfo)); |
| |
| std::atomic<bool> timeUp(false); |
| std::atomic<uint64_t> allocationCount(0); |
| auto threadLoop = [&]() { |
| while (!timeUp) { |
| mGralloc->getAllocator()->allocate( |
| descriptor, 1, [&](const auto&, const auto&, const auto&) { allocationCount++; }); |
| } |
| }; |
| |
| std::vector<std::thread> threads; |
| for (int i = 0; i < 8; i++) { |
| threads.push_back(std::thread(threadLoop)); |
| } |
| |
| std::this_thread::sleep_for(std::chrono::seconds(3)); |
| timeUp = true; |
| LOG(VERBOSE) << "Made " << allocationCount << " threaded allocations"; |
| |
| for (auto& thread : threads) { |
| thread.join(); |
| } |
| } |
| |
| /** |
| * Test IMapper::createDescriptor with valid descriptor info. |
| */ |
| TEST_F(GraphicsMapperHidlTest, CreateDescriptorBasic) { |
| ASSERT_NO_FATAL_FAILURE(mGralloc->createDescriptor(mDummyDescriptorInfo)); |
| } |
| |
| /** |
| * Test IMapper::createDescriptor with invalid descriptor info. |
| */ |
| TEST_F(GraphicsMapperHidlTest, CreateDescriptorNegative) { |
| auto info = mDummyDescriptorInfo; |
| info.width = 0; |
| mGralloc->getMapper()->createDescriptor(info, [&](const auto& tmpError, const auto&) { |
| EXPECT_EQ(Error::BAD_VALUE, tmpError) << "createDescriptor did not fail with BAD_VALUE"; |
| }); |
| } |
| |
| /** |
| * Test IMapper::importBuffer and IMapper::freeBuffer with allocated buffers. |
| */ |
| TEST_F(GraphicsMapperHidlTest, ImportFreeBufferBasic) { |
| const native_handle_t* bufferHandle; |
| ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(mDummyDescriptorInfo, true)); |
| ASSERT_NO_FATAL_FAILURE(mGralloc->freeBuffer(bufferHandle)); |
| } |
| |
| /** |
| * Test IMapper::importBuffer and IMapper::freeBuffer with cloned buffers. |
| */ |
| TEST_F(GraphicsMapperHidlTest, ImportFreeBufferClone) { |
| const native_handle_t* clonedBufferHandle; |
| ASSERT_NO_FATAL_FAILURE(clonedBufferHandle = mGralloc->allocate(mDummyDescriptorInfo, false)); |
| |
| // A cloned handle is a raw handle. Check that we can import it multiple |
| // times. |
| const native_handle_t* importedBufferHandles[2]; |
| ASSERT_NO_FATAL_FAILURE(importedBufferHandles[0] = mGralloc->importBuffer(clonedBufferHandle)); |
| ASSERT_NO_FATAL_FAILURE(importedBufferHandles[1] = mGralloc->importBuffer(clonedBufferHandle)); |
| ASSERT_NO_FATAL_FAILURE(mGralloc->freeBuffer(importedBufferHandles[0])); |
| ASSERT_NO_FATAL_FAILURE(mGralloc->freeBuffer(importedBufferHandles[1])); |
| |
| ASSERT_NO_FATAL_FAILURE(mGralloc->freeBuffer(clonedBufferHandle)); |
| } |
| |
| /** |
| * Test IMapper::importBuffer and IMapper::freeBuffer cross mapper instances. |
| */ |
| TEST_F(GraphicsMapperHidlTest, ImportFreeBufferSingleton) { |
| const native_handle_t* rawHandle; |
| ASSERT_NO_FATAL_FAILURE(rawHandle = mGralloc->allocate(mDummyDescriptorInfo, false)); |
| |
| native_handle_t* importedHandle = nullptr; |
| mGralloc->getMapper()->importBuffer(rawHandle, [&](const auto& tmpError, const auto& buffer) { |
| ASSERT_EQ(Error::NONE, tmpError); |
| importedHandle = static_cast<native_handle_t*>(buffer); |
| }); |
| |
| // free the imported handle with another mapper |
| std::unique_ptr<Gralloc> anotherGralloc; |
| ASSERT_NO_FATAL_FAILURE( |
| anotherGralloc = std::make_unique<Gralloc>( |
| GraphicsMapperHidlEnvironment::Instance()->getServiceName<IAllocator>(), |
| GraphicsMapperHidlEnvironment::Instance()->getServiceName<IMapper>())); |
| Error error = mGralloc->getMapper()->freeBuffer(importedHandle); |
| ASSERT_EQ(Error::NONE, error); |
| |
| ASSERT_NO_FATAL_FAILURE(mGralloc->freeBuffer(rawHandle)); |
| } |
| |
| /** |
| * Test IMapper::importBuffer and IMapper::freeBuffer do not leak. |
| */ |
| TEST_F(GraphicsMapperHidlTest, ImportFreeBufferNoLeak) { |
| auto info = mDummyDescriptorInfo; |
| info.width = 1024; |
| info.height = 1024; |
| |
| for (int i = 0; i < 2048; i++) { |
| auto bufferHandle = mGralloc->allocate(info, true); |
| mGralloc->freeBuffer(bufferHandle); |
| } |
| } |
| |
| /** |
| * Test IMapper::importBuffer with invalid buffers. |
| */ |
| TEST_F(GraphicsMapperHidlTest, ImportBufferNegative) { |
| native_handle_t* invalidHandle = nullptr; |
| mGralloc->getMapper()->importBuffer(invalidHandle, [&](const auto& tmpError, const auto&) { |
| EXPECT_EQ(Error::BAD_BUFFER, tmpError) |
| << "importBuffer with nullptr did not fail with BAD_BUFFER"; |
| }); |
| |
| invalidHandle = native_handle_create(0, 0); |
| mGralloc->getMapper()->importBuffer(invalidHandle, [&](const auto& tmpError, const auto&) { |
| EXPECT_EQ(Error::BAD_BUFFER, tmpError) |
| << "importBuffer with invalid handle did not fail with BAD_BUFFER"; |
| }); |
| native_handle_delete(invalidHandle); |
| } |
| |
| /** |
| * Test IMapper::freeBuffer with invalid buffers. |
| */ |
| TEST_F(GraphicsMapperHidlTest, FreeBufferNegative) { |
| native_handle_t* invalidHandle = nullptr; |
| Error error = mGralloc->getMapper()->freeBuffer(invalidHandle); |
| EXPECT_EQ(Error::BAD_BUFFER, error) << "freeBuffer with nullptr did not fail with BAD_BUFFER"; |
| |
| invalidHandle = native_handle_create(0, 0); |
| error = mGralloc->getMapper()->freeBuffer(invalidHandle); |
| EXPECT_EQ(Error::BAD_BUFFER, error) |
| << "freeBuffer with invalid handle did not fail with BAD_BUFFER"; |
| native_handle_delete(invalidHandle); |
| |
| const native_handle_t* clonedBufferHandle; |
| ASSERT_NO_FATAL_FAILURE(clonedBufferHandle = mGralloc->allocate(mDummyDescriptorInfo, false)); |
| error = mGralloc->getMapper()->freeBuffer(invalidHandle); |
| EXPECT_EQ(Error::BAD_BUFFER, error) |
| << "freeBuffer with un-imported handle did not fail with BAD_BUFFER"; |
| |
| mGralloc->freeBuffer(clonedBufferHandle); |
| } |
| |
| /** |
| * Test IMapper::lock and IMapper::unlock. |
| */ |
| TEST_F(GraphicsMapperHidlTest, LockUnlockBasic) { |
| const auto& info = mDummyDescriptorInfo; |
| |
| const native_handle_t* bufferHandle; |
| uint32_t stride; |
| ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(info, true, &stride)); |
| |
| // lock buffer for writing |
| const IMapper::Rect region{0, 0, static_cast<int32_t>(info.width), |
| static_cast<int32_t>(info.height)}; |
| int fence = -1; |
| uint8_t* data; |
| int32_t bytesPerPixel = -1; |
| int32_t bytesPerStride = -1; |
| ASSERT_NO_FATAL_FAILURE( |
| data = static_cast<uint8_t*>(mGralloc->lock(bufferHandle, info.usage, region, fence, |
| &bytesPerPixel, &bytesPerStride))); |
| |
| // Valid return values are -1 for unsupported or the number bytes for supported which is >=0 |
| EXPECT_GT(bytesPerPixel, -1); |
| EXPECT_GT(bytesPerStride, -1); |
| |
| // RGBA_8888 |
| size_t strideInBytes = stride * 4; |
| size_t writeInBytes = info.width * 4; |
| |
| for (uint32_t y = 0; y < info.height; y++) { |
| memset(data, y, writeInBytes); |
| data += strideInBytes; |
| } |
| |
| ASSERT_NO_FATAL_FAILURE(fence = mGralloc->unlock(bufferHandle)); |
| |
| bytesPerPixel = -1; |
| bytesPerStride = -1; |
| |
| // lock again for reading |
| ASSERT_NO_FATAL_FAILURE( |
| data = static_cast<uint8_t*>(mGralloc->lock(bufferHandle, info.usage, region, fence, |
| &bytesPerPixel, &bytesPerStride))); |
| for (uint32_t y = 0; y < info.height; y++) { |
| for (size_t i = 0; i < writeInBytes; i++) { |
| EXPECT_EQ(static_cast<uint8_t>(y), data[i]); |
| } |
| data += strideInBytes; |
| } |
| |
| EXPECT_GT(bytesPerPixel, -1); |
| EXPECT_GT(bytesPerStride, -1); |
| |
| ASSERT_NO_FATAL_FAILURE(fence = mGralloc->unlock(bufferHandle)); |
| if (fence >= 0) { |
| close(fence); |
| } |
| } |
| |
| /** |
| * Test IMapper::lockYCbCr. This locks a YV12 buffer, and makes sure we can |
| * write to and read from it. |
| */ |
| TEST_F(GraphicsMapperHidlTest, LockYCbCrBasic) { |
| auto info = mDummyDescriptorInfo; |
| info.format = PixelFormat::YV12; |
| |
| const native_handle_t* bufferHandle; |
| uint32_t stride; |
| ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(info, true, &stride)); |
| |
| // lock buffer for writing |
| const IMapper::Rect region{0, 0, static_cast<int32_t>(info.width), |
| static_cast<int32_t>(info.height)}; |
| int fence = -1; |
| YCbCrLayout layout; |
| ASSERT_NO_FATAL_FAILURE(layout = mGralloc->lockYCbCr(bufferHandle, info.usage, region, fence)); |
| |
| auto yData = static_cast<uint8_t*>(layout.y); |
| auto cbData = static_cast<uint8_t*>(layout.cb); |
| auto crData = static_cast<uint8_t*>(layout.cr); |
| for (uint32_t y = 0; y < info.height; y++) { |
| for (uint32_t x = 0; x < info.width; x++) { |
| auto val = static_cast<uint8_t>(info.height * y + x); |
| |
| yData[layout.yStride * y + x] = val; |
| if (y % 2 == 0 && x % 2 == 0) { |
| cbData[layout.cStride * y / 2 + x / 2] = val; |
| crData[layout.cStride * y / 2 + x / 2] = val; |
| } |
| } |
| } |
| |
| ASSERT_NO_FATAL_FAILURE(fence = mGralloc->unlock(bufferHandle)); |
| |
| // lock again for reading |
| ASSERT_NO_FATAL_FAILURE(layout = mGralloc->lockYCbCr(bufferHandle, info.usage, region, fence)); |
| |
| yData = static_cast<uint8_t*>(layout.y); |
| cbData = static_cast<uint8_t*>(layout.cb); |
| crData = static_cast<uint8_t*>(layout.cr); |
| for (uint32_t y = 0; y < info.height; y++) { |
| for (uint32_t x = 0; x < info.width; x++) { |
| auto val = static_cast<uint8_t>(info.height * y + x); |
| |
| EXPECT_EQ(val, yData[layout.yStride * y + x]); |
| if (y % 2 == 0 && x % 2 == 0) { |
| EXPECT_EQ(val, cbData[layout.cStride * y / 2 + x / 2]); |
| EXPECT_EQ(val, crData[layout.cStride * y / 2 + x / 2]); |
| } |
| } |
| } |
| |
| ASSERT_NO_FATAL_FAILURE(fence = mGralloc->unlock(bufferHandle)); |
| if (fence >= 0) { |
| close(fence); |
| } |
| } |
| |
| /** |
| * Test IMapper::unlock with invalid buffers. |
| */ |
| TEST_F(GraphicsMapperHidlTest, UnlockNegative) { |
| native_handle_t* invalidHandle = nullptr; |
| mGralloc->getMapper()->unlock(invalidHandle, [&](const auto& tmpError, const auto&) { |
| EXPECT_EQ(Error::BAD_BUFFER, tmpError) |
| << "unlock with nullptr did not fail with BAD_BUFFER"; |
| }); |
| |
| invalidHandle = native_handle_create(0, 0); |
| mGralloc->getMapper()->unlock(invalidHandle, [&](const auto& tmpError, const auto&) { |
| EXPECT_EQ(Error::BAD_BUFFER, tmpError) |
| << "unlock with invalid handle did not fail with BAD_BUFFER"; |
| }); |
| native_handle_delete(invalidHandle); |
| |
| ASSERT_NO_FATAL_FAILURE(invalidHandle = const_cast<native_handle_t*>( |
| mGralloc->allocate(mDummyDescriptorInfo, false))); |
| mGralloc->getMapper()->unlock(invalidHandle, [&](const auto& tmpError, const auto&) { |
| EXPECT_EQ(Error::BAD_BUFFER, tmpError) |
| << "unlock with un-imported handle did not fail with BAD_BUFFER"; |
| }); |
| mGralloc->freeBuffer(invalidHandle); |
| |
| // disabled as it fails on many existing drivers |
| #if 0 |
| ASSERT_NO_FATAL_FAILURE(invalidHandle = const_cast<native_handle_t*>( |
| mGralloc->allocate(mDummyDescriptorInfo, true))); |
| mGralloc->getMapper()->unlock( |
| invalidHandle, [&](const auto& tmpError, const auto&) { |
| EXPECT_EQ(Error::BAD_BUFFER, tmpError) |
| << "unlock with unlocked handle did not fail with BAD_BUFFER"; |
| }); |
| mGralloc->freeBuffer(invalidHandle); |
| #endif |
| } |
| |
| /** |
| * Test IMapper::isSupported with required format RGBA_8888 |
| */ |
| TEST_F(GraphicsMapperHidlTest, IsSupportedRGBA8888) { |
| const auto& info = mDummyDescriptorInfo; |
| bool supported = false; |
| |
| ASSERT_NO_FATAL_FAILURE(supported = mGralloc->isSupported(info)); |
| ASSERT_TRUE(supported); |
| } |
| |
| /** |
| * Test IMapper::isSupported with required format YV12 |
| */ |
| TEST_F(GraphicsMapperHidlTest, IsSupportedYV12) { |
| auto info = mDummyDescriptorInfo; |
| info.format = PixelFormat::YV12; |
| bool supported = false; |
| |
| ASSERT_NO_FATAL_FAILURE(supported = mGralloc->isSupported(info)); |
| ASSERT_TRUE(supported); |
| } |
| |
| /** |
| * Test IMapper::isSupported with optional format Y16 |
| */ |
| TEST_F(GraphicsMapperHidlTest, IsSupportedY16) { |
| auto info = mDummyDescriptorInfo; |
| info.format = PixelFormat::Y16; |
| bool supported = false; |
| |
| ASSERT_NO_FATAL_FAILURE(supported = mGralloc->isSupported(info)); |
| } |
| |
| } // namespace |
| } // namespace vts |
| } // namespace V3_0 |
| } // namespace mapper |
| } // namespace graphics |
| } // namespace hardware |
| } // namespace android |
| |
| int main(int argc, char** argv) { |
| using android::hardware::graphics::mapper::V3_0::vts::GraphicsMapperHidlEnvironment; |
| ::testing::AddGlobalTestEnvironment(GraphicsMapperHidlEnvironment::Instance()); |
| ::testing::InitGoogleTest(&argc, argv); |
| GraphicsMapperHidlEnvironment::Instance()->init(&argc, argv); |
| int status = RUN_ALL_TESTS(); |
| LOG(INFO) << "Test result = " << status; |
| return status; |
| } |