blob: 5c7e90ea470e02f4a71251255be3036c234407d6 [file] [log] [blame]
/*
* 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