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