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