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"
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).
29 class MockTimeSingleThreadTaskRunner : public base::SingleThreadTaskRunner {
31 MockTimeSingleThreadTaskRunner();
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;
43 const base::TimeTicks& GetCurrentTime() const;
45 void FastForwardBy(base::TimeDelta delta);
46 void FastForwardUntilNoTasksRemain();
49 // Strict weak temporal ordering of tasks.
53 const std::pair<base::TimeTicks, base::Closure>& first_task,
54 const std::pair<base::TimeTicks, base::Closure>& second_task) const;
57 virtual ~MockTimeSingleThreadTaskRunner();
60 std::priority_queue<std::pair<base::TimeTicks, base::Closure>,
61 std::vector<std::pair<base::TimeTicks, base::Closure> >,
62 TemporalOrder> tasks_;
64 DISALLOW_COPY_AND_ASSIGN(MockTimeSingleThreadTaskRunner);
67 // A base::TickClock that uses a MockTimeSingleThreadTaskRunner as the source of
69 class MockClock : public base::TickClock {
71 explicit MockClock(scoped_refptr<MockTimeSingleThreadTaskRunner> task_runner);
75 virtual base::TimeTicks NowTicks() OVERRIDE;
78 scoped_refptr<MockTimeSingleThreadTaskRunner> task_runner_;
80 DISALLOW_COPY_AND_ASSIGN(MockClock);
84 MockTimeSingleThreadTaskRunner::MockTimeSingleThreadTaskRunner() {
87 bool MockTimeSingleThreadTaskRunner::RunsTasksOnCurrentThread() const {
91 bool 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));
99 bool MockTimeSingleThreadTaskRunner::PostNonNestableDelayedTask(
100 const tracked_objects::Location& from_here,
101 const base::Closure& task,
102 base::TimeDelta delay) {
107 const base::TimeTicks& MockTimeSingleThreadTaskRunner::GetCurrentTime() const {
111 void 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;
122 void MockTimeSingleThreadTaskRunner::FastForwardUntilNoTasksRemain() {
123 while (!tasks_.empty()) {
124 now_ = tasks_.top().first;
125 base::Closure task = tasks_.top().second;
131 bool 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;
137 MockTimeSingleThreadTaskRunner::~MockTimeSingleThreadTaskRunner() {
140 MockClock::MockClock(scoped_refptr<MockTimeSingleThreadTaskRunner> task_runner)
141 : task_runner_(task_runner) {
144 MockClock::~MockClock() {
147 base::TimeTicks MockClock::NowTicks() {
148 return task_runner_->GetCurrentTime();
153 class LogoutConfirmationControllerTest : public testing::Test {
155 LogoutConfirmationControllerTest();
156 virtual ~LogoutConfirmationControllerTest();
160 bool log_out_called_;
162 scoped_refptr<MockTimeSingleThreadTaskRunner> runner_;
163 base::ThreadTaskRunnerHandle runner_handle_;
165 LogoutConfirmationController controller_;
168 DISALLOW_COPY_AND_ASSIGN(LogoutConfirmationControllerTest);
171 LogoutConfirmationControllerTest::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_)));
181 LogoutConfirmationControllerTest::~LogoutConfirmationControllerTest() {
184 void LogoutConfirmationControllerTest::LogOut() {
185 log_out_called_ = true;
188 // Verifies that the user is logged out immediately if logout confirmation with
189 // a zero-length countdown is requested.
190 TEST_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_);
197 // Verifies that the user is logged out when the countdown expires.
198 TEST_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_);
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.
211 TEST_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_);
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.
228 TEST_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_);
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.
242 TEST_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_);
251 // Verifies that when the user confirms the logout request, the user is logged
253 TEST_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_);
261 // Verifies that when the user denies the logout request, the user is not logged
262 // out, even when the original countdown expires.
263 TEST_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_);
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
275 TEST_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_);
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_);
292 } // namespace internal