Home | History | Annotate | Download | only in session
      1 // Copyright 2014 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 "ash/system/chromeos/session/logout_confirmation_controller.h"
      6 
      7 #include <queue>
      8 #include <utility>
      9 #include <vector>
     10 
     11 #include "base/bind.h"
     12 #include "base/bind_helpers.h"
     13 #include "base/compiler_specific.h"
     14 #include "base/location.h"
     15 #include "base/memory/ref_counted.h"
     16 #include "base/single_thread_task_runner.h"
     17 #include "base/thread_task_runner_handle.h"
     18 #include "base/time/tick_clock.h"
     19 #include "testing/gtest/include/gtest/gtest.h"
     20 
     21 namespace ash {
     22 namespace {
     23 
     24 // A SingleThreadTaskRunner that mocks the current time and allows it to be
     25 // fast-forwarded. TODO(bartfab): Copies of this class exist in several tests.
     26 // Consolidate them (crbug.com/329911).
     27 class MockTimeSingleThreadTaskRunner : public base::SingleThreadTaskRunner {
     28  public:
     29   MockTimeSingleThreadTaskRunner();
     30 
     31   // base::SingleThreadTaskRunner:
     32   virtual bool RunsTasksOnCurrentThread() const OVERRIDE;
     33   virtual bool PostDelayedTask(const tracked_objects::Location& from_here,
     34                                const base::Closure& task,
     35                                base::TimeDelta delay) OVERRIDE;
     36   virtual bool PostNonNestableDelayedTask(
     37       const tracked_objects::Location& from_here,
     38       const base::Closure& task,
     39       base::TimeDelta delay) OVERRIDE;
     40 
     41   const base::TimeTicks& GetCurrentTime() const;
     42 
     43   void FastForwardBy(base::TimeDelta delta);
     44   void FastForwardUntilNoTasksRemain();
     45 
     46  private:
     47   // Strict weak temporal ordering of tasks.
     48   class TemporalOrder {
     49    public:
     50     bool operator()(
     51         const std::pair<base::TimeTicks, base::Closure>& first_task,
     52         const std::pair<base::TimeTicks, base::Closure>& second_task) const;
     53   };
     54 
     55   virtual ~MockTimeSingleThreadTaskRunner();
     56 
     57   base::TimeTicks now_;
     58   std::priority_queue<std::pair<base::TimeTicks, base::Closure>,
     59                       std::vector<std::pair<base::TimeTicks, base::Closure> >,
     60                       TemporalOrder> tasks_;
     61 
     62   DISALLOW_COPY_AND_ASSIGN(MockTimeSingleThreadTaskRunner);
     63 };
     64 
     65 // A base::TickClock that uses a MockTimeSingleThreadTaskRunner as the source of
     66 // the current time.
     67 class MockClock : public base::TickClock {
     68  public:
     69   explicit MockClock(scoped_refptr<MockTimeSingleThreadTaskRunner> task_runner);
     70   virtual ~MockClock();
     71 
     72   // base::TickClock:
     73   virtual base::TimeTicks NowTicks() OVERRIDE;
     74 
     75  private:
     76   scoped_refptr<MockTimeSingleThreadTaskRunner> task_runner_;
     77 
     78   DISALLOW_COPY_AND_ASSIGN(MockClock);
     79 };
     80 
     81 
     82 MockTimeSingleThreadTaskRunner::MockTimeSingleThreadTaskRunner() {
     83 }
     84 
     85 bool MockTimeSingleThreadTaskRunner::RunsTasksOnCurrentThread() const {
     86   return true;
     87 }
     88 
     89 bool MockTimeSingleThreadTaskRunner::PostDelayedTask(
     90     const tracked_objects::Location& from_here,
     91     const base::Closure& task,
     92     base::TimeDelta delay) {
     93   tasks_.push(std::make_pair(now_ + delay, task));
     94   return true;
     95 }
     96 
     97 bool MockTimeSingleThreadTaskRunner::PostNonNestableDelayedTask(
     98     const tracked_objects::Location& from_here,
     99     const base::Closure& task,
    100     base::TimeDelta delay) {
    101   NOTREACHED();
    102   return false;
    103 }
    104 
    105 const base::TimeTicks& MockTimeSingleThreadTaskRunner::GetCurrentTime() const {
    106   return now_;
    107 }
    108 
    109 void MockTimeSingleThreadTaskRunner::FastForwardBy(base::TimeDelta delta) {
    110   const base::TimeTicks latest = now_ + delta;
    111   while (!tasks_.empty() && tasks_.top().first <= latest) {
    112     now_ = tasks_.top().first;
    113     base::Closure task = tasks_.top().second;
    114     tasks_.pop();
    115     task.Run();
    116   }
    117   now_ = latest;
    118 }
    119 
    120 void MockTimeSingleThreadTaskRunner::FastForwardUntilNoTasksRemain() {
    121   while (!tasks_.empty()) {
    122     now_ = tasks_.top().first;
    123     base::Closure task = tasks_.top().second;
    124     tasks_.pop();
    125     task.Run();
    126   }
    127 }
    128 
    129 bool MockTimeSingleThreadTaskRunner::TemporalOrder::operator()(
    130     const std::pair<base::TimeTicks, base::Closure>& first_task,
    131     const std::pair<base::TimeTicks, base::Closure>& second_task) const {
    132   return first_task.first > second_task.first;
    133 }
    134 
    135 MockTimeSingleThreadTaskRunner::~MockTimeSingleThreadTaskRunner() {
    136 }
    137 
    138 MockClock::MockClock(scoped_refptr<MockTimeSingleThreadTaskRunner> task_runner)
    139     : task_runner_(task_runner) {
    140 }
    141 
    142 MockClock::~MockClock() {
    143 }
    144 
    145 base::TimeTicks MockClock::NowTicks() {
    146   return task_runner_->GetCurrentTime();
    147 }
    148 
    149 }  // namespace
    150 
    151 class LogoutConfirmationControllerTest : public testing::Test {
    152  protected:
    153   LogoutConfirmationControllerTest();
    154   virtual ~LogoutConfirmationControllerTest();
    155 
    156   void LogOut();
    157 
    158   bool log_out_called_;
    159 
    160   scoped_refptr<MockTimeSingleThreadTaskRunner> runner_;
    161   base::ThreadTaskRunnerHandle runner_handle_;
    162 
    163   LogoutConfirmationController controller_;
    164 
    165  private:
    166   DISALLOW_COPY_AND_ASSIGN(LogoutConfirmationControllerTest);
    167 };
    168 
    169 LogoutConfirmationControllerTest::LogoutConfirmationControllerTest()
    170     : log_out_called_(false),
    171       runner_(new MockTimeSingleThreadTaskRunner),
    172       runner_handle_(runner_),
    173       controller_(base::Bind(&LogoutConfirmationControllerTest::LogOut,
    174                              base::Unretained(this))) {
    175   controller_.SetClockForTesting(
    176       scoped_ptr<base::TickClock>(new MockClock(runner_)));
    177 }
    178 
    179 LogoutConfirmationControllerTest::~LogoutConfirmationControllerTest() {
    180 }
    181 
    182 void LogoutConfirmationControllerTest::LogOut() {
    183   log_out_called_ = true;
    184 }
    185 
    186 // Verifies that the user is logged out immediately if logout confirmation with
    187 // a zero-length countdown is requested.
    188 TEST_F(LogoutConfirmationControllerTest, ZeroDuration) {
    189   controller_.ConfirmLogout(runner_->GetCurrentTime());
    190   EXPECT_FALSE(log_out_called_);
    191   runner_->FastForwardBy(base::TimeDelta());
    192   EXPECT_TRUE(log_out_called_);
    193 }
    194 
    195 // Verifies that the user is logged out when the countdown expires.
    196 TEST_F(LogoutConfirmationControllerTest, DurationExpired) {
    197   controller_.ConfirmLogout(
    198       runner_->GetCurrentTime() + base::TimeDelta::FromSeconds(10));
    199   EXPECT_FALSE(log_out_called_);
    200   runner_->FastForwardBy(base::TimeDelta::FromSeconds(9));
    201   EXPECT_FALSE(log_out_called_);
    202   runner_->FastForwardBy(base::TimeDelta::FromSeconds(2));
    203   EXPECT_TRUE(log_out_called_);
    204 }
    205 
    206 // Verifies that when a second request to confirm logout is made and the second
    207 // request's countdown ends before the original request's, the user is logged
    208 // out when the new countdown expires.
    209 TEST_F(LogoutConfirmationControllerTest, DurationShortened) {
    210   controller_.ConfirmLogout(
    211       runner_->GetCurrentTime() + base::TimeDelta::FromSeconds(30));
    212   EXPECT_FALSE(log_out_called_);
    213   runner_->FastForwardBy(base::TimeDelta::FromSeconds(9));
    214   EXPECT_FALSE(log_out_called_);
    215   controller_.ConfirmLogout(
    216       runner_->GetCurrentTime() + base::TimeDelta::FromSeconds(10));
    217   runner_->FastForwardBy(base::TimeDelta::FromSeconds(9));
    218   EXPECT_FALSE(log_out_called_);
    219   runner_->FastForwardBy(base::TimeDelta::FromSeconds(2));
    220   EXPECT_TRUE(log_out_called_);
    221 }
    222 
    223 // Verifies that when a second request to confirm logout is made and the second
    224 // request's countdown ends after the original request's, the user is logged
    225 // out when the original countdown expires.
    226 TEST_F(LogoutConfirmationControllerTest, DurationExtended) {
    227   controller_.ConfirmLogout(
    228       runner_->GetCurrentTime() + base::TimeDelta::FromSeconds(10));
    229   EXPECT_FALSE(log_out_called_);
    230   runner_->FastForwardBy(base::TimeDelta::FromSeconds(9));
    231   EXPECT_FALSE(log_out_called_);
    232   controller_.ConfirmLogout(
    233       runner_->GetCurrentTime() + base::TimeDelta::FromSeconds(10));
    234   runner_->FastForwardBy(base::TimeDelta::FromSeconds(2));
    235   EXPECT_TRUE(log_out_called_);
    236 }
    237 
    238 // Verifies that when the screen is locked while the countdown is running, the
    239 // user is not logged out, even when the original countdown expires.
    240 TEST_F(LogoutConfirmationControllerTest, Lock) {
    241   controller_.ConfirmLogout(
    242       runner_->GetCurrentTime() + base::TimeDelta::FromSeconds(10));
    243   EXPECT_FALSE(log_out_called_);
    244   controller_.OnLockStateChanged(true);
    245   runner_->FastForwardUntilNoTasksRemain();
    246   EXPECT_FALSE(log_out_called_);
    247 }
    248 
    249 // Verifies that when the user confirms the logout request, the user is logged
    250 // out immediately.
    251 TEST_F(LogoutConfirmationControllerTest, UserAccepted) {
    252   controller_.ConfirmLogout(
    253       runner_->GetCurrentTime() + base::TimeDelta::FromSeconds(10));
    254   EXPECT_FALSE(log_out_called_);
    255   controller_.OnLogoutConfirmed();
    256   EXPECT_TRUE(log_out_called_);
    257 }
    258 
    259 // Verifies that when the user denies the logout request, the user is not logged
    260 // out, even when the original countdown expires.
    261 TEST_F(LogoutConfirmationControllerTest, UserDenied) {
    262   controller_.ConfirmLogout(
    263       runner_->GetCurrentTime() + base::TimeDelta::FromSeconds(10));
    264   EXPECT_FALSE(log_out_called_);
    265   controller_.OnDialogClosed();
    266   runner_->FastForwardUntilNoTasksRemain();
    267   EXPECT_FALSE(log_out_called_);
    268 }
    269 
    270 // Verifies that after the user has denied a logout request, a subsequent logout
    271 // request is handled correctly and the user is logged out when the countdown
    272 // expires.
    273 TEST_F(LogoutConfirmationControllerTest, DurationExpiredAfterDeniedRequest) {
    274   controller_.ConfirmLogout(
    275       runner_->GetCurrentTime() + base::TimeDelta::FromSeconds(10));
    276   EXPECT_FALSE(log_out_called_);
    277   controller_.OnDialogClosed();
    278   runner_->FastForwardUntilNoTasksRemain();
    279   EXPECT_FALSE(log_out_called_);
    280 
    281   controller_.ConfirmLogout(
    282       runner_->GetCurrentTime() + base::TimeDelta::FromSeconds(10));
    283   EXPECT_FALSE(log_out_called_);
    284   runner_->FastForwardBy(base::TimeDelta::FromSeconds(9));
    285   EXPECT_FALSE(log_out_called_);
    286   runner_->FastForwardBy(base::TimeDelta::FromSeconds(2));
    287   EXPECT_TRUE(log_out_called_);
    288 }
    289 
    290 }  // namespace ash
    291