Commit 7e458c67 authored by Oleg Davydov's avatar Oleg Davydov Committed by Commit Bot

Add test for SessionLengthLimiter suspend case

When device is suspened during the session, session length limit should
apply correctly. The new test simulates suspend and resume and verifies
that session will be terminated at correct time.

Bug: b:160454261
Change-Id: I382d5bd3c7b5a64e49783ed97eec08e19ff91810
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2283591Reviewed-by: default avatarNico Weber <thakis@chromium.org>
Reviewed-by: default avatarOwen Min <zmin@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Commit-Queue: Oleg Davydov <burunduk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#805269}
parent d8bcd4ad
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/power_monitor/test/fake_power_monitor_source.h"
#include <memory>
#include "base/power_monitor/power_monitor.h"
#include "base/power_monitor/power_monitor_source.h"
namespace base {
namespace test {
class FakePowerMonitorSource : public base::PowerMonitorSource {
public:
void Resume() { ProcessPowerEvent(RESUME_EVENT); }
void Suspend() { ProcessPowerEvent(SUSPEND_EVENT); }
// base::PowerMonitorSource:
bool IsOnBatteryPowerImpl() override { return false; }
};
// ScopedFakePowerMonitorSource implementation
ScopedFakePowerMonitorSource::ScopedFakePowerMonitorSource() {
auto fake_power_monitor_source = std::make_unique<FakePowerMonitorSource>();
fake_power_monitor_source_ = fake_power_monitor_source.get();
base::PowerMonitor::Initialize(std::move(fake_power_monitor_source));
}
ScopedFakePowerMonitorSource::~ScopedFakePowerMonitorSource() {
base::PowerMonitor::ShutdownForTesting();
}
void ScopedFakePowerMonitorSource::Resume() {
fake_power_monitor_source_->Resume();
}
void ScopedFakePowerMonitorSource::Suspend() {
fake_power_monitor_source_->Suspend();
}
} // namespace test
} // namespace base
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_POWER_MONITOR_TEST_FAKE_POWER_MONITOR_SOURCE_H_
#define BASE_POWER_MONITOR_TEST_FAKE_POWER_MONITOR_SOURCE_H_
namespace base {
namespace test {
// Use FakePowerMonitorSource via ScopedFakePowerMonitorSource wrapper when you
// need to simulate power events (suspend and resume).
class FakePowerMonitorSource;
class ScopedFakePowerMonitorSource {
public:
ScopedFakePowerMonitorSource();
~ScopedFakePowerMonitorSource();
ScopedFakePowerMonitorSource(const ScopedFakePowerMonitorSource&) = delete;
ScopedFakePowerMonitorSource& operator=(const ScopedFakePowerMonitorSource&) =
delete;
// Use this method to send a power resume event.
void Resume();
// Use this method to send a power suspend event.
void Suspend();
private:
// Owned by PowerMonitor.
FakePowerMonitorSource* fake_power_monitor_source_ = nullptr;
};
} // namespace test
} // namespace base
#endif // BASE_POWER_MONITOR_TEST_FAKE_POWER_MONITOR_SOURCE_H_
...@@ -34,6 +34,8 @@ static_library("test_config") { ...@@ -34,6 +34,8 @@ static_library("test_config") {
static_library("test_support") { static_library("test_support") {
testonly = true testonly = true
sources = [ sources = [
"../power_monitor/test/fake_power_monitor_source.cc",
"../power_monitor/test/fake_power_monitor_source.h",
"../task/sequence_manager/test/fake_task.cc", "../task/sequence_manager/test/fake_task.cc",
"../task/sequence_manager/test/fake_task.h", "../task/sequence_manager/test/fake_task.h",
"../task/sequence_manager/test/mock_time_domain.cc", "../task/sequence_manager/test/mock_time_domain.cc",
......
...@@ -233,6 +233,11 @@ void TestMockTimeTaskRunner::AdvanceMockTickClock(TimeDelta delta) { ...@@ -233,6 +233,11 @@ void TestMockTimeTaskRunner::AdvanceMockTickClock(TimeDelta delta) {
ForwardClocksUntilTickTime(NowTicks() + delta); ForwardClocksUntilTickTime(NowTicks() + delta);
} }
void TestMockTimeTaskRunner::AdvanceWallClock(TimeDelta delta) {
now_ += delta;
OnAfterTimePassed();
}
void TestMockTimeTaskRunner::RunUntilIdle() { void TestMockTimeTaskRunner::RunUntilIdle() {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
ProcessAllTasksNoLaterThan(TimeDelta()); ProcessAllTasksNoLaterThan(TimeDelta());
......
...@@ -149,6 +149,12 @@ class TestMockTimeTaskRunner : public SingleThreadTaskRunner, ...@@ -149,6 +149,12 @@ class TestMockTimeTaskRunner : public SingleThreadTaskRunner,
// Fast-forwards virtual time by |delta| but not causing any task execution. // Fast-forwards virtual time by |delta| but not causing any task execution.
void AdvanceMockTickClock(TimeDelta delta); void AdvanceMockTickClock(TimeDelta delta);
// Fast-forward virtual time, but not tick time. May be useful for testing
// timers when simulating suspend/resume or time adjustments. As it doesn't
// advance tick time, no tasks are automatically processed
// (ProcessAllTasksNoLaterThan is not called).
void AdvanceWallClock(TimeDelta delta);
// Fast-forwards virtual time just until all tasks are executed. // Fast-forwards virtual time just until all tasks are executed.
void FastForwardUntilNoTasksRemain(); void FastForwardUntilNoTasksRemain();
......
...@@ -7,8 +7,7 @@ ...@@ -7,8 +7,7 @@
#include <memory> #include <memory>
#include <utility> #include <utility>
#include "base/power_monitor/power_monitor.h" #include "base/power_monitor/test/fake_power_monitor_source.h"
#include "base/power_monitor/power_monitor_source.h"
#include "base/test/mock_callback.h" #include "base/test/mock_callback.h"
#include "base/test/simple_test_clock.h" #include "base/test/simple_test_clock.h"
#include "base/test/task_environment.h" #include "base/test/task_environment.h"
...@@ -17,32 +16,8 @@ ...@@ -17,32 +16,8 @@
namespace util { namespace util {
namespace {
class StubPowerMonitorSource : public base::PowerMonitorSource {
public:
// Use this method to send a power resume event.
void Resume() { ProcessPowerEvent(RESUME_EVENT); }
// Use this method to send a power suspend event.
void Suspend() { ProcessPowerEvent(SUSPEND_EVENT); }
// base::PowerMonitorSource:
bool IsOnBatteryPowerImpl() override { return false; }
};
} // namespace
class WallClockTimerTest : public ::testing::Test { class WallClockTimerTest : public ::testing::Test {
protected: protected:
WallClockTimerTest() {
auto mock_power_monitor_source = std::make_unique<StubPowerMonitorSource>();
mock_power_monitor_source_ = mock_power_monitor_source.get();
base::PowerMonitor::Initialize(std::move(mock_power_monitor_source));
}
~WallClockTimerTest() override { base::PowerMonitor::ShutdownForTesting(); }
// Fast-forwards virtual time by |delta|. If |with_power| is true, both // Fast-forwards virtual time by |delta|. If |with_power| is true, both
// |clock_| and |task_environment_| time will be fast-forwarded. Otherwise, // |clock_| and |task_environment_| time will be fast-forwarded. Otherwise,
// only |clock_| time will be changed to mimic the behavior when machine is // only |clock_| time will be changed to mimic the behavior when machine is
...@@ -50,20 +25,19 @@ class WallClockTimerTest : public ::testing::Test { ...@@ -50,20 +25,19 @@ class WallClockTimerTest : public ::testing::Test {
// Power event will be triggered if |with_power| is set to false. // Power event will be triggered if |with_power| is set to false.
void FastForwardBy(base::TimeDelta delay, bool with_power = true) { void FastForwardBy(base::TimeDelta delay, bool with_power = true) {
if (!with_power) if (!with_power)
mock_power_monitor_source_->Suspend(); fake_power_monitor_source_.Suspend();
clock_.SetNow(clock_.Now() + delay); clock_.SetNow(clock_.Now() + delay);
if (with_power) { if (with_power) {
task_environment_.FastForwardBy(delay); task_environment_.FastForwardBy(delay);
} else { } else {
mock_power_monitor_source_->Resume(); fake_power_monitor_source_.Resume();
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
} }
} }
// Owned by power_monitor_. Use this to simulate a power suspend and resume. base::test::ScopedFakePowerMonitorSource fake_power_monitor_source_;
StubPowerMonitorSource* mock_power_monitor_source_ = nullptr;
base::test::SingleThreadTaskEnvironment task_environment_{ base::test::SingleThreadTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME}; base::test::TaskEnvironment::TimeSource::MOCK_TIME};
base::SimpleTestClock clock_; base::SimpleTestClock clock_;
...@@ -234,10 +208,10 @@ TEST_F(WallClockTimerTest, NonStopTickClock) { ...@@ -234,10 +208,10 @@ TEST_F(WallClockTimerTest, NonStopTickClock) {
constexpr auto past_time = base::TimeDelta::FromSeconds(30); constexpr auto past_time = base::TimeDelta::FromSeconds(30);
// Fastword with both clocks even the power is suspended. // Fastword with both clocks even the power is suspended.
mock_power_monitor_source_->Suspend(); fake_power_monitor_source_.Suspend();
clock_.SetNow(clock_.Now() + past_time); clock_.SetNow(clock_.Now() + past_time);
task_environment_.FastForwardBy(past_time); task_environment_.FastForwardBy(past_time);
mock_power_monitor_source_->Resume(); fake_power_monitor_source_.Resume();
// Ensure that the timer has not yet fired. // Ensure that the timer has not yet fired.
::testing::Mock::VerifyAndClearExpectations(&callback); ::testing::Mock::VerifyAndClearExpectations(&callback);
...@@ -270,10 +244,10 @@ TEST_F(WallClockTimerTest, NonStopTickClockWithLongPause) { ...@@ -270,10 +244,10 @@ TEST_F(WallClockTimerTest, NonStopTickClockWithLongPause) {
// Fastword with both clocks even the power is suspended. Timer fires at the // Fastword with both clocks even the power is suspended. Timer fires at the
// moment of power resume. // moment of power resume.
EXPECT_CALL(callback, Run()); EXPECT_CALL(callback, Run());
mock_power_monitor_source_->Suspend(); fake_power_monitor_source_.Suspend();
clock_.SetNow(clock_.Now() + past_time); clock_.SetNow(clock_.Now() + past_time);
task_environment_.FastForwardBy(past_time); task_environment_.FastForwardBy(past_time);
mock_power_monitor_source_->Resume(); fake_power_monitor_source_.Resume();
::testing::Mock::VerifyAndClearExpectations(&callback); ::testing::Mock::VerifyAndClearExpectations(&callback);
EXPECT_FALSE(wall_clock_timer.IsRunning()); EXPECT_FALSE(wall_clock_timer.IsRunning());
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/power_monitor/test/fake_power_monitor_source.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/test/test_mock_time_task_runner.h" #include "base/test/test_mock_time_task_runner.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
...@@ -32,8 +33,40 @@ class MockSessionLengthLimiterDelegate : public SessionLengthLimiter::Delegate { ...@@ -32,8 +33,40 @@ class MockSessionLengthLimiterDelegate : public SessionLengthLimiter::Delegate {
MOCK_METHOD0(StopSession, void(void)); MOCK_METHOD0(StopSession, void(void));
}; };
// Helper class to simulate wall clock advance during suspend/resume.
class WallClockForwarder {
public:
explicit WallClockForwarder(base::TestMockTimeTaskRunner* runner);
~WallClockForwarder();
WallClockForwarder(const WallClockForwarder&) = delete;
WallClockForwarder& operator=(const WallClockForwarder&) = delete;
void ForwardWhileSuspended(const base::TimeDelta& delta);
private:
// Unowned, must outlive this.
base::TestMockTimeTaskRunner* const runner_;
// Used to simulate a power suspend and resume.
base::test::ScopedFakePowerMonitorSource fake_power_monitor_source_;
};
} // namespace } // namespace
WallClockForwarder::WallClockForwarder(base::TestMockTimeTaskRunner* runner)
: runner_(runner) {}
WallClockForwarder::~WallClockForwarder() = default;
void WallClockForwarder::ForwardWhileSuspended(const base::TimeDelta& delta) {
fake_power_monitor_source_.Suspend();
runner_->AdvanceWallClock(delta);
fake_power_monitor_source_.Resume();
runner_->RunUntilIdle();
}
class SessionLengthLimiterTest : public testing::Test { class SessionLengthLimiterTest : public testing::Test {
protected: protected:
SessionLengthLimiterTest(); SessionLengthLimiterTest();
...@@ -71,6 +104,7 @@ class SessionLengthLimiterTest : public testing::Test { ...@@ -71,6 +104,7 @@ class SessionLengthLimiterTest : public testing::Test {
void DestroySessionLengthLimiter(); void DestroySessionLengthLimiter();
scoped_refptr<base::TestMockTimeTaskRunner> runner_; scoped_refptr<base::TestMockTimeTaskRunner> runner_;
std::unique_ptr<WallClockForwarder> wall_clock_forwarder_;
base::Time session_start_time_; base::Time session_start_time_;
base::Time session_stop_time_; base::Time session_stop_time_;
...@@ -92,10 +126,12 @@ void SessionLengthLimiterTest::SetUp() { ...@@ -92,10 +126,12 @@ void SessionLengthLimiterTest::SetUp() {
TestingBrowserProcess::GetGlobal()->SetLocalState(&local_state_); TestingBrowserProcess::GetGlobal()->SetLocalState(&local_state_);
SessionLengthLimiter::RegisterPrefs(local_state_.registry()); SessionLengthLimiter::RegisterPrefs(local_state_.registry());
runner_ = new base::TestMockTimeTaskRunner; runner_ = new base::TestMockTimeTaskRunner;
wall_clock_forwarder_ = std::make_unique<WallClockForwarder>(runner_.get());
runner_->FastForwardBy(base::TimeDelta::FromInternalValue(1000)); runner_->FastForwardBy(base::TimeDelta::FromInternalValue(1000));
} }
void SessionLengthLimiterTest::TearDown() { void SessionLengthLimiterTest::TearDown() {
wall_clock_forwarder_.reset();
session_length_limiter_.reset(); session_length_limiter_.reset();
TestingBrowserProcess::GetGlobal()->SetLocalState(NULL); TestingBrowserProcess::GetGlobal()->SetLocalState(NULL);
} }
...@@ -737,4 +773,56 @@ TEST_F(SessionLengthLimiterTest, RunAndRemoveSessionLengthLimit) { ...@@ -737,4 +773,56 @@ TEST_F(SessionLengthLimiterTest, RunAndRemoveSessionLengthLimit) {
runner_->FastForwardUntilNoTasksRemain(); runner_->FastForwardUntilNoTasksRemain();
} }
// Tests that session is stopped immediately if limit was hit with when device
// was suspended.
TEST_F(SessionLengthLimiterTest, SuspendAndStop) {
base::ThreadTaskRunnerHandle runner_handler(runner_);
// Set a 60 second session time limit.
SetSessionLengthLimitPref(base::TimeDelta::FromSeconds(60));
CreateSessionLengthLimiter(false);
// Verify that the timer fires and the session is terminated when the session
// length limit is reached.
ExpectStopSession();
// Simulate 30 seconds of the active session, then 60 seconds of sleep
// (suspended device). Given that session length limit is 60 seconds, it will
// hit exactly if the middle of sleen (and processed when device is resumed,
// so real session length will be 90 seconds).
runner_->FastForwardBy(base::TimeDelta::FromSeconds(30));
wall_clock_forwarder_->ForwardWhileSuspended(
base::TimeDelta::FromSeconds(60));
EXPECT_EQ(session_start_time_ + base::TimeDelta::FromSeconds(90),
session_stop_time_);
}
// Tests that session is stopped withing timeout, even when part of session time
// device was suspended.
TEST_F(SessionLengthLimiterTest, SuspendAndRun) {
base::ThreadTaskRunnerHandle runner_handler(runner_);
// Set a 60 second session time limit.
SetSessionLengthLimitPref(base::TimeDelta::FromSeconds(60));
CreateSessionLengthLimiter(false);
// Simulate 20 seconds of the active session, then 30 seconds of sleep
// (suspended device). Given that session length limit is 60 seconds, and
// total 50 seconds passed, there will be 10 seconds of the session left (and
// the second FastForwardBy will hit the limit).
runner_->FastForwardBy(base::TimeDelta::FromSeconds(20));
wall_clock_forwarder_->ForwardWhileSuspended(
base::TimeDelta::FromSeconds(30));
// Verify that the timer fires and the session is terminated when the session
// length limit is reached.
ExpectStopSession();
runner_->FastForwardBy(base::TimeDelta::FromSeconds(20));
EXPECT_EQ(session_start_time_ + base::TimeDelta::FromSeconds(60),
session_stop_time_);
}
} // namespace chromeos } // namespace chromeos
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