| /* |
| * Copyright 2019 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 "reactor.h" |
| |
| #include <sys/eventfd.h> |
| #include <chrono> |
| #include <future> |
| #include <thread> |
| |
| #include "base/logging.h" |
| #include "gtest/gtest.h" |
| |
| namespace bluetooth { |
| namespace common { |
| namespace { |
| |
| constexpr int kReadReadyValue = 100; |
| |
| std::promise<int>* g_promise; |
| |
| class ReactorTest : public ::testing::Test { |
| protected: |
| void SetUp() override { |
| g_promise = new std::promise<int>; |
| reactor_ = new Reactor; |
| } |
| |
| void TearDown() override { |
| delete g_promise; |
| g_promise = nullptr; |
| delete reactor_; |
| reactor_ = nullptr; |
| } |
| |
| Reactor* reactor_; |
| }; |
| |
| class SampleReactable { |
| public: |
| SampleReactable() : fd_(eventfd(0, EFD_NONBLOCK)) { |
| EXPECT_NE(fd_, 0); |
| } |
| |
| ~SampleReactable() { |
| close(fd_); |
| } |
| |
| void OnReadReady() {} |
| |
| void OnWriteReady() {} |
| |
| int fd_; |
| }; |
| |
| class FakeReactable { |
| public: |
| enum EventFdValue { |
| kSetPromise = 1, |
| kRegisterSampleReactable, |
| kUnregisterSampleReactable, |
| kSampleOutputValue, |
| }; |
| FakeReactable() : fd_(eventfd(0, 0)), reactor_(nullptr) { |
| EXPECT_NE(fd_, 0); |
| } |
| |
| FakeReactable(Reactor* reactor) : fd_(eventfd(0, 0)), reactor_(reactor) { |
| EXPECT_NE(fd_, 0); |
| } |
| |
| ~FakeReactable() { |
| close(fd_); |
| } |
| |
| void OnReadReady() { |
| uint64_t value = 0; |
| auto read_result = eventfd_read(fd_, &value); |
| EXPECT_EQ(read_result, 0); |
| if (value == kSetPromise && g_promise != nullptr) { |
| g_promise->set_value(kReadReadyValue); |
| } |
| if (value == kRegisterSampleReactable) { |
| reactable_ = reactor_->Register(sample_reactable_.fd_, [this] { this->sample_reactable_.OnReadReady(); }, |
| [this] { this->sample_reactable_.OnWriteReady(); }); |
| g_promise->set_value(kReadReadyValue); |
| } |
| if (value == kUnregisterSampleReactable) { |
| reactor_->Unregister(reactable_); |
| g_promise->set_value(kReadReadyValue); |
| } |
| } |
| |
| void OnWriteReady() { |
| auto write_result = eventfd_write(fd_, output_data_); |
| output_data_ = 0; |
| EXPECT_EQ(write_result, 0); |
| } |
| |
| SampleReactable sample_reactable_; |
| Reactor::Reactable* reactable_ = nullptr; |
| int fd_; |
| |
| private: |
| Reactor* reactor_; |
| uint64_t output_data_ = kSampleOutputValue; |
| }; |
| |
| TEST_F(ReactorTest, start_and_stop) { |
| auto reactor_thread = std::thread(&Reactor::Run, reactor_); |
| reactor_->Stop(); |
| reactor_thread.join(); |
| } |
| |
| TEST_F(ReactorTest, stop_and_start) { |
| auto reactor_thread = std::thread(&Reactor::Stop, reactor_); |
| auto another_thread = std::thread(&Reactor::Run, reactor_); |
| reactor_thread.join(); |
| another_thread.join(); |
| } |
| |
| TEST_F(ReactorTest, stop_multi_times) { |
| auto reactor_thread = std::thread(&Reactor::Run, reactor_); |
| for (int i = 0; i < 5; i++) { |
| reactor_->Stop(); |
| } |
| reactor_thread.join(); |
| } |
| |
| TEST_F(ReactorTest, cold_register_only) { |
| FakeReactable fake_reactable; |
| auto* reactable = |
| reactor_->Register(fake_reactable.fd_, std::bind(&FakeReactable::OnReadReady, &fake_reactable), nullptr); |
| |
| reactor_->Unregister(reactable); |
| } |
| |
| TEST_F(ReactorTest, cold_register) { |
| FakeReactable fake_reactable; |
| auto* reactable = |
| reactor_->Register(fake_reactable.fd_, std::bind(&FakeReactable::OnReadReady, &fake_reactable), nullptr); |
| auto reactor_thread = std::thread(&Reactor::Run, reactor_); |
| auto future = g_promise->get_future(); |
| |
| auto write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kSetPromise); |
| EXPECT_EQ(write_result, 0); |
| EXPECT_EQ(future.get(), kReadReadyValue); |
| reactor_->Stop(); |
| reactor_thread.join(); |
| reactor_->Unregister(reactable); |
| } |
| |
| TEST_F(ReactorTest, hot_register_from_different_thread) { |
| auto reactor_thread = std::thread(&Reactor::Run, reactor_); |
| auto future = g_promise->get_future(); |
| |
| FakeReactable fake_reactable; |
| auto* reactable = |
| reactor_->Register(fake_reactable.fd_, std::bind(&FakeReactable::OnReadReady, &fake_reactable), nullptr); |
| auto write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kSetPromise); |
| EXPECT_EQ(write_result, 0); |
| EXPECT_EQ(future.get(), kReadReadyValue); |
| reactor_->Stop(); |
| reactor_thread.join(); |
| |
| reactor_->Unregister(reactable); |
| } |
| |
| TEST_F(ReactorTest, hot_unregister_from_different_thread) { |
| FakeReactable fake_reactable; |
| auto* reactable = |
| reactor_->Register(fake_reactable.fd_, std::bind(&FakeReactable::OnReadReady, &fake_reactable), nullptr); |
| auto reactor_thread = std::thread(&Reactor::Run, reactor_); |
| reactor_->Unregister(reactable); |
| auto future = g_promise->get_future(); |
| |
| auto write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kSetPromise); |
| EXPECT_EQ(write_result, 0); |
| future.wait_for(std::chrono::milliseconds(10)); |
| g_promise->set_value(2); |
| EXPECT_EQ(future.get(), 2); |
| reactor_->Stop(); |
| reactor_thread.join(); |
| } |
| |
| TEST_F(ReactorTest, hot_register_from_same_thread) { |
| auto reactor_thread = std::thread(&Reactor::Run, reactor_); |
| auto future = g_promise->get_future(); |
| |
| FakeReactable fake_reactable(reactor_); |
| auto* reactable = |
| reactor_->Register(fake_reactable.fd_, std::bind(&FakeReactable::OnReadReady, &fake_reactable), nullptr); |
| auto write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kRegisterSampleReactable); |
| EXPECT_EQ(write_result, 0); |
| EXPECT_EQ(future.get(), kReadReadyValue); |
| reactor_->Stop(); |
| reactor_thread.join(); |
| |
| reactor_->Unregister(reactable); |
| } |
| |
| TEST_F(ReactorTest, hot_unregister_from_same_thread) { |
| auto reactor_thread = std::thread(&Reactor::Run, reactor_); |
| auto future = g_promise->get_future(); |
| |
| FakeReactable fake_reactable(reactor_); |
| auto* reactable = |
| reactor_->Register(fake_reactable.fd_, std::bind(&FakeReactable::OnReadReady, &fake_reactable), nullptr); |
| auto write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kRegisterSampleReactable); |
| EXPECT_EQ(write_result, 0); |
| EXPECT_EQ(future.get(), kReadReadyValue); |
| delete g_promise; |
| g_promise = new std::promise<int>; |
| future = g_promise->get_future(); |
| write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kUnregisterSampleReactable); |
| EXPECT_EQ(write_result, 0); |
| EXPECT_EQ(future.get(), kReadReadyValue); |
| reactor_->Stop(); |
| reactor_thread.join(); |
| LOG(INFO); |
| |
| reactor_->Unregister(reactable); |
| LOG(INFO); |
| } |
| |
| TEST_F(ReactorTest, start_and_stop_multi_times) { |
| auto reactor_thread = std::thread(&Reactor::Run, reactor_); |
| reactor_->Stop(); |
| reactor_thread.join(); |
| for (int i = 0; i < 5; i++) { |
| reactor_thread = std::thread(&Reactor::Run, reactor_); |
| reactor_->Stop(); |
| reactor_thread.join(); |
| } |
| } |
| |
| TEST_F(ReactorTest, on_write_ready) { |
| FakeReactable fake_reactable; |
| auto* reactable = |
| reactor_->Register(fake_reactable.fd_, nullptr, std::bind(&FakeReactable::OnWriteReady, &fake_reactable)); |
| auto reactor_thread = std::thread(&Reactor::Run, reactor_); |
| uint64_t value = 0; |
| auto read_result = eventfd_read(fake_reactable.fd_, &value); |
| EXPECT_EQ(read_result, 0); |
| EXPECT_EQ(value, FakeReactable::kSampleOutputValue); |
| |
| reactor_->Stop(); |
| reactor_thread.join(); |
| |
| reactor_->Unregister(reactable); |
| } |
| |
| TEST_F(ReactorTest, modify_registration) { |
| FakeReactable fake_reactable; |
| auto* reactable = |
| reactor_->Register(fake_reactable.fd_, std::bind(&FakeReactable::OnReadReady, &fake_reactable), nullptr); |
| reactor_->ModifyRegistration(reactable, nullptr, std::bind(&FakeReactable::OnWriteReady, &fake_reactable)); |
| auto reactor_thread = std::thread(&Reactor::Run, reactor_); |
| uint64_t value = 0; |
| auto read_result = eventfd_read(fake_reactable.fd_, &value); |
| EXPECT_EQ(read_result, 0); |
| EXPECT_EQ(value, FakeReactable::kSampleOutputValue); |
| |
| reactor_->Stop(); |
| reactor_thread.join(); |
| } |
| |
| } // namespace |
| } // namespace common |
| } // namespace bluetooth |