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.
5 #include "ash/system/chromeos/session/logout_confirmation_controller.h"
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"
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 {
29 MockTimeSingleThreadTaskRunner();
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;
41 const base::TimeTicks& GetCurrentTime() const;
43 void FastForwardBy(base::TimeDelta delta);
44 void FastForwardUntilNoTasksRemain();
47 // Strict weak temporal ordering of tasks.
51 const std::pair<base::TimeTicks, base::Closure>& first_task,
52 const std::pair<base::TimeTicks, base::Closure>& second_task) const;
55 virtual ~MockTimeSingleThreadTaskRunner();
58 std::priority_queue<std::pair<base::TimeTicks, base::Closure>,
59 std::vector<std::pair<base::TimeTicks, base::Closure> >,
60 TemporalOrder> tasks_;
62 DISALLOW_COPY_AND_ASSIGN(MockTimeSingleThreadTaskRunner);
65 // A base::TickClock that uses a MockTimeSingleThreadTaskRunner as the source of
67 class MockClock : public base::TickClock {
69 explicit MockClock(scoped_refptr<MockTimeSingleThreadTaskRunner> task_runner);
73 virtual base::TimeTicks NowTicks() OVERRIDE;
76 scoped_refptr<MockTimeSingleThreadTaskRunner> task_runner_;
78 DISALLOW_COPY_AND_ASSIGN(MockClock);
82 MockTimeSingleThreadTaskRunner::MockTimeSingleThreadTaskRunner() {
85 bool MockTimeSingleThreadTaskRunner::RunsTasksOnCurrentThread() const {
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));
97 bool MockTimeSingleThreadTaskRunner::PostNonNestableDelayedTask(
98 const tracked_objects::Location& from_here,
99 const base::Closure& task,
100 base::TimeDelta delay) {
105 const base::TimeTicks& MockTimeSingleThreadTaskRunner::GetCurrentTime() const {
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;
120 void MockTimeSingleThreadTaskRunner::FastForwardUntilNoTasksRemain() {
121 while (!tasks_.empty()) {
122 now_ = tasks_.top().first;
123 base::Closure task = tasks_.top().second;
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;
135 MockTimeSingleThreadTaskRunner::~MockTimeSingleThreadTaskRunner() {
138 MockClock::MockClock(scoped_refptr<MockTimeSingleThreadTaskRunner> task_runner)
139 : task_runner_(task_runner) {
142 MockClock::~MockClock() {
145 base::TimeTicks MockClock::NowTicks() {
146 return task_runner_->GetCurrentTime();
151 class LogoutConfirmationControllerTest : public testing::Test {
153 LogoutConfirmationControllerTest();
154 virtual ~LogoutConfirmationControllerTest();
158 bool log_out_called_;
160 scoped_refptr<MockTimeSingleThreadTaskRunner> runner_;
161 base::ThreadTaskRunnerHandle runner_handle_;
163 LogoutConfirmationController controller_;
166 DISALLOW_COPY_AND_ASSIGN(LogoutConfirmationControllerTest);
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_)));
179 LogoutConfirmationControllerTest::~LogoutConfirmationControllerTest() {
182 void LogoutConfirmationControllerTest::LogOut() {
183 log_out_called_ = true;
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_);
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_);
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_);
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_);
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_);
249 // Verifies that when the user confirms the logout request, the user is logged
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_);
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_);
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
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_);
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_);