blob: 39495a456d7bdb8a972c706d1696cda5b860aa78 [file] [log] [blame]
// Copyright (c) 2012 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 "chrome/browser/chromeos/session_length_limiter.h"
#include <queue>
#include <utility>
#include <vector>
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/prefs/testing_pref_service.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/thread_task_runner_handle.h"
#include "base/values.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_browser_process.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::Invoke;
using ::testing::Mock;
using ::testing::NiceMock;
namespace chromeos {
namespace {
class MockSessionLengthLimiterDelegate : public SessionLengthLimiter::Delegate {
public:
MOCK_CONST_METHOD0(GetCurrentTime, const base::TimeTicks(void));
MOCK_METHOD0(StopSession, void(void));
};
// A SingleThreadTaskRunner that mocks the current time and allows it to be
// fast-forwarded.
class MockTimeSingleThreadTaskRunner : public base::SingleThreadTaskRunner {
public:
MockTimeSingleThreadTaskRunner();
// base::SingleThreadTaskRunner:
virtual bool RunsTasksOnCurrentThread() const OVERRIDE;
virtual bool PostDelayedTask(const tracked_objects::Location& from_here,
const base::Closure& task,
base::TimeDelta delay) OVERRIDE;
virtual bool PostNonNestableDelayedTask(
const tracked_objects::Location& from_here,
const base::Closure& task,
base::TimeDelta delay) OVERRIDE;
const base::TimeTicks& GetCurrentTime() const;
void FastForwardBy(int64 milliseconds);
void FastForwardUntilNoTasksRemain();
private:
// Strict weak temporal ordering of tasks.
class TemporalOrder {
public:
bool operator()(
const std::pair<base::TimeTicks, base::Closure>& first_task,
const std::pair<base::TimeTicks, base::Closure>& second_task) const;
};
virtual ~MockTimeSingleThreadTaskRunner();
base::TimeTicks now_;
std::priority_queue<std::pair<base::TimeTicks, base::Closure>,
std::vector<std::pair<base::TimeTicks, base::Closure> >,
TemporalOrder> tasks_;
};
} // namespace
class SessionLengthLimiterTest : public testing::Test {
protected:
SessionLengthLimiterTest();
// testing::Test:
virtual void SetUp() OVERRIDE;
virtual void TearDown() OVERRIDE;
void SetSessionStartTimePref(int64 session_start_time);
void VerifySessionStartTimePref();
void SetSessionLengthLimitPref(int64 session_length_limit);
void ExpectStopSession();
void CheckStopSessionTime();
void CreateSessionLengthLimiter(bool browser_restarted);
TestingPrefServiceSimple local_state_;
scoped_refptr<MockTimeSingleThreadTaskRunner> runner_;
base::TimeTicks session_start_time_;
base::TimeTicks session_end_time_;
MockSessionLengthLimiterDelegate* delegate_; // Owned by
// session_length_limiter_.
scoped_ptr<SessionLengthLimiter> session_length_limiter_;
};
MockTimeSingleThreadTaskRunner::MockTimeSingleThreadTaskRunner() {
}
bool MockTimeSingleThreadTaskRunner::RunsTasksOnCurrentThread() const {
return true;
}
bool MockTimeSingleThreadTaskRunner::PostDelayedTask(
const tracked_objects::Location& from_here,
const base::Closure& task,
base::TimeDelta delay) {
tasks_.push(std::pair<base::TimeTicks, base::Closure>(now_ + delay, task));
return true;
}
bool MockTimeSingleThreadTaskRunner::PostNonNestableDelayedTask(
const tracked_objects::Location& from_here,
const base::Closure& task,
base::TimeDelta delay) {
NOTREACHED();
return false;
}
const base::TimeTicks& MockTimeSingleThreadTaskRunner::GetCurrentTime() const {
return now_;
}
void MockTimeSingleThreadTaskRunner::FastForwardBy(int64 delta) {
const base::TimeTicks latest =
now_ + base::TimeDelta::FromMilliseconds(delta);
while (!tasks_.empty() && tasks_.top().first <= latest) {
now_ = tasks_.top().first;
base::Closure task = tasks_.top().second;
tasks_.pop();
task.Run();
}
now_ = latest;
}
void MockTimeSingleThreadTaskRunner::FastForwardUntilNoTasksRemain() {
while (!tasks_.empty()) {
now_ = tasks_.top().first;
base::Closure task = tasks_.top().second;
tasks_.pop();
task.Run();
}
}
bool MockTimeSingleThreadTaskRunner::TemporalOrder::operator()(
const std::pair<base::TimeTicks, base::Closure>& first_task,
const std::pair<base::TimeTicks, base::Closure>& second_task) const {
return first_task.first >= second_task.first;
}
MockTimeSingleThreadTaskRunner::~MockTimeSingleThreadTaskRunner() {
}
SessionLengthLimiterTest::SessionLengthLimiterTest() : delegate_(NULL) {
}
void SessionLengthLimiterTest::SetUp() {
TestingBrowserProcess::GetGlobal()->SetLocalState(&local_state_);
SessionLengthLimiter::RegisterPrefs(local_state_.registry());
runner_ = new MockTimeSingleThreadTaskRunner;
session_start_time_ = runner_->GetCurrentTime();
delegate_ = new NiceMock<MockSessionLengthLimiterDelegate>;
ON_CALL(*delegate_, GetCurrentTime())
.WillByDefault(Invoke(runner_.get(),
&MockTimeSingleThreadTaskRunner::GetCurrentTime));
EXPECT_CALL(*delegate_, StopSession()).Times(0);
}
void SessionLengthLimiterTest::TearDown() {
TestingBrowserProcess::GetGlobal()->SetLocalState(NULL);
}
void SessionLengthLimiterTest::SetSessionStartTimePref(
int64 session_start_time) {
local_state_.SetUserPref(prefs::kSessionStartTime,
base::Value::CreateStringValue(
base::Int64ToString(session_start_time)));
}
void SessionLengthLimiterTest::VerifySessionStartTimePref() {
base::TimeTicks session_start_time(base::TimeTicks::FromInternalValue(
local_state_.GetInt64(prefs::kSessionStartTime)));
EXPECT_EQ(session_start_time_, session_start_time);
}
void SessionLengthLimiterTest::SetSessionLengthLimitPref(
int64 session_length_limit) {
session_end_time_ = session_start_time_ +
base::TimeDelta::FromMilliseconds(session_length_limit);
// If the new session end time has passed already, the session should end now.
if (session_end_time_ < runner_->GetCurrentTime())
session_end_time_ = runner_->GetCurrentTime();
local_state_.SetUserPref(prefs::kSessionLengthLimit,
base::Value::CreateIntegerValue(
session_length_limit));
}
void SessionLengthLimiterTest::ExpectStopSession() {
Mock::VerifyAndClearExpectations(delegate_);
EXPECT_CALL(*delegate_, StopSession())
.Times(1)
.WillOnce(Invoke(this, &SessionLengthLimiterTest::CheckStopSessionTime));
}
void SessionLengthLimiterTest::CheckStopSessionTime() {
EXPECT_EQ(session_end_time_, runner_->GetCurrentTime());
}
void SessionLengthLimiterTest::CreateSessionLengthLimiter(
bool browser_restarted) {
session_length_limiter_.reset(
new SessionLengthLimiter(delegate_, browser_restarted));
}
// Verifies that the session start time in local state is updated during login
// if no session start time has been stored before.
TEST_F(SessionLengthLimiterTest, StartWithSessionStartTimeUnset) {
CreateSessionLengthLimiter(false);
VerifySessionStartTimePref();
}
// Verifies that the session start time in local state is updated during login
// if a session start time lying in the future has been stored before.
TEST_F(SessionLengthLimiterTest, StartWithSessionStartTimeFuture) {
SetSessionStartTimePref(
(session_start_time_ + base::TimeDelta::FromHours(2)).ToInternalValue());
CreateSessionLengthLimiter(false);
VerifySessionStartTimePref();
}
// Verifies that the session start time in local state is updated during login
// if a valid session start time has been stored before.
TEST_F(SessionLengthLimiterTest, StartWithSessionStartTimeValid) {
SetSessionStartTimePref(
(session_start_time_ - base::TimeDelta::FromHours(2)).ToInternalValue());
CreateSessionLengthLimiter(false);
VerifySessionStartTimePref();
}
// Verifies that the session start time in local state is updated during restart
// after a crash if no session start time has been stored before.
TEST_F(SessionLengthLimiterTest, RestartWithSessionStartTimeUnset) {
CreateSessionLengthLimiter(true);
VerifySessionStartTimePref();
}
// Verifies that the session start time in local state is updated during restart
// after a crash if a session start time lying in the future has been stored
// before.
TEST_F(SessionLengthLimiterTest, RestartWithSessionStartTimeFuture) {
SetSessionStartTimePref(
(session_start_time_ + base::TimeDelta::FromHours(2)).ToInternalValue());
CreateSessionLengthLimiter(true);
VerifySessionStartTimePref();
}
// Verifies that the session start time in local state is *not* updated during
// restart after a crash if a valid session start time has been stored before.
TEST_F(SessionLengthLimiterTest, RestartWithSessionStartTimeValid) {
session_start_time_ -= base::TimeDelta::FromHours(2);
SetSessionStartTimePref(session_start_time_.ToInternalValue());
CreateSessionLengthLimiter(true);
VerifySessionStartTimePref();
}
// Creates a SessionLengthLimiter without setting a limit. Verifies that the
// limiter does not start a timer.
TEST_F(SessionLengthLimiterTest, RunWithoutSessionLengthLimit) {
base::ThreadTaskRunnerHandle runner_handler(runner_);
// Create a SessionLengthLimiter.
CreateSessionLengthLimiter(false);
// Verify that no timer fires to terminate the session.
runner_->FastForwardUntilNoTasksRemain();
}
// Creates a SessionLengthLimiter after setting a limit. Verifies that the
// limiter starts a timer and that when the session length reaches the limit,
// the session is terminated.
TEST_F(SessionLengthLimiterTest, RunWithSessionLengthLimit) {
base::ThreadTaskRunnerHandle runner_handler(runner_);
// Set a 60 second session time limit.
SetSessionLengthLimitPref(60 * 1000); // 60 seconds.
// Create a SessionLengthLimiter.
CreateSessionLengthLimiter(false);
// Verify that the timer fires and the session is terminated when the session
// length limit is reached.
ExpectStopSession();
runner_->FastForwardUntilNoTasksRemain();
}
// Creates a SessionLengthLimiter after setting a 60 second limit, allows 50
// seconds of session time to pass, then increases the limit to 90 seconds.
// Verifies that when the session time reaches the new 90 second limit, the
// session is terminated.
TEST_F(SessionLengthLimiterTest, RunAndIncreaseSessionLengthLimit) {
base::ThreadTaskRunnerHandle runner_handler(runner_);
// Set a 60 second session time limit.
SetSessionLengthLimitPref(60 * 1000); // 60 seconds.
// Create a SessionLengthLimiter.
CreateSessionLengthLimiter(false);
// Fast forward the time by 50 seconds, verifying that no timer fires to
// terminate the session.
runner_->FastForwardBy(50 * 1000); // 50 seconds.
// Increase the session length limit to 90 seconds.
SetSessionLengthLimitPref(90 * 1000); // 90 seconds.
// Verify that the the timer fires and the session is terminated when the
// session length limit is reached.
ExpectStopSession();
runner_->FastForwardUntilNoTasksRemain();
}
// Creates a SessionLengthLimiter after setting a 60 second limit, allows 50
// seconds of session time to pass, then decreases the limit to 40 seconds.
// Verifies that when the limit is decreased to 40 seconds after 50 seconds of
// session time have passed, the next timer tick causes the session to be
// terminated.
TEST_F(SessionLengthLimiterTest, RunAndDecreaseSessionLengthLimit) {
base::ThreadTaskRunnerHandle runner_handler(runner_);
// Set a 60 second session time limit.
SetSessionLengthLimitPref(60 * 1000); // 60 seconds.
// Create a SessionLengthLimiter.
CreateSessionLengthLimiter(false);
// Fast forward the time by 50 seconds, verifying that no timer fires to
// terminate the session.
runner_->FastForwardBy(50 * 1000); // 50 seconds.
// Verify that reducing the session length limit below the 50 seconds that
// have already elapsed causes the session to be terminated immediately.
ExpectStopSession();
SetSessionLengthLimitPref(40 * 1000); // 40 seconds.
}
// Creates a SessionLengthLimiter after setting a 60 second limit, allows 50
// seconds of session time to pass, then removes the limit. Verifies that after
// the limit is removed, the session is not terminated when the session time
// reaches the original 60 second limit.
TEST_F(SessionLengthLimiterTest, RunAndRemoveSessionLengthLimit) {
base::ThreadTaskRunnerHandle runner_handler(runner_);
// Set a 60 second session time limit.
SetSessionLengthLimitPref(60 * 1000); // 60 seconds.
// Create a SessionLengthLimiter.
CreateSessionLengthLimiter(false);
// Fast forward the time by 50 seconds, verifying that no timer fires to
// terminate the session.
runner_->FastForwardBy(50 * 1000); // 50 seconds.
// Remove the session length limit.
local_state_.RemoveUserPref(prefs::kSessionLengthLimit);
// Verify that no timer fires to terminate the session.
runner_->FastForwardUntilNoTasksRemain();
}
} // namespace chromeos