普通文本  |  281行  |  8.06 KB

/*
 * 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 "os/reactor.h"

#include <sys/eventfd.h>
#include <chrono>
#include <future>
#include <thread>

#include "gtest/gtest.h"

namespace bluetooth {
namespace os {
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();

  reactor_->Unregister(reactable);
}

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 os
}  // namespace bluetooth