Home | History | Annotate | Download | only in chromeos
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/chromeos/session_length_limiter.h"
      6 
      7 #include <queue>
      8 #include <utility>
      9 #include <vector>
     10 
     11 #include "base/callback.h"
     12 #include "base/compiler_specific.h"
     13 #include "base/location.h"
     14 #include "base/logging.h"
     15 #include "base/memory/ref_counted.h"
     16 #include "base/memory/scoped_ptr.h"
     17 #include "base/prefs/testing_pref_service.h"
     18 #include "base/single_thread_task_runner.h"
     19 #include "base/strings/string_number_conversions.h"
     20 #include "base/thread_task_runner_handle.h"
     21 #include "base/values.h"
     22 #include "chrome/common/pref_names.h"
     23 #include "chrome/test/base/testing_browser_process.h"
     24 #include "testing/gmock/include/gmock/gmock.h"
     25 #include "testing/gtest/include/gtest/gtest.h"
     26 
     27 using ::testing::Invoke;
     28 using ::testing::Mock;
     29 using ::testing::NiceMock;
     30 
     31 namespace chromeos {
     32 
     33 namespace {
     34 
     35 class MockSessionLengthLimiterDelegate : public SessionLengthLimiter::Delegate {
     36  public:
     37   MOCK_CONST_METHOD0(GetCurrentTime, const base::TimeTicks(void));
     38   MOCK_METHOD0(StopSession, void(void));
     39 };
     40 
     41 // A SingleThreadTaskRunner that mocks the current time and allows it to be
     42 // fast-forwarded.
     43 class MockTimeSingleThreadTaskRunner : public base::SingleThreadTaskRunner {
     44  public:
     45   MockTimeSingleThreadTaskRunner();
     46 
     47   // base::SingleThreadTaskRunner:
     48   virtual bool RunsTasksOnCurrentThread() const OVERRIDE;
     49   virtual bool PostDelayedTask(const tracked_objects::Location& from_here,
     50                                const base::Closure& task,
     51                                base::TimeDelta delay) OVERRIDE;
     52   virtual bool PostNonNestableDelayedTask(
     53       const tracked_objects::Location& from_here,
     54       const base::Closure& task,
     55       base::TimeDelta delay) OVERRIDE;
     56 
     57   const base::TimeTicks& GetCurrentTime() const;
     58 
     59   void FastForwardBy(int64 milliseconds);
     60   void FastForwardUntilNoTasksRemain();
     61 
     62  private:
     63   // Strict weak temporal ordering of tasks.
     64   class TemporalOrder {
     65    public:
     66     bool operator()(
     67         const std::pair<base::TimeTicks, base::Closure>& first_task,
     68         const std::pair<base::TimeTicks, base::Closure>& second_task) const;
     69   };
     70 
     71   virtual ~MockTimeSingleThreadTaskRunner();
     72 
     73   base::TimeTicks now_;
     74   std::priority_queue<std::pair<base::TimeTicks, base::Closure>,
     75                       std::vector<std::pair<base::TimeTicks, base::Closure> >,
     76                       TemporalOrder> tasks_;
     77 };
     78 
     79 }  // namespace
     80 
     81 class SessionLengthLimiterTest : public testing::Test {
     82  protected:
     83   SessionLengthLimiterTest();
     84 
     85   // testing::Test:
     86   virtual void SetUp() OVERRIDE;
     87   virtual void TearDown() OVERRIDE;
     88 
     89   void SetSessionStartTimePref(int64 session_start_time);
     90   void VerifySessionStartTimePref();
     91   void SetSessionLengthLimitPref(int64 session_length_limit);
     92 
     93   void ExpectStopSession();
     94   void CheckStopSessionTime();
     95 
     96   void CreateSessionLengthLimiter(bool browser_restarted);
     97 
     98   TestingPrefServiceSimple local_state_;
     99   scoped_refptr<MockTimeSingleThreadTaskRunner> runner_;
    100   base::TimeTicks session_start_time_;
    101   base::TimeTicks session_end_time_;
    102 
    103   MockSessionLengthLimiterDelegate* delegate_;  // Owned by
    104                                                 // session_length_limiter_.
    105   scoped_ptr<SessionLengthLimiter> session_length_limiter_;
    106 };
    107 
    108 MockTimeSingleThreadTaskRunner::MockTimeSingleThreadTaskRunner() {
    109 }
    110 
    111 bool MockTimeSingleThreadTaskRunner::RunsTasksOnCurrentThread() const {
    112   return true;
    113 }
    114 
    115 bool MockTimeSingleThreadTaskRunner::PostDelayedTask(
    116     const tracked_objects::Location& from_here,
    117     const base::Closure& task,
    118     base::TimeDelta delay) {
    119   tasks_.push(std::pair<base::TimeTicks, base::Closure>(now_ + delay, task));
    120   return true;
    121 }
    122 
    123 bool MockTimeSingleThreadTaskRunner::PostNonNestableDelayedTask(
    124     const tracked_objects::Location& from_here,
    125     const base::Closure& task,
    126     base::TimeDelta delay) {
    127   NOTREACHED();
    128   return false;
    129 }
    130 
    131 const base::TimeTicks& MockTimeSingleThreadTaskRunner::GetCurrentTime() const {
    132   return now_;
    133 }
    134 
    135 void MockTimeSingleThreadTaskRunner::FastForwardBy(int64 delta) {
    136   const base::TimeTicks latest =
    137       now_ + base::TimeDelta::FromMilliseconds(delta);
    138   while (!tasks_.empty() && tasks_.top().first <= latest) {
    139     now_ = tasks_.top().first;
    140     base::Closure task = tasks_.top().second;
    141     tasks_.pop();
    142     task.Run();
    143   }
    144   now_ = latest;
    145 }
    146 
    147 void MockTimeSingleThreadTaskRunner::FastForwardUntilNoTasksRemain() {
    148   while (!tasks_.empty()) {
    149     now_ = tasks_.top().first;
    150     base::Closure task = tasks_.top().second;
    151     tasks_.pop();
    152     task.Run();
    153   }
    154 }
    155 
    156 bool MockTimeSingleThreadTaskRunner::TemporalOrder::operator()(
    157     const std::pair<base::TimeTicks, base::Closure>& first_task,
    158     const std::pair<base::TimeTicks, base::Closure>& second_task) const {
    159   return first_task.first >= second_task.first;
    160 }
    161 
    162 MockTimeSingleThreadTaskRunner::~MockTimeSingleThreadTaskRunner() {
    163 }
    164 
    165 SessionLengthLimiterTest::SessionLengthLimiterTest() : delegate_(NULL) {
    166 }
    167 
    168 void SessionLengthLimiterTest::SetUp() {
    169   TestingBrowserProcess::GetGlobal()->SetLocalState(&local_state_);
    170   SessionLengthLimiter::RegisterPrefs(local_state_.registry());
    171   runner_ = new MockTimeSingleThreadTaskRunner;
    172   session_start_time_ = runner_->GetCurrentTime();
    173 
    174   delegate_ = new NiceMock<MockSessionLengthLimiterDelegate>;
    175   ON_CALL(*delegate_, GetCurrentTime())
    176       .WillByDefault(Invoke(runner_.get(),
    177                             &MockTimeSingleThreadTaskRunner::GetCurrentTime));
    178   EXPECT_CALL(*delegate_, StopSession()).Times(0);
    179 }
    180 
    181 void SessionLengthLimiterTest::TearDown() {
    182   TestingBrowserProcess::GetGlobal()->SetLocalState(NULL);
    183 }
    184 
    185 void SessionLengthLimiterTest::SetSessionStartTimePref(
    186     int64 session_start_time) {
    187   local_state_.SetUserPref(prefs::kSessionStartTime,
    188                            base::Value::CreateStringValue(
    189                                base::Int64ToString(session_start_time)));
    190 }
    191 
    192 void SessionLengthLimiterTest::VerifySessionStartTimePref() {
    193   base::TimeTicks session_start_time(base::TimeTicks::FromInternalValue(
    194       local_state_.GetInt64(prefs::kSessionStartTime)));
    195   EXPECT_EQ(session_start_time_, session_start_time);
    196 }
    197 
    198 void SessionLengthLimiterTest::SetSessionLengthLimitPref(
    199     int64 session_length_limit) {
    200   session_end_time_ = session_start_time_ +
    201       base::TimeDelta::FromMilliseconds(session_length_limit);
    202   // If the new session end time has passed already, the session should end now.
    203   if (session_end_time_ < runner_->GetCurrentTime())
    204     session_end_time_ = runner_->GetCurrentTime();
    205   local_state_.SetUserPref(prefs::kSessionLengthLimit,
    206                            base::Value::CreateIntegerValue(
    207                                session_length_limit));
    208 }
    209 
    210 void SessionLengthLimiterTest::ExpectStopSession() {
    211   Mock::VerifyAndClearExpectations(delegate_);
    212   EXPECT_CALL(*delegate_, StopSession())
    213     .Times(1)
    214     .WillOnce(Invoke(this, &SessionLengthLimiterTest::CheckStopSessionTime));
    215 }
    216 
    217 void SessionLengthLimiterTest::CheckStopSessionTime() {
    218   EXPECT_EQ(session_end_time_, runner_->GetCurrentTime());
    219 }
    220 
    221 void SessionLengthLimiterTest::CreateSessionLengthLimiter(
    222     bool browser_restarted) {
    223   session_length_limiter_.reset(
    224       new SessionLengthLimiter(delegate_, browser_restarted));
    225 }
    226 // Verifies that the session start time in local state is updated during login
    227 // if no session start time has been stored before.
    228 TEST_F(SessionLengthLimiterTest, StartWithSessionStartTimeUnset) {
    229   CreateSessionLengthLimiter(false);
    230   VerifySessionStartTimePref();
    231 }
    232 
    233 // Verifies that the session start time in local state is updated during login
    234 // if a session start time lying in the future has been stored before.
    235 TEST_F(SessionLengthLimiterTest, StartWithSessionStartTimeFuture) {
    236   SetSessionStartTimePref(
    237       (session_start_time_ + base::TimeDelta::FromHours(2)).ToInternalValue());
    238   CreateSessionLengthLimiter(false);
    239   VerifySessionStartTimePref();
    240 }
    241 
    242 // Verifies that the session start time in local state is updated during login
    243 // if a valid session start time has been stored before.
    244 TEST_F(SessionLengthLimiterTest, StartWithSessionStartTimeValid) {
    245   SetSessionStartTimePref(
    246       (session_start_time_ - base::TimeDelta::FromHours(2)).ToInternalValue());
    247   CreateSessionLengthLimiter(false);
    248   VerifySessionStartTimePref();
    249 }
    250 
    251 // Verifies that the session start time in local state is updated during restart
    252 // after a crash if no session start time has been stored before.
    253 TEST_F(SessionLengthLimiterTest, RestartWithSessionStartTimeUnset) {
    254   CreateSessionLengthLimiter(true);
    255   VerifySessionStartTimePref();
    256 }
    257 
    258 // Verifies that the session start time in local state is updated during restart
    259 // after a crash if a session start time lying in the future has been stored
    260 // before.
    261 TEST_F(SessionLengthLimiterTest, RestartWithSessionStartTimeFuture) {
    262   SetSessionStartTimePref(
    263       (session_start_time_ + base::TimeDelta::FromHours(2)).ToInternalValue());
    264   CreateSessionLengthLimiter(true);
    265   VerifySessionStartTimePref();
    266 }
    267 
    268 // Verifies that the session start time in local state is *not* updated during
    269 // restart after a crash if a valid session start time has been stored before.
    270 TEST_F(SessionLengthLimiterTest, RestartWithSessionStartTimeValid) {
    271   session_start_time_ -= base::TimeDelta::FromHours(2);
    272   SetSessionStartTimePref(session_start_time_.ToInternalValue());
    273   CreateSessionLengthLimiter(true);
    274   VerifySessionStartTimePref();
    275 }
    276 
    277 // Creates a SessionLengthLimiter without setting a limit. Verifies that the
    278 // limiter does not start a timer.
    279 TEST_F(SessionLengthLimiterTest, RunWithoutSessionLengthLimit) {
    280   base::ThreadTaskRunnerHandle runner_handler(runner_);
    281 
    282   // Create a SessionLengthLimiter.
    283   CreateSessionLengthLimiter(false);
    284 
    285   // Verify that no timer fires to terminate the session.
    286   runner_->FastForwardUntilNoTasksRemain();
    287 }
    288 
    289 // Creates a SessionLengthLimiter after setting a limit. Verifies that the
    290 // limiter starts a timer and that when the session length reaches the limit,
    291 // the session is terminated.
    292 TEST_F(SessionLengthLimiterTest, RunWithSessionLengthLimit) {
    293   base::ThreadTaskRunnerHandle runner_handler(runner_);
    294 
    295   // Set a 60 second session time limit.
    296   SetSessionLengthLimitPref(60 * 1000);  // 60 seconds.
    297 
    298   // Create a SessionLengthLimiter.
    299   CreateSessionLengthLimiter(false);
    300 
    301   // Verify that the timer fires and the session is terminated when the session
    302   // length limit is reached.
    303   ExpectStopSession();
    304   runner_->FastForwardUntilNoTasksRemain();
    305 }
    306 
    307 // Creates a SessionLengthLimiter after setting a 60 second limit, allows 50
    308 // seconds of session time to pass, then increases the limit to 90 seconds.
    309 // Verifies that when the session time reaches the new 90 second limit, the
    310 // session is terminated.
    311 TEST_F(SessionLengthLimiterTest, RunAndIncreaseSessionLengthLimit) {
    312   base::ThreadTaskRunnerHandle runner_handler(runner_);
    313 
    314   // Set a 60 second session time limit.
    315   SetSessionLengthLimitPref(60 * 1000);  // 60 seconds.
    316 
    317   // Create a SessionLengthLimiter.
    318   CreateSessionLengthLimiter(false);
    319 
    320   // Fast forward the time by 50 seconds, verifying that no timer fires to
    321   // terminate the session.
    322   runner_->FastForwardBy(50 * 1000);  // 50 seconds.
    323 
    324   // Increase the session length limit to 90 seconds.
    325   SetSessionLengthLimitPref(90 * 1000);  // 90 seconds.
    326 
    327   // Verify that the the timer fires and the session is terminated when the
    328   // session length limit is reached.
    329   ExpectStopSession();
    330   runner_->FastForwardUntilNoTasksRemain();
    331 }
    332 
    333 // Creates a SessionLengthLimiter after setting a 60 second limit, allows 50
    334 // seconds of session time to pass, then decreases the limit to 40 seconds.
    335 // Verifies that when the limit is decreased to 40 seconds after 50 seconds of
    336 // session time have passed, the next timer tick causes the session to be
    337 // terminated.
    338 TEST_F(SessionLengthLimiterTest, RunAndDecreaseSessionLengthLimit) {
    339   base::ThreadTaskRunnerHandle runner_handler(runner_);
    340 
    341   // Set a 60 second session time limit.
    342   SetSessionLengthLimitPref(60 * 1000);  // 60 seconds.
    343 
    344   // Create a SessionLengthLimiter.
    345   CreateSessionLengthLimiter(false);
    346 
    347   // Fast forward the time by 50 seconds, verifying that no timer fires to
    348   // terminate the session.
    349   runner_->FastForwardBy(50 * 1000);  // 50 seconds.
    350 
    351   // Verify that reducing the session length limit below the 50 seconds that
    352   // have already elapsed causes the session to be terminated immediately.
    353   ExpectStopSession();
    354   SetSessionLengthLimitPref(40 * 1000);  // 40 seconds.
    355 }
    356 
    357 // Creates a SessionLengthLimiter after setting a 60 second limit, allows 50
    358 // seconds of session time to pass, then removes the limit. Verifies that after
    359 // the limit is removed, the session is not terminated when the session time
    360 // reaches the original 60 second limit.
    361 TEST_F(SessionLengthLimiterTest, RunAndRemoveSessionLengthLimit) {
    362   base::ThreadTaskRunnerHandle runner_handler(runner_);
    363 
    364   // Set a 60 second session time limit.
    365   SetSessionLengthLimitPref(60 * 1000);  // 60 seconds.
    366 
    367   // Create a SessionLengthLimiter.
    368   CreateSessionLengthLimiter(false);
    369 
    370   // Fast forward the time by 50 seconds, verifying that no timer fires to
    371   // terminate the session.
    372   runner_->FastForwardBy(50 * 1000);  // 50 seconds.
    373 
    374   // Remove the session length limit.
    375   local_state_.RemoveUserPref(prefs::kSessionLengthLimit);
    376 
    377   // Verify that no timer fires to terminate the session.
    378   runner_->FastForwardUntilNoTasksRemain();
    379 }
    380 
    381 }  // namespace chromeos
    382