/* * 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 #include #include #include #include #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* promise) { counter_++; if (counter_ == scheduled_tasks) { promise->set_value(); } } void GetName(std::string* name, std::promise* 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* 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 TearDown() override { if (promise_ != nullptr) { delete promise_; promise_ = nullptr; } if (timer_ != nullptr) { delete timer_; timer_ = nullptr; } } int counter_; OnceTimer* timer_; std::promise* 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); }