#include "base/task/thread_pool/delayed_task_manager.h"
#include <memory>
#include <utility>
#include "base/cancelable_callback.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/task_features.h"
#include "base/task/thread_pool/task.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base::internal {
namespace {
constexpr TimeDelta kLongerDelay = Hours(3);
constexpr TimeDelta kLongDelay = Hours(1);
constexpr TimeDelta kLeeway = base::kDefaultLeeway;
class MockCallback {
public:
MOCK_METHOD0(Run, void());
};
void PostTaskNow(Task task) {
std::move(task.task).Run();
}
Task ConstructMockedTask(testing::StrictMock<MockCallback>& mock_task,
TimeTicks now,
TimeDelta delay) {
Task task(FROM_HERE, BindOnce(&MockCallback::Run, Unretained(&mock_task)),
now, delay);
return task;
}
Task ConstructMockedTask(testing::StrictMock<MockCallback>& mock_task,
TimeTicks now,
TimeTicks delayed_run_time,
subtle::DelayPolicy delay_policy) {
Task task(FROM_HERE, BindOnce(&MockCallback::Run, Unretained(&mock_task)),
now, delayed_run_time, kLeeway, delay_policy);
return task;
}
class ThreadPoolDelayedTaskManagerTest : public testing::Test {
public:
ThreadPoolDelayedTaskManagerTest(const ThreadPoolDelayedTaskManagerTest&) =
delete;
ThreadPoolDelayedTaskManagerTest& operator=(
const ThreadPoolDelayedTaskManagerTest&) = delete;
protected:
ThreadPoolDelayedTaskManagerTest() {
service_thread_task_runner_->AdvanceMockTickClock(Milliseconds(1));
task_ = ConstructMockedTask(
mock_callback_, service_thread_task_runner_->NowTicks(), kLongDelay);
}
~ThreadPoolDelayedTaskManagerTest() override = default;
const scoped_refptr<TestMockTimeTaskRunner> service_thread_task_runner_ =
MakeRefCounted<TestMockTimeTaskRunner>();
DelayedTaskManager delayed_task_manager_{
service_thread_task_runner_->GetMockTickClock()};
testing::StrictMock<MockCallback> mock_callback_;
Task task_;
};
}
TEST_F(ThreadPoolDelayedTaskManagerTest, DelayedTaskDoesNotRunBeforeStart) {
delayed_task_manager_.AddDelayedTask(std::move(task_),
BindOnce(&PostTaskNow));
service_thread_task_runner_->FastForwardBy(kLongDelay);
}
TEST_F(ThreadPoolDelayedTaskManagerTest,
DelayedTaskPostedBeforeStartExpiresAfterStartRunsOnExpire) {
delayed_task_manager_.AddDelayedTask(std::move(task_),
BindOnce(&PostTaskNow));
delayed_task_manager_.Start(service_thread_task_runner_);
service_thread_task_runner_->RunUntilIdle();
EXPECT_CALL(mock_callback_, Run());
service_thread_task_runner_->FastForwardBy(kLongDelay);
}
TEST_F(ThreadPoolDelayedTaskManagerTest,
DelayedTaskPostedBeforeStartExpiresBeforeStartRunsOnStart) {
delayed_task_manager_.AddDelayedTask(std::move(task_),
BindOnce(&PostTaskNow));
service_thread_task_runner_->RunUntilIdle();
service_thread_task_runner_->FastForwardBy(kLongDelay);
EXPECT_CALL(mock_callback_, Run());
delayed_task_manager_.Start(service_thread_task_runner_);
service_thread_task_runner_->RunUntilIdle();
}
TEST_F(ThreadPoolDelayedTaskManagerTest, DelayedTaskDoesNotRunTooEarly) {
delayed_task_manager_.Start(service_thread_task_runner_);
delayed_task_manager_.AddDelayedTask(std::move(task_),
BindOnce(&PostTaskNow));
service_thread_task_runner_->RunUntilIdle();
}
TEST_F(ThreadPoolDelayedTaskManagerTest, DelayedTaskRunsAfterDelay) {
delayed_task_manager_.Start(service_thread_task_runner_);
delayed_task_manager_.AddDelayedTask(std::move(task_),
BindOnce(&PostTaskNow));
EXPECT_CALL(mock_callback_, Run());
service_thread_task_runner_->FastForwardBy(kLongDelay);
}
TEST_F(ThreadPoolDelayedTaskManagerTest,
DelayedTaskRunsAtTime_FlexiblePreferEarly) {
const TimeDelta kUnalignedLongDelay = kLongDelay + Milliseconds(1);
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(kAlignWakeUps);
delayed_task_manager_.Start(service_thread_task_runner_);
TimeTicks now = service_thread_task_runner_->NowTicks();
Task task =
ConstructMockedTask(mock_callback_, now, now + kUnalignedLongDelay,
base::subtle::DelayPolicy::kFlexiblePreferEarly);
delayed_task_manager_.AddDelayedTask(std::move(task), BindOnce(&PostTaskNow));
service_thread_task_runner_->FastForwardBy(kUnalignedLongDelay - kLeeway -
Milliseconds(1));
testing::Mock::VerifyAndClear(&mock_callback_);
EXPECT_CALL(mock_callback_, Run());
service_thread_task_runner_->FastForwardBy(kLeeway + Milliseconds(1));
}
TEST_F(ThreadPoolDelayedTaskManagerTest, DelayedTaskRunsAfterCancelled) {
static_assert(kLongerDelay > kLongDelay, "");
delayed_task_manager_.Start(service_thread_task_runner_);
CancelableOnceClosure cancelable_closure(DoNothing());
bool post_cancelable_task_now_invoked = false;
Task cancelable_task(FROM_HERE, cancelable_closure.callback(),
TimeTicks::Now(), kLongerDelay);
auto post_cancelable_task_now = BindLambdaForTesting(
[&](Task task) { post_cancelable_task_now_invoked = true; });
delayed_task_manager_.AddDelayedTask(std::move(cancelable_task),
post_cancelable_task_now);
delayed_task_manager_.AddDelayedTask(std::move(task_),
BindOnce(&PostTaskNow));
cancelable_closure.Cancel();
EXPECT_CALL(mock_callback_, Run());
service_thread_task_runner_->FastForwardBy(kLongDelay);
EXPECT_TRUE(post_cancelable_task_now_invoked);
}
TEST_F(ThreadPoolDelayedTaskManagerTest, DelayedTasksRunAfterDelay) {
delayed_task_manager_.Start(service_thread_task_runner_);
testing::StrictMock<MockCallback> mock_callback_a;
Task task_a = ConstructMockedTask(
mock_callback_a, service_thread_task_runner_->NowTicks(), Hours(1));
testing::StrictMock<MockCallback> mock_callback_b;
Task task_b = ConstructMockedTask(
mock_callback_b, service_thread_task_runner_->NowTicks(), Hours(2));
testing::StrictMock<MockCallback> mock_callback_c;
Task task_c = ConstructMockedTask(
mock_callback_c, service_thread_task_runner_->NowTicks(), Hours(1));
delayed_task_manager_.AddDelayedTask(std::move(task_a),
BindOnce(&PostTaskNow));
delayed_task_manager_.AddDelayedTask(std::move(task_b),
BindOnce(&PostTaskNow));
delayed_task_manager_.AddDelayedTask(std::move(task_c),
BindOnce(&PostTaskNow));
service_thread_task_runner_->RunUntilIdle();
EXPECT_CALL(mock_callback_a, Run());
EXPECT_CALL(mock_callback_c, Run());
service_thread_task_runner_->FastForwardBy(Hours(1));
testing::Mock::VerifyAndClear(&mock_callback_a);
testing::Mock::VerifyAndClear(&mock_callback_c);
EXPECT_CALL(mock_callback_b, Run());
service_thread_task_runner_->FastForwardBy(Hours(1));
testing::Mock::VerifyAndClear(&mock_callback_b);
}
TEST_F(ThreadPoolDelayedTaskManagerTest,
DelayedTasksRunAtTime_MixedDelayPolicy) {
delayed_task_manager_.Start(service_thread_task_runner_);
TimeTicks now = service_thread_task_runner_->NowTicks();
testing::StrictMock<MockCallback> mock_callback_a;
Task task_a =
ConstructMockedTask(mock_callback_a, now, now + Milliseconds(8),
base::subtle::DelayPolicy::kFlexibleNoSooner);
testing::StrictMock<MockCallback> mock_callback_b;
Task task_b =
ConstructMockedTask(mock_callback_b, now, now + Milliseconds(10),
base::subtle::DelayPolicy::kPrecise);
delayed_task_manager_.AddDelayedTask(std::move(task_a),
BindOnce(&PostTaskNow));
EXPECT_EQ(base::subtle::DelayPolicy::kFlexibleNoSooner,
delayed_task_manager_.TopTaskDelayPolicyForTesting());
delayed_task_manager_.AddDelayedTask(std::move(task_b),
BindOnce(&PostTaskNow));
EXPECT_EQ(base::subtle::DelayPolicy::kPrecise,
delayed_task_manager_.TopTaskDelayPolicyForTesting());
service_thread_task_runner_->FastForwardBy(Milliseconds(10) -
Milliseconds(1));
service_thread_task_runner_->RunUntilIdle();
EXPECT_CALL(mock_callback_b, Run());
EXPECT_CALL(mock_callback_a, Run());
service_thread_task_runner_->FastForwardBy(Milliseconds(1));
}
TEST_F(ThreadPoolDelayedTaskManagerTest, PostTaskDuringStart) {
Thread other_thread("Test");
other_thread.StartAndWaitForTesting();
WaitableEvent task_posted;
other_thread.task_runner()->PostTask(FROM_HERE, BindLambdaForTesting([&] {
delayed_task_manager_.AddDelayedTask(
std::move(task_),
BindOnce(&PostTaskNow));
task_posted.Signal();
}));
delayed_task_manager_.Start(service_thread_task_runner_);
task_posted.Wait();
EXPECT_CALL(mock_callback_, Run());
service_thread_task_runner_->FastForwardBy(kLongDelay);
}
}