/* * Copyright 2023 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 "module_jniloop_unittest.h" #include #include #include #include #include #include #include "btif/include/btif_jni_task.h" #include "gtest/gtest.h" #include "module.h" #include "os/handler.h" #include "os/thread.h" using namespace bluetooth; namespace { constexpr int sync_timeout_in_ms = 3000; std::promise external_function_promise; std::promise private_impl_promise; std::promise protected_method_promise; } // namespace // Global function with C linkage void external_function_jni(int /* a */, double /* b */, char /* c */) { external_function_promise.set_value(base::PlatformThread::CurrentId()); } // Module private implementation that is inaccessible externally struct TestJniModule::PrivateImpl : public ModuleJniloop { const int kMaxTestModuleRecurseDepth = 10; void privateCallableMethod(int /* a */, double /* b */, char /* c */) { private_impl_promise.set_value(base::PlatformThread::CurrentId()); } void repostMethodTest(int /* a */, double /* b */, char /* c */) { private_impl_promise.set_value(base::PlatformThread::CurrentId()); } void privateCallableRepostMethod( std::shared_ptr ptr, int a, double b, char c) { PostMethodOnJni(ptr, &PrivateImpl::repostMethodTest, a, b, c); } void privateCallableRecursiveMethod( std::shared_ptr ptr, int depth, double b, char c) { if (depth > kMaxTestModuleRecurseDepth) { private_impl_promise.set_value(base::PlatformThread::CurrentId()); return; } PostMethodOnJni(ptr, &PrivateImpl::privateCallableRecursiveMethod, ptr, depth + 1, b, c); } }; // Protected module method executed on handler void TestJniModule::call_on_handler_protected_method(int loop_tid, int a, int b, int c) { protected_method_promise = std::promise(); auto future = protected_method_promise.get_future(); CallOn(this, &TestJniModule::protected_method, a, b, c); ASSERT_EQ(future.wait_for(std::chrono::seconds(3)), std::future_status::ready); ASSERT_EQ(future.get(), loop_tid); } // Global external function executed on jni loop void TestJniModule::call_on_jni_external_function(int loop_tid, int a, double b, char c) { external_function_promise = std::promise(); auto future = external_function_promise.get_future(); PostFunctionOnJni(&external_function_jni, a, b, c); ASSERT_EQ(future.wait_for(std::chrono::seconds(3)), std::future_status::ready); ASSERT_EQ(future.get(), loop_tid); } // Private implementation method executed on main loop void TestJniModule::call_on_jni(int loop_tid, int a, int b, int c) { private_impl_promise = std::promise(); auto future = private_impl_promise.get_future(); PostMethodOnJni(pimpl_, &TestJniModule::PrivateImpl::privateCallableMethod, a, b, c); ASSERT_EQ(future.wait_for(std::chrono::seconds(3)), std::future_status::ready); ASSERT_EQ(future.get(), loop_tid); } // Private implementation method executed on jni loop and reposted void TestJniModule::call_on_jni_repost(int loop_tid, int a, int b, int c) { private_impl_promise = std::promise(); auto future = private_impl_promise.get_future(); PostMethodOnJni( pimpl_, &TestJniModule::PrivateImpl::privateCallableRepostMethod, pimpl_, a, b, c); ASSERT_EQ(future.wait_for(std::chrono::seconds(3)), std::future_status::ready); ASSERT_EQ(future.get(), loop_tid); } // Private implementation method executed on jni loop recursively void TestJniModule::call_on_jni_recurse(int loop_tid, int depth, int b, int c) { private_impl_promise = std::promise(); auto future = private_impl_promise.get_future(); PostMethodOnJni( pimpl_, &TestJniModule::PrivateImpl::privateCallableRecursiveMethod, pimpl_, depth, b, c); ASSERT_EQ(future.wait_for(std::chrono::seconds(3)), std::future_status::ready); ASSERT_EQ(future.get(), loop_tid); } void TestJniModule::protected_method(int /* a */, int /* b */, int /* c */) { protected_method_promise.set_value(base::PlatformThread::CurrentId()); } bool TestJniModule::IsStarted() const { return pimpl_ != nullptr; } void TestJniModule::Start() { ASSERT_FALSE(IsStarted()); pimpl_ = std::make_shared(); } void TestJniModule::Stop() { ASSERT_TRUE(IsStarted()); pimpl_.reset(); } std::string TestJniModule::ToString() const { return std::string(__func__); } const bluetooth::ModuleFactory TestJniModule::Factory = bluetooth::ModuleFactory([]() { return new TestJniModule(); }); // // Module GDx Testing Below // class ModuleGdxJniTest : public ::testing::Test { protected: void SetUp() override { test_framework_tid_ = base::PlatformThread::CurrentId(); module_ = new TestJniModule(); jni_thread_startup(); jniloop_tid_ = get_jniloop_tid(); } void TearDown() override { sync_jni_handler(); jni_thread_shutdown(); delete module_; } void sync_jni_handler() { std::promise promise = std::promise(); std::future future = promise.get_future(); post_on_bt_jni([&promise]() { promise.set_value(); }); future.wait_for(std::chrono::milliseconds(sync_timeout_in_ms)); }; static pid_t get_jniloop_tid() { std::promise pid_promise = std::promise(); auto future = pid_promise.get_future(); post_on_bt_jni([&pid_promise]() { pid_promise.set_value(base::PlatformThread::CurrentId()); }); return future.get(); } pid_t test_framework_tid_{-1}; pid_t jniloop_tid_{-1}; TestModuleRegistry module_registry_; TestJniModule* module_; }; class ModuleGdxWithJniStackTest : public ModuleGdxJniTest { protected: void SetUp() override { ModuleGdxJniTest::SetUp(); module_registry_.InjectTestModule(&TestJniModule::Factory, module_ /* pass ownership */); module_ = nullptr; // ownership is passed handler_tid_ = get_handler_tid(module_registry_.GetTestModuleHandler(&TestJniModule::Factory)); } static pid_t get_handler_tid(os::Handler* handler) { std::promise handler_tid_promise = std::promise(); std::future future = handler_tid_promise.get_future(); handler->Post(common::BindOnce( [](std::promise promise) { promise.set_value(base::PlatformThread::CurrentId()); }, std::move(handler_tid_promise))); return future.get(); } void TearDown() override { module_registry_.StopAll(); ModuleGdxJniTest::TearDown(); } TestJniModule* Mod() { return module_registry_.GetModuleUnderTest(); } pid_t handler_tid_{-1}; }; TEST_F(ModuleGdxJniTest, nop) {} TEST_F(ModuleGdxJniTest, lifecycle) { ::bluetooth::os::Thread* thread = new bluetooth::os::Thread("Name", bluetooth::os::Thread::Priority::REAL_TIME); ASSERT_FALSE(module_registry_.IsStarted()); module_registry_.Start(thread); ASSERT_TRUE(module_registry_.IsStarted()); module_registry_.StopAll(); ASSERT_FALSE(module_registry_.IsStarted()); delete thread; } TEST_F(ModuleGdxWithJniStackTest, call_on_handler_protected_method) { Mod()->call_on_handler_protected_method(handler_tid_, 1, 2, 3); } TEST_F(ModuleGdxWithJniStackTest, test_call_on_jni) { Mod()->call_on_jni(jniloop_tid_, 1, 2, 3); } TEST_F(ModuleGdxWithJniStackTest, test_call_external_function) { Mod()->call_on_jni_external_function(jniloop_tid_, 1, 2.3, 'c'); } TEST_F(ModuleGdxWithJniStackTest, test_call_on_jni_repost) { Mod()->call_on_jni_repost(jniloop_tid_, 1, 2, 3); } TEST_F(ModuleGdxWithJniStackTest, test_call_on_jni_recurse) { Mod()->call_on_jni_recurse(jniloop_tid_, 1, 2, 3); }