1// Copyright 2013 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/autoclick/autoclick_controller.h"
6#include "ash/shell.h"
7#include "ash/test/ash_test_base.h"
8#include "ui/aura/test/test_window_delegate.h"
9#include "ui/aura/window.h"
10#include "ui/aura/window_event_dispatcher.h"
11#include "ui/events/event.h"
12#include "ui/events/event_constants.h"
13#include "ui/events/event_handler.h"
14#include "ui/events/keycodes/keyboard_codes.h"
15#include "ui/events/test/event_generator.h"
16
17namespace ash {
18
19class MouseEventCapturer : public ui::EventHandler {
20 public:
21  MouseEventCapturer() { Reset(); }
22  virtual ~MouseEventCapturer() {}
23
24  void Reset() {
25    events_.clear();
26  }
27
28  virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE {
29    if (!(event->flags() & ui::EF_LEFT_MOUSE_BUTTON))
30      return;
31    // Filter out extraneous mouse events like mouse entered, exited,
32    // capture changed, etc.
33    ui::EventType type = event->type();
34    if (type == ui::ET_MOUSE_MOVED || type == ui::ET_MOUSE_PRESSED ||
35        type == ui::ET_MOUSE_RELEASED) {
36      events_.push_back(ui::MouseEvent(
37          event->type(),
38          event->location(),
39          event->root_location(),
40          event->flags(),
41          event->changed_button_flags()));
42      // Stop event propagation so we don't click on random stuff that
43      // might break test assumptions.
44      event->StopPropagation();
45    }
46
47    // If there is a possibility that we're in an infinite loop, we should
48    // exit early with a sensible error rather than letting the test time out.
49    ASSERT_LT(events_.size(), 100u);
50  }
51
52  const std::vector<ui::MouseEvent>& captured_events() const {
53    return events_;
54  }
55
56 private:
57  std::vector<ui::MouseEvent> events_;
58
59  DISALLOW_COPY_AND_ASSIGN(MouseEventCapturer);
60};
61
62class AutoclickTest : public test::AshTestBase {
63 public:
64  AutoclickTest() {}
65  virtual ~AutoclickTest() {}
66
67  virtual void SetUp() OVERRIDE {
68    test::AshTestBase::SetUp();
69    Shell::GetInstance()->AddPreTargetHandler(&mouse_event_capturer_);
70    GetAutoclickController()->SetAutoclickDelay(0);
71
72    // Move mouse to deterministic location at the start of each test.
73    GetEventGenerator().MoveMouseTo(100, 100);
74  }
75
76  virtual void TearDown() OVERRIDE {
77    Shell::GetInstance()->RemovePreTargetHandler(&mouse_event_capturer_);
78    test::AshTestBase::TearDown();
79  }
80
81  void MoveMouseWithFlagsTo(int x, int y, ui::EventFlags flags) {
82    GetEventGenerator().set_flags(flags);
83    GetEventGenerator().MoveMouseTo(x, y);
84    GetEventGenerator().set_flags(ui::EF_NONE);
85  }
86
87  const std::vector<ui::MouseEvent>& WaitForMouseEvents() {
88    mouse_event_capturer_.Reset();
89    RunAllPendingInMessageLoop();
90    return mouse_event_capturer_.captured_events();
91  }
92
93  AutoclickController* GetAutoclickController() {
94    return Shell::GetInstance()->autoclick_controller();
95  }
96
97 private:
98  MouseEventCapturer mouse_event_capturer_;
99
100  DISALLOW_COPY_AND_ASSIGN(AutoclickTest);
101};
102
103TEST_F(AutoclickTest, ToggleEnabled) {
104  std::vector<ui::MouseEvent> events;
105
106  // We should not see any events initially.
107  EXPECT_FALSE(GetAutoclickController()->IsEnabled());
108  events = WaitForMouseEvents();
109  EXPECT_EQ(0u, events.size());
110
111  // Enable autoclick, and we should see a mouse pressed and
112  // a mouse released event, simulating a click.
113  GetAutoclickController()->SetEnabled(true);
114  GetEventGenerator().MoveMouseTo(0, 0);
115  EXPECT_TRUE(GetAutoclickController()->IsEnabled());
116  events = WaitForMouseEvents();
117  EXPECT_EQ(2u, events.size());
118  EXPECT_EQ(ui::ET_MOUSE_PRESSED, events[0].type());
119  EXPECT_EQ(ui::ET_MOUSE_RELEASED, events[1].type());
120
121  // We should not get any more clicks until we move the mouse.
122  events = WaitForMouseEvents();
123  EXPECT_EQ(0u, events.size());
124  GetEventGenerator().MoveMouseTo(30, 30);
125  events = WaitForMouseEvents();
126  EXPECT_EQ(2u, events.size());
127  EXPECT_EQ(ui::ET_MOUSE_PRESSED, events[0].type());
128  EXPECT_EQ(ui::ET_MOUSE_RELEASED, events[1].type());
129
130  // Disable autoclick, and we should see the original behaviour.
131  GetAutoclickController()->SetEnabled(false);
132  EXPECT_FALSE(GetAutoclickController()->IsEnabled());
133  events = WaitForMouseEvents();
134  EXPECT_EQ(0u, events.size());
135}
136
137TEST_F(AutoclickTest, MouseMovement) {
138  std::vector<ui::MouseEvent> events;
139  GetAutoclickController()->SetEnabled(true);
140
141  gfx::Point p1(0, 0);
142  gfx::Point p2(20, 20);
143  gfx::Point p3(40, 40);
144
145  // Move mouse to p1.
146  GetEventGenerator().MoveMouseTo(p1);
147  events = WaitForMouseEvents();
148  EXPECT_EQ(2u, events.size());
149  EXPECT_EQ(p1.ToString(), events[0].root_location().ToString());
150  EXPECT_EQ(p1.ToString(), events[1].root_location().ToString());
151
152  // Move mouse to multiple locations and finally arrive at p3.
153  GetEventGenerator().MoveMouseTo(p2);
154  GetEventGenerator().MoveMouseTo(p1);
155  GetEventGenerator().MoveMouseTo(p3);
156  events = WaitForMouseEvents();
157  EXPECT_EQ(2u, events.size());
158  EXPECT_EQ(p3.ToString(), events[0].root_location().ToString());
159  EXPECT_EQ(p3.ToString(), events[1].root_location().ToString());
160}
161
162TEST_F(AutoclickTest, MovementThreshold) {
163  GetAutoclickController()->SetEnabled(true);
164  GetEventGenerator().MoveMouseTo(0, 0);
165  EXPECT_EQ(2u, WaitForMouseEvents().size());
166
167  // Small mouse movements should not trigger an autoclick.
168  GetEventGenerator().MoveMouseTo(1, 1);
169  EXPECT_EQ(0u, WaitForMouseEvents().size());
170  GetEventGenerator().MoveMouseTo(2, 2);
171  EXPECT_EQ(0u, WaitForMouseEvents().size());
172  GetEventGenerator().MoveMouseTo(0, 0);
173  EXPECT_EQ(0u, WaitForMouseEvents().size());
174
175  // A large mouse movement should trigger an autoclick.
176  GetEventGenerator().MoveMouseTo(100, 100);
177  EXPECT_EQ(2u, WaitForMouseEvents().size());
178}
179
180TEST_F(AutoclickTest, SingleKeyModifier) {
181  GetAutoclickController()->SetEnabled(true);
182  MoveMouseWithFlagsTo(20, 20, ui::EF_SHIFT_DOWN);
183  std::vector<ui::MouseEvent> events = WaitForMouseEvents();
184  EXPECT_EQ(2u, events.size());
185  EXPECT_EQ(ui::EF_SHIFT_DOWN, events[0].flags() & ui::EF_SHIFT_DOWN);
186  EXPECT_EQ(ui::EF_SHIFT_DOWN, events[1].flags() & ui::EF_SHIFT_DOWN);
187}
188
189TEST_F(AutoclickTest, MultipleKeyModifiers) {
190  GetAutoclickController()->SetEnabled(true);
191  ui::EventFlags modifier_flags = static_cast<ui::EventFlags>(
192      ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN);
193  MoveMouseWithFlagsTo(30, 30, modifier_flags);
194  std::vector<ui::MouseEvent> events = WaitForMouseEvents();
195  EXPECT_EQ(2u, events.size());
196  EXPECT_EQ(modifier_flags, events[0].flags() & modifier_flags);
197  EXPECT_EQ(modifier_flags, events[1].flags() & modifier_flags);
198}
199
200TEST_F(AutoclickTest, KeyModifiersReleased) {
201  GetAutoclickController()->SetEnabled(true);
202
203  ui::EventFlags modifier_flags = static_cast<ui::EventFlags>(
204      ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN);
205  MoveMouseWithFlagsTo(12, 12, modifier_flags);
206
207  // Simulate releasing key modifiers by sending key released events.
208  GetEventGenerator().ReleaseKey(ui::VKEY_CONTROL,
209      static_cast<ui::EventFlags>(ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN));
210  GetEventGenerator().ReleaseKey(ui::VKEY_SHIFT, ui::EF_ALT_DOWN);
211
212  std::vector<ui::MouseEvent> events;
213  events = WaitForMouseEvents();
214  EXPECT_EQ(2u, events.size());
215  EXPECT_EQ(0, events[0].flags() & ui::EF_CONTROL_DOWN);
216  EXPECT_EQ(0, events[0].flags() & ui::EF_SHIFT_DOWN);
217  EXPECT_EQ(ui::EF_ALT_DOWN, events[0].flags() & ui::EF_ALT_DOWN);
218}
219
220TEST_F(AutoclickTest, ExtendedDisplay) {
221  UpdateDisplay("1280x1024,800x600");
222  RunAllPendingInMessageLoop();
223  aura::Window::Windows root_windows = Shell::GetAllRootWindows();
224  EXPECT_EQ(2u, root_windows.size());
225
226  GetAutoclickController()->SetEnabled(true);
227  std::vector<ui::MouseEvent> events;
228
229  // Test first root window.
230  ui::test::EventGenerator generator1(root_windows[0]);
231  generator1.MoveMouseTo(100, 200);
232  events = WaitForMouseEvents();
233  EXPECT_EQ(2u, events.size());
234  EXPECT_EQ(100, events[0].root_location().x());
235  EXPECT_EQ(200, events[0].root_location().y());
236
237  // Test second root window.
238  ui::test::EventGenerator generator2(root_windows[1]);
239  generator2.MoveMouseTo(300, 400);
240  events = WaitForMouseEvents();
241  EXPECT_EQ(2u, events.size());
242  EXPECT_EQ(300, events[0].root_location().x());
243  EXPECT_EQ(400, events[0].root_location().y());
244
245  // Test movement threshold between displays.
246}
247
248TEST_F(AutoclickTest, UserInputCancelsAutoclick) {
249  GetAutoclickController()->SetEnabled(true);
250  std::vector<ui::MouseEvent> events;
251
252  // Pressing a normal key should cancel the autoclick.
253  GetEventGenerator().MoveMouseTo(200, 200);
254  GetEventGenerator().PressKey(ui::VKEY_K, ui::EF_NONE);
255  GetEventGenerator().ReleaseKey(ui::VKEY_K, ui::EF_NONE);
256  events = WaitForMouseEvents();
257  EXPECT_EQ(0u, events.size());
258
259  // Pressing a modifier key should NOT cancel the autoclick.
260  GetEventGenerator().MoveMouseTo(100, 100);
261  GetEventGenerator().PressKey(ui::VKEY_SHIFT, ui::EF_SHIFT_DOWN);
262  GetEventGenerator().ReleaseKey(ui::VKEY_SHIFT, ui::EF_NONE);
263  events = WaitForMouseEvents();
264  EXPECT_EQ(2u, events.size());
265
266  // Performing a gesture should cancel the autoclick.
267  GetEventGenerator().MoveMouseTo(200, 200);
268  GetEventGenerator().GestureTapDownAndUp(gfx::Point(100, 100));
269  events = WaitForMouseEvents();
270  EXPECT_EQ(0u, events.size());
271
272  // Test another gesture.
273  GetEventGenerator().MoveMouseTo(100, 100);
274  GetEventGenerator().GestureScrollSequence(
275      gfx::Point(100, 100),
276      gfx::Point(200, 200),
277      base::TimeDelta::FromMilliseconds(200),
278      3);
279  events = WaitForMouseEvents();
280  EXPECT_EQ(0u, events.size());
281
282  // Test scroll events.
283  GetEventGenerator().MoveMouseTo(200, 200);
284  GetEventGenerator().ScrollSequence(
285      gfx::Point(100, 100), base::TimeDelta::FromMilliseconds(200),
286      0, 100, 3, 2);
287  events = WaitForMouseEvents();
288  EXPECT_EQ(0u, events.size());
289}
290
291TEST_F(AutoclickTest, SynthesizedMouseMovesIgnored) {
292  GetAutoclickController()->SetEnabled(true);
293  std::vector<ui::MouseEvent> events;
294  GetEventGenerator().MoveMouseTo(100, 100);
295  events = WaitForMouseEvents();
296  EXPECT_EQ(2u, events.size());
297
298  // Show a window and make sure the new window is under the cursor. As a
299  // result, synthesized mouse events will be dispatched to the window, but it
300  // should not trigger an autoclick.
301  aura::test::EventCountDelegate delegate;
302  scoped_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate(
303      &delegate, 123, gfx::Rect(50, 50, 100, 100)));
304  window->Show();
305  events = WaitForMouseEvents();
306  EXPECT_EQ(0u, events.size());
307  EXPECT_EQ("1 1 0", delegate.GetMouseMotionCountsAndReset());
308}
309
310}  // namespace ash
311