Suppress a Valgrind report.
[chromium-blink-merge.git] / ash / sticky_keys / sticky_keys_unittest.cc
blob16bdd20da3c8da0f4ed84e991616e65bad7724d6
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.
5 #include "ash/sticky_keys/sticky_keys_controller.h"
7 #include <X11/Xlib.h>
8 #undef None
9 #undef Bool
10 #undef RootWindow
12 #include "ash/shell.h"
13 #include "ash/test/ash_test_base.h"
14 #include "base/bind.h"
15 #include "base/callback.h"
16 #include "base/memory/scoped_vector.h"
17 #include "ui/aura/window.h"
18 #include "ui/aura/window_tree_host.h"
19 #include "ui/events/event_source.h"
20 #include "ui/events/test/events_test_utils_x11.h"
22 namespace ash {
24 namespace {
26 // The device id of the test touchpad device.
27 const unsigned int kTouchPadDeviceId = 1;
29 } // namespace
31 class StickyKeysTest : public test::AshTestBase {
32 protected:
33 StickyKeysTest()
34 : target_(NULL),
35 root_window_(NULL) {}
37 void SetUp() override {
38 test::AshTestBase::SetUp();
40 // |target_| owned by root window of shell. It is still safe to delete
41 // it ourselves.
42 target_ = CreateTestWindowInShellWithId(0);
43 root_window_ = target_->GetRootWindow();
45 ui::SetUpTouchPadForTest(kTouchPadDeviceId);
48 void TearDown() override { test::AshTestBase::TearDown(); }
50 virtual void OnShortcutPressed() {
51 if (target_) {
52 delete target_;
53 target_ = NULL;
57 ui::KeyEvent* GenerateKey(ui::EventType type, ui::KeyboardCode code) {
58 scoped_xevent_.InitKeyEvent(type, code, 0);
59 ui::KeyEvent* event = new ui::KeyEvent(scoped_xevent_);
60 return event;
63 // Creates a mouse event backed by a native XInput2 generic button event.
64 // This is the standard native event on Chromebooks.
65 ui::MouseEvent* GenerateMouseEvent(ui::EventType type) {
66 return GenerateMouseEventAt(type, gfx::Point());
69 // Creates a mouse event backed by a native XInput2 generic button event.
70 // The |location| should be in physical pixels.
71 ui::MouseEvent* GenerateMouseEventAt(ui::EventType type,
72 const gfx::Point& location) {
73 scoped_xevent_.InitGenericButtonEvent(
74 kTouchPadDeviceId,
75 type,
76 location,
77 0);
78 ui::MouseEvent* event = new ui::MouseEvent(scoped_xevent_);
79 return event;
82 ui::MouseWheelEvent* GenerateMouseWheelEvent(int wheel_delta) {
83 EXPECT_NE(0, wheel_delta);
84 scoped_xevent_.InitGenericMouseWheelEvent(
85 kTouchPadDeviceId, wheel_delta, 0);
86 ui::MouseWheelEvent* event = new ui::MouseWheelEvent(scoped_xevent_);
87 ui::Event::DispatcherApi dispatcher(event);
88 dispatcher.set_target(target_);
89 return event;
92 ui::ScrollEvent* GenerateScrollEvent(int scroll_delta) {
93 scoped_xevent_.InitScrollEvent(kTouchPadDeviceId, // deviceid
94 0, // x_offset
95 scroll_delta, // y_offset
96 0, // x_offset_ordinal
97 scroll_delta, // y_offset_ordinal
98 2); // finger_count
99 ui::ScrollEvent* event = new ui::ScrollEvent(scoped_xevent_);
100 ui::Event::DispatcherApi dispatcher(event);
101 dispatcher.set_target(target_);
102 return event;
105 ui::ScrollEvent* GenerateFlingScrollEvent(int fling_delta,
106 bool is_cancel) {
107 scoped_xevent_.InitFlingScrollEvent(
108 kTouchPadDeviceId, // deviceid
109 0, // x_velocity
110 fling_delta, // y_velocity
111 0, // x_velocity_ordinal
112 fling_delta, // y_velocity_ordinal
113 is_cancel); // is_cancel
114 ui::ScrollEvent* event = new ui::ScrollEvent(scoped_xevent_);
115 ui::Event::DispatcherApi dispatcher(event);
116 dispatcher.set_target(target_);
117 return event;
120 // Creates a synthesized KeyEvent that is not backed by a native event.
121 ui::KeyEvent* GenerateSynthesizedKeyEvent(ui::EventType type,
122 ui::KeyboardCode code) {
123 return new ui::KeyEvent(type, code, ui::EF_NONE);
126 // Creates a synthesized MouseEvent that is not backed by a native event.
127 ui::MouseEvent* GenerateSynthesizedMouseEventAt(ui::EventType event_type,
128 const gfx::Point& location) {
129 ui::MouseEvent* event = new ui::MouseEvent(event_type,
130 location,
131 location,
132 ui::EF_LEFT_MOUSE_BUTTON,
133 ui::EF_LEFT_MOUSE_BUTTON);
134 ui::Event::DispatcherApi dispatcher(event);
135 dispatcher.set_target(target_);
136 return event;
139 // Creates a synthesized mouse press or release event.
140 ui::MouseEvent* GenerateSynthesizedMouseClickEvent(
141 ui::EventType type,
142 const gfx::Point& location) {
143 return GenerateSynthesizedMouseEventAt(type, location);
146 // Creates a synthesized ET_MOUSE_MOVED event.
147 ui::MouseEvent* GenerateSynthesizedMouseMoveEvent(
148 const gfx::Point& location) {
149 return GenerateSynthesizedMouseEventAt(ui::ET_MOUSE_MOVED, location);
152 // Creates a synthesized MouseWHeel event.
153 ui::MouseWheelEvent* GenerateSynthesizedMouseWheelEvent(int wheel_delta) {
154 scoped_ptr<ui::MouseEvent> mev(
155 GenerateSynthesizedMouseEventAt(ui::ET_MOUSEWHEEL, gfx::Point(0, 0)));
156 ui::MouseWheelEvent* event = new ui::MouseWheelEvent(*mev, 0, wheel_delta);
157 ui::Event::DispatcherApi dispatcher(event);
158 dispatcher.set_target(target_);
159 return event;
162 void SendActivateStickyKeyPattern(StickyKeysHandler* handler,
163 ui::KeyboardCode key_code) {
164 bool released = false;
165 int down_flags = 0;
166 scoped_ptr<ui::KeyEvent> ev;
167 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, key_code));
168 handler->HandleKeyEvent(*ev.get(), key_code, &down_flags, &released);
169 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, key_code));
170 handler->HandleKeyEvent(*ev.get(), key_code, &down_flags, &released);
173 bool HandleKeyEvent(const ui::KeyEvent& key_event,
174 StickyKeysHandler* handler,
175 int* down,
176 bool* up) {
177 return handler->HandleKeyEvent(key_event, key_event.key_code(), down, up);
180 int HandleKeyEventForDownFlags(const ui::KeyEvent& key_event,
181 StickyKeysHandler* handler) {
182 bool released = false;
183 int down = 0;
184 handler->HandleKeyEvent(key_event, key_event.key_code(), &down, &released);
185 return down;
188 aura::Window* target() { return target_; }
190 private:
191 // Owned by root window of shell, but we can still delete |target_| safely.
192 aura::Window* target_;
193 // The root window of |target_|. Not owned.
194 aura::Window* root_window_;
196 // Used to construct the various X events.
197 ui::ScopedXI2Event scoped_xevent_;
199 DISALLOW_COPY_AND_ASSIGN(StickyKeysTest);
202 TEST_F(StickyKeysTest, BasicOneshotScenarioTest) {
203 scoped_ptr<ui::KeyEvent> ev;
204 StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN);
206 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
208 // By typing Shift key, internal state become ENABLED.
209 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
210 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
212 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_A));
213 bool released = false;
214 int mod_down_flags = 0;
215 HandleKeyEvent(*ev.get(), &sticky_key, &mod_down_flags, &released);
216 // Next keyboard event is shift modified.
217 EXPECT_TRUE(mod_down_flags & ui::EF_SHIFT_DOWN);
218 // Modifier release notification happens.
219 EXPECT_TRUE(released);
221 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_A));
222 released = false;
223 mod_down_flags = 0;
224 HandleKeyEvent(*ev.get(), &sticky_key, &mod_down_flags, &released);
226 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
227 // Making sure Shift up keyboard event is available.
228 scoped_ptr<ui::Event> up_event;
229 ASSERT_EQ(0, sticky_key.GetModifierUpEvent(&up_event));
230 EXPECT_TRUE(up_event.get());
231 EXPECT_EQ(ui::ET_KEY_RELEASED, up_event->type());
232 EXPECT_EQ(ui::VKEY_SHIFT,
233 static_cast<const ui::KeyEvent*>(up_event.get())->key_code());
235 // Enabled state is one shot, so next key event should not be shift modified.
236 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_A));
237 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
238 EXPECT_FALSE(mod_down_flags & ui::EF_SHIFT_DOWN);
240 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_A));
241 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
242 EXPECT_FALSE(mod_down_flags & ui::EF_SHIFT_DOWN);
245 TEST_F(StickyKeysTest, BasicLockedScenarioTest) {
246 scoped_ptr<ui::KeyEvent> ev;
247 StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN);
249 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
251 // By typing shift key, internal state become ENABLED.
252 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
253 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
255 // By typing shift key again, internal state become LOCKED.
256 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
257 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
259 // All keyboard events including keyUp become shift modified.
260 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_A));
261 int mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
262 EXPECT_TRUE(mod_down_flags & ui::EF_SHIFT_DOWN);
264 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_A));
265 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
266 EXPECT_TRUE(mod_down_flags & ui::EF_SHIFT_DOWN);
268 // Locked state keeps after normal keyboard event.
269 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
271 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_B));
272 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
273 EXPECT_TRUE(mod_down_flags & ui::EF_SHIFT_DOWN);
275 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_B));
276 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
277 EXPECT_TRUE(mod_down_flags & ui::EF_SHIFT_DOWN);
279 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
281 // By typing shift key again, internal state become back to DISABLED.
282 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
283 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
286 TEST_F(StickyKeysTest, NonTargetModifierTest) {
287 scoped_ptr<ui::KeyEvent> ev;
288 StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN);
290 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
292 // Non target modifier key does not affect internal state
293 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_MENU));
294 int mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
295 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
296 EXPECT_EQ(ui::EF_NONE, mod_down_flags);
298 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_MENU));
299 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
300 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
301 EXPECT_EQ(ui::EF_NONE, mod_down_flags);
303 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
304 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
306 // Non target modifier key does not affect internal state
307 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_MENU));
308 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
309 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
310 EXPECT_EQ(ui::EF_NONE, mod_down_flags);
312 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_MENU));
313 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
314 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
315 EXPECT_EQ(ui::EF_NONE, mod_down_flags);
317 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
318 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
320 // Non target modifier key does not affect internal state
321 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_MENU));
322 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
323 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
324 EXPECT_EQ(ui::EF_NONE, mod_down_flags);
326 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_MENU));
327 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
328 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
329 EXPECT_EQ(ui::EF_NONE, mod_down_flags);
332 TEST_F(StickyKeysTest, NormalShortcutTest) {
333 // Sticky keys should not be enabled if we perform a normal shortcut.
334 scoped_ptr<ui::KeyEvent> ev;
335 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
337 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
339 // Perform ctrl+n shortcut.
340 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL));
341 int mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
342 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_N));
343 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
344 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_N));
345 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
346 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
347 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL));
348 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
350 // Sticky keys should not be enabled afterwards.
351 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
352 EXPECT_EQ(ui::EF_NONE, mod_down_flags);
354 // Perform ctrl+n shortcut, releasing ctrl first.
355 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL));
356 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
357 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_N));
358 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
359 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
360 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL));
361 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
362 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_N));
363 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
365 // Sticky keys should not be enabled afterwards.
366 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
367 EXPECT_EQ(ui::EF_NONE, mod_down_flags);
370 TEST_F(StickyKeysTest, NormalModifiedClickTest) {
371 scoped_ptr<ui::KeyEvent> kev;
372 scoped_ptr<ui::MouseEvent> mev;
373 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
375 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
377 // Perform ctrl+click.
378 kev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL));
379 int mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key);
380 mev.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED));
381 bool released = false;
382 sticky_key.HandleMouseEvent(*mev.get(), &mod_down_flags, &released);
383 mev.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED));
384 sticky_key.HandleMouseEvent(*mev.get(), &mod_down_flags, &released);
386 // Sticky keys should not be enabled afterwards.
387 kev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL));
388 mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key);
389 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
390 EXPECT_EQ(ui::EF_NONE, mod_down_flags);
393 TEST_F(StickyKeysTest, MouseMovedModifierTest) {
394 scoped_ptr<ui::KeyEvent> kev;
395 scoped_ptr<ui::MouseEvent> mev;
396 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
398 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
400 // Press ctrl and handle mouse move events.
401 kev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL));
402 int mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key);
403 mev.reset(GenerateSynthesizedMouseMoveEvent(gfx::Point(0, 0)));
404 bool released = false;
405 sticky_key.HandleMouseEvent(*mev.get(), &mod_down_flags, &released);
406 mev.reset(GenerateSynthesizedMouseMoveEvent(gfx::Point(100, 100)));
407 sticky_key.HandleMouseEvent(*mev.get(), &mod_down_flags, &released);
409 // Sticky keys should be enabled afterwards.
410 kev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL));
411 mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key);
412 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
413 EXPECT_EQ(ui::EF_NONE, mod_down_flags);
416 TEST_F(StickyKeysTest, NormalModifiedScrollTest) {
417 scoped_ptr<ui::KeyEvent> kev;
418 scoped_ptr<ui::ScrollEvent> sev;
419 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
421 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
423 // Perform ctrl+scroll.
424 kev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL));
425 int mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key);
426 sev.reset(GenerateFlingScrollEvent(0, true));
427 bool released = false;
428 sticky_key.HandleScrollEvent(*sev.get(), &mod_down_flags, &released);
429 sev.reset(GenerateScrollEvent(10));
430 sticky_key.HandleScrollEvent(*sev.get(), &mod_down_flags, &released);
431 sev.reset(GenerateFlingScrollEvent(10, false));
432 sticky_key.HandleScrollEvent(*sev.get(), &mod_down_flags, &released);
434 // Sticky keys should not be enabled afterwards.
435 kev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL));
436 mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key);
437 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
438 EXPECT_EQ(ui::EF_NONE, mod_down_flags);
441 TEST_F(StickyKeysTest, MouseEventOneshot) {
442 scoped_ptr<ui::MouseEvent> ev;
443 scoped_ptr<ui::KeyEvent> kev;
444 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
446 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
447 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
448 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
450 // We should still be in the ENABLED state until we get the mouse
451 // release event.
452 ev.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED));
453 bool released = false;
454 int mod_down_flags = 0;
455 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released);
456 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
457 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
459 ev.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED));
460 released = false;
461 mod_down_flags = 0;
462 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released);
463 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
464 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
466 // Making sure modifier key release event is dispatched in the right order.
467 EXPECT_TRUE(released);
468 scoped_ptr<ui::Event> up_event;
469 ASSERT_EQ(0, sticky_key.GetModifierUpEvent(&up_event));
470 EXPECT_TRUE(up_event.get());
471 EXPECT_EQ(ui::ET_KEY_RELEASED, up_event->type());
472 EXPECT_EQ(ui::VKEY_CONTROL,
473 static_cast<const ui::KeyEvent*>(up_event.get())->key_code());
475 // Enabled state is one shot, so next click should not be control modified.
476 ev.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED));
477 released = false;
478 mod_down_flags = 0;
479 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released);
480 EXPECT_FALSE(mod_down_flags & ui::EF_CONTROL_DOWN);
482 ev.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED));
483 released = false;
484 mod_down_flags = 0;
485 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released);
486 EXPECT_FALSE(mod_down_flags & ui::EF_CONTROL_DOWN);
489 TEST_F(StickyKeysTest, MouseEventLocked) {
490 scoped_ptr<ui::MouseEvent> ev;
491 scoped_ptr<ui::KeyEvent> kev;
492 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
494 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
496 // Pressing modifier key twice should make us enter lock state.
497 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
498 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
499 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
500 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
502 // Mouse events should not disable locked mode.
503 for (int i = 0; i < 3; ++i) {
504 bool released = false;
505 int mod_down_flags = 0;
506 ev.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED));
507 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released);
508 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
509 ev.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED));
510 released = false;
511 mod_down_flags = 0;
512 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released);
513 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
514 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
517 // Test with mouse wheel.
518 for (int i = 0; i < 3; ++i) {
519 bool released = false;
520 int mod_down_flags = 0;
521 ev.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta));
522 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released);
523 ev.reset(GenerateMouseWheelEvent(-ui::MouseWheelEvent::kWheelDelta));
524 released = false;
525 mod_down_flags = 0;
526 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released);
527 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
528 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
531 // Test mixed case with mouse events and key events.
532 ev.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta));
533 bool released = false;
534 int mod_down_flags = 0;
535 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released);
536 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
537 kev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_N));
538 mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key);
539 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
540 mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key);
541 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
543 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
546 TEST_F(StickyKeysTest, ScrollEventOneshot) {
547 scoped_ptr<ui::ScrollEvent> ev;
548 scoped_ptr<ui::KeyEvent> kev;
549 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
551 int scroll_deltas[] = {-10, 10};
552 for (int i = 0; i < 2; ++i) {
553 // Enable sticky keys.
554 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
555 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
556 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
558 // Test a scroll sequence. Sticky keys should only be disabled at the end
559 // of the scroll sequence. Fling cancel event starts the scroll sequence.
560 ev.reset(GenerateFlingScrollEvent(0, true));
561 bool released = false;
562 int mod_down_flags = 0;
563 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released);
564 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
565 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
567 // Scrolls should all be modified but not disable sticky keys.
568 for (int j = 0; j < 3; ++j) {
569 ev.reset(GenerateScrollEvent(scroll_deltas[i]));
570 released = false;
571 mod_down_flags = 0;
572 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released);
573 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
574 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
577 // Fling start event ends scroll sequence.
578 ev.reset(GenerateFlingScrollEvent(scroll_deltas[i], false));
579 released = false;
580 mod_down_flags = 0;
581 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released);
582 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
583 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
585 scoped_ptr<ui::Event> up_event;
586 EXPECT_TRUE(released);
587 ASSERT_EQ(0, sticky_key.GetModifierUpEvent(&up_event));
588 EXPECT_TRUE(up_event.get());
589 EXPECT_EQ(ui::ET_KEY_RELEASED, up_event->type());
590 EXPECT_EQ(ui::VKEY_CONTROL,
591 static_cast<const ui::KeyEvent*>(up_event.get())->key_code());
595 TEST_F(StickyKeysTest, ScrollDirectionChanged) {
596 scoped_ptr<ui::ScrollEvent> ev;
597 scoped_ptr<ui::KeyEvent> kev;
598 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
600 // Test direction change with both boundary value and negative value.
601 const int direction_change_values[2] = {0, -10};
602 for (int i = 0; i < 2; ++i) {
603 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
604 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
606 // Fling cancel starts scroll sequence.
607 ev.reset(GenerateFlingScrollEvent(0, true));
608 bool released = false;
609 int mod_down_flags = 0;
610 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released);
611 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
613 // Test that changing directions in a scroll sequence will
614 // return sticky keys to DISABLED state.
615 for (int j = 0; j < 3; ++j) {
616 ev.reset(GenerateScrollEvent(10));
617 released = false;
618 mod_down_flags = 0;
619 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released);
620 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
621 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
624 ev.reset(GenerateScrollEvent(direction_change_values[i]));
625 released = false;
626 mod_down_flags = 0;
627 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released);
628 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
632 TEST_F(StickyKeysTest, ScrollEventLocked) {
633 scoped_ptr<ui::ScrollEvent> ev;
634 scoped_ptr<ui::KeyEvent> kev;
635 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
637 // Lock sticky keys.
638 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
639 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
640 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
642 // Test scroll events are correctly modified in locked state.
643 for (int i = 0; i < 5; ++i) {
644 // Fling cancel starts scroll sequence.
645 ev.reset(GenerateFlingScrollEvent(0, true));
646 bool released = false;
647 int mod_down_flags = 0;
648 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released);
650 ev.reset(GenerateScrollEvent(10));
651 released = false;
652 mod_down_flags = 0;
653 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released);
654 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
655 ev.reset(GenerateScrollEvent(-10));
656 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released);
657 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
659 // Fling start ends scroll sequence.
660 ev.reset(GenerateFlingScrollEvent(-10, false));
661 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released);
664 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
667 TEST_F(StickyKeysTest, SynthesizedEvents) {
668 // Non-native, internally generated events should be properly handled
669 // by sticky keys.
670 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
672 // Test non-native key events.
673 scoped_ptr<ui::KeyEvent> kev;
674 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
675 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
677 kev.reset(GenerateSynthesizedKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_K));
678 int mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key);
679 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
680 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
682 kev.reset(GenerateSynthesizedKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_K));
683 mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key);
684 EXPECT_FALSE(mod_down_flags & ui::EF_CONTROL_DOWN);
685 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
687 // Test non-native mouse events.
688 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
689 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
691 scoped_ptr<ui::MouseEvent> mev;
692 mev.reset(GenerateSynthesizedMouseClickEvent(ui::ET_MOUSE_PRESSED,
693 gfx::Point(0, 0)));
694 bool released = false;
695 sticky_key.HandleMouseEvent(*mev.get(), &mod_down_flags, &released);
696 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
697 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
699 mev.reset(GenerateSynthesizedMouseClickEvent(ui::ET_MOUSE_RELEASED,
700 gfx::Point(0, 0)));
701 released = false;
702 mod_down_flags = 0;
703 sticky_key.HandleMouseEvent(*mev.get(), &mod_down_flags, &released);
704 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
705 EXPECT_TRUE(released);
706 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
709 } // namespace ash