aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPierre Langlois <pierre.langlois@arm.com>2015-01-15 10:54:13 +0000
committerPierre Langlois <pierre.langlois@arm.com>2016-03-04 18:01:18 +0000
commitb9b37c4bbed6c388ccca4ef8048d0a428b12f16f (patch)
treed95d6f954e79bc65547acc6ff5d0a116d2b9f0d7
parentc85602db731ce1d0de9289b5d8a2af51d74f0916 (diff)
downloadkdbinder-b9b37c4bbed6c388ccca4ef8048d0a428b12f16f.tar.gz
libkdbinder: add a test suite for the Binder API
This test suite is greatly inspired by the `binderLibTest` test suite in libbinder. It creates a server in a seperate process in the background and runs the test as a client in the foreground. Change-Id: I50d9e412f2749b6166f4053c69c4a91194684545
-rw-r--r--libs/kdbinder/tests/Android.mk16
-rw-r--r--libs/kdbinder/tests/kdbinderTest.cpp324
2 files changed, 340 insertions, 0 deletions
diff --git a/libs/kdbinder/tests/Android.mk b/libs/kdbinder/tests/Android.mk
index 587a08f..a8b6331 100644
--- a/libs/kdbinder/tests/Android.mk
+++ b/libs/kdbinder/tests/Android.mk
@@ -13,6 +13,8 @@
# limitations under the License.
#
+kdbinderTest_sources := kdbinderTest.cpp
+
kdbinderKDBUSTest_sources := \
kdbus/bus.cpp \
kdbus/connection.cpp
@@ -21,6 +23,20 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+LOCAL_MODULE := kdbinderTest
+LOCAL_MODULE_TAGS := eng
+LOCAL_SRC_FILES := $(kdbinderTest_sources)
+LOCAL_SHARED_LIBRARIES := libkdbinderd libutils
+LOCAL_C_INCLUDES += \
+ external/gtest/include \
+ frameworks/native/include/kdbinder
+LOCAL_CFLAGS += $(kdbinder_cflags) $(kdbinder_cflags_debug)
+include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
LOCAL_MODULE := kdbinderKDBUSTest
LOCAL_MODULE_TAGS := eng
LOCAL_SRC_FILES := $(kdbinderKDBUSTest_sources)
diff --git a/libs/kdbinder/tests/kdbinderTest.cpp b/libs/kdbinder/tests/kdbinderTest.cpp
new file mode 100644
index 0000000..b4864f2
--- /dev/null
+++ b/libs/kdbinder/tests/kdbinderTest.cpp
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2015 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 <gtest/gtest.h>
+
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+#include <binder/ProcessState.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+#include <pthread.h>
+
+#include <utils/String16.h>
+#include <utils/String8.h>
+
+#include <vector>
+#include <string>
+#include <thread>
+#include <future>
+
+using namespace android; // NOLINT
+
+static testing::Environment *kdbinder_env;
+static String16 kdbinderTestServiceName = String16("test.kdbinder");
+static char *kdbinderservername;
+
+int start_server(String16 name);
+
+class IKDBinderTestService : public IInterface {
+ public:
+ DECLARE_META_INTERFACE(KDBinderTestService);
+
+ enum {
+ PLUS_ONE = 0x120
+ };
+
+ virtual uint32_t plusOne(uint32_t in) = 0;
+};
+
+class BnKDBinderTestService : public BnInterface<IKDBinderTestService> {
+ public:
+ BnKDBinderTestService() : BnInterface<IKDBinderTestService>() {}
+ virtual ~BnKDBinderTestService() {}
+
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t /*flags = 0*/) override {
+ switch (code) {
+ case PLUS_ONE: {
+ uint32_t in = data.readInt32();
+ uint32_t out = 0;
+
+ out = plusOne(in);
+
+ reply->writeInt32(out);
+ return NO_ERROR;
+ }
+ default:
+ return UNKNOWN_TRANSACTION;
+ }
+ }
+};
+
+class BpKDBinderTestService : public BpInterface<IKDBinderTestService> {
+ public:
+ explicit BpKDBinderTestService(const sp<IBinder>& binder)
+ : BpInterface<IKDBinderTestService>(binder) {}
+ virtual ~BpKDBinderTestService() {}
+
+ uint32_t plusOne(uint32_t in) override {
+ Parcel data;
+ Parcel reply;
+
+ data.writeInt32(in);
+
+ remote()->transact(PLUS_ONE, data, &reply);
+
+ return reply.readInt32();
+ }
+};
+
+class KDBinderTestService : public BnKDBinderTestService {
+ public:
+ KDBinderTestService() : BnKDBinderTestService() {}
+ virtual ~KDBinderTestService() {}
+
+ uint32_t plusOne(uint32_t in) override {
+ return in + 1;
+ }
+};
+
+IMPLEMENT_META_INTERFACE(KDBinderTestService,
+ "android.test.KDBinderTestService");
+
+class KDBinderTestDeathRecipient : public IBinder::DeathRecipient {
+ public:
+ KDBinderTestDeathRecipient() : mTriggered(false) {
+ pthread_mutex_init(&mMutex, NULL);
+ pthread_cond_init(&mCond, NULL);
+ }
+
+ void binderDied(const wp<IBinder>& who) override {
+ (void)who;
+ trigger();
+ }
+
+ void wait() {
+ pthread_mutex_lock(&mMutex);
+ if (!mTriggered) {
+ pthread_cond_wait(&mCond, &mMutex);
+ }
+ pthread_mutex_unlock(&mMutex);
+ }
+
+ void trigger() {
+ pthread_mutex_lock(&mMutex);
+ pthread_cond_signal(&mCond);
+ mTriggered = true;
+ pthread_mutex_unlock(&mMutex);
+ }
+
+ bool triggered() const {
+ return mTriggered;
+ }
+
+ private:
+ pthread_mutex_t mMutex;
+ pthread_cond_t mCond;
+ bool mTriggered;
+};
+
+TEST(KDBinderTest, service_manager) {
+ sp<IServiceManager> sm = defaultServiceManager();
+
+ ASSERT_TRUE(sm != nullptr);
+}
+
+TEST(KDBinderTest, getService) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> remote = sm->getService(kdbinderTestServiceName);
+
+ ASSERT_TRUE(remote != NULL);
+ ASSERT_TRUE(remote->remoteBinder() != NULL);
+ ASSERT_TRUE(remote->localBinder() == NULL);
+}
+
+TEST(KDBinderTest, listService) {
+ sp<IServiceManager> sm = defaultServiceManager();
+
+ Vector<String16> list = sm->listServices();
+
+ auto s = std::find_if(list.begin(), list.end(), [](const String16& name) {
+ return name == kdbinderTestServiceName;
+ });
+
+ ASSERT_TRUE(s != list.end());
+}
+
+TEST(KDBinderTest, transaction) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> proxy = sm->getService(kdbinderTestServiceName);
+ sp<IKDBinderTestService> service = interface_cast<IKDBinderTestService>(
+ proxy);
+
+ std::vector<std::future<int>> results;
+ std::vector<std::thread> threads;
+
+ // Package a 100 transactions in a vector of results.
+ for (int i = 0; i < 100; i++) {
+ std::packaged_task<int()> task([&service]{
+ return service->plusOne(42);
+ });
+ results.emplace_back(task.get_future());
+ threads.emplace_back(std::move(task));
+ }
+
+ // Wait for all threads to finish.
+ for (auto& thread : threads) {
+ thread.join();
+ }
+
+ for (auto& result : results) {
+ ASSERT_EQ(result.get(), 43);
+ }
+}
+
+TEST(KDBinderTest, linkToDeath) {
+ pid_t pid = start_server(String16("short.lived"));
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> proxy = sm->getService(String16("short.lived"));
+ sp<KDBinderTestDeathRecipient> death_recipient =
+ new KDBinderTestDeathRecipient;
+
+ ASSERT_TRUE(proxy->linkToDeath(death_recipient) == NO_ERROR);
+
+ kill(pid, SIGKILL);
+
+ death_recipient->wait();
+
+ wait(NULL);
+
+ ASSERT_TRUE(death_recipient->triggered());
+}
+
+TEST(KDBinderTest, unlinkToDeath) {
+ pid_t pid = start_server(String16("short.lived"));
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> proxy = sm->getService(String16("short.lived"));
+ sp<KDBinderTestDeathRecipient> death_recipient =
+ new KDBinderTestDeathRecipient;
+
+ ASSERT_TRUE(proxy->linkToDeath(death_recipient) == NO_ERROR);
+ ASSERT_TRUE(proxy->unlinkToDeath(death_recipient) == NO_ERROR);
+
+ kill(pid, SIGKILL);
+ wait(NULL);
+
+ ASSERT_FALSE(death_recipient->triggered());
+}
+
+int run_server(int readypipefd, char *name) {
+ status_t ret;
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ ret = sm->addService(String16(name), new KDBinderTestService);
+
+ write(readypipefd, &ret, sizeof(ret));
+ close(readypipefd);
+
+ if (ret) {
+ return 1;
+ }
+
+ ProcessState::self()->startThreadPool();
+ IPCThreadState::self()->joinThreadPool();
+ return 1; /* joinThreadPool should not return */
+}
+
+int start_server(String16 name) {
+ pid_t pid;
+ int pipefd[2];
+ int ret;
+ char strpipe[16];
+ status_t status;
+ char *args[] = {
+ kdbinderservername,
+ strpipe,
+ (char *) String8(name).string(), // NOLINT
+ NULL
+ };
+
+ ret = pipe(pipefd);
+ if (ret < 0) {
+ return ret;
+ }
+
+ snprintf(strpipe, sizeof(strpipe), "%d", pipefd[1]);
+
+ pid = fork();
+ if (pid == -1) {
+ return pid;
+ }
+ if (pid == 0) {
+ close(pipefd[0]);
+ execv(kdbinderservername, args);
+ status = -errno;
+ write(pipefd[1], &status, sizeof(status));
+ std::terminate();
+ }
+ close(pipefd[1]);
+ ret = read(pipefd[0], &status, sizeof(status));
+ close(pipefd[0]);
+ if (ret == sizeof(status)) {
+ ret = status;
+ } else {
+ kill(pid, SIGKILL);
+ if (ret >= 0) {
+ ret = NO_INIT;
+ }
+ }
+ if (ret < 0) {
+ wait(NULL);
+ return ret;
+ }
+
+ return pid;
+}
+
+int main(int argc, char **argv) {
+ kdbinderservername = argv[0];
+
+ if (argc == 3) {
+ return run_server(atoi(argv[1]), argv[2]);
+ }
+
+ pid_t server_pid = start_server(kdbinderTestServiceName);
+
+ testing::InitGoogleTest(&argc, argv);
+ ProcessState::self()->startThreadPool();
+
+ int ret = RUN_ALL_TESTS();
+
+ kill(server_pid, SIGKILL);
+
+ return ret;
+}