Commit aec1fa66 authored by Matt Mueller's avatar Matt Mueller Committed by Commit Bot

Add base::TaskEnvironment::AdvanceClock() which advances the mock time without running tasks.

Change-Id: Ide200b9cd0cf7f483381eeb63a8fafbee14df34e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1814640
Commit-Queue: Matt Mueller <mattm@chromium.org>
Reviewed-by: default avatarGabriel Charette <gab@chromium.org>
Cr-Commit-Position: refs/heads/master@{#698600}
parent 87f91553
......@@ -166,6 +166,12 @@ class TaskEnvironment::MockTimeDomain : public sequence_manager::TimeDomain,
return TimeDomain::NextScheduledRunTime();
}
void AdvanceClock(TimeDelta delta) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
AutoLock lock(now_ticks_lock_);
now_ticks_ += delta;
}
static std::unique_ptr<TaskEnvironment::MockTimeDomain> CreateAndRegister(
sequence_manager::SequenceManager* sequence_manager) {
auto mock_time_domain =
......@@ -646,6 +652,13 @@ void TaskEnvironment::FastForwardUntilNoTasksRemain() {
FastForwardBy(TimeDelta::Max());
}
void TaskEnvironment::AdvanceClock(TimeDelta delta) {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
DCHECK(mock_time_domain_);
DCHECK_GE(delta, TimeDelta());
mock_time_domain_->AdvanceClock(delta);
}
const TickClock* TaskEnvironment::GetMockTickClock() const {
DCHECK(mock_time_domain_);
return mock_time_domain_.get();
......
......@@ -224,6 +224,12 @@ class TaskEnvironment {
// to spin forever (any RepeatingTimer will cause this).
void FastForwardUntilNoTasksRemain();
// Only valid for instances using TimeSource::MOCK_TIME. Advances virtual time
// by |delta|. Unlike FastForwardBy, this does not run tasks. Prefer
// FastForwardBy() when possible but this can be useful when testing blocked
// pending tasks where being idle (required to fast-forward) is not possible.
void AdvanceClock(TimeDelta delta);
// Only valid for instances using TimeSource::MOCK_TIME. Returns a
// TickClock whose time is updated by FastForward(By|UntilNoTasksRemain).
const TickClock* GetMockTickClock() const;
......
......@@ -451,6 +451,63 @@ TEST_F(TaskEnvironmentTest, FastForwardAdvanceTimeTicks) {
EXPECT_EQ(start_time + kDelay, base::TimeTicks::Now());
}
TEST_F(TaskEnvironmentTest, AdvanceClockAdvanceTickClock) {
constexpr base::TimeDelta kDelay = TimeDelta::FromSeconds(42);
TaskEnvironment task_environment(TaskEnvironment::TimeSource::MOCK_TIME);
const base::TickClock* tick_clock = task_environment.GetMockTickClock();
const base::TimeTicks start_time = tick_clock->NowTicks();
task_environment.AdvanceClock(kDelay);
EXPECT_EQ(start_time + kDelay, tick_clock->NowTicks());
}
TEST_F(TaskEnvironmentTest, AdvanceClockAdvanceMockClock) {
constexpr base::TimeDelta kDelay = TimeDelta::FromSeconds(42);
TaskEnvironment task_environment(TaskEnvironment::TimeSource::MOCK_TIME);
const Clock* clock = task_environment.GetMockClock();
const Time start_time = clock->Now();
task_environment.AdvanceClock(kDelay);
EXPECT_EQ(start_time + kDelay, clock->Now());
}
TEST_F(TaskEnvironmentTest, AdvanceClockAdvanceTime) {
constexpr base::TimeDelta kDelay = TimeDelta::FromSeconds(42);
TaskEnvironment task_environment(TaskEnvironment::TimeSource::MOCK_TIME);
const Time start_time = base::Time::Now();
task_environment.AdvanceClock(kDelay);
EXPECT_EQ(start_time + kDelay, base::Time::Now());
}
TEST_F(TaskEnvironmentTest, AdvanceClockAdvanceTimeTicks) {
constexpr base::TimeDelta kDelay = TimeDelta::FromSeconds(42);
TaskEnvironment task_environment(TaskEnvironment::TimeSource::MOCK_TIME);
const TimeTicks start_time = base::TimeTicks::Now();
task_environment.AdvanceClock(kDelay);
EXPECT_EQ(start_time + kDelay, base::TimeTicks::Now());
}
TEST_F(TaskEnvironmentTest, AdvanceClockDoesNotRunTasks) {
TaskEnvironment task_environment(TaskEnvironment::TimeSource::MOCK_TIME);
constexpr base::TimeDelta kTaskDelay = TimeDelta::FromDays(1);
ThreadTaskRunnerHandle::Get()->PostDelayedTask(FROM_HERE, base::DoNothing(),
kTaskDelay);
EXPECT_EQ(1U, task_environment.GetPendingMainThreadTaskCount());
EXPECT_TRUE(task_environment.NextTaskIsDelayed());
task_environment.AdvanceClock(kTaskDelay);
// The task is still pending, but is now runnable.
EXPECT_EQ(1U, task_environment.GetPendingMainThreadTaskCount());
EXPECT_FALSE(task_environment.NextTaskIsDelayed());
}
// Verify that FastForwardBy() runs existing immediate tasks before advancing,
// then advances to the next delayed task, runs it, then advances the remainder
// of time when out of tasks.
......
......@@ -189,9 +189,15 @@ trait. This makes it such that delayed tasks and `base::Time::Now()` +
Under this mode, the mock clock will start at the current system time but will
then only advance when explicitly requested by `TaskEnvironment::FastForward*()`
methods *or* when `RunLoop::Run()` is running and all managed threads become
idle (auto-advances to the soonest delayed task, if any, amongst all managed
threads).
and `TaskEnvironment::AdvanceClock()` methods *or* when `RunLoop::Run()` is
running and all managed threads become idle (auto-advances to the soonest
delayed task, if any, amongst all managed threads).
`TaskEnvironment::FastForwardBy()` repeatedly runs existing immediately
executable tasks until idle and then advances the mock clock incrementally to
run the next delayed task within the time delta. It may advance time by more
than the requested amount if running the tasks causes nested
time-advancing-method calls.
This makes it possible to test code with flush intervals, repeating timers,
timeouts, etc. without any test-specific seams in the product code, e.g.:
......@@ -231,6 +237,43 @@ TEST_F(FooStorageTest, Set) {
}
```
In contrast, `TaskEnvironment::AdvanceClock()` simply advances the mock time by
the requested amount, and does not run tasks. This may be useful in
cases where `TaskEnvironment::FastForwardBy()` would result in a livelock. For
example, if one task is blocked on a `WaitableEvent` and there is a delayed
task that would signal the event (e.g., a timeout), then
`TaskEnvironment::FastForwardBy()` will never complete. In this case, you could
advance the clock enough that the delayed task becomes runnable, and then
`TaskEnvironment::RunUntilIdle()` would run the delayed task, signalling the
event.
```
TEST(FooTest, TimeoutExceeded)
{
base::test::TaskEnvironment task_environment{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
base::WaitableEvent event;
base::RunLoop run_loop;
base::PostTaskAndReply(
FROM_HERE, {base::ThreadPool(), base::MayBlock()},
base::BindOnce(&BlocksOnEvent, base::Unretained(&event)),
run_loop.QuitClosure());
base::PostDelayedTask(
FROM_HERE, {base::ThreadPool()},
base::BindOnce(&WaitableEvent::Signal, base::Unretained(&event)),
kTimeout);
// Can't use task_environment.FastForwardBy() since BlocksOnEvent blocks
// and the task pool will not become idle.
// Instead, advance time until the timeout task becomes runnable.
task_environment.AdvanceClock(kTimeout);
// Now the timeout task is runable.
task_environment.RunUntilIdle();
// The reply task should already have been executed, but run the run_loop to
// verify.
run_loop.Run();
}
```
### MainThreadType trait
The average component only cares about running its tasks and
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment