Commit dd0505ac authored by Gabriel Charette's avatar Gabriel Charette Committed by Commit Bot

[ScopedTaskEnvironment] Introduce the TimeSource trait to replace MOCK_TIME...

[ScopedTaskEnvironment] Introduce the TimeSource trait to replace MOCK_TIME MainThreadTypes and NowSource

The impl now only relies on the new trait (after a conversion from
the TimeSourceForTraits() helper).

Only basic test were added as the current tests cover the old modes
and TimeSourceForTraits(). Tests will be cleaned up after the
migration (which will reduce the number of parametrized test
instantiation :)).

Users of the old traits will be mass-migrated in a follow-up
scripted change.

R=alexclarke@chromium.org, fdoray@chromium.org

Bug: 946657
Change-Id: Ice18fe1e170a519fc40873783ad4ef18665e268d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1689400
Commit-Queue: Gabriel Charette <gab@chromium.org>
Reviewed-by: default avatarAlex Clarke <alexclarke@chromium.org>
Reviewed-by: default avatarFrançois Doray <fdoray@chromium.org>
Cr-Commit-Position: refs/heads/master@{#676390}
parent bfa93ec1
...@@ -148,15 +148,10 @@ class ScopedTaskEnvironment::MockTimeDomain ...@@ -148,15 +148,10 @@ class ScopedTaskEnvironment::MockTimeDomain
: public sequence_manager::TimeDomain, : public sequence_manager::TimeDomain,
public TickClock { public TickClock {
public: public:
MockTimeDomain(ScopedTaskEnvironment::NowSource now_source, explicit MockTimeDomain(sequence_manager::SequenceManager* sequence_manager)
sequence_manager::SequenceManager* sequence_manager)
: sequence_manager_(sequence_manager) { : sequence_manager_(sequence_manager) {
DCHECK_EQ(nullptr, current_mock_time_domain_); DCHECK_EQ(nullptr, current_mock_time_domain_);
current_mock_time_domain_ = this; current_mock_time_domain_ = this;
if (now_source == ScopedTaskEnvironment::NowSource::MAIN_THREAD_MOCK_TIME) {
time_overrides_ = std::make_unique<subtle::ScopedTimeClockOverrides>(
&MockTimeDomain::GetTime, &MockTimeDomain::GetTimeTicks, nullptr);
}
} }
~MockTimeDomain() override { ~MockTimeDomain() override {
...@@ -182,19 +177,12 @@ class ScopedTaskEnvironment::MockTimeDomain ...@@ -182,19 +177,12 @@ class ScopedTaskEnvironment::MockTimeDomain
} }
static std::unique_ptr<ScopedTaskEnvironment::MockTimeDomain> static std::unique_ptr<ScopedTaskEnvironment::MockTimeDomain>
CreateAndRegister(ScopedTaskEnvironment::MainThreadType main_thread_type, CreateAndRegister(sequence_manager::SequenceManager* sequence_manager) {
ScopedTaskEnvironment::NowSource now_source, auto mock_time_domain =
sequence_manager::SequenceManager* sequence_manager) { std::make_unique<ScopedTaskEnvironment::MockTimeDomain>(
if (main_thread_type == MainThreadType::MOCK_TIME || sequence_manager);
main_thread_type == MainThreadType::UI_MOCK_TIME || sequence_manager->RegisterTimeDomain(mock_time_domain.get());
main_thread_type == MainThreadType::IO_MOCK_TIME) { return mock_time_domain;
auto mock_time_donain =
std::make_unique<ScopedTaskEnvironment::MockTimeDomain>(
now_source, sequence_manager);
sequence_manager->RegisterTimeDomain(mock_time_donain.get());
return mock_time_donain;
}
return nullptr;
} }
void SetThreadPool(internal::ThreadPoolImpl* thread_pool, void SetThreadPool(internal::ThreadPoolImpl* thread_pool,
...@@ -361,8 +349,6 @@ class ScopedTaskEnvironment::MockTimeDomain ...@@ -361,8 +349,6 @@ class ScopedTaskEnvironment::MockTimeDomain
internal::ThreadPoolImpl* thread_pool_ = nullptr; internal::ThreadPoolImpl* thread_pool_ = nullptr;
const TestTaskTracker* thread_pool_task_tracker_ = nullptr; const TestTaskTracker* thread_pool_task_tracker_ = nullptr;
std::unique_ptr<subtle::ScopedTimeClockOverrides> time_overrides_;
// Protects |now_ticks_| // Protects |now_ticks_|
mutable Lock now_ticks_lock_; mutable Lock now_ticks_lock_;
...@@ -376,9 +362,9 @@ ScopedTaskEnvironment::MockTimeDomain* ...@@ -376,9 +362,9 @@ ScopedTaskEnvironment::MockTimeDomain*
ScopedTaskEnvironment::MockTimeDomain::current_mock_time_domain_ = nullptr; ScopedTaskEnvironment::MockTimeDomain::current_mock_time_domain_ = nullptr;
ScopedTaskEnvironment::ScopedTaskEnvironment( ScopedTaskEnvironment::ScopedTaskEnvironment(
TimeSource time_source,
MainThreadType main_thread_type, MainThreadType main_thread_type,
ThreadPoolExecutionMode thread_pool_execution_mode, ThreadPoolExecutionMode thread_pool_execution_mode,
NowSource now_source,
ThreadingMode threading_mode, ThreadingMode threading_mode,
bool subclass_creates_default_taskrunner, bool subclass_creates_default_taskrunner,
trait_helpers::NotATraitTag) trait_helpers::NotATraitTag)
...@@ -389,9 +375,15 @@ ScopedTaskEnvironment::ScopedTaskEnvironment( ...@@ -389,9 +375,15 @@ ScopedTaskEnvironment::ScopedTaskEnvironment(
sequence_manager_( sequence_manager_(
CreateSequenceManagerForMainThreadType(main_thread_type)), CreateSequenceManagerForMainThreadType(main_thread_type)),
mock_time_domain_( mock_time_domain_(
MockTimeDomain::CreateAndRegister(main_thread_type, time_source != TimeSource::SYSTEM_TIME
now_source, ? MockTimeDomain::CreateAndRegister(sequence_manager_.get())
sequence_manager_.get())), : nullptr),
time_overrides_(time_source == TimeSource::MOCK_TIME_AND_NOW
? std::make_unique<subtle::ScopedTimeClockOverrides>(
&MockTimeDomain::GetTime,
&MockTimeDomain::GetTimeTicks,
nullptr)
: nullptr),
mock_clock_(mock_time_domain_ ? std::make_unique<TickClockBasedClock>( mock_clock_(mock_time_domain_ ? std::make_unique<TickClockBasedClock>(
mock_time_domain_.get()) mock_time_domain_.get())
: nullptr), : nullptr),
...@@ -407,9 +399,6 @@ ScopedTaskEnvironment::ScopedTaskEnvironment( ...@@ -407,9 +399,6 @@ ScopedTaskEnvironment::ScopedTaskEnvironment(
TestTimeouts::action_max_timeout(), TestTimeouts::action_max_timeout(),
MakeExpectedNotRunClosure(FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE,
"RunLoop::Run() timed out."))) { "RunLoop::Run() timed out."))) {
CHECK(now_source == NowSource::REAL_TIME || mock_time_domain_)
<< "NowSource must be REAL_TIME unless we're using mock time";
CHECK(!base::ThreadTaskRunnerHandle::IsSet()); CHECK(!base::ThreadTaskRunnerHandle::IsSet());
// If |subclass_creates_default_taskrunner| is true then initialization is // If |subclass_creates_default_taskrunner| is true then initialization is
// deferred until DeferredInitFromSubclass(). // deferred until DeferredInitFromSubclass().
......
...@@ -24,6 +24,10 @@ class Clock; ...@@ -24,6 +24,10 @@ class Clock;
class FileDescriptorWatcher; class FileDescriptorWatcher;
class TickClock; class TickClock;
namespace subtle {
class ScopedTimeClockOverrides;
}
namespace test { namespace test {
// ScopedTaskEnvironment allows usage of these APIs within its scope: // ScopedTaskEnvironment allows usage of these APIs within its scope:
...@@ -73,24 +77,51 @@ class ScopedTaskEnvironment { ...@@ -73,24 +77,51 @@ class ScopedTaskEnvironment {
struct SubclassCreatesDefaultTaskRunner {}; struct SubclassCreatesDefaultTaskRunner {};
public: public:
enum class TimeSource {
// Delayed tasks and Time/TimeTicks::Now() use the real-time system clock.
SYSTEM_TIME,
// Delayed tasks use a mock clock which only advances (in increments to the
// soonest delay) when reaching idle during a FastForward*() call to this
// ScopedTaskEnvironment. Or when RunLoop::Run() goes idle on the main
// thread with no tasks remaining in the thread pool.
// Note: this does not affect threads outside this ScopedTaskEnvironment's
// purview (notably: independent base::Thread's).
MOCK_TIME,
// Mock Time/TimeTicks::Now() with the same mock clock used for delayed
// tasks. This is useful when a delayed task under test needs to check the
// amount of time that has passed since a previous sample of Now() (e.g.
// cache expiry).
//
// Warning some platform APIs are still real-time, and don't interact with
// MOCK_TIME as expected, e.g.:
// PlatformThread::Sleep
// WaitableEvent::TimedWait
// WaitableEvent::TimedWaitUntil
// ConditionVariable::TimedWait
//
// TODO(crbug.com/905412): Make MOCK_TIME always mock Time/TimeTicks::Now().
MOCK_TIME_AND_NOW,
// TODO(gab): Consider making MOCK_TIME the default mode.
DEFAULT = SYSTEM_TIME
};
enum class MainThreadType { enum class MainThreadType {
// The main thread doesn't pump system messages. // The main thread doesn't pump system messages.
DEFAULT, DEFAULT,
// The main thread doesn't pump system messages and uses a mock clock for
// delayed tasks (controllable via FastForward*() methods).
// TODO(gab): Make MOCK_TIME configurable independent of MainThreadType.
MOCK_TIME,
// The main thread pumps UI messages. // The main thread pumps UI messages.
UI, UI,
// The main thread pumps UI messages and uses a mock clock for delayed tasks
// (controllable via FastForward*() methods).
UI_MOCK_TIME,
// The main thread pumps asynchronous IO messages and supports the // The main thread pumps asynchronous IO messages and supports the
// FileDescriptorWatcher API on POSIX. // FileDescriptorWatcher API on POSIX.
IO, IO,
// The main thread pumps IO messages and uses a mock clock for delayed tasks
// (controllable via FastForward*() methods). In addition it supports the // TODO(gab): Migrate users of these APIs.
// FileDescriptorWatcher API on POSIX. // Deprecated: Use TimeSource::MOCK_TIME instead.
MOCK_TIME,
// Deprecated:: Use MainThreadType::UI/IO + TimeSource::MOCK_TIME instead.
UI_MOCK_TIME,
IO_MOCK_TIME, IO_MOCK_TIME,
}; };
...@@ -108,6 +139,8 @@ class ScopedTaskEnvironment { ...@@ -108,6 +139,8 @@ class ScopedTaskEnvironment {
DEFAULT = ASYNC DEFAULT = ASYNC
}; };
// Deprecated: Use TimeSource::MOCK_TIME_AND_NOW instead.
// TODO(gab): Migrate users.
enum class NowSource { enum class NowSource {
// base::Time::Now() and base::TimeTicks::Now() are real time. // base::Time::Now() and base::TimeTicks::Now() are real time.
REAL_TIME, REAL_TIME,
...@@ -137,6 +170,7 @@ class ScopedTaskEnvironment { ...@@ -137,6 +170,7 @@ class ScopedTaskEnvironment {
// List of traits that are valid inputs for the constructor below. // List of traits that are valid inputs for the constructor below.
struct ValidTrait { struct ValidTrait {
ValidTrait(TimeSource);
ValidTrait(MainThreadType); ValidTrait(MainThreadType);
ValidTrait(ThreadPoolExecutionMode); ValidTrait(ThreadPoolExecutionMode);
ValidTrait(NowSource); ValidTrait(NowSource);
...@@ -151,11 +185,11 @@ class ScopedTaskEnvironment { ...@@ -151,11 +185,11 @@ class ScopedTaskEnvironment {
trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value>> trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value>>
NOINLINE ScopedTaskEnvironment(ArgTypes... args) NOINLINE ScopedTaskEnvironment(ArgTypes... args)
: ScopedTaskEnvironment( : ScopedTaskEnvironment(
TimeSourceForTraits(args...),
trait_helpers::GetEnum<MainThreadType, MainThreadType::DEFAULT>( trait_helpers::GetEnum<MainThreadType, MainThreadType::DEFAULT>(
args...), args...),
trait_helpers::GetEnum<ThreadPoolExecutionMode, trait_helpers::GetEnum<ThreadPoolExecutionMode,
ThreadPoolExecutionMode::DEFAULT>(args...), ThreadPoolExecutionMode::DEFAULT>(args...),
trait_helpers::GetEnum<NowSource, NowSource::REAL_TIME>(args...),
trait_helpers::GetEnum<ThreadingMode, ThreadingMode::DEFAULT>( trait_helpers::GetEnum<ThreadingMode, ThreadingMode::DEFAULT>(
args...), args...),
trait_helpers::HasTrait<SubclassCreatesDefaultTaskRunner>(args...), trait_helpers::HasTrait<SubclassCreatesDefaultTaskRunner>(args...),
...@@ -306,21 +340,64 @@ class ScopedTaskEnvironment { ...@@ -306,21 +340,64 @@ class ScopedTaskEnvironment {
// The template constructor has to be in the header but it delegates to this // The template constructor has to be in the header but it delegates to this
// constructor to initialize all other members out-of-line. // constructor to initialize all other members out-of-line.
ScopedTaskEnvironment(MainThreadType main_thread_type, ScopedTaskEnvironment(TimeSource time_source,
MainThreadType main_thread_type,
ThreadPoolExecutionMode thread_pool_execution_mode, ThreadPoolExecutionMode thread_pool_execution_mode,
NowSource now_source,
ThreadingMode threading_mode, ThreadingMode threading_mode,
bool subclass_creates_default_taskrunner, bool subclass_creates_default_taskrunner,
trait_helpers::NotATraitTag tag); trait_helpers::NotATraitTag tag);
// Helper to extract TimeSource from a set of traits provided to
// ScopedTaskEnvironment's constructor. Helper for the migration (while
// TimeSource is optionally defined by MainThreadType or NowSource).
template <class... ArgTypes>
constexpr TimeSource TimeSourceForTraits(ArgTypes... args) {
const auto explicit_time_source =
trait_helpers::GetOptionalEnum<TimeSource>(args...);
const auto explicit_main_thread_type =
trait_helpers::GetOptionalEnum<MainThreadType>(args...);
const auto explicit_now_source =
trait_helpers::GetOptionalEnum<NowSource>(args...);
const bool requested_mock_time_via_main_thread_type =
explicit_main_thread_type &&
(*explicit_main_thread_type == MainThreadType::MOCK_TIME ||
*explicit_main_thread_type == MainThreadType::UI_MOCK_TIME ||
*explicit_main_thread_type == MainThreadType::IO_MOCK_TIME);
if (explicit_time_source) {
DCHECK(!explicit_now_source) << "NowSource is deprecated, use TimeSource";
DCHECK(!requested_mock_time_via_main_thread_type)
<< "Don't specify MOCK_TIME via MainThreadType, TimeSource is "
"sufficient";
return *explicit_time_source;
} else if (explicit_now_source &&
*explicit_now_source == NowSource::MAIN_THREAD_MOCK_TIME) {
// NowSource::MAIN_THREAD_MOCK_TIME was previously combined with
// MainThread::.*MOCK_TIME.
DCHECK(requested_mock_time_via_main_thread_type);
return TimeSource::MOCK_TIME_AND_NOW;
} else if (requested_mock_time_via_main_thread_type) {
return TimeSource::MOCK_TIME;
}
return TimeSource::DEFAULT;
}
const MainThreadType main_thread_type_; const MainThreadType main_thread_type_;
const ThreadPoolExecutionMode thread_pool_execution_mode_; const ThreadPoolExecutionMode thread_pool_execution_mode_;
const ThreadingMode threading_mode_; const ThreadingMode threading_mode_;
const bool subclass_creates_default_taskrunner_; const bool subclass_creates_default_taskrunner_;
std::unique_ptr<sequence_manager::SequenceManager> sequence_manager_; std::unique_ptr<sequence_manager::SequenceManager> sequence_manager_;
// Manages the clock under TimeSource::MOCK_TIME modes. Null in
// TimeSource::SYSTEM_TIME mode.
std::unique_ptr<MockTimeDomain> mock_time_domain_; std::unique_ptr<MockTimeDomain> mock_time_domain_;
// Overrides Time/TimeTicks::Now() under TimeSource::MOCK_TIME_AND_NOW mode.
// Null in other modes.
std::unique_ptr<subtle::ScopedTimeClockOverrides> time_overrides_;
scoped_refptr<sequence_manager::TaskQueue> task_queue_; scoped_refptr<sequence_manager::TaskQueue> task_queue_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_; scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
......
...@@ -1045,5 +1045,30 @@ INSTANTIATE_TEST_SUITE_P( ...@@ -1045,5 +1045,30 @@ INSTANTIATE_TEST_SUITE_P(
ScopedTaskEnvironmentMockedTime, ScopedTaskEnvironmentMockedTime,
::testing::Values(ScopedTaskEnvironment::MainThreadType::IO_MOCK_TIME)); ::testing::Values(ScopedTaskEnvironment::MainThreadType::IO_MOCK_TIME));
TEST_F(ScopedTaskEnvironmentTest, TimeSourceMockTime) {
ScopedTaskEnvironment scoped_task_environment(
ScopedTaskEnvironment::TimeSource::MOCK_TIME);
const TimeTicks start_time = scoped_task_environment.NowTicks();
constexpr TimeDelta kDelay = TimeDelta::FromSeconds(10);
ThreadTaskRunnerHandle::Get()->PostDelayedTask(FROM_HERE, DoNothing(),
kDelay);
scoped_task_environment.FastForwardUntilNoTasksRemain();
EXPECT_EQ(scoped_task_environment.NowTicks(), start_time + kDelay);
}
TEST_F(ScopedTaskEnvironmentTest, TimeSourceMockTimeAndNow) {
ScopedTaskEnvironment scoped_task_environment(
ScopedTaskEnvironment::TimeSource::MOCK_TIME_AND_NOW);
const TimeTicks start_time = scoped_task_environment.NowTicks();
EXPECT_EQ(TimeTicks::Now(), start_time);
constexpr TimeDelta kDelay = TimeDelta::FromSeconds(10);
scoped_task_environment.FastForwardBy(kDelay);
EXPECT_EQ(TimeTicks::Now(), start_time + kDelay);
}
} // namespace test } // namespace test
} // namespace base } // namespace base
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