Index: chrome/browser/chromeos/power/session_length_limiter_unittest.cc |
diff --git a/chrome/browser/chromeos/power/session_length_limiter_unittest.cc b/chrome/browser/chromeos/power/session_length_limiter_unittest.cc |
index 40a86bdb2d91e8c10569081125c7770621a79796..bf9a4778058441cb7afa4a91cb8703e4968cefc9 100644 |
--- a/chrome/browser/chromeos/power/session_length_limiter_unittest.cc |
+++ b/chrome/browser/chromeos/power/session_length_limiter_unittest.cc |
@@ -4,8 +4,9 @@ |
#include "chrome/browser/chromeos/power/session_length_limiter.h" |
-#include <deque> |
+#include <queue> |
#include <utility> |
+#include <vector> |
#include "base/callback.h" |
#include "base/compiler_specific.h" |
@@ -16,16 +17,13 @@ |
#include "base/single_thread_task_runner.h" |
#include "base/strings/string_number_conversions.h" |
#include "base/thread_task_runner_handle.h" |
-#include "base/time.h" |
#include "base/values.h" |
-#include "chrome/browser/browser_process.h" |
#include "chrome/common/pref_names.h" |
#include "chrome/test/base/testing_browser_process.h" |
#include "chrome/test/base/testing_pref_service.h" |
#include "testing/gmock/include/gmock/gmock.h" |
#include "testing/gtest/include/gtest/gtest.h" |
-using ::testing::_; |
using ::testing::Invoke; |
using ::testing::Mock; |
using ::testing::NiceMock; |
@@ -34,159 +32,197 @@ namespace chromeos { |
namespace { |
-// The interval at which the SessionLengthLimiter checks whether the remaining |
-// session time has reachzed zero. |
-const base::TimeDelta kSessionLengthLimitTimerInterval( |
- base::TimeDelta::FromSeconds(1)); |
- |
-const base::TimeDelta kZeroTimeDelta; |
-const base::TimeDelta kTenSeconds(base::TimeDelta::FromSeconds(10)); |
- |
class MockSessionLengthLimiterDelegate : public SessionLengthLimiter::Delegate { |
public: |
MOCK_CONST_METHOD0(GetCurrentTime, const base::Time(void)); |
MOCK_METHOD0(StopSession, void(void)); |
}; |
-// A SingleThreadTaskRunner that allows the task queue to be inspected and |
-// delayed tasks to be run without waiting for the actual delays to expire. |
-class ImmediateSingleThreadTaskRunner : public base::SingleThreadTaskRunner { |
+// A SingleThreadTaskRunner that mocks the current time and allows it to be |
+// fast-forwarded. |
+class MockTimeSingleThreadTaskRunner : public base::SingleThreadTaskRunner { |
public: |
- virtual bool RunsTasksOnCurrentThread() const OVERRIDE { |
- return true; |
- } |
+ 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 { |
- tasks_.push_back(std::pair<base::TimeDelta, base::Closure>(delay, task)); |
- return true; |
- } |
- |
+ base::TimeDelta delay) OVERRIDE; |
virtual bool PostNonNestableDelayedTask( |
const tracked_objects::Location& from_here, |
const base::Closure& task, |
- base::TimeDelta delay) OVERRIDE { |
- NOTREACHED(); |
- return false; |
- } |
+ base::TimeDelta delay) OVERRIDE; |
- void RunTasks() { |
- std::deque<std::pair<base::TimeDelta, base::Closure> > tasks; |
- tasks.swap(tasks_); |
- for (std::deque<std::pair<base::TimeDelta, base::Closure> >::iterator |
- it = tasks.begin(); it != tasks.end(); ++it) { |
- it->second.Run(); |
- } |
- } |
+ const base::Time& GetCurrentTime() const; |
- const std::deque<std::pair<base::TimeDelta, base::Closure> >& tasks() const { |
- return tasks_; |
- } |
+ void FastForwardBy(int64 milliseconds); |
+ void FastForwardUntilNoTasksRemain(); |
private: |
- std::deque<std::pair<base::TimeDelta, base::Closure> > tasks_; |
+ // Strict weak temporal ordering of tasks. |
+ class TemporalOrder { |
+ public: |
+ bool operator()( |
+ const std::pair<base::Time, base::Closure>& first_task, |
+ const std::pair<base::Time, base::Closure>& second_task) const; |
+ }; |
+ |
+ virtual ~MockTimeSingleThreadTaskRunner(); |
- virtual ~ImmediateSingleThreadTaskRunner() {} |
+ base::Time now_; |
+ std::priority_queue<std::pair<base::Time, base::Closure>, |
+ std::vector<std::pair<base::Time, base::Closure> >, |
+ TemporalOrder> tasks_; |
}; |
} // namespace |
class SessionLengthLimiterTest : public testing::Test { |
protected: |
- SessionLengthLimiterTest() : delegate_(NULL) { |
- } |
+ 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); |
- virtual void SetUp() { |
- TestingBrowserProcess::GetGlobal()->SetLocalState(&local_state_); |
- SessionLengthLimiter::RegisterPrefs(local_state_.registry()); |
+ void ExpectStopSession(); |
+ void CheckStopSessionTime(); |
- delegate_ = new NiceMock<MockSessionLengthLimiterDelegate>; |
- ON_CALL(*delegate_, GetCurrentTime()) |
- .WillByDefault(Invoke(this, &SessionLengthLimiterTest::GetCurrentTime)); |
- EXPECT_CALL(*delegate_, StopSession()).Times(0); |
- runner_ = new ImmediateSingleThreadTaskRunner; |
+ void CreateSessionLengthLimiter(bool browser_restarted); |
+ TestingPrefServiceSimple local_state_; |
+ scoped_refptr<MockTimeSingleThreadTaskRunner> runner_; |
+ base::Time session_start_time_; |
+ base::Time session_end_time_; |
+ |
+ MockSessionLengthLimiterDelegate* delegate_; // Owned by |
+ // session_length_limiter_. |
+ scoped_ptr<SessionLengthLimiter> session_length_limiter_; |
+}; |
+ |
+MockTimeSingleThreadTaskRunner::MockTimeSingleThreadTaskRunner() |
// Initialize the mock clock to a fixed value, ensuring that timezone |
// differences or DST changes do not affect the test. |
- now_ = base::Time::UnixEpoch() + base::TimeDelta::FromDays(40 * 365); |
- session_start_time_ = now_; |
- } |
+ : now_(base::Time::UnixEpoch() + base::TimeDelta::FromDays(40 * 365)) { |
+} |
- virtual void TearDown() { |
- TestingBrowserProcess::GetGlobal()->SetLocalState(NULL); |
- } |
+bool MockTimeSingleThreadTaskRunner::RunsTasksOnCurrentThread() const { |
+ return true; |
+} |
- void SetSessionStartTimePref(int64 session_start_time) { |
- local_state_.SetUserPref(prefs::kSessionStartTime, |
- base::Value::CreateStringValue( |
- base::Int64ToString(session_start_time))); |
- } |
+bool MockTimeSingleThreadTaskRunner::PostDelayedTask( |
+ const tracked_objects::Location& from_here, |
+ const base::Closure& task, |
+ base::TimeDelta delay) { |
+ tasks_.push(std::pair<base::Time, base::Closure>(now_ + delay, task)); |
+ return true; |
+} |
- void VerifySessionStartTimePref() { |
- base::Time session_start_time(base::Time::FromInternalValue( |
- local_state_.GetInt64(prefs::kSessionStartTime))); |
- EXPECT_EQ(session_start_time_, session_start_time); |
- } |
+bool MockTimeSingleThreadTaskRunner::PostNonNestableDelayedTask( |
+ const tracked_objects::Location& from_here, |
+ const base::Closure& task, |
+ base::TimeDelta delay) { |
+ NOTREACHED(); |
+ return false; |
+} |
- void SetSessionLengthLimitPref(int64 session_length_limit) { |
- local_state_.SetUserPref(prefs::kSessionLengthLimit, |
- base::Value::CreateIntegerValue( |
- session_length_limit)); |
- base::TimeDelta remaining( |
- base::TimeDelta::FromMilliseconds(session_length_limit) - |
- (now_ - session_start_time_)); |
- if (remaining < kZeroTimeDelta) |
- remaining = kZeroTimeDelta; |
- remaining_.reset(new base::TimeDelta(remaining)); |
- } |
+const base::Time& MockTimeSingleThreadTaskRunner::GetCurrentTime() const { |
+ return now_; |
+} |
- void ExpectStopSession() { |
- Mock::VerifyAndClearExpectations(delegate_); |
- EXPECT_CALL(*delegate_, StopSession()).Times(1); |
+void MockTimeSingleThreadTaskRunner::FastForwardBy(int64 delta) { |
+ const base::Time latest = now_ + base::TimeDelta::FromMilliseconds(delta); |
+ while (!tasks_.empty() && tasks_.top().first <= latest) { |
+ now_ = tasks_.top().first; |
+ tasks_.top().second.Run(); |
+ tasks_.pop(); |
} |
+ now_ = latest; |
+} |
- void CreateSessionLengthLimiter(bool browser_restarted) { |
- session_length_limiter_.reset( |
- new SessionLengthLimiter(delegate_, browser_restarted)); |
+void MockTimeSingleThreadTaskRunner::FastForwardUntilNoTasksRemain() { |
+ while (!tasks_.empty()) { |
+ now_ = tasks_.top().first; |
+ tasks_.top().second.Run(); |
+ tasks_.pop(); |
} |
+} |
- void VerifyNoTimerTickIsEnqueued() { |
- EXPECT_TRUE(runner_->tasks().empty()); |
- } |
+bool MockTimeSingleThreadTaskRunner::TemporalOrder::operator()( |
+ const std::pair<base::Time, base::Closure>& first_task, |
+ const std::pair<base::Time, base::Closure>& second_task) const { |
+ return first_task.first < second_task.first; |
+} |
- void VerifyTimerTickIsEnqueued() { |
- ASSERT_EQ(1U, runner_->tasks().size()); |
- EXPECT_EQ(kSessionLengthLimitTimerInterval, |
- runner_->tasks().front().first); |
- } |
+MockTimeSingleThreadTaskRunner::~MockTimeSingleThreadTaskRunner() { |
+} |
- void VerifyTimerTick() { |
- VerifyTimerTickIsEnqueued(); |
- runner_->RunTasks(); |
+SessionLengthLimiterTest::SessionLengthLimiterTest() : delegate_(NULL) { |
+} |
- now_ += kSessionLengthLimitTimerInterval; |
- *remaining_ -= kSessionLengthLimitTimerInterval; |
- if (*remaining_ < kZeroTimeDelta) |
- remaining_.reset(new base::TimeDelta(kZeroTimeDelta)); |
- } |
+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); |
+} |
- base::Time GetCurrentTime() const { |
- return now_; |
- } |
+void SessionLengthLimiterTest::TearDown() { |
+ TestingBrowserProcess::GetGlobal()->SetLocalState(NULL); |
+} |
- TestingPrefServiceSimple local_state_; |
- MockSessionLengthLimiterDelegate* delegate_; // Owned by |
- // session_length_limiter_. |
- scoped_refptr<ImmediateSingleThreadTaskRunner> runner_; |
+void SessionLengthLimiterTest::SetSessionStartTimePref( |
+ int64 session_start_time) { |
+ local_state_.SetUserPref(prefs::kSessionStartTime, |
+ base::Value::CreateStringValue( |
+ base::Int64ToString(session_start_time))); |
+} |
- base::Time session_start_time_; |
- base::Time now_; |
- scoped_ptr<base::TimeDelta> remaining_; |
+void SessionLengthLimiterTest::VerifySessionStartTimePref() { |
+ base::Time session_start_time(base::Time::FromInternalValue( |
+ local_state_.GetInt64(prefs::kSessionStartTime))); |
+ EXPECT_EQ(session_start_time_, session_start_time); |
+} |
- scoped_ptr<SessionLengthLimiter> session_length_limiter_; |
-}; |
+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) { |
@@ -206,7 +242,7 @@ TEST_F(SessionLengthLimiterTest, StartWithSessionStartTimeInvalid) { |
// if a session start time lying in the future has been stored before. |
TEST_F(SessionLengthLimiterTest, StartWithSessionStartTimeFuture) { |
SetSessionStartTimePref( |
- (now_ + base::TimeDelta::FromHours(2)).ToInternalValue()); |
+ (session_start_time_ + base::TimeDelta::FromHours(2)).ToInternalValue()); |
CreateSessionLengthLimiter(false); |
VerifySessionStartTimePref(); |
} |
@@ -214,8 +250,8 @@ TEST_F(SessionLengthLimiterTest, StartWithSessionStartTimeFuture) { |
// 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) { |
- const base::Time previous_start_time = now_ - base::TimeDelta::FromHours(2); |
- SetSessionStartTimePref(previous_start_time.ToInternalValue()); |
+ SetSessionStartTimePref( |
+ (session_start_time_ - base::TimeDelta::FromHours(2)).ToInternalValue()); |
CreateSessionLengthLimiter(false); |
VerifySessionStartTimePref(); |
} |
@@ -240,7 +276,7 @@ TEST_F(SessionLengthLimiterTest, RestartWithSessionStartTimeInvalid) { |
// before. |
TEST_F(SessionLengthLimiterTest, RestartWithSessionStartTimeFuture) { |
SetSessionStartTimePref( |
- (now_ + base::TimeDelta::FromHours(2)).ToInternalValue()); |
+ (session_start_time_ + base::TimeDelta::FromHours(2)).ToInternalValue()); |
CreateSessionLengthLimiter(true); |
VerifySessionStartTimePref(); |
} |
@@ -262,8 +298,8 @@ TEST_F(SessionLengthLimiterTest, RunWithoutSessionLengthLimit) { |
// Create a SessionLengthLimiter. |
CreateSessionLengthLimiter(false); |
- // Verify that no timer tick has been enqueued. |
- VerifyNoTimerTickIsEnqueued(); |
+ // Verify that no timer fires to terminate the session. |
+ runner_->FastForwardUntilNoTasksRemain(); |
} |
// Creates a SessionLengthLimiter after setting a limit. Verifies that the |
@@ -278,13 +314,10 @@ TEST_F(SessionLengthLimiterTest, RunWithSessionLengthLimit) { |
// Create a SessionLengthLimiter. |
CreateSessionLengthLimiter(false); |
- // Check timer ticks until the remaining session time reaches zero. |
- while (*remaining_ > kZeroTimeDelta) |
- VerifyTimerTick(); |
- |
- // Check that the next timer tick leads to the session being terminated. |
+ // Verify that the timer fires and the session is terminated when the session |
+ // length limit is reached. |
ExpectStopSession(); |
- VerifyTimerTick(); |
+ runner_->FastForwardUntilNoTasksRemain(); |
} |
// Creates a SessionLengthLimiter after setting a 60 second limit, allows 50 |
@@ -300,20 +333,17 @@ TEST_F(SessionLengthLimiterTest, RunAndIncreaseSessionLengthLimit) { |
// Create a SessionLengthLimiter. |
CreateSessionLengthLimiter(false); |
- // Check timer ticks for 50 seconds of session time. |
- while (*remaining_ > kTenSeconds) |
- VerifyTimerTick(); |
+ // 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. |
- // Check timer ticks until the remaining session time reaches zero. |
- while (*remaining_ > kZeroTimeDelta) |
- VerifyTimerTick(); |
- |
- // Check that the next timer tick leads to the session being terminated. |
+ // Verify that the the timer fires and the session is terminated when the |
+ // session length limit is reached. |
ExpectStopSession(); |
- VerifyTimerTick(); |
+ runner_->FastForwardUntilNoTasksRemain(); |
} |
// Creates a SessionLengthLimiter after setting a 60 second limit, allows 50 |
@@ -330,17 +360,14 @@ TEST_F(SessionLengthLimiterTest, RunAndDecreaseSessionLengthLimit) { |
// Create a SessionLengthLimiter. |
CreateSessionLengthLimiter(false); |
- // Check timer ticks for 50 seconds of session time. |
- while (*remaining_ > kTenSeconds) |
- VerifyTimerTick(); |
- |
- // Reduce the session length limit below the 50 seconds that have already |
- // elapsed. |
- SetSessionLengthLimitPref(40 * 1000); // 40 seconds. |
+ // Fast forward the time by 50 seconds, verifying that no timer fires to |
+ // terminate the session. |
+ runner_->FastForwardBy(50 * 1000); // 50 seconds. |
- // Check that the next timer tick causes the session to be terminated. |
+ // Verify that reducing the session length limit below the 50 seconds that |
+ // have already elapsed causes the session to be terminated immediately. |
ExpectStopSession(); |
- VerifyTimerTick(); |
+ SetSessionLengthLimitPref(40 * 1000); // 40 seconds. |
} |
// Creates a SessionLengthLimiter after setting a 60 second limit, allows 50 |
@@ -356,28 +383,15 @@ TEST_F(SessionLengthLimiterTest, RunAndRemoveSessionLengthLimit) { |
// Create a SessionLengthLimiter. |
CreateSessionLengthLimiter(false); |
- // Check timer ticks for 50 seconds of session time. |
- while (*remaining_ > kTenSeconds) |
- VerifyTimerTick(); |
+ // 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); |
- // Continue advancing the session time until it reaches the original 60 second |
- // limit. |
- while (*remaining_ > kZeroTimeDelta) { |
- runner_->RunTasks(); |
- |
- now_ += kSessionLengthLimitTimerInterval; |
- *remaining_ -= kSessionLengthLimitTimerInterval; |
- if (*remaining_ < kZeroTimeDelta) |
- remaining_.reset(new base::TimeDelta(kZeroTimeDelta)); |
- } |
- |
- // Check that the next timer tick does not lead to the session being |
- // terminated. |
- now_ += kSessionLengthLimitTimerInterval; |
- runner_->RunTasks(); |
+ // Verify that no timer fires to terminate the session. |
+ runner_->FastForwardUntilNoTasksRemain(); |
} |
} // namespace chromeos |