/* * 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 "repeating_timer.h" using bluetooth::common::MessageLoopThread; using bluetooth::common::RepeatingTimer; // Allowed error between the expected and actual delay for DoInThreadDelayed(). constexpr uint32_t delay_error_ms = 3; /** * Unit tests to verify Task Scheduler. */ class RepeatingTimerTest : 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 VerifyDelayTimeAndSleep(std::chrono::steady_clock::time_point start_time, int interval_ms, int scheduled_tasks, int task_length_ms, std::promise* promise) { auto end_time = std::chrono::steady_clock::now(); auto actual_delay = std::chrono::duration_cast( end_time - start_time); counter_++; int64_t scheduled_delay_ms = interval_ms * counter_; if (counter_ == scheduled_tasks) { promise->set_value(); } ASSERT_NEAR(scheduled_delay_ms, actual_delay.count(), delay_error_ms); std::this_thread::sleep_for(std::chrono::milliseconds(task_length_ms)); } void VerifyMultipleDelayedTasks(int scheduled_tasks, int task_length_ms, int interval_between_tasks_ms) { std::string name = "test_thread"; MessageLoopThread message_loop_thread(name); message_loop_thread.StartUp(); message_loop_thread.EnableRealTimeScheduling(); auto future = promise_->get_future(); auto start_time = std::chrono::steady_clock::now(); timer_->SchedulePeriodic( message_loop_thread.GetWeakPtr(), FROM_HERE, base::BindRepeating(&RepeatingTimerTest::VerifyDelayTimeAndSleep, base::Unretained(this), start_time, interval_between_tasks_ms, scheduled_tasks, task_length_ms, promise_), base::TimeDelta::FromMilliseconds(interval_between_tasks_ms)); future.get(); timer_->CancelAndWait(); } void CancelRepeatingTimerAndWait() { timer_->CancelAndWait(); } protected: void SetUp() override { ::testing::Test::SetUp(); counter_ = 0; timer_ = new RepeatingTimer(); promise_ = new std::promise(); } void TearDown() override { if (promise_ != nullptr) { delete promise_; promise_ = nullptr; } if (timer_ != nullptr) { delete timer_; timer_ = nullptr; } } int counter_; RepeatingTimer* timer_; std::promise* promise_; }; TEST_F(RepeatingTimerTest, initial_is_not_scheduled) { ASSERT_FALSE(timer_->IsScheduled()); } TEST_F(RepeatingTimerTest, 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(RepeatingTimerTest, periodic_run) { std::string name = "test_thread"; MessageLoopThread message_loop_thread(name); message_loop_thread.StartUp(); auto future = promise_->get_future(); uint32_t delay_ms = 5; int num_tasks = 200; timer_->SchedulePeriodic( message_loop_thread.GetWeakPtr(), FROM_HERE, base::BindRepeating(&RepeatingTimerTest::IncreaseTaskCounter, base::Unretained(this), num_tasks, promise_), base::TimeDelta::FromMilliseconds(delay_ms)); future.get(); ASSERT_GE(counter_, num_tasks); timer_->CancelAndWait(); } TEST_F(RepeatingTimerTest, schedule_periodic_task_zero_interval) { std::string name = "test_thread"; MessageLoopThread message_loop_thread(name); message_loop_thread.StartUp(); uint32_t interval_ms = 0; ASSERT_FALSE(timer_->SchedulePeriodic( message_loop_thread.GetWeakPtr(), FROM_HERE, base::BindRepeating(&RepeatingTimerTest::ShouldNotHappen, base::Unretained(this)), base::TimeDelta::FromMilliseconds(interval_ms))); std::this_thread::sleep_for(std::chrono::milliseconds(delay_error_ms)); } // Verify that deleting the timer without cancelling it will cancel the task TEST_F(RepeatingTimerTest, periodic_delete_without_cancel) { std::string name = "test_thread"; MessageLoopThread message_loop_thread(name); message_loop_thread.StartUp(); uint32_t delay_ms = 5; timer_->SchedulePeriodic( message_loop_thread.GetWeakPtr(), FROM_HERE, base::BindRepeating(&RepeatingTimerTest::ShouldNotHappen, base::Unretained(this)), base::TimeDelta::FromMilliseconds(delay_ms)); delete timer_; timer_ = nullptr; std::this_thread::sleep_for(std::chrono::milliseconds(delay_error_ms)); } TEST_F(RepeatingTimerTest, cancel_single_task_near_fire_no_race_condition) { std::string name = "test_thread"; MessageLoopThread message_loop_thread(name); message_loop_thread.StartUp(); uint32_t delay_ms = 5; timer_->SchedulePeriodic(message_loop_thread.GetWeakPtr(), FROM_HERE, base::DoNothing(), base::TimeDelta::FromMilliseconds(delay_ms)); std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms)); timer_->CancelAndWait(); } TEST_F(RepeatingTimerTest, cancel_periodic_task) { std::string name = "test_thread"; MessageLoopThread message_loop_thread(name); message_loop_thread.StartUp(); uint32_t delay_ms = 5; int num_tasks = 5; auto future = promise_->get_future(); timer_->SchedulePeriodic( message_loop_thread.GetWeakPtr(), FROM_HERE, base::BindRepeating(&RepeatingTimerTest::IncreaseTaskCounter, base::Unretained(this), num_tasks, promise_), base::TimeDelta::FromMilliseconds(delay_ms)); future.wait(); timer_->CancelAndWait(); std::this_thread::sleep_for( std::chrono::milliseconds(delay_ms + delay_error_ms)); int counter = counter_; std::this_thread::sleep_for( std::chrono::milliseconds(delay_ms + delay_error_ms)); ASSERT_EQ(counter, counter_); } // Schedule 10 short periodic tasks with interval 1 ms between each; verify the // functionality TEST_F(RepeatingTimerTest, schedule_multiple_delayed_tasks) { VerifyMultipleDelayedTasks(10, 0, 1); } // Schedule 10 periodic tasks with interval 2 ms between each and each takes 1 // ms; verify the functionality TEST_F(RepeatingTimerTest, schedule_multiple_delayed_slow_tasks) { VerifyMultipleDelayedTasks(10, 1, 2); } TEST_F(RepeatingTimerTest, message_loop_thread_down_cancel_scheduled_periodic_task) { std::string name = "test_thread"; MessageLoopThread message_loop_thread(name); message_loop_thread.StartUp(); std::string my_name; auto future = promise_->get_future(); uint32_t delay_ms = 5; int num_tasks = 5; timer_->SchedulePeriodic( message_loop_thread.GetWeakPtr(), FROM_HERE, base::BindRepeating(&RepeatingTimerTest::IncreaseTaskCounter, base::Unretained(this), num_tasks, promise_), base::TimeDelta::FromMilliseconds(delay_ms)); future.wait(); message_loop_thread.ShutDown(); std::this_thread::sleep_for( std::chrono::milliseconds(delay_ms + delay_error_ms)); int counter = counter_; std::this_thread::sleep_for( std::chrono::milliseconds(delay_ms + delay_error_ms)); ASSERT_EQ(counter, counter_); }