Bufferpool: add BufferpoolUnitTest
Bug: 202748986
Test: atest BufferpoolUnitTest
Change-Id: I883e74b025f653f0a566baa28259542db8464892
diff --git a/media/bufferpool/2.0/tests/Android.bp b/media/bufferpool/2.0/tests/Android.bp
index 803a813..5e26e3a 100644
--- a/media/bufferpool/2.0/tests/Android.bp
+++ b/media/bufferpool/2.0/tests/Android.bp
@@ -80,3 +80,22 @@
],
compile_multilib: "both",
}
+
+cc_test {
+ name: "BufferpoolUnitTest",
+ test_suites: ["device-tests"],
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: [
+ "allocator.cpp",
+ "BufferpoolUnitTest.cpp",
+ ],
+ static_libs: [
+ "android.hardware.media.bufferpool@2.0",
+ "libcutils",
+ "libstagefright_bufferpool@2.0.1",
+ ],
+ shared_libs: [
+ "libfmq",
+ ],
+ compile_multilib: "both",
+}
diff --git a/media/bufferpool/2.0/tests/AndroidTest.xml b/media/bufferpool/2.0/tests/AndroidTest.xml
new file mode 100644
index 0000000..b027ad0
--- /dev/null
+++ b/media/bufferpool/2.0/tests/AndroidTest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+<configuration description="Test module config for bufferpool unit tests">
+ <option name="test-suite-tag" value="BufferpoolUnitTest" />
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="BufferpoolUnitTest" />
+ </test>
+</configuration>
diff --git a/media/bufferpool/2.0/tests/BufferpoolUnitTest.cpp b/media/bufferpool/2.0/tests/BufferpoolUnitTest.cpp
new file mode 100644
index 0000000..d9bc2ce
--- /dev/null
+++ b/media/bufferpool/2.0/tests/BufferpoolUnitTest.cpp
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2021 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_NDEBUG 0
+#define LOG_TAG "BufferpoolUnitTest"
+#include <utils/Log.h>
+
+#include <binder/ProcessState.h>
+#include <bufferpool/ClientManager.h>
+#include <gtest/gtest.h>
+#include <hidl/LegacySupport.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unordered_set>
+#include <vector>
+#include "allocator.h"
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::media::bufferpool::BufferPoolData;
+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 namespace android;
+
+// communication message types between processes.
+enum PipeCommand : int32_t {
+ INIT,
+ TRANSFER,
+ STOP,
+
+ INIT_OK,
+ INIT_ERROR,
+ TRANSFER_OK,
+ TRANSFER_ERROR,
+ STOP_OK,
+ STOP_ERROR,
+};
+
+// communication message between processes.
+union PipeMessage {
+ struct {
+ int32_t command;
+ int32_t memsetValue;
+ BufferId bufferId;
+ ConnectionId connectionId;
+ TransactionId transactionId;
+ int64_t timestampUs;
+ } data;
+ char array[0];
+};
+
+static int32_t kNumIterationCount = 10;
+
+class BufferpoolTest {
+ public:
+ BufferpoolTest() : mConnectionValid(false), mManager(nullptr), mAllocator(nullptr) {
+ mConnectionId = -1;
+ }
+
+ ~BufferpoolTest() {
+ if (mConnectionValid) {
+ mManager->close(mConnectionId);
+ }
+ }
+
+ protected:
+ bool mConnectionValid;
+ ConnectionId mConnectionId;
+
+ android::sp<ClientManager> mManager;
+ std::shared_ptr<BufferPoolAllocator> mAllocator;
+
+ void setupBufferpoolManager();
+};
+
+void BufferpoolTest::setupBufferpoolManager() {
+ // retrieving per process bufferpool object sp<ClientManager>
+ mManager = ClientManager::getInstance();
+ ASSERT_NE(mManager, nullptr) << "unable to get ClientManager\n";
+
+ mAllocator = std::make_shared<TestBufferPoolAllocator>();
+ ASSERT_NE(mAllocator, nullptr) << "unable to create TestBufferPoolAllocator\n";
+
+ // set-up local bufferpool connection for sender
+ ResultStatus status = mManager->create(mAllocator, &mConnectionId);
+ ASSERT_EQ(status, ResultStatus::OK)
+ << "unable to set-up local bufferpool connection for sender\n";
+ mConnectionValid = true;
+}
+
+class BufferpoolUnitTest : public BufferpoolTest, public ::testing::Test {
+ public:
+ virtual void SetUp() override { setupBufferpoolManager(); }
+
+ virtual void TearDown() override {}
+};
+
+class BufferpoolFunctionalityTest : public BufferpoolTest, public ::testing::Test {
+ public:
+ virtual void SetUp() override {
+ mReceiverPid = -1;
+
+ ASSERT_TRUE(pipe(mCommandPipeFds) == 0) << "pipe connection failed for commandPipe\n";
+ ASSERT_TRUE(pipe(mResultPipeFds) == 0) << "pipe connection failed for resultPipe\n";
+
+ mReceiverPid = fork();
+ ASSERT_TRUE(mReceiverPid >= 0) << "fork failed\n";
+
+ if (mReceiverPid == 0) {
+ doReceiver();
+ // In order to ignore gtest behaviour, wait for being killed from tearDown
+ pause();
+ }
+ setupBufferpoolManager();
+ }
+
+ virtual void TearDown() override {
+ if (mReceiverPid > 0) {
+ kill(mReceiverPid, SIGKILL);
+ int wstatus;
+ wait(&wstatus);
+ }
+ }
+
+ protected:
+ 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();
+};
+
+void BufferpoolFunctionalityTest::doReceiver() {
+ // Configures the threadpool used for handling incoming RPC calls in this process.
+ configureRpcThreadpool(1 /*threads*/, false /*willJoin*/);
+ bool receiverRunning = true;
+ while (receiverRunning) {
+ PipeMessage message;
+ receiveMessage(mCommandPipeFds, &message);
+ ResultStatus err = ResultStatus::OK;
+ switch (message.data.command) {
+ case PipeCommand::INIT: {
+ // receiver manager creation
+ 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);
+ break;
+ }
+ case PipeCommand::TRANSFER: {
+ native_handle_t* receiveHandle = nullptr;
+ std::shared_ptr<BufferPoolData> receiveBuffer;
+ err = mManager->receive(message.data.connectionId, message.data.transactionId,
+ message.data.bufferId, message.data.timestampUs,
+ &receiveHandle, &receiveBuffer);
+ if (err != ResultStatus::OK) {
+ message.data.command = PipeCommand::TRANSFER_ERROR;
+ sendMessage(mResultPipeFds, message);
+ return;
+ }
+ if (!TestBufferPoolAllocator::Verify(receiveHandle, message.data.memsetValue)) {
+ message.data.command = PipeCommand::TRANSFER_ERROR;
+ sendMessage(mResultPipeFds, message);
+ return;
+ }
+ if (receiveHandle) {
+ native_handle_close(receiveHandle);
+ native_handle_delete(receiveHandle);
+ }
+ receiveHandle = nullptr;
+ receiveBuffer.reset();
+ message.data.command = PipeCommand::TRANSFER_OK;
+ sendMessage(mResultPipeFds, message);
+ break;
+ }
+ case PipeCommand::STOP: {
+ err = mManager->close(message.data.connectionId);
+ if (err != ResultStatus::OK) {
+ message.data.command = PipeCommand::STOP_ERROR;
+ sendMessage(mResultPipeFds, message);
+ return;
+ }
+ message.data.command = PipeCommand::STOP_OK;
+ sendMessage(mResultPipeFds, message);
+ receiverRunning = false;
+ break;
+ }
+ default:
+ ALOGE("unknown command. try again");
+ break;
+ }
+ }
+}
+
+// Buffer allocation test.
+// Check whether each buffer allocation is done successfully with unique buffer id.
+TEST_F(BufferpoolUnitTest, AllocateBuffer) {
+ std::vector<uint8_t> vecParams;
+ getTestAllocatorParams(&vecParams);
+
+ std::vector<std::shared_ptr<BufferPoolData>> buffers{};
+ std::vector<native_handle_t*> allocHandle{};
+ ResultStatus status;
+ for (int i = 0; i < kNumIterationCount; ++i) {
+ native_handle_t* handle = nullptr;
+ std::shared_ptr<BufferPoolData> buffer{};
+ status = mManager->allocate(mConnectionId, vecParams, &handle, &buffer);
+ ASSERT_EQ(status, ResultStatus::OK) << "allocate failed for " << i << "iteration";
+
+ buffers.push_back(std::move(buffer));
+ if (handle) {
+ allocHandle.push_back(std::move(handle));
+ }
+ }
+
+ for (int i = 0; i < kNumIterationCount; ++i) {
+ for (int j = i + 1; j < kNumIterationCount; ++j) {
+ ASSERT_TRUE(buffers[i]->mId != buffers[j]->mId) << "allocated buffers are not unique";
+ }
+ }
+ // delete the buffer handles
+ for (auto handle : allocHandle) {
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+ // clear the vectors
+ buffers.clear();
+ allocHandle.clear();
+}
+
+// Buffer recycle test.
+// Check whether de-allocated buffers are recycled.
+TEST_F(BufferpoolUnitTest, RecycleBuffer) {
+ std::vector<uint8_t> vecParams;
+ getTestAllocatorParams(&vecParams);
+
+ ResultStatus status;
+ std::vector<BufferId> bid{};
+ std::vector<native_handle_t*> allocHandle{};
+ for (int i = 0; i < kNumIterationCount; ++i) {
+ native_handle_t* handle = nullptr;
+ std::shared_ptr<BufferPoolData> buffer;
+ status = mManager->allocate(mConnectionId, vecParams, &handle, &buffer);
+ ASSERT_EQ(status, ResultStatus::OK) << "allocate failed for " << i << "iteration";
+
+ bid.push_back(buffer->mId);
+ if (handle) {
+ allocHandle.push_back(std::move(handle));
+ }
+ buffer.reset();
+ }
+
+ std::unordered_set<BufferId> set(bid.begin(), bid.end());
+ ASSERT_EQ(set.size(), 1) << "buffers are not recycled properly";
+
+ // delete the buffer handles
+ for (auto handle : allocHandle) {
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+ allocHandle.clear();
+}
+
+// Buffer transfer test between processes.
+TEST_F(BufferpoolFunctionalityTest, TransferBuffer) {
+ // initialize the receiver
+ PipeMessage message;
+ message.data.command = PipeCommand::INIT;
+ sendMessage(mCommandPipeFds, message);
+ ASSERT_TRUE(receiveMessage(mResultPipeFds, &message)) << "receiveMessage failed\n";
+ ASSERT_EQ(message.data.command, PipeCommand::INIT_OK) << "receiver init failed";
+
+ android::sp<IClientManager> receiver = IClientManager::getService();
+ ASSERT_NE(receiver, nullptr) << "getService failed for receiver\n";
+
+ ConnectionId receiverId;
+ ResultStatus status = mManager->registerSender(receiver, mConnectionId, &receiverId);
+ ASSERT_EQ(status, ResultStatus::OK)
+ << "registerSender failed for connection id " << mConnectionId << "\n";
+
+ std::vector<uint8_t> vecParams;
+ getTestAllocatorParams(&vecParams);
+
+ for (int i = 0; i < kNumIterationCount; ++i) {
+ native_handle_t* handle = nullptr;
+ std::shared_ptr<BufferPoolData> buffer;
+ status = mManager->allocate(mConnectionId, vecParams, &handle, &buffer);
+ ASSERT_EQ(status, ResultStatus::OK) << "allocate failed for " << i << "iteration";
+
+ ASSERT_TRUE(TestBufferPoolAllocator::Fill(handle, i))
+ << "Fill fail for buffer handle " << handle << "\n";
+
+ // send the buffer to the receiver
+ int64_t postUs;
+ TransactionId transactionId;
+ status = mManager->postSend(receiverId, buffer, &transactionId, &postUs);
+ ASSERT_EQ(status, ResultStatus::OK)
+ << "postSend failed for receiver " << receiverId << "\n";
+
+ // PipeMessage message;
+ message.data.command = PipeCommand::TRANSFER;
+ message.data.memsetValue = i;
+ message.data.bufferId = buffer->mId;
+ message.data.connectionId = receiverId;
+ message.data.transactionId = transactionId;
+ message.data.timestampUs = postUs;
+ sendMessage(mCommandPipeFds, message);
+ // delete buffer handle
+ if (handle) {
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+ ASSERT_TRUE(receiveMessage(mResultPipeFds, &message)) << "receiveMessage failed\n";
+ ASSERT_EQ(message.data.command, PipeCommand::TRANSFER_OK)
+ << "received error during buffer transfer\n";
+ }
+ message.data.command = PipeCommand::STOP;
+ sendMessage(mCommandPipeFds, message);
+ ASSERT_TRUE(receiveMessage(mResultPipeFds, &message)) << "receiveMessage failed\n";
+ ASSERT_EQ(message.data.command, PipeCommand::STOP_OK)
+ << "received error during buffer transfer\n";
+}
+
+int main(int argc, char** argv) {
+ android::hardware::details::setTrebleTestingOverride(true);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ ALOGV("Test result = %d\n", status);
+ return status;
+}
diff --git a/media/bufferpool/2.0/tests/README.md b/media/bufferpool/2.0/tests/README.md
new file mode 100644
index 0000000..5efd966
--- /dev/null
+++ b/media/bufferpool/2.0/tests/README.md
@@ -0,0 +1,33 @@
+## Media Testing ##
+---
+#### Bufferpool :
+The Bufferpool Test Suite validates bufferpool library in android.
+
+Run the following steps to build the test suite:
+```
+m BufferpoolUnitTest
+```
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+```
+adb push ${OUT}/data/nativetest64/BufferpoolUnitTest/BufferpoolUnitTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/BufferpoolUnitTest/BufferpoolUnitTest /data/local/tmp/
+```
+
+usage: BufferpoolUnitTest
+```
+adb shell /data/local/tmp/BufferpoolUnitTest
+```
+Alternatively, the test can also be run using atest command.
+
+```
+atest BufferpoolUnitTest
+```