/* * 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_mainloop_unittest.h" #include #include #include #include #include #include #include "gtest/gtest.h" #include "module.h" #include "os/handler.h" #include "os/thread.h" #include "stack/include/main_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_main(int /* a */, double /* b */, char /* c */) { external_function_promise.set_value(base::PlatformThread::CurrentId()); } // Module private implementation that is inaccessible externally struct TestModule::PrivateImpl : public ModuleMainloop { 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) { PostMethodOnMain(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; } PostMethodOnMain(ptr, &PrivateImpl::privateCallableRecursiveMethod, ptr, depth + 1, b, c); } }; // Protected module method executed on handler void TestModule::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, &TestModule::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 main loop void TestModule::call_on_main_external_function(int loop_tid, int a, double b, char c) { external_function_promise = std::promise(); auto future = external_function_promise.get_future(); PostFunctionOnMain(&external_function_main, 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 TestModule::call_on_main(int loop_tid, int a, int b, int c) { private_impl_promise = std::promise(); auto future = private_impl_promise.get_future(); PostMethodOnMain(pimpl_, &TestModule::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 main loop and reposted void TestModule::call_on_main_repost(int loop_tid, int a, int b, int c) { private_impl_promise = std::promise(); auto future = private_impl_promise.get_future(); PostMethodOnMain(pimpl_, &TestModule::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 main loop recursively void TestModule::call_on_main_recurse(int loop_tid, int depth, int b, int c) { private_impl_promise = std::promise(); auto future = private_impl_promise.get_future(); PostMethodOnMain( pimpl_, &TestModule::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 TestModule::protected_method(int /* a */, int /* b */, int /* c */) { protected_method_promise.set_value(base::PlatformThread::CurrentId()); } bool TestModule::IsStarted() const { return pimpl_ != nullptr; } void TestModule::Start() { ASSERT_FALSE(IsStarted()); pimpl_ = std::make_shared(); } void TestModule::Stop() { ASSERT_TRUE(IsStarted()); pimpl_.reset(); } std::string TestModule::ToString() const { return std::string(__func__); } const bluetooth::ModuleFactory TestModule::Factory = bluetooth::ModuleFactory([]() { return new TestModule(); }); // // Module GDx Testing Below // class ModuleMainGdxTest : public ::testing::Test { protected: void SetUp() override { test_framework_tid_ = base::PlatformThread::CurrentId(); module_ = new TestModule(); main_thread_start_up(); mainloop_tid_ = get_mainloop_tid(); } void TearDown() override { sync_main_handler(); main_thread_shut_down(); delete module_; } void sync_main_handler() { std::promise promise = std::promise(); std::future future = promise.get_future(); post_on_bt_main([&promise]() { promise.set_value(); }); future.wait_for(std::chrono::milliseconds(sync_timeout_in_ms)); }; static pid_t get_mainloop_tid() { std::promise pid_promise = std::promise(); auto future = pid_promise.get_future(); post_on_bt_main([&pid_promise]() { pid_promise.set_value(base::PlatformThread::CurrentId()); }); return future.get(); } pid_t test_framework_tid_{-1}; pid_t mainloop_tid_{-1}; TestModuleRegistry module_registry_; TestModule* module_; }; class ModuleMainGdxWithStackTest : public ModuleMainGdxTest { protected: void SetUp() override { ModuleMainGdxTest::SetUp(); module_registry_.InjectTestModule(&TestModule::Factory, module_ /* pass ownership */); module_ = nullptr; // ownership is passed handler_tid_ = get_handler_tid(module_registry_.GetTestModuleHandler(&TestModule::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(); ModuleMainGdxTest::TearDown(); } TestModule* Mod() { return module_registry_.GetModuleUnderTest(); } pid_t handler_tid_{-1}; }; TEST_F(ModuleMainGdxTest, nop) {} TEST_F(ModuleMainGdxTest, 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(ModuleMainGdxWithStackTest, call_on_handler_protected_method) { Mod()->call_on_handler_protected_method(handler_tid_, 1, 2, 3); } TEST_F(ModuleMainGdxWithStackTest, test_call_on_main) { Mod()->call_on_main(mainloop_tid_, 1, 2, 3); } TEST_F(ModuleMainGdxWithStackTest, test_call_external_function) { Mod()->call_on_main_external_function(mainloop_tid_, 1, 2.3, 'c'); } TEST_F(ModuleMainGdxWithStackTest, test_call_on_main_repost) { Mod()->call_on_main_repost(mainloop_tid_, 1, 2, 3); } TEST_F(ModuleMainGdxWithStackTest, test_call_on_main_recurse) { Mod()->call_on_main_recurse(mainloop_tid_, 1, 2, 3); }