1// Copyright (c) 2012 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/wm/user_activity_detector.h"
6
7#include "ash/shell.h"
8#include "ash/test/ash_test_base.h"
9#include "ash/wm/user_activity_observer.h"
10#include "base/compiler_specific.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/time/time.h"
13#include "ui/aura/test/test_windows.h"
14#include "ui/aura/window.h"
15#include "ui/base/events/event.h"
16#include "ui/base/events/event_constants.h"
17#include "ui/base/keycodes/keyboard_codes.h"
18#include "ui/gfx/point.h"
19
20namespace {
21
22void SetEventTarget(ui::EventTarget* target, ui::Event* event) {
23  ui::Event::DispatcherApi dispatch_helper(event);
24  dispatch_helper.set_target(target);
25}
26
27}
28
29namespace ash {
30namespace test {
31
32// Implementation that just counts the number of times we've been told that the
33// user is active.
34class TestUserActivityObserver : public UserActivityObserver {
35 public:
36  TestUserActivityObserver() : num_invocations_(0) {}
37
38  int num_invocations() const { return num_invocations_; }
39  void reset_stats() { num_invocations_ = 0; }
40
41  // UserActivityObserver implementation.
42  virtual void OnUserActivity(const ui::Event* event) OVERRIDE {
43    num_invocations_++;
44  }
45
46 private:
47  // Number of times that OnUserActivity() has been called.
48  int num_invocations_;
49
50  DISALLOW_COPY_AND_ASSIGN(TestUserActivityObserver);
51};
52
53class UserActivityDetectorTest : public AshTestBase {
54 public:
55  UserActivityDetectorTest() {}
56  virtual ~UserActivityDetectorTest() {}
57
58  virtual void SetUp() OVERRIDE {
59    AshTestBase::SetUp();
60    observer_.reset(new TestUserActivityObserver);
61    detector_ = Shell::GetInstance()->user_activity_detector();
62    detector_->AddObserver(observer_.get());
63
64    now_ = base::TimeTicks::Now();
65    detector_->set_now_for_test(now_);
66  }
67
68  virtual void TearDown() OVERRIDE {
69    detector_->RemoveObserver(observer_.get());
70    AshTestBase::TearDown();
71  }
72
73 protected:
74  // Move |detector_|'s idea of the current time forward by |delta|.
75  void AdvanceTime(base::TimeDelta delta) {
76    now_ += delta;
77    detector_->set_now_for_test(now_);
78  }
79
80  UserActivityDetector* detector_;  // not owned
81
82  scoped_ptr<TestUserActivityObserver> observer_;
83
84  base::TimeTicks now_;
85
86 private:
87  DISALLOW_COPY_AND_ASSIGN(UserActivityDetectorTest);
88};
89
90// Checks that the observer is notified in response to different types of input
91// events.
92TEST_F(UserActivityDetectorTest, Basic) {
93  scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(12345));
94
95  ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_NONE, false);
96  SetEventTarget(window.get(), &key_event);
97  detector_->OnKeyEvent(&key_event);
98  EXPECT_FALSE(key_event.handled());
99  EXPECT_EQ(now_.ToInternalValue(),
100            detector_->last_activity_time().ToInternalValue());
101  EXPECT_EQ(1, observer_->num_invocations());
102  observer_->reset_stats();
103
104  base::TimeDelta advance_delta = base::TimeDelta::FromMilliseconds(
105      UserActivityDetector::kNotifyIntervalMs);
106  AdvanceTime(advance_delta);
107  ui::MouseEvent mouse_event(
108      ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), ui::EF_NONE);
109  SetEventTarget(window.get(), &mouse_event);
110  detector_->OnMouseEvent(&mouse_event);
111  EXPECT_FALSE(mouse_event.handled());
112  EXPECT_EQ(now_.ToInternalValue(),
113            detector_->last_activity_time().ToInternalValue());
114  EXPECT_EQ(1, observer_->num_invocations());
115  observer_->reset_stats();
116
117  base::TimeTicks time_before_ignore = now_;
118
119  // Temporarily ignore mouse events when displays are turned on or off.
120  detector_->OnDisplayPowerChanging();
121  detector_->OnMouseEvent(&mouse_event);
122  EXPECT_FALSE(mouse_event.handled());
123  EXPECT_EQ(time_before_ignore.ToInternalValue(),
124            detector_->last_activity_time().ToInternalValue());
125  EXPECT_EQ(0, observer_->num_invocations());
126  observer_->reset_stats();
127
128  const base::TimeDelta kIgnoreMouseTime =
129      base::TimeDelta::FromMilliseconds(
130          UserActivityDetector::kDisplayPowerChangeIgnoreMouseMs);
131  AdvanceTime(kIgnoreMouseTime / 2);
132  detector_->OnMouseEvent(&mouse_event);
133  EXPECT_FALSE(mouse_event.handled());
134  EXPECT_EQ(time_before_ignore.ToInternalValue(),
135            detector_->last_activity_time().ToInternalValue());
136  EXPECT_EQ(0, observer_->num_invocations());
137  observer_->reset_stats();
138
139  // After enough time has passed, mouse events should be reported again.
140  AdvanceTime(std::max(kIgnoreMouseTime, advance_delta));
141  detector_->OnMouseEvent(&mouse_event);
142  EXPECT_FALSE(mouse_event.handled());
143  EXPECT_EQ(now_.ToInternalValue(),
144            detector_->last_activity_time().ToInternalValue());
145  EXPECT_EQ(1, observer_->num_invocations());
146  observer_->reset_stats();
147
148  AdvanceTime(advance_delta);
149  ui::TouchEvent touch_event(
150      ui::ET_TOUCH_PRESSED, gfx::Point(), 0, base::TimeDelta());
151  SetEventTarget(window.get(), &touch_event);
152  detector_->OnTouchEvent(&touch_event);
153  EXPECT_FALSE(touch_event.handled());
154  EXPECT_EQ(now_.ToInternalValue(),
155            detector_->last_activity_time().ToInternalValue());
156  EXPECT_EQ(1, observer_->num_invocations());
157  observer_->reset_stats();
158
159  AdvanceTime(advance_delta);
160  ui::GestureEvent gesture_event(
161      ui::ET_GESTURE_TAP, 0, 0, ui::EF_NONE,
162      base::TimeDelta::FromMilliseconds(base::Time::Now().ToDoubleT() * 1000),
163      ui::GestureEventDetails(ui::ET_GESTURE_TAP, 0, 0), 0U);
164  SetEventTarget(window.get(), &gesture_event);
165  detector_->OnGestureEvent(&gesture_event);
166  EXPECT_FALSE(gesture_event.handled());
167  EXPECT_EQ(now_.ToInternalValue(),
168            detector_->last_activity_time().ToInternalValue());
169  EXPECT_EQ(1, observer_->num_invocations());
170  observer_->reset_stats();
171}
172
173// Checks that observers aren't notified too frequently.
174TEST_F(UserActivityDetectorTest, RateLimitNotifications) {
175  scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(12345));
176
177  // The observer should be notified about a key event.
178  ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_NONE, false);
179  SetEventTarget(window.get(), &event);
180  detector_->OnKeyEvent(&event);
181  EXPECT_FALSE(event.handled());
182  EXPECT_EQ(1, observer_->num_invocations());
183  observer_->reset_stats();
184
185  // It shouldn't be notified if a second event occurs
186  // in the same instant in time.
187  detector_->OnKeyEvent(&event);
188  EXPECT_FALSE(event.handled());
189  EXPECT_EQ(0, observer_->num_invocations());
190  observer_->reset_stats();
191
192  // Advance the time, but not quite enough for another notification to be sent.
193  AdvanceTime(
194      base::TimeDelta::FromMilliseconds(
195          UserActivityDetector::kNotifyIntervalMs - 100));
196  detector_->OnKeyEvent(&event);
197  EXPECT_FALSE(event.handled());
198  EXPECT_EQ(0, observer_->num_invocations());
199  observer_->reset_stats();
200
201  // Advance time by the notification interval, definitely moving out of the
202  // rate limit. This should let us trigger another notification.
203  AdvanceTime(base::TimeDelta::FromMilliseconds(
204      UserActivityDetector::kNotifyIntervalMs));
205
206  detector_->OnKeyEvent(&event);
207  EXPECT_FALSE(event.handled());
208  EXPECT_EQ(1, observer_->num_invocations());
209}
210
211// Checks that the detector ignores synthetic mouse events.
212TEST_F(UserActivityDetectorTest, IgnoreSyntheticMouseEvents) {
213  scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(12345));
214  ui::MouseEvent mouse_event(
215      ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), ui::EF_IS_SYNTHESIZED);
216  SetEventTarget(window.get(), &mouse_event);
217  detector_->OnMouseEvent(&mouse_event);
218  EXPECT_FALSE(mouse_event.handled());
219  EXPECT_EQ(base::TimeTicks().ToInternalValue(),
220            detector_->last_activity_time().ToInternalValue());
221  EXPECT_EQ(0, observer_->num_invocations());
222}
223
224}  // namespace test
225}  // namespace ash
226