Add ICU message format support
[chromium-blink-merge.git] / ash / sticky_keys / sticky_keys_unittest.cc
blobe00c9dc1a8d30eef6cfb6f91ea3a703ecd7dfea5
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/event_utils.h"
21 #include "ui/events/test/events_test_utils_x11.h"
23 namespace ash {
25 namespace {
27 // The device id of the test touchpad device.
28 const unsigned int kTouchPadDeviceId = 1;
30 } // namespace
32 class StickyKeysTest : public test::AshTestBase {
33 protected:
34 StickyKeysTest()
35 : target_(NULL),
36 root_window_(NULL) {}
38 void SetUp() override {
39 test::AshTestBase::SetUp();
41 // |target_| owned by root window of shell. It is still safe to delete
42 // it ourselves.
43 target_ = CreateTestWindowInShellWithId(0);
44 root_window_ = target_->GetRootWindow();
46 ui::SetUpTouchPadForTest(kTouchPadDeviceId);
49 void TearDown() override { test::AshTestBase::TearDown(); }
51 virtual void OnShortcutPressed() {
52 if (target_) {
53 delete target_;
54 target_ = NULL;
58 ui::KeyEvent* GenerateKey(ui::EventType type, ui::KeyboardCode code) {
59 scoped_xevent_.InitKeyEvent(type, code, 0);
60 ui::KeyEvent* event = new ui::KeyEvent(scoped_xevent_);
61 return event;
64 // Creates a mouse event backed by a native XInput2 generic button event.
65 // This is the standard native event on Chromebooks.
66 ui::MouseEvent* GenerateMouseEvent(ui::EventType type) {
67 return GenerateMouseEventAt(type, gfx::Point());
70 // Creates a mouse event backed by a native XInput2 generic button event.
71 // The |location| should be in physical pixels.
72 ui::MouseEvent* GenerateMouseEventAt(ui::EventType type,
73 const gfx::Point& location) {
74 scoped_xevent_.InitGenericButtonEvent(
75 kTouchPadDeviceId,
76 type,
77 location,
78 0);
79 ui::MouseEvent* event = new ui::MouseEvent(scoped_xevent_);
80 return event;
83 ui::MouseWheelEvent* GenerateMouseWheelEvent(int wheel_delta) {
84 EXPECT_NE(0, wheel_delta);
85 scoped_xevent_.InitGenericMouseWheelEvent(
86 kTouchPadDeviceId, wheel_delta, 0);
87 ui::MouseWheelEvent* event = new ui::MouseWheelEvent(scoped_xevent_);
88 ui::Event::DispatcherApi dispatcher(event);
89 dispatcher.set_target(target_);
90 return event;
93 ui::ScrollEvent* GenerateScrollEvent(int scroll_delta) {
94 scoped_xevent_.InitScrollEvent(kTouchPadDeviceId, // deviceid
95 0, // x_offset
96 scroll_delta, // y_offset
97 0, // x_offset_ordinal
98 scroll_delta, // y_offset_ordinal
99 2); // finger_count
100 ui::ScrollEvent* event = new ui::ScrollEvent(scoped_xevent_);
101 ui::Event::DispatcherApi dispatcher(event);
102 dispatcher.set_target(target_);
103 return event;
106 ui::ScrollEvent* GenerateFlingScrollEvent(int fling_delta,
107 bool is_cancel) {
108 scoped_xevent_.InitFlingScrollEvent(
109 kTouchPadDeviceId, // deviceid
110 0, // x_velocity
111 fling_delta, // y_velocity
112 0, // x_velocity_ordinal
113 fling_delta, // y_velocity_ordinal
114 is_cancel); // is_cancel
115 ui::ScrollEvent* event = new ui::ScrollEvent(scoped_xevent_);
116 ui::Event::DispatcherApi dispatcher(event);
117 dispatcher.set_target(target_);
118 return event;
121 // Creates a synthesized KeyEvent that is not backed by a native event.
122 ui::KeyEvent* GenerateSynthesizedKeyEvent(ui::EventType type,
123 ui::KeyboardCode code) {
124 return new ui::KeyEvent(type, code, ui::EF_NONE);
127 // Creates a synthesized MouseEvent that is not backed by a native event.
128 ui::MouseEvent* GenerateSynthesizedMouseEventAt(ui::EventType event_type,
129 const gfx::Point& location) {
130 ui::MouseEvent* event = new ui::MouseEvent(
131 event_type, location, location, ui::EventTimeForNow(),
132 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
133 ui::Event::DispatcherApi dispatcher(event);
134 dispatcher.set_target(target_);
135 return event;
138 // Creates a synthesized mouse press or release event.
139 ui::MouseEvent* GenerateSynthesizedMouseClickEvent(
140 ui::EventType type,
141 const gfx::Point& location) {
142 return GenerateSynthesizedMouseEventAt(type, location);
145 // Creates a synthesized ET_MOUSE_MOVED event.
146 ui::MouseEvent* GenerateSynthesizedMouseMoveEvent(
147 const gfx::Point& location) {
148 return GenerateSynthesizedMouseEventAt(ui::ET_MOUSE_MOVED, location);
151 // Creates a synthesized MouseWHeel event.
152 ui::MouseWheelEvent* GenerateSynthesizedMouseWheelEvent(int wheel_delta) {
153 scoped_ptr<ui::MouseEvent> mev(
154 GenerateSynthesizedMouseEventAt(ui::ET_MOUSEWHEEL, gfx::Point(0, 0)));
155 ui::MouseWheelEvent* event = new ui::MouseWheelEvent(*mev, 0, wheel_delta);
156 ui::Event::DispatcherApi dispatcher(event);
157 dispatcher.set_target(target_);
158 return event;
161 void SendActivateStickyKeyPattern(StickyKeysHandler* handler,
162 ui::KeyboardCode key_code) {
163 bool released = false;
164 int down_flags = 0;
165 scoped_ptr<ui::KeyEvent> ev;
166 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, key_code));
167 handler->HandleKeyEvent(*ev.get(), key_code, &down_flags, &released);
168 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, key_code));
169 handler->HandleKeyEvent(*ev.get(), key_code, &down_flags, &released);
172 bool HandleKeyEvent(const ui::KeyEvent& key_event,
173 StickyKeysHandler* handler,
174 int* down,
175 bool* up) {
176 return handler->HandleKeyEvent(key_event, key_event.key_code(), down, up);
179 int HandleKeyEventForDownFlags(const ui::KeyEvent& key_event,
180 StickyKeysHandler* handler) {
181 bool released = false;
182 int down = 0;
183 handler->HandleKeyEvent(key_event, key_event.key_code(), &down, &released);
184 return down;
187 aura::Window* target() { return target_; }
189 private:
190 // Owned by root window of shell, but we can still delete |target_| safely.
191 aura::Window* target_;
192 // The root window of |target_|. Not owned.
193 aura::Window* root_window_;
195 // Used to construct the various X events.
196 ui::ScopedXI2Event scoped_xevent_;
198 DISALLOW_COPY_AND_ASSIGN(StickyKeysTest);
201 TEST_F(StickyKeysTest, BasicOneshotScenarioTest) {
202 scoped_ptr<ui::KeyEvent> ev;
203 StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN);
205 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
207 // By typing Shift key, internal state become ENABLED.
208 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
209 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
211 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_A));
212 bool released = false;
213 int mod_down_flags = 0;
214 HandleKeyEvent(*ev.get(), &sticky_key, &mod_down_flags, &released);
215 // Next keyboard event is shift modified.
216 EXPECT_TRUE(mod_down_flags & ui::EF_SHIFT_DOWN);
217 // Modifier release notification happens.
218 EXPECT_TRUE(released);
220 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_A));
221 released = false;
222 mod_down_flags = 0;
223 HandleKeyEvent(*ev.get(), &sticky_key, &mod_down_flags, &released);
225 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
226 // Making sure Shift up keyboard event is available.
227 scoped_ptr<ui::Event> up_event;
228 ASSERT_EQ(0, sticky_key.GetModifierUpEvent(&up_event));
229 EXPECT_TRUE(up_event.get());
230 EXPECT_EQ(ui::ET_KEY_RELEASED, up_event->type());
231 EXPECT_EQ(ui::VKEY_SHIFT,
232 static_cast<const ui::KeyEvent*>(up_event.get())->key_code());
234 // Enabled state is one shot, so next key event should not be shift modified.
235 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_A));
236 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
237 EXPECT_FALSE(mod_down_flags & ui::EF_SHIFT_DOWN);
239 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_A));
240 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
241 EXPECT_FALSE(mod_down_flags & ui::EF_SHIFT_DOWN);
244 TEST_F(StickyKeysTest, BasicLockedScenarioTest) {
245 scoped_ptr<ui::KeyEvent> ev;
246 StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN);
248 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
250 // By typing shift key, internal state become ENABLED.
251 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
252 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
254 // By typing shift key again, internal state become LOCKED.
255 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
256 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
258 // All keyboard events including keyUp become shift modified.
259 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_A));
260 int mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
261 EXPECT_TRUE(mod_down_flags & ui::EF_SHIFT_DOWN);
263 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_A));
264 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
265 EXPECT_TRUE(mod_down_flags & ui::EF_SHIFT_DOWN);
267 // Locked state keeps after normal keyboard event.
268 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
270 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_B));
271 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
272 EXPECT_TRUE(mod_down_flags & ui::EF_SHIFT_DOWN);
274 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_B));
275 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
276 EXPECT_TRUE(mod_down_flags & ui::EF_SHIFT_DOWN);
278 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
280 // By typing shift key again, internal state become back to DISABLED.
281 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
282 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
285 TEST_F(StickyKeysTest, NonTargetModifierTest) {
286 scoped_ptr<ui::KeyEvent> ev;
287 StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN);
289 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
291 // Non target modifier key does not affect internal state
292 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_MENU));
293 int mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
294 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
295 EXPECT_EQ(ui::EF_NONE, mod_down_flags);
297 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_MENU));
298 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
299 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
300 EXPECT_EQ(ui::EF_NONE, mod_down_flags);
302 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
303 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
305 // Non target modifier key does not affect internal state
306 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_MENU));
307 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
308 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
309 EXPECT_EQ(ui::EF_NONE, mod_down_flags);
311 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_MENU));
312 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
313 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
314 EXPECT_EQ(ui::EF_NONE, mod_down_flags);
316 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
317 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
319 // Non target modifier key does not affect internal state
320 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_MENU));
321 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
322 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
323 EXPECT_EQ(ui::EF_NONE, mod_down_flags);
325 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_MENU));
326 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
327 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
328 EXPECT_EQ(ui::EF_NONE, mod_down_flags);
331 TEST_F(StickyKeysTest, NormalShortcutTest) {
332 // Sticky keys should not be enabled if we perform a normal shortcut.
333 scoped_ptr<ui::KeyEvent> ev;
334 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
336 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
338 // Perform ctrl+n shortcut.
339 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL));
340 int mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
341 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_N));
342 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
343 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_N));
344 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
345 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
346 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL));
347 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
349 // Sticky keys should not be enabled afterwards.
350 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
351 EXPECT_EQ(ui::EF_NONE, mod_down_flags);
353 // Perform ctrl+n shortcut, releasing ctrl first.
354 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL));
355 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
356 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_N));
357 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
358 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
359 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL));
360 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
361 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_N));
362 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key);
364 // Sticky keys should not be enabled afterwards.
365 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
366 EXPECT_EQ(ui::EF_NONE, mod_down_flags);
369 TEST_F(StickyKeysTest, NormalModifiedClickTest) {
370 scoped_ptr<ui::KeyEvent> kev;
371 scoped_ptr<ui::MouseEvent> mev;
372 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
374 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
376 // Perform ctrl+click.
377 kev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL));
378 int mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key);
379 mev.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED));
380 bool released = false;
381 sticky_key.HandleMouseEvent(*mev.get(), &mod_down_flags, &released);
382 mev.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED));
383 sticky_key.HandleMouseEvent(*mev.get(), &mod_down_flags, &released);
385 // Sticky keys should not be enabled afterwards.
386 kev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL));
387 mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key);
388 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
389 EXPECT_EQ(ui::EF_NONE, mod_down_flags);
392 TEST_F(StickyKeysTest, MouseMovedModifierTest) {
393 scoped_ptr<ui::KeyEvent> kev;
394 scoped_ptr<ui::MouseEvent> mev;
395 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
397 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
399 // Press ctrl and handle mouse move events.
400 kev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL));
401 int mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key);
402 mev.reset(GenerateSynthesizedMouseMoveEvent(gfx::Point(0, 0)));
403 bool released = false;
404 sticky_key.HandleMouseEvent(*mev.get(), &mod_down_flags, &released);
405 mev.reset(GenerateSynthesizedMouseMoveEvent(gfx::Point(100, 100)));
406 sticky_key.HandleMouseEvent(*mev.get(), &mod_down_flags, &released);
408 // Sticky keys should be enabled afterwards.
409 kev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL));
410 mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key);
411 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
412 EXPECT_EQ(ui::EF_NONE, mod_down_flags);
415 TEST_F(StickyKeysTest, NormalModifiedScrollTest) {
416 scoped_ptr<ui::KeyEvent> kev;
417 scoped_ptr<ui::ScrollEvent> sev;
418 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
420 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
422 // Perform ctrl+scroll.
423 kev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL));
424 int mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key);
425 sev.reset(GenerateFlingScrollEvent(0, true));
426 bool released = false;
427 sticky_key.HandleScrollEvent(*sev.get(), &mod_down_flags, &released);
428 sev.reset(GenerateScrollEvent(10));
429 sticky_key.HandleScrollEvent(*sev.get(), &mod_down_flags, &released);
430 sev.reset(GenerateFlingScrollEvent(10, false));
431 sticky_key.HandleScrollEvent(*sev.get(), &mod_down_flags, &released);
433 // Sticky keys should not be enabled afterwards.
434 kev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL));
435 mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key);
436 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
437 EXPECT_EQ(ui::EF_NONE, mod_down_flags);
440 TEST_F(StickyKeysTest, MouseEventOneshot) {
441 scoped_ptr<ui::MouseEvent> ev;
442 scoped_ptr<ui::KeyEvent> kev;
443 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
445 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
446 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
447 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
449 // We should still be in the ENABLED state until we get the mouse
450 // release event.
451 ev.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED));
452 bool released = false;
453 int mod_down_flags = 0;
454 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released);
455 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
456 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
458 ev.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED));
459 released = false;
460 mod_down_flags = 0;
461 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released);
462 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
463 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
465 // Making sure modifier key release event is dispatched in the right order.
466 EXPECT_TRUE(released);
467 scoped_ptr<ui::Event> up_event;
468 ASSERT_EQ(0, sticky_key.GetModifierUpEvent(&up_event));
469 EXPECT_TRUE(up_event.get());
470 EXPECT_EQ(ui::ET_KEY_RELEASED, up_event->type());
471 EXPECT_EQ(ui::VKEY_CONTROL,
472 static_cast<const ui::KeyEvent*>(up_event.get())->key_code());
474 // Enabled state is one shot, so next click should not be control modified.
475 ev.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED));
476 released = false;
477 mod_down_flags = 0;
478 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released);
479 EXPECT_FALSE(mod_down_flags & ui::EF_CONTROL_DOWN);
481 ev.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED));
482 released = false;
483 mod_down_flags = 0;
484 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released);
485 EXPECT_FALSE(mod_down_flags & ui::EF_CONTROL_DOWN);
488 TEST_F(StickyKeysTest, MouseEventLocked) {
489 scoped_ptr<ui::MouseEvent> ev;
490 scoped_ptr<ui::KeyEvent> kev;
491 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
493 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
495 // Pressing modifier key twice should make us enter lock state.
496 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
497 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
498 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
499 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
501 // Mouse events should not disable locked mode.
502 for (int i = 0; i < 3; ++i) {
503 bool released = false;
504 int mod_down_flags = 0;
505 ev.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED));
506 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released);
507 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
508 ev.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED));
509 released = false;
510 mod_down_flags = 0;
511 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released);
512 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
513 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
516 // Test with mouse wheel.
517 for (int i = 0; i < 3; ++i) {
518 bool released = false;
519 int mod_down_flags = 0;
520 ev.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta));
521 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released);
522 ev.reset(GenerateMouseWheelEvent(-ui::MouseWheelEvent::kWheelDelta));
523 released = false;
524 mod_down_flags = 0;
525 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released);
526 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
527 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
530 // Test mixed case with mouse events and key events.
531 ev.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta));
532 bool released = false;
533 int mod_down_flags = 0;
534 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released);
535 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
536 kev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_N));
537 mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key);
538 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
539 mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key);
540 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
542 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
545 TEST_F(StickyKeysTest, ScrollEventOneshot) {
546 scoped_ptr<ui::ScrollEvent> ev;
547 scoped_ptr<ui::KeyEvent> kev;
548 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
550 int scroll_deltas[] = {-10, 10};
551 for (int i = 0; i < 2; ++i) {
552 // Enable sticky keys.
553 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
554 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
555 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
557 // Test a scroll sequence. Sticky keys should only be disabled at the end
558 // of the scroll sequence. Fling cancel event starts the scroll sequence.
559 ev.reset(GenerateFlingScrollEvent(0, true));
560 bool released = false;
561 int mod_down_flags = 0;
562 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released);
563 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
564 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
566 // Scrolls should all be modified but not disable sticky keys.
567 for (int j = 0; j < 3; ++j) {
568 ev.reset(GenerateScrollEvent(scroll_deltas[i]));
569 released = false;
570 mod_down_flags = 0;
571 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released);
572 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
573 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
576 // Fling start event ends scroll sequence.
577 ev.reset(GenerateFlingScrollEvent(scroll_deltas[i], false));
578 released = false;
579 mod_down_flags = 0;
580 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released);
581 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
582 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
584 scoped_ptr<ui::Event> up_event;
585 EXPECT_TRUE(released);
586 ASSERT_EQ(0, sticky_key.GetModifierUpEvent(&up_event));
587 EXPECT_TRUE(up_event.get());
588 EXPECT_EQ(ui::ET_KEY_RELEASED, up_event->type());
589 EXPECT_EQ(ui::VKEY_CONTROL,
590 static_cast<const ui::KeyEvent*>(up_event.get())->key_code());
594 TEST_F(StickyKeysTest, ScrollDirectionChanged) {
595 scoped_ptr<ui::ScrollEvent> ev;
596 scoped_ptr<ui::KeyEvent> kev;
597 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
599 // Test direction change with both boundary value and negative value.
600 const int direction_change_values[2] = {0, -10};
601 for (int i = 0; i < 2; ++i) {
602 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
603 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
605 // Fling cancel starts scroll sequence.
606 ev.reset(GenerateFlingScrollEvent(0, true));
607 bool released = false;
608 int mod_down_flags = 0;
609 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released);
610 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
612 // Test that changing directions in a scroll sequence will
613 // return sticky keys to DISABLED state.
614 for (int j = 0; j < 3; ++j) {
615 ev.reset(GenerateScrollEvent(10));
616 released = false;
617 mod_down_flags = 0;
618 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released);
619 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
620 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
623 ev.reset(GenerateScrollEvent(direction_change_values[i]));
624 released = false;
625 mod_down_flags = 0;
626 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released);
627 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
631 TEST_F(StickyKeysTest, ScrollEventLocked) {
632 scoped_ptr<ui::ScrollEvent> ev;
633 scoped_ptr<ui::KeyEvent> kev;
634 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
636 // Lock sticky keys.
637 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
638 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
639 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
641 // Test scroll events are correctly modified in locked state.
642 for (int i = 0; i < 5; ++i) {
643 // Fling cancel starts scroll sequence.
644 ev.reset(GenerateFlingScrollEvent(0, true));
645 bool released = false;
646 int mod_down_flags = 0;
647 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released);
649 ev.reset(GenerateScrollEvent(10));
650 released = false;
651 mod_down_flags = 0;
652 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released);
653 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
654 ev.reset(GenerateScrollEvent(-10));
655 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released);
656 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
658 // Fling start ends scroll sequence.
659 ev.reset(GenerateFlingScrollEvent(-10, false));
660 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released);
663 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
666 TEST_F(StickyKeysTest, SynthesizedEvents) {
667 // Non-native, internally generated events should be properly handled
668 // by sticky keys.
669 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
671 // Test non-native key events.
672 scoped_ptr<ui::KeyEvent> kev;
673 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
674 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
676 kev.reset(GenerateSynthesizedKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_K));
677 int mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key);
678 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
679 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
681 kev.reset(GenerateSynthesizedKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_K));
682 mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key);
683 EXPECT_FALSE(mod_down_flags & ui::EF_CONTROL_DOWN);
684 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
686 // Test non-native mouse events.
687 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
688 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
690 scoped_ptr<ui::MouseEvent> mev;
691 mev.reset(GenerateSynthesizedMouseClickEvent(ui::ET_MOUSE_PRESSED,
692 gfx::Point(0, 0)));
693 bool released = false;
694 sticky_key.HandleMouseEvent(*mev.get(), &mod_down_flags, &released);
695 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
696 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
698 mev.reset(GenerateSynthesizedMouseClickEvent(ui::ET_MOUSE_RELEASED,
699 gfx::Point(0, 0)));
700 released = false;
701 mod_down_flags = 0;
702 sticky_key.HandleMouseEvent(*mev.get(), &mod_down_flags, &released);
703 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN);
704 EXPECT_TRUE(released);
705 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
708 } // namespace ash