diff --git a/media/bufferpool/2.0/tests/Android.bp b/media/bufferpool/2.0/tests/Android.bp new file mode 100644 index 0000000000..8b44f617cc --- /dev/null +++ b/media/bufferpool/2.0/tests/Android.bp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 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. + */ + +cc_test { + name: "VtsVndkHidlBufferpoolV2_0TargetSingleTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: [ + "allocator.cpp", + "single.cpp", + ], + static_libs: [ + "android.hardware.media.bufferpool@2.0", + "libcutils", + "libstagefright_bufferpool@2.0", + ], + shared_libs: [ + "libfmq", + ], + compile_multilib: "both", +} + +cc_test { + name: "VtsVndkHidlBufferpoolV2_0TargetMultiTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: [ + "allocator.cpp", + "multi.cpp", + ], + static_libs: [ + "android.hardware.media.bufferpool@2.0", + "libcutils", + "libstagefright_bufferpool@2.0", + ], + shared_libs: [ + "libfmq", + ], + compile_multilib: "both", +} diff --git a/media/bufferpool/2.0/tests/OWNERS b/media/bufferpool/2.0/tests/OWNERS new file mode 100644 index 0000000000..6733e0cbd1 --- /dev/null +++ b/media/bufferpool/2.0/tests/OWNERS @@ -0,0 +1,9 @@ +# Media team +lajos@google.com +pawin@google.com +taklee@google.com +wonsik@google.com + +# VTS team +yim@google.com +zhuoyao@google.com diff --git a/media/bufferpool/2.0/tests/allocator.cpp b/media/bufferpool/2.0/tests/allocator.cpp new file mode 100644 index 0000000000..78f7edbd01 --- /dev/null +++ b/media/bufferpool/2.0/tests/allocator.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (C) 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. + */ + +#include +#include "allocator.h" + +union Params { + struct { + uint32_t capacity; + } data; + uint8_t array[0]; + Params() : data{0} {} + Params(uint32_t size) + : data{size} {} +}; + + +namespace { + +struct HandleAshmem : public native_handle_t { + HandleAshmem(int ashmemFd, size_t size) + : native_handle_t(cHeader), + mFds{ ashmemFd }, + mInts{ int (size & 0xFFFFFFFF), int((uint64_t(size) >> 32) & 0xFFFFFFFF), kMagic } {} + + int ashmemFd() const { return mFds.mAshmem; } + size_t size() const { + return size_t(unsigned(mInts.mSizeLo)) + | size_t(uint64_t(unsigned(mInts.mSizeHi)) << 32); + } + +protected: + struct { + int mAshmem; + } mFds; + struct { + int mSizeLo; + int mSizeHi; + int mMagic; + } mInts; + +private: + enum { + kMagic = 'ahm\x00', + numFds = sizeof(mFds) / sizeof(int), + numInts = sizeof(mInts) / sizeof(int), + version = sizeof(native_handle_t) + }; + const static native_handle_t cHeader; +}; + +const native_handle_t HandleAshmem::cHeader = { + HandleAshmem::version, + HandleAshmem::numFds, + HandleAshmem::numInts, + {} +}; + +class AllocationAshmem { +private: + AllocationAshmem(int ashmemFd, size_t capacity, bool res) + : mHandle(ashmemFd, capacity), + mInit(res) {} + +public: + static AllocationAshmem *Alloc(size_t size) { + constexpr static const char *kAllocationTag = "bufferpool_test"; + int ashmemFd = ashmem_create_region(kAllocationTag, size); + return new AllocationAshmem(ashmemFd, size, ashmemFd >= 0); + } + + ~AllocationAshmem() { + if (mInit) { + native_handle_close(&mHandle); + } + } + + const HandleAshmem *handle() { + return &mHandle; + } + +private: + HandleAshmem mHandle; + bool mInit; + // TODO: mapping and map fd +}; + +struct AllocationDtor { + AllocationDtor(const std::shared_ptr &alloc) + : mAlloc(alloc) {} + + void operator()(BufferPoolAllocation *poolAlloc) { delete poolAlloc; } + + const std::shared_ptr mAlloc; +}; + +} + + +ResultStatus TestBufferPoolAllocator::allocate( + const std::vector ¶ms, + std::shared_ptr *alloc, + size_t *allocSize) { + Params ashmemParams; + memcpy(&ashmemParams, params.data(), std::min(sizeof(Params), params.size())); + + std::shared_ptr ashmemAlloc = + std::shared_ptr( + AllocationAshmem::Alloc(ashmemParams.data.capacity)); + if (ashmemAlloc) { + BufferPoolAllocation *ptr = new BufferPoolAllocation(ashmemAlloc->handle()); + if (ptr) { + *alloc = std::shared_ptr(ptr, AllocationDtor(ashmemAlloc)); + if (*alloc) { + *allocSize = ashmemParams.data.capacity; + return ResultStatus::OK; + } + delete ptr; + return ResultStatus::NO_MEMORY; + } + } + return ResultStatus::CRITICAL_ERROR; +} + +bool TestBufferPoolAllocator::compatible(const std::vector &newParams, + const std::vector &oldParams) { + size_t newSize = newParams.size(); + size_t oldSize = oldParams.size(); + if (newSize == oldSize) { + for (size_t i = 0; i < newSize; ++i) { + if (newParams[i] != oldParams[i]) { + return false; + } + } + return true; + } + return false; +} + +void getTestAllocatorParams(std::vector *params) { + constexpr static int kAllocationSize = 1024 * 10; + Params ashmemParams(kAllocationSize); + + params->assign(ashmemParams.array, ashmemParams.array + sizeof(ashmemParams)); +} diff --git a/media/bufferpool/2.0/tests/allocator.h b/media/bufferpool/2.0/tests/allocator.h new file mode 100644 index 0000000000..216944ed0f --- /dev/null +++ b/media/bufferpool/2.0/tests/allocator.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 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. + */ + +#ifndef VNDK_HIDL_BUFFERPOOL_V2_0_ALLOCATOR_H +#define VNDK_HIDL_BUFFERPOOL_V2_0_ALLOCATOR_H + +#include + +using android::hardware::media::bufferpool::V2_0::ResultStatus; +using android::hardware::media::bufferpool::V2_0::implementation:: + BufferPoolAllocation; +using android::hardware::media::bufferpool::V2_0::implementation:: + BufferPoolAllocator; + +// buffer allocator for the tests +class TestBufferPoolAllocator : public BufferPoolAllocator { + public: + TestBufferPoolAllocator() {} + + ~TestBufferPoolAllocator() override {} + + ResultStatus allocate(const std::vector ¶ms, + std::shared_ptr *alloc, + size_t *allocSize) override; + + bool compatible(const std::vector &newParams, + const std::vector &oldParams) override; + +}; + +// retrieve buffer allocator paramters +void getTestAllocatorParams(std::vector *params); + +#endif // VNDK_HIDL_BUFFERPOOL_V2_0_ALLOCATOR_H diff --git a/media/bufferpool/2.0/tests/multi.cpp b/media/bufferpool/2.0/tests/multi.cpp new file mode 100644 index 0000000000..220808722c --- /dev/null +++ b/media/bufferpool/2.0/tests/multi.cpp @@ -0,0 +1,219 @@ +/* + * Copyright (C) 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 "buffferpool_unit_test" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "allocator.h" + +using android::hardware::configureRpcThreadpool; +using android::hardware::hidl_handle; +using android::hardware::media::bufferpool::V2_0::IClientManager; +using android::hardware::media::bufferpool::V2_0::ResultStatus; +using android::hardware::media::bufferpool::V2_0::implementation::BufferId; +using android::hardware::media::bufferpool::V2_0::implementation::ClientManager; +using android::hardware::media::bufferpool::V2_0::implementation::ConnectionId; +using android::hardware::media::bufferpool::V2_0::implementation::TransactionId; +using android::hardware::media::bufferpool::BufferPoolData; + +namespace { + +// communication message types between processes. +enum PipeCommand : int32_t { + INIT_OK = 0, + INIT_ERROR, + SEND, + RECEIVE_OK, + RECEIVE_ERROR, +}; + +// communication message between processes. +union PipeMessage { + struct { + int32_t command; + BufferId bufferId; + ConnectionId connectionId; + TransactionId transactionId; + int64_t timestampUs; + } data; + char array[0]; +}; + +// media.bufferpool test setup +class BufferpoolMultiTest : public ::testing::Test { + public: + virtual void SetUp() override { + ResultStatus status; + mReceiverPid = -1; + mConnectionValid = false; + + ASSERT_TRUE(pipe(mCommandPipeFds) == 0); + ASSERT_TRUE(pipe(mResultPipeFds) == 0); + + mReceiverPid = fork(); + ASSERT_TRUE(mReceiverPid >= 0); + + if (mReceiverPid == 0) { + doReceiver(); + // In order to ignore gtest behaviour, wait for being killed from + // tearDown + pause(); + } + + mManager = ClientManager::getInstance(); + ASSERT_NE(mManager, nullptr); + + mAllocator = std::make_shared(); + ASSERT_TRUE((bool)mAllocator); + + status = mManager->create(mAllocator, &mConnectionId); + ASSERT_TRUE(status == ResultStatus::OK); + mConnectionValid = true; + } + + virtual void TearDown() override { + if (mReceiverPid > 0) { + kill(mReceiverPid, SIGKILL); + int wstatus; + wait(&wstatus); + } + + if (mConnectionValid) { + mManager->close(mConnectionId); + } + } + + protected: + static void description(const std::string& description) { + RecordProperty("description", description); + } + + android::sp mManager; + std::shared_ptr mAllocator; + bool mConnectionValid; + ConnectionId mConnectionId; + pid_t mReceiverPid; + int mCommandPipeFds[2]; + int mResultPipeFds[2]; + + bool sendMessage(int *pipes, const PipeMessage &message) { + int ret = write(pipes[1], message.array, sizeof(PipeMessage)); + return ret == sizeof(PipeMessage); + } + + bool receiveMessage(int *pipes, PipeMessage *message) { + int ret = read(pipes[0], message->array, sizeof(PipeMessage)); + return ret == sizeof(PipeMessage); + } + + void doReceiver() { + configureRpcThreadpool(1, false); + PipeMessage message; + mManager = ClientManager::getInstance(); + if (!mManager) { + message.data.command = PipeCommand::INIT_ERROR; + sendMessage(mResultPipeFds, message); + return; + } + android::status_t status = mManager->registerAsService(); + if (status != android::OK) { + message.data.command = PipeCommand::INIT_ERROR; + sendMessage(mResultPipeFds, message); + return; + } + message.data.command = PipeCommand::INIT_OK; + sendMessage(mResultPipeFds, message); + + receiveMessage(mCommandPipeFds, &message); + { + native_handle_t *rhandle = nullptr; + std::shared_ptr rbuffer; + ResultStatus status = mManager->receive( + message.data.connectionId, message.data.transactionId, + message.data.bufferId, message.data.timestampUs, &rhandle, &rbuffer); + mManager->close(message.data.connectionId); + if (status != ResultStatus::OK) { + message.data.command = PipeCommand::RECEIVE_ERROR; + sendMessage(mResultPipeFds, message); + return; + } + } + message.data.command = PipeCommand::RECEIVE_OK; + sendMessage(mResultPipeFds, message); + } +}; + +// Buffer transfer test between processes. +TEST_F(BufferpoolMultiTest, TransferBuffer) { + ResultStatus status; + PipeMessage message; + + ASSERT_TRUE(receiveMessage(mResultPipeFds, &message)); + + android::sp receiver = IClientManager::getService(); + ConnectionId receiverId; + ASSERT_TRUE((bool)receiver); + + status = mManager->registerSender(receiver, mConnectionId, &receiverId); + ASSERT_TRUE(status == ResultStatus::OK); + { + native_handle_t *shandle = nullptr; + std::shared_ptr sbuffer; + TransactionId transactionId; + int64_t postUs; + std::vector vecParams; + + getTestAllocatorParams(&vecParams); + status = mManager->allocate(mConnectionId, vecParams, &shandle, &sbuffer); + ASSERT_TRUE(status == ResultStatus::OK); + + status = mManager->postSend(receiverId, sbuffer, &transactionId, &postUs); + ASSERT_TRUE(status == ResultStatus::OK); + + message.data.command = PipeCommand::SEND; + message.data.bufferId = sbuffer->mId; + message.data.connectionId = receiverId; + message.data.transactionId = transactionId; + message.data.timestampUs = postUs; + sendMessage(mCommandPipeFds, message); + } + EXPECT_TRUE(receiveMessage(mResultPipeFds, &message)); +} + +} // anonymous namespace + +int main(int argc, char** argv) { + setenv("TREBLE_TESTING_OVERRIDE", "true", true); + ::testing::InitGoogleTest(&argc, argv); + int status = RUN_ALL_TESTS(); + LOG(INFO) << "Test result = " << status; + return status; +} diff --git a/media/bufferpool/2.0/tests/single.cpp b/media/bufferpool/2.0/tests/single.cpp new file mode 100644 index 0000000000..5c6d1c7e95 --- /dev/null +++ b/media/bufferpool/2.0/tests/single.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (C) 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 "buffferpool_unit_test" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "allocator.h" + +using android::hardware::hidl_handle; +using android::hardware::media::bufferpool::V2_0::ResultStatus; +using android::hardware::media::bufferpool::V2_0::implementation::BufferId; +using android::hardware::media::bufferpool::V2_0::implementation::ClientManager; +using android::hardware::media::bufferpool::V2_0::implementation::ConnectionId; +using android::hardware::media::bufferpool::V2_0::implementation::TransactionId; +using android::hardware::media::bufferpool::BufferPoolData; + +namespace { + +// Number of iteration for buffer allocation test. +constexpr static int kNumAllocationTest = 3; + +// Number of iteration for buffer recycling test. +constexpr static int kNumRecycleTest = 3; + +// media.bufferpool test setup +class BufferpoolSingleTest : public ::testing::Test { + public: + virtual void SetUp() override { + ResultStatus status; + mConnectionValid = false; + + mManager = ClientManager::getInstance(); + ASSERT_NE(mManager, nullptr); + + mAllocator = std::make_shared(); + ASSERT_TRUE((bool)mAllocator); + + status = mManager->create(mAllocator, &mConnectionId); + ASSERT_TRUE(status == ResultStatus::OK); + + mConnectionValid = true; + + status = mManager->registerSender(mManager, mConnectionId, &mReceiverId); + ASSERT_TRUE(status == ResultStatus::ALREADY_EXISTS && + mReceiverId == mConnectionId); + } + + virtual void TearDown() override { + if (mConnectionValid) { + mManager->close(mConnectionId); + } + } + + protected: + static void description(const std::string& description) { + RecordProperty("description", description); + } + + android::sp mManager; + std::shared_ptr mAllocator; + bool mConnectionValid; + ConnectionId mConnectionId; + ConnectionId mReceiverId; + +}; + +// Buffer allocation test. +// Check whether each buffer allocation is done successfully with +// unique buffer id. +TEST_F(BufferpoolSingleTest, AllocateBuffer) { + ResultStatus status; + std::vector vecParams; + getTestAllocatorParams(&vecParams); + + std::shared_ptr buffer[kNumAllocationTest]; + native_handle_t *allocHandle = nullptr; + for (int i = 0; i < kNumAllocationTest; ++i) { + status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &buffer[i]); + ASSERT_TRUE(status == ResultStatus::OK); + } + for (int i = 0; i < kNumAllocationTest; ++i) { + for (int j = i + 1; j < kNumAllocationTest; ++j) { + ASSERT_TRUE(buffer[i]->mId != buffer[j]->mId); + } + } + EXPECT_TRUE(kNumAllocationTest > 1); +} + +// Buffer recycle test. +// Check whether de-allocated buffers are recycled. +TEST_F(BufferpoolSingleTest, RecycleBuffer) { + ResultStatus status; + std::vector vecParams; + getTestAllocatorParams(&vecParams); + + BufferId bid[kNumRecycleTest]; + for (int i = 0; i < kNumRecycleTest; ++i) { + std::shared_ptr buffer; + native_handle_t *allocHandle = nullptr; + status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &buffer); + ASSERT_TRUE(status == ResultStatus::OK); + bid[i] = buffer->mId; + } + for (int i = 1; i < kNumRecycleTest; ++i) { + ASSERT_TRUE(bid[i - 1] == bid[i]); + } + EXPECT_TRUE(kNumRecycleTest > 1); +} + +// Buffer transfer test. +// Check whether buffer is transferred to another client successfully. +TEST_F(BufferpoolSingleTest, TransferBuffer) { + ResultStatus status; + std::vector vecParams; + getTestAllocatorParams(&vecParams); + std::shared_ptr sbuffer, rbuffer; + native_handle_t *allocHandle = nullptr; + native_handle_t *recvHandle = nullptr; + + TransactionId transactionId; + int64_t postUs; + + status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &sbuffer); + ASSERT_TRUE(status == ResultStatus::OK); + status = mManager->postSend(mReceiverId, sbuffer, &transactionId, &postUs); + ASSERT_TRUE(status == ResultStatus::OK); + status = mManager->receive(mReceiverId, transactionId, sbuffer->mId, postUs, + &recvHandle, &rbuffer); + EXPECT_TRUE(status == ResultStatus::OK); +} + +} // anonymous namespace + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + int status = RUN_ALL_TESTS(); + LOG(INFO) << "Test result = " << status; + return status; +}