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 "remoting/client/plugin/normalizing_input_filter_cros.h"
6
7#include "remoting/proto/event.pb.h"
8#include "remoting/protocol/protocol_mock_objects.h"
9#include "testing/gmock/include/gmock/gmock.h"
10#include "testing/gtest/include/gtest/gtest.h"
11
12using ::testing::InSequence;
13using remoting::protocol::InputStub;
14using remoting::protocol::KeyEvent;
15using remoting::protocol::MockInputStub;
16using remoting::protocol::MouseEvent;
17
18namespace remoting {
19
20namespace {
21
22const unsigned int kUsbLeftOsKey      = 0x0700e3;
23const unsigned int kUsbRightOsKey     = 0x0700e7;
24const unsigned int kUsbLeftAltKey     = 0x0700e2;
25const unsigned int kUsbRightAltKey    = 0x0700e6;
26
27const unsigned int kUsbFunctionKey    = 0x07003a;  // F1
28const unsigned int kUsbExtendedKey    = 0x070049;  // Insert
29const unsigned int kUsbOtherKey       = 0x07002b;  // Tab
30
31// A hardcoded value used to verify |lock_states| is preserved.
32static const uint32 kTestLockStates = protocol::KeyEvent::LOCK_STATES_NUMLOCK;
33
34MATCHER_P2(EqualsKeyEvent, usb_keycode, pressed, "") {
35  return arg.usb_keycode() == static_cast<uint32>(usb_keycode) &&
36         arg.pressed() == pressed &&
37         arg.lock_states() == kTestLockStates;
38}
39
40KeyEvent MakeKeyEvent(uint32 keycode, bool pressed) {
41  KeyEvent event;
42  event.set_usb_keycode(keycode);
43  event.set_pressed(pressed);
44  event.set_lock_states(kTestLockStates);
45  return event;
46}
47
48void PressAndReleaseKey(InputStub* input_stub, uint32 keycode) {
49  input_stub->InjectKeyEvent(MakeKeyEvent(keycode, true));
50  input_stub->InjectKeyEvent(MakeKeyEvent(keycode, false));
51}
52
53MATCHER_P2(EqualsMouseMoveEvent, x, y, "") {
54  return arg.x() == x && arg.y() == y;
55}
56
57MATCHER_P2(EqualsMouseButtonEvent, button, button_down, "") {
58  return arg.button() == button && arg.button_down() == button_down;
59}
60
61static MouseEvent MakeMouseMoveEvent(int x, int y) {
62  MouseEvent event;
63  event.set_x(x);
64  event.set_y(y);
65  return event;
66}
67
68static MouseEvent MakeMouseButtonEvent(MouseEvent::MouseButton button,
69                                       bool button_down) {
70  MouseEvent event;
71  event.set_button(button);
72  event.set_button_down(button_down);
73  return event;
74}
75
76}  // namespace
77
78// Test OSKey press/release.
79TEST(NormalizingInputFilterCrosTest, PressReleaseOsKey) {
80  MockInputStub stub;
81  scoped_ptr<protocol::InputFilter> processor(
82      new NormalizingInputFilterCros(&stub));
83
84  {
85    InSequence s;
86
87    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbLeftOsKey, true)));
88    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbLeftOsKey, false)));
89
90    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbRightOsKey, true)));
91    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbRightOsKey, false)));
92  }
93
94  // Inject press & release events for left & right OSKeys.
95  PressAndReleaseKey(processor.get(), kUsbLeftOsKey);
96  PressAndReleaseKey(processor.get(), kUsbRightOsKey);
97}
98
99// Test OSKey key repeat switches it to "modifying" mode.
100TEST(NormalizingInputFilterCrosTest, OSKeyRepeats) {
101  MockInputStub stub;
102  scoped_ptr<protocol::InputFilter> processor(
103      new NormalizingInputFilterCros(&stub));
104
105  {
106    InSequence s;
107
108    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbLeftOsKey, true)));
109    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbLeftOsKey, true)));
110    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbLeftOsKey, true)));
111  }
112
113  // Inject a press and repeats for the left OSKey, but don't release it, and
114  // verify that the repeats result in press events.
115  processor->InjectKeyEvent(MakeKeyEvent(kUsbLeftOsKey, true));
116  processor->InjectKeyEvent(MakeKeyEvent(kUsbLeftOsKey, true));
117  processor->InjectKeyEvent(MakeKeyEvent(kUsbLeftOsKey, true));
118}
119
120// Test OSKey press followed by function key press and release results in
121// just the function key events.
122TEST(NormalizingInputFilterCrosTest, FunctionKey) {
123  MockInputStub stub;
124  scoped_ptr<protocol::InputFilter> processor(
125      new NormalizingInputFilterCros(&stub));
126
127  {
128    InSequence s;
129
130    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbFunctionKey, true)));
131    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbFunctionKey, false)));
132  }
133
134  // Hold the left OSKey while pressing & releasing the function key.
135  processor->InjectKeyEvent(MakeKeyEvent(kUsbLeftOsKey, true));
136  PressAndReleaseKey(processor.get(), kUsbFunctionKey);
137  processor->InjectKeyEvent(MakeKeyEvent(kUsbLeftOsKey, false));
138}
139
140// Test OSKey press followed by extended key press and release results in
141// just the function key events.
142TEST(NormalizingInputFilterCrosTest, ExtendedKey) {
143  MockInputStub stub;
144  scoped_ptr<protocol::InputFilter> processor(
145      new NormalizingInputFilterCros(&stub));
146
147  {
148    InSequence s;
149
150    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbExtendedKey, true)));
151    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbExtendedKey, false)));
152  }
153
154  // Hold the left OSKey while pressing & releasing the function key.
155  processor->InjectKeyEvent(MakeKeyEvent(kUsbLeftOsKey, true));
156  PressAndReleaseKey(processor.get(), kUsbExtendedKey);
157  processor->InjectKeyEvent(MakeKeyEvent(kUsbLeftOsKey, false));
158}
159
160// Test OSKey press followed by non-function, non-extended key press and release
161// results in normal-looking sequence.
162TEST(NormalizingInputFilterCrosTest, OtherKey) {
163  MockInputStub stub;
164  scoped_ptr<protocol::InputFilter> processor(
165      new NormalizingInputFilterCros(&stub));
166
167  {
168    InSequence s;
169
170    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbLeftOsKey, true)));
171    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbOtherKey, true)));
172    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbOtherKey, false)));
173    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbLeftOsKey, false)));
174  }
175
176  // Hold the left OSKey while pressing & releasing the function key.
177  processor->InjectKeyEvent(MakeKeyEvent(kUsbLeftOsKey, true));
178  PressAndReleaseKey(processor.get(), kUsbOtherKey);
179  processor->InjectKeyEvent(MakeKeyEvent(kUsbLeftOsKey, false));
180}
181
182// Test OSKey press followed by extended key press, then normal key press
183// results in OSKey switching to modifying mode for the normal key.
184TEST(NormalizingInputFilterCrosTest, ExtendedThenOtherKey) {
185  MockInputStub stub;
186  scoped_ptr<protocol::InputFilter> processor(
187      new NormalizingInputFilterCros(&stub));
188
189  {
190    InSequence s;
191
192    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbExtendedKey, true)));
193    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbExtendedKey, false)));
194    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbLeftOsKey, true)));
195    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbOtherKey, true)));
196    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbOtherKey, false)));
197    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbLeftOsKey, false)));
198  }
199
200  // Hold the left OSKey while pressing & releasing the function key.
201  processor->InjectKeyEvent(MakeKeyEvent(kUsbLeftOsKey, true));
202  PressAndReleaseKey(processor.get(), kUsbExtendedKey);
203  PressAndReleaseKey(processor.get(), kUsbOtherKey);
204  processor->InjectKeyEvent(MakeKeyEvent(kUsbLeftOsKey, false));
205}
206
207// Test OSKey press followed by mouse event puts the OSKey into modifying mode.
208TEST(NormalizingInputFilterCrosTest, MouseEvent) {
209  MockInputStub stub;
210  scoped_ptr<protocol::InputFilter> processor(
211      new NormalizingInputFilterCros(&stub));
212
213  {
214    InSequence s;
215
216    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbLeftOsKey, true)));
217    EXPECT_CALL(stub, InjectMouseEvent(EqualsMouseMoveEvent(0, 0)));
218    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbLeftOsKey, false)));
219  }
220
221  // Hold the left OSKey while pressing & releasing the function key.
222  processor->InjectKeyEvent(MakeKeyEvent(kUsbLeftOsKey, true));
223  processor->InjectMouseEvent(MakeMouseMoveEvent(0, 0));
224  processor->InjectKeyEvent(MakeKeyEvent(kUsbLeftOsKey, false));
225}
226
227// Test left alt + right click is remapped to left alt + left click.
228TEST(NormalizingInputFilterCrosTest, LeftAltClick) {
229  MockInputStub stub;
230  scoped_ptr<protocol::InputFilter> processor(
231      new NormalizingInputFilterCros(&stub));
232
233  {
234    InSequence s;
235
236    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbLeftAltKey, true)));
237    EXPECT_CALL(stub, InjectMouseEvent(
238        EqualsMouseButtonEvent(MouseEvent::BUTTON_LEFT, true)));
239    EXPECT_CALL(stub, InjectMouseEvent(
240        EqualsMouseButtonEvent(MouseEvent::BUTTON_LEFT, false)));
241    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbLeftAltKey, false)));
242  }
243
244  // Hold the left alt key while left-clicking. ChromeOS will rewrite this as
245  // Alt+RightClick
246  processor->InjectKeyEvent(MakeKeyEvent(kUsbLeftAltKey, true));
247  processor->InjectMouseEvent(
248      MakeMouseButtonEvent(MouseEvent::BUTTON_RIGHT, true));
249  processor->InjectMouseEvent(
250      MakeMouseButtonEvent(MouseEvent::BUTTON_RIGHT, false));
251  processor->InjectKeyEvent(MakeKeyEvent(kUsbLeftAltKey, false));
252}
253
254// Test that right alt + right click is unchanged.
255TEST(NormalizingInputFilterCrosTest, RightAltClick) {
256  MockInputStub stub;
257  scoped_ptr<protocol::InputFilter> processor(
258      new NormalizingInputFilterCros(&stub));
259
260  {
261    InSequence s;
262
263    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbRightAltKey, true)));
264    EXPECT_CALL(stub, InjectMouseEvent(
265        EqualsMouseButtonEvent(MouseEvent::BUTTON_RIGHT, true)));
266    EXPECT_CALL(stub, InjectMouseEvent(
267        EqualsMouseButtonEvent(MouseEvent::BUTTON_RIGHT, false)));
268    EXPECT_CALL(stub, InjectKeyEvent(EqualsKeyEvent(kUsbRightAltKey, false)));
269  }
270
271  // Hold the right alt key while left-clicking. ChromeOS will rewrite this as
272  // Alt+RightClick
273  processor->InjectKeyEvent(MakeKeyEvent(kUsbRightAltKey, true));
274  processor->InjectMouseEvent(
275      MakeMouseButtonEvent(MouseEvent::BUTTON_RIGHT, true));
276  processor->InjectMouseEvent(
277      MakeMouseButtonEvent(MouseEvent::BUTTON_RIGHT, false));
278  processor->InjectKeyEvent(MakeKeyEvent(kUsbRightAltKey, false));
279}
280
281}  // namespace remoting
282