1 // Copyright 2017 The Fuchsia 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 <hid/hid.h>
6 #include <hid/usages.h>
7 #include <unittest/unittest.h>
8 
9 #include "keyboard-vt100.h"
10 #include "keyboard.h"
11 
12 namespace {
13 
14 // State reported to keypress_handler().
15 uint8_t g_keycode;
16 int g_modifiers;
17 bool g_got_keypress = false;
18 
keypress_handler(uint8_t keycode,int modifiers)19 void keypress_handler(uint8_t keycode, int modifiers) {
20     g_keycode = keycode;
21     g_modifiers = modifiers;
22     g_got_keypress = true;
23 }
24 
expect_keypress(uint8_t expected_keycode,int expected_modifiers,uint8_t expected_char)25 void expect_keypress(uint8_t expected_keycode, int expected_modifiers,
26                      uint8_t expected_char) {
27 
28     EXPECT_EQ(g_got_keypress, true, "");
29     g_got_keypress = false;
30 
31     EXPECT_EQ(g_keycode, expected_keycode, "");
32     EXPECT_EQ(g_modifiers, expected_modifiers, "");
33 
34     char output[4] = {};
35     uint32_t length = hid_key_to_vt100_code(
36         g_keycode, g_modifiers, qwerty_map, output, sizeof(output));
37     if (expected_char == 0) {
38         EXPECT_EQ(length, 0, "");
39     } else {
40         EXPECT_EQ(length, 1, "");
41         EXPECT_EQ(output[0], expected_char, "");
42     }
43 }
44 
45 class KeyboardInputHelper {
46 public:
KeyboardInputHelper()47     KeyboardInputHelper() {
48         EXPECT_EQ(vc_input_create(&vi_, keypress_handler, -1), ZX_OK, "");
49     }
50 
~KeyboardInputHelper()51     ~KeyboardInputHelper() {
52     }
53 
WriteReportBuf()54     void WriteReportBuf() {
55         vc_input_process(vi_, report_buf_);
56     }
57 
58     // Byte 0 contains one bit per modifier key.
set_modifiers_byte(uint8_t value)59     void set_modifiers_byte(uint8_t value) { report_buf_[0] = value; }
60     // Bytes 2+ contain USB HID key codes.
set_first_keycode(uint8_t value)61     void set_first_keycode(uint8_t value) { report_buf_[2] = value; }
62 
63 private:
64     // USB HID key state buffer.
65     uint8_t report_buf_[8] = {};
66 
67     vc_input_t* vi_;
68 };
69 
test_keyboard_input_thread()70 bool test_keyboard_input_thread() {
71     BEGIN_TEST;
72 
73     KeyboardInputHelper helper;
74 
75     // Test pressing keys without any modifiers.
76     helper.set_first_keycode(HID_USAGE_KEY_M);
77     helper.WriteReportBuf();
78     expect_keypress(HID_USAGE_KEY_M, 0, 'm');
79 
80     helper.set_first_keycode(HID_USAGE_KEY_6);
81     helper.WriteReportBuf();
82     expect_keypress(HID_USAGE_KEY_6, 0, '6');
83 
84     // Press a modifier (but no other keys).
85     helper.set_first_keycode(0); // Unset the earlier key
86     helper.set_modifiers_byte(2); // Left Shift key
87     helper.WriteReportBuf();
88     expect_keypress(HID_USAGE_KEY_LEFT_SHIFT, MOD_LSHIFT, '\0');
89 
90     // Test keys with modifiers pressed.
91     // Test Shift-N.
92     helper.set_first_keycode(HID_USAGE_KEY_N);
93     helper.WriteReportBuf();
94     expect_keypress(HID_USAGE_KEY_N, MOD_LSHIFT, 'N');
95 
96     // Test Shift-8.
97     helper.set_first_keycode(HID_USAGE_KEY_8);
98     helper.WriteReportBuf();
99     expect_keypress(HID_USAGE_KEY_8, MOD_LSHIFT, '*');
100 
101     // Test Ctrl modifier.  First send a separate report_buf event to
102     // report unsetting the Shift key state, to account for a quirk of the
103     // current implementation.
104     helper.set_modifiers_byte(0);
105     helper.WriteReportBuf();
106     helper.set_modifiers_byte(1); // Left Ctrl key
107     helper.WriteReportBuf();
108     expect_keypress(HID_USAGE_KEY_LEFT_CTRL, MOD_LCTRL, '\0');
109 
110     // Test Ctrl-J.
111     helper.set_first_keycode(HID_USAGE_KEY_J);
112     helper.WriteReportBuf();
113     expect_keypress(HID_USAGE_KEY_J, MOD_LCTRL, 10);
114 
115     // Test Ctrl-1.  The Ctrl modifier should be ignored in this case so
116     // that we just get '1'.
117     helper.set_first_keycode(HID_USAGE_KEY_1);
118     helper.WriteReportBuf();
119     expect_keypress(HID_USAGE_KEY_1, MOD_LCTRL, '1');
120 
121     // Try Shift and Ctrl together.
122     helper.set_first_keycode(0);
123     helper.set_modifiers_byte(1 | 2); // Left Shift and Left Ctrl keys
124     helper.WriteReportBuf();
125     expect_keypress(HID_USAGE_KEY_LEFT_SHIFT, MOD_LSHIFT | MOD_LCTRL, '\0');
126 
127     // Test Shift-Ctrl-J.  This should be equivalent to Ctrl-J.
128     helper.set_first_keycode(HID_USAGE_KEY_J);
129     helper.WriteReportBuf();
130     expect_keypress(HID_USAGE_KEY_J, MOD_LSHIFT | MOD_LCTRL, 10);
131 
132     // Test Shift-Ctrl-1.  This should be equivalent to Shift-1.
133     helper.set_first_keycode(HID_USAGE_KEY_1);
134     helper.WriteReportBuf();
135     expect_keypress(HID_USAGE_KEY_1, MOD_LSHIFT | MOD_LCTRL, '!');
136 
137     END_TEST;
138 }
139 
test_caps_lock()140 bool test_caps_lock() {
141     BEGIN_TEST;
142 
143     KeyboardInputHelper helper;
144 
145     helper.set_first_keycode(HID_USAGE_KEY_CAPSLOCK);
146     helper.WriteReportBuf();
147     expect_keypress(HID_USAGE_KEY_CAPSLOCK, MOD_CAPSLOCK, '\0');
148 
149     // Test that letters are capitalized.
150     helper.set_first_keycode(HID_USAGE_KEY_M);
151     helper.WriteReportBuf();
152     expect_keypress(HID_USAGE_KEY_M, MOD_CAPSLOCK, 'M');
153 
154     // Non-letter characters should not be affected.  This isn't Shift Lock.
155     helper.set_first_keycode(HID_USAGE_KEY_1);
156     helper.WriteReportBuf();
157     expect_keypress(HID_USAGE_KEY_1, MOD_CAPSLOCK, '1');
158 
159     // Test unsetting Caps Lock.
160     helper.set_first_keycode(HID_USAGE_KEY_CAPSLOCK);
161     helper.WriteReportBuf();
162     expect_keypress(HID_USAGE_KEY_CAPSLOCK, 0, '\0');
163 
164     helper.set_first_keycode(HID_USAGE_KEY_M);
165     helper.WriteReportBuf();
166     expect_keypress(HID_USAGE_KEY_M, 0, 'm');
167 
168     END_TEST;
169 }
170 
test_caps_lock_with_shift()171 bool test_caps_lock_with_shift() {
172     BEGIN_TEST;
173 
174     KeyboardInputHelper helper;
175 
176     helper.set_modifiers_byte(2); // Left Shift key
177     helper.WriteReportBuf();
178     expect_keypress(HID_USAGE_KEY_LEFT_SHIFT, MOD_LSHIFT, '\0');
179     helper.set_first_keycode(HID_USAGE_KEY_CAPSLOCK);
180     helper.WriteReportBuf();
181     expect_keypress(HID_USAGE_KEY_CAPSLOCK, MOD_LSHIFT | MOD_CAPSLOCK, '\0');
182 
183     // Shift should undo the effect of Caps Lock for letters.
184     helper.set_first_keycode(HID_USAGE_KEY_M);
185     helper.WriteReportBuf();
186     expect_keypress(HID_USAGE_KEY_M, MOD_LSHIFT | MOD_CAPSLOCK, 'm');
187 
188     helper.set_first_keycode(HID_USAGE_KEY_1);
189     helper.WriteReportBuf();
190     expect_keypress(HID_USAGE_KEY_1, MOD_LSHIFT | MOD_CAPSLOCK, '!');
191 
192     // Test unsetting Caps Lock.
193     helper.set_first_keycode(HID_USAGE_KEY_CAPSLOCK);
194     helper.WriteReportBuf();
195     expect_keypress(HID_USAGE_KEY_CAPSLOCK, MOD_LSHIFT, '\0');
196 
197     helper.set_first_keycode(HID_USAGE_KEY_M);
198     helper.WriteReportBuf();
199     expect_keypress(HID_USAGE_KEY_M, MOD_LSHIFT, 'M');
200 
201     END_TEST;
202 }
203 
204 BEGIN_TEST_CASE(gfxconsole_keyboard_tests)
205 RUN_TEST(test_keyboard_input_thread)
206 RUN_TEST(test_caps_lock)
207 RUN_TEST(test_caps_lock_with_shift)
208 END_TEST_CASE(gfxconsole_keyboard_tests)
209 
210 }
211