#include "base/test/test_mock_time_task_runner.h"
#include <utility>
#include "base/check_op.h"
#include "base/containers/circular_deque.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/task/single_thread_task_runner.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace base {
class TestMockTimeTaskRunner::NonOwningProxyTaskRunner
: public SingleThreadTaskRunner {
public:
explicit NonOwningProxyTaskRunner(SingleThreadTaskRunner* target)
: target_(target) {
DCHECK(target_);
}
NonOwningProxyTaskRunner(const NonOwningProxyTaskRunner&) = delete;
NonOwningProxyTaskRunner& operator=(const NonOwningProxyTaskRunner&) = delete;
void Detach() {
AutoLock scoped_lock(lock_);
target_ = nullptr;
}
bool RunsTasksInCurrentSequence() const override {
AutoLock scoped_lock(lock_);
if (target_)
return target_->RunsTasksInCurrentSequence();
return thread_checker_.CalledOnValidThread();
}
bool PostDelayedTask(const Location& from_here,
OnceClosure task,
TimeDelta delay) override {
AutoLock scoped_lock(lock_);
if (target_)
return target_->PostDelayedTask(from_here, std::move(task), delay);
return false;
}
bool PostNonNestableDelayedTask(const Location& from_here,
OnceClosure task,
TimeDelta delay) override {
AutoLock scoped_lock(lock_);
if (target_) {
return target_->PostNonNestableDelayedTask(from_here, std::move(task),
delay);
}
return false;
}
private:
friend class RefCountedThreadSafe<NonOwningProxyTaskRunner>;
~NonOwningProxyTaskRunner() override = default;
mutable Lock lock_;
raw_ptr<SingleThreadTaskRunner> target_;
ThreadCheckerImpl thread_checker_;
};
struct TestMockTimeTaskRunner::TestOrderedPendingTask
: public base::TestPendingTask {
TestOrderedPendingTask();
TestOrderedPendingTask(const Location& location,
OnceClosure task,
TimeTicks post_time,
TimeDelta delay,
size_t ordinal,
TestNestability nestability);
TestOrderedPendingTask(const TestOrderedPendingTask&) = delete;
TestOrderedPendingTask& operator=(const TestOrderedPendingTask&) = delete;
TestOrderedPendingTask(TestOrderedPendingTask&&);
~TestOrderedPendingTask();
TestOrderedPendingTask& operator=(TestOrderedPendingTask&&);
size_t ordinal;
};
TestMockTimeTaskRunner::TestOrderedPendingTask::TestOrderedPendingTask()
: ordinal(0) {
}
TestMockTimeTaskRunner::TestOrderedPendingTask::TestOrderedPendingTask(
TestOrderedPendingTask&&) = default;
TestMockTimeTaskRunner::TestOrderedPendingTask::TestOrderedPendingTask(
const Location& location,
OnceClosure task,
TimeTicks post_time,
TimeDelta delay,
size_t ordinal,
TestNestability nestability)
: base::TestPendingTask(location,
std::move(task),
post_time,
delay,
nestability),
ordinal(ordinal) {}
TestMockTimeTaskRunner::TestOrderedPendingTask::~TestOrderedPendingTask() =
default;
TestMockTimeTaskRunner::TestOrderedPendingTask&
TestMockTimeTaskRunner::TestOrderedPendingTask::operator=(
TestOrderedPendingTask&&) = default;
TestMockTimeTaskRunner::ScopedContext::ScopedContext(
scoped_refptr<TestMockTimeTaskRunner> scope)
: single_thread_task_runner_current_default_handle_override_(scope) {
scope->RunUntilIdle();
}
TestMockTimeTaskRunner::ScopedContext::~ScopedContext() = default;
bool TestMockTimeTaskRunner::TemporalOrder::operator()(
const TestOrderedPendingTask& first_task,
const TestOrderedPendingTask& second_task) const {
if (first_task.GetTimeToRun() == second_task.GetTimeToRun())
return first_task.ordinal > second_task.ordinal;
return first_task.GetTimeToRun() > second_task.GetTimeToRun();
}
TestMockTimeTaskRunner::TestMockTimeTaskRunner(Type type)
: TestMockTimeTaskRunner(Time::UnixEpoch(), TimeTicks(), type) {}
TestMockTimeTaskRunner::TestMockTimeTaskRunner(Time start_time,
TimeTicks start_ticks,
Type type)
: now_(start_time),
now_ticks_(start_ticks),
tasks_lock_cv_(&tasks_lock_),
proxy_task_runner_(MakeRefCounted<NonOwningProxyTaskRunner>(this)),
mock_clock_(this) {
if (type == Type::kBoundToThread) {
RunLoop::RegisterDelegateForCurrentThread(this);
thread_task_runner_handle_ =
std::make_unique<SingleThreadTaskRunner::CurrentDefaultHandle>(
proxy_task_runner_);
}
}
TestMockTimeTaskRunner::~TestMockTimeTaskRunner() {
proxy_task_runner_->Detach();
}
void TestMockTimeTaskRunner::FastForwardBy(TimeDelta delta) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_GE(delta, TimeDelta());
const TimeTicks original_now_ticks = NowTicks();
ProcessTasksNoLaterThan(delta);
ForwardClocksUntilTickTime(original_now_ticks + delta);
}
void TestMockTimeTaskRunner::AdvanceMockTickClock(TimeDelta delta) {
ForwardClocksUntilTickTime(NowTicks() + delta);
}
void TestMockTimeTaskRunner::AdvanceWallClock(TimeDelta delta) {
now_ += delta;
OnAfterTimePassed();
}
void TestMockTimeTaskRunner::RunUntilIdle() {
DCHECK(thread_checker_.CalledOnValidThread());
ProcessTasksNoLaterThan(TimeDelta());
}
void TestMockTimeTaskRunner::ProcessNextNTasks(int n) {
DCHECK(thread_checker_.CalledOnValidThread());
ProcessTasksNoLaterThan(TimeDelta::Max(), n);
}
void TestMockTimeTaskRunner::FastForwardUntilNoTasksRemain() {
DCHECK(thread_checker_.CalledOnValidThread());
ProcessTasksNoLaterThan(TimeDelta::Max());
}
void TestMockTimeTaskRunner::ClearPendingTasks() {
AutoLock scoped_lock(tasks_lock_);
while (!tasks_.empty()) {
TaskPriorityQueue cleanup_tasks;
tasks_.swap(cleanup_tasks);
AutoUnlock scoped_unlock(tasks_lock_);
while (!cleanup_tasks.empty())
cleanup_tasks.pop();
}
}
Time TestMockTimeTaskRunner::Now() const {
AutoLock scoped_lock(tasks_lock_);
return now_;
}
TimeTicks TestMockTimeTaskRunner::NowTicks() const {
AutoLock scoped_lock(tasks_lock_);
return now_ticks_;
}
Clock* TestMockTimeTaskRunner::GetMockClock() const {
DCHECK(thread_checker_.CalledOnValidThread());
return &mock_clock_;
}
const TickClock* TestMockTimeTaskRunner::GetMockTickClock() const {
DCHECK(thread_checker_.CalledOnValidThread());
return &mock_clock_;
}
base::circular_deque<TestPendingTask>
TestMockTimeTaskRunner::TakePendingTasks() {
AutoLock scoped_lock(tasks_lock_);
base::circular_deque<TestPendingTask> tasks;
while (!tasks_.empty()) {
if (!tasks_.top().task.IsCancelled()) {
tasks.push_back(
std::move(const_cast<TestOrderedPendingTask&>(tasks_.top())));
}
tasks_.pop();
}
return tasks;
}
bool TestMockTimeTaskRunner::HasPendingTask() {
DCHECK(thread_checker_.CalledOnValidThread());
AutoLock scoped_lock(tasks_lock_);
while (!tasks_.empty() && tasks_.top().task.IsCancelled())
tasks_.pop();
return !tasks_.empty();
}
size_t TestMockTimeTaskRunner::GetPendingTaskCount() {
DCHECK(thread_checker_.CalledOnValidThread());
AutoLock scoped_lock(tasks_lock_);
TaskPriorityQueue preserved_tasks;
while (!tasks_.empty()) {
if (!tasks_.top().task.IsCancelled()) {
preserved_tasks.push(
std::move(const_cast<TestOrderedPendingTask&>(tasks_.top())));
}
tasks_.pop();
}
tasks_.swap(preserved_tasks);
return tasks_.size();
}
TimeDelta TestMockTimeTaskRunner::NextPendingTaskDelay() {
DCHECK(thread_checker_.CalledOnValidThread());
AutoLock scoped_lock(tasks_lock_);
while (!tasks_.empty() && tasks_.top().task.IsCancelled())
tasks_.pop();
return tasks_.empty() ? TimeDelta::Max()
: tasks_.top().GetTimeToRun() - now_ticks_;
}
void TestMockTimeTaskRunner::DetachFromThread() {
thread_checker_.DetachFromThread();
}
bool TestMockTimeTaskRunner::RunsTasksInCurrentSequence() const {
return thread_checker_.CalledOnValidThread();
}
bool TestMockTimeTaskRunner::PostDelayedTask(const Location& from_here,
OnceClosure task,
TimeDelta delay) {
AutoLock scoped_lock(tasks_lock_);
tasks_.push(TestOrderedPendingTask(from_here, std::move(task), now_ticks_,
delay, next_task_ordinal_++,
TestPendingTask::NESTABLE));
tasks_lock_cv_.Signal();
return true;
}
bool TestMockTimeTaskRunner::PostDelayedTaskAt(
subtle::PostDelayedTaskPassKey,
const Location& from_here,
OnceClosure task,
TimeTicks delayed_run_time,
subtle::DelayPolicy deadline_policy) {
return PostDelayedTask(
from_here, std::move(task),
delayed_run_time.is_null() ? TimeDelta() : delayed_run_time - now_ticks_);
}
bool TestMockTimeTaskRunner::PostNonNestableDelayedTask(
const Location& from_here,
OnceClosure task,
TimeDelta delay) {
return PostDelayedTask(from_here, std::move(task), delay);
}
void TestMockTimeTaskRunner::OnBeforeSelectingTask() {
}
void TestMockTimeTaskRunner::OnAfterTimePassed() {
}
void TestMockTimeTaskRunner::OnAfterTaskRun() {
}
void TestMockTimeTaskRunner::ProcessTasksNoLaterThan(TimeDelta max_delta,
int limit) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_GE(max_delta, TimeDelta());
absl::optional<SingleThreadTaskRunner::CurrentHandleOverrideForTesting>
ttrh_override;
if (!SingleThreadTaskRunner::HasCurrentDefault() ||
SingleThreadTaskRunner::GetCurrentDefault() != proxy_task_runner_.get()) {
ttrh_override.emplace(proxy_task_runner_.get());
}
const TimeTicks original_now_ticks = NowTicks();
for (int i = 0; !quit_run_loop_ && (limit < 0 || i < limit); i++) {
OnBeforeSelectingTask();
TestPendingTask task_info;
if (!DequeueNextTask(original_now_ticks, max_delta, &task_info))
break;
if (task_info.task.IsCancelled())
continue;
ForwardClocksUntilTickTime(task_info.GetTimeToRun());
std::move(task_info.task).Run();
OnAfterTaskRun();
}
}
void TestMockTimeTaskRunner::ForwardClocksUntilTickTime(TimeTicks later_ticks) {
DCHECK(thread_checker_.CalledOnValidThread());
{
AutoLock scoped_lock(tasks_lock_);
if (later_ticks <= now_ticks_)
return;
now_ += later_ticks - now_ticks_;
now_ticks_ = later_ticks;
}
OnAfterTimePassed();
}
bool TestMockTimeTaskRunner::DequeueNextTask(const TimeTicks& reference,
const TimeDelta& max_delta,
TestPendingTask* next_task) {
DCHECK(thread_checker_.CalledOnValidThread());
AutoLock scoped_lock(tasks_lock_);
if (!tasks_.empty() &&
(tasks_.top().GetTimeToRun() - reference) <= max_delta) {
*next_task = std::move(const_cast<TestOrderedPendingTask&>(tasks_.top()));
tasks_.pop();
return true;
}
return false;
}
void TestMockTimeTaskRunner::Run(bool application_tasks_allowed,
TimeDelta timeout) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(application_tasks_allowed)
<< "This is a nested RunLoop instance and needs to be of "
"Type::kNestableTasksAllowed.";
TimeTicks run_until = now_ticks_ + timeout;
while (!quit_run_loop_ && now_ticks_ < run_until) {
RunUntilIdle();
if (quit_run_loop_ || ShouldQuitWhenIdle())
break;
TimeDelta auto_fast_forward_by;
{
AutoLock scoped_lock(tasks_lock_);
if (tasks_.empty()) {
while (tasks_.empty())
tasks_lock_cv_.Wait();
continue;
}
auto_fast_forward_by =
std::min(run_until, tasks_.top().GetTimeToRun()) - now_ticks_;
}
FastForwardBy(auto_fast_forward_by);
}
quit_run_loop_ = false;
}
void TestMockTimeTaskRunner::Quit() {
DCHECK(thread_checker_.CalledOnValidThread());
quit_run_loop_ = true;
}
void TestMockTimeTaskRunner::EnsureWorkScheduled() {
}
TimeTicks TestMockTimeTaskRunner::MockClock::NowTicks() const {
return task_runner_->NowTicks();
}
Time TestMockTimeTaskRunner::MockClock::Now() const {
return task_runner_->Now();
}
}