普通文本  |  226行  |  7.63 KB

/*
 * Copyright 2018 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 <base/bind.h>
#include <base/bind_helpers.h>
#include <base/logging.h>
#include <gtest/gtest.h>
#include <future>

#include "message_loop_thread.h"
#include "once_timer.h"

using bluetooth::common::MessageLoopThread;
using bluetooth::common::OnceTimer;

// Allowed error between the expected and actual delay for DoInThreadDelayed().
constexpr uint32_t delay_error_ms = 3;

/**
 * Unit tests to verify Task Scheduler.
 */
class OnceTimerTest : public ::testing::Test {
 public:
  void ShouldNotHappen() { FAIL() << "Should not happen"; }

  void IncreaseTaskCounter(int scheduled_tasks, std::promise<void>* promise) {
    counter_++;
    if (counter_ == scheduled_tasks) {
      promise->set_value();
    }
  }

  void GetName(std::string* name, std::promise<void>* promise) {
    char my_name[256];
    pthread_getname_np(pthread_self(), my_name, sizeof(my_name));
    name->append(my_name);
    promise->set_value();
  }

  void SleepAndIncreaseCounter(std::promise<void>* promise, int sleep_ms) {
    promise->set_value();
    std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms));
    counter_++;
  }

  void CancelTimerAndWait() { timer_->CancelAndWait(); }

 protected:
  void SetUp() override {
    ::testing::Test::SetUp();
    counter_ = 0;
    timer_ = new OnceTimer();
    promise_ = new std::promise<void>();
  }

  void TearDown() override {
    if (promise_ != nullptr) {
      delete promise_;
      promise_ = nullptr;
    }
    if (timer_ != nullptr) {
      delete timer_;
      timer_ = nullptr;
    }
  }

  int counter_;
  OnceTimer* timer_;
  std::promise<void>* promise_;
};

TEST_F(OnceTimerTest, initial_is_not_scheduled) {
  ASSERT_FALSE(timer_->IsScheduled());
}

TEST_F(OnceTimerTest, schedule_task) {
  std::string name = "test_thread";
  MessageLoopThread message_loop_thread(name);
  message_loop_thread.StartUp();
  auto future = promise_->get_future();
  std::string my_name;
  uint32_t delay_ms = 5;

  timer_->Schedule(message_loop_thread.GetWeakPtr(), FROM_HERE,
                   base::BindOnce(&OnceTimerTest::GetName,
                                  base::Unretained(this), &my_name, promise_),
                   base::TimeDelta::FromMilliseconds(delay_ms));
  EXPECT_TRUE(timer_->IsScheduled());
  future.get();
  ASSERT_EQ(name, my_name);
  std::this_thread::sleep_for(std::chrono::milliseconds(delay_error_ms));
  EXPECT_FALSE(timer_->IsScheduled());
}

TEST_F(OnceTimerTest, cancel_without_scheduling) {
  std::string name = "test_thread";
  MessageLoopThread message_loop_thread(name);
  message_loop_thread.StartUp();

  EXPECT_FALSE(timer_->IsScheduled());
  timer_->CancelAndWait();
  EXPECT_FALSE(timer_->IsScheduled());
}

TEST_F(OnceTimerTest, cancel_in_callback_no_deadlock) {
  std::string name = "test_thread";
  MessageLoopThread message_loop_thread(name);
  message_loop_thread.StartUp();
  uint32_t delay_ms = 5;

  timer_->Schedule(message_loop_thread.GetWeakPtr(), FROM_HERE,
                   base::BindOnce(&OnceTimerTest::CancelTimerAndWait,
                                  base::Unretained(this)),
                   base::TimeDelta::FromMilliseconds(delay_ms));
}

TEST_F(OnceTimerTest, cancel_single_task) {
  std::string name = "test_thread";
  MessageLoopThread message_loop_thread(name);
  message_loop_thread.StartUp();
  uint32_t delay_ms = 100000000;
  timer_->Schedule(
      message_loop_thread.GetWeakPtr(), FROM_HERE,
      base::BindOnce(&OnceTimerTest::ShouldNotHappen, base::Unretained(this)),
      base::TimeDelta::FromMilliseconds(delay_ms));
  std::this_thread::sleep_for(std::chrono::milliseconds(delay_error_ms));
  timer_->CancelAndWait();
}

TEST_F(OnceTimerTest, message_loop_thread_down_cancel_task) {
  std::string name = "test_thread";
  {
    MessageLoopThread message_loop_thread(name);
    message_loop_thread.StartUp();
    uint32_t delay_ms = 100000000;
    timer_->Schedule(
        message_loop_thread.GetWeakPtr(), FROM_HERE,
        base::BindOnce(&OnceTimerTest::ShouldNotHappen, base::Unretained(this)),
        base::TimeDelta::FromMilliseconds(delay_ms));
    std::this_thread::sleep_for(std::chrono::milliseconds(delay_error_ms));
  }
}

// Verify that if a task is being executed, then cancelling it is no-op
TEST_F(OnceTimerTest, cancel_current_task_no_effect) {
  std::string name = "test_thread";
  MessageLoopThread message_loop_thread(name);
  message_loop_thread.StartUp();
  auto future = promise_->get_future();
  uint32_t delay_ms = 5;

  timer_->Schedule(message_loop_thread.GetWeakPtr(), FROM_HERE,
                   base::BindOnce(&OnceTimerTest::SleepAndIncreaseCounter,
                                  base::Unretained(this), promise_, delay_ms),
                   base::TimeDelta::FromMilliseconds(delay_ms));
  EXPECT_TRUE(timer_->IsScheduled());
  future.get();
  timer_->CancelAndWait();
  ASSERT_EQ(counter_, 1);
  EXPECT_FALSE(timer_->IsScheduled());
}

TEST_F(OnceTimerTest, reschedule_task_dont_invoke_new_task_early) {
  std::string name = "test_thread";
  MessageLoopThread message_loop_thread(name);
  message_loop_thread.StartUp();
  uint32_t delay_ms = 5;
  timer_->Schedule(message_loop_thread.GetWeakPtr(), FROM_HERE,
                   base::DoNothing(),
                   base::TimeDelta::FromMilliseconds(delay_ms));
  std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms - 2));
  timer_->Schedule(
      message_loop_thread.GetWeakPtr(), FROM_HERE,
      base::BindOnce(&OnceTimerTest::ShouldNotHappen, base::Unretained(this)),
      base::TimeDelta::FromMilliseconds(delay_ms + 1000));
  std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms));
}

TEST_F(OnceTimerTest, reschedule_task_when_firing_dont_invoke_new_task_early) {
  std::string name = "test_thread";
  MessageLoopThread message_loop_thread(name);
  message_loop_thread.StartUp();
  uint32_t delay_ms = 5;
  timer_->Schedule(message_loop_thread.GetWeakPtr(), FROM_HERE,
                   base::DoNothing(),
                   base::TimeDelta::FromMilliseconds(delay_ms));
  std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms));
  timer_->Schedule(
      message_loop_thread.GetWeakPtr(), FROM_HERE,
      base::BindOnce(&OnceTimerTest::ShouldNotHappen, base::Unretained(this)),
      base::TimeDelta::FromMilliseconds(delay_ms + 1000));
  std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms));
}

TEST_F(OnceTimerTest, reschedule_task_when_firing_must_schedule_new_task) {
  std::string name = "test_thread";
  MessageLoopThread message_loop_thread(name);
  message_loop_thread.StartUp();
  uint32_t delay_ms = 5;
  std::string my_name;
  auto future = promise_->get_future();
  timer_->Schedule(message_loop_thread.GetWeakPtr(), FROM_HERE,
                   base::DoNothing(),
                   base::TimeDelta::FromMilliseconds(delay_ms));
  std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms));
  timer_->Schedule(message_loop_thread.GetWeakPtr(), FROM_HERE,
                   base::BindOnce(&OnceTimerTest::GetName,
                                  base::Unretained(this), &my_name, promise_),
                   base::TimeDelta::FromMilliseconds(delay_ms));
  future.get();
  ASSERT_EQ(name, my_name);
}