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