diff options
author | Pierre Langlois <pierre.langlois@arm.com> | 2015-01-15 10:54:13 +0000 |
---|---|---|
committer | Pierre Langlois <pierre.langlois@arm.com> | 2016-03-04 18:01:18 +0000 |
commit | b9b37c4bbed6c388ccca4ef8048d0a428b12f16f (patch) | |
tree | d95d6f954e79bc65547acc6ff5d0a116d2b9f0d7 | |
parent | c85602db731ce1d0de9289b5d8a2af51d74f0916 (diff) | |
download | kdbinder-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.mk | 16 | ||||
-rw-r--r-- | libs/kdbinder/tests/kdbinderTest.cpp | 324 |
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; +} |