Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests: hid: import hid-tools hid-keyboards tests

These tests have been developed in the hid-tools[0] tree for a while.
Now that we have a proper selftests/hid kernel entry and that the tests
are more reliable, it is time to directly include those in the kernel
tree.

[0] https://gitlab.freedesktop.org/libevdev/hid-tools

Cc: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
Cc: Peter Hutterer <peter.hutterer@who-t.net>
Cc: Roderick Colenbrander <roderick.colenbrander@sony.com>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Roderick Colenbrander <roderick.colenbrander@sony.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

+493
+1
tools/testing/selftests/hid/Makefile
··· 7 7 8 8 TEST_PROGS := hid-core.sh 9 9 TEST_PROGS += hid-gamepad.sh 10 + TEST_PROGS += hid-keyboard.sh 10 11 11 12 CXX ?= $(CROSS_COMPILE)g++ 12 13
+7
tools/testing/selftests/hid/hid-keyboard.sh
··· 1 + #!/bin/sh 2 + # SPDX-License-Identifier: GPL-2.0 3 + # Runs tests for the HID subsystem 4 + 5 + export TARGET=test_keyboard.py 6 + 7 + bash ./run-hid-tools-tests.sh
+485
tools/testing/selftests/hid/tests/test_keyboard.py
··· 1 + #!/bin/env python3 2 + # SPDX-License-Identifier: GPL-2.0 3 + # -*- coding: utf-8 -*- 4 + # 5 + # Copyright (c) 2018 Benjamin Tissoires <benjamin.tissoires@gmail.com> 6 + # Copyright (c) 2018 Red Hat, Inc. 7 + # 8 + 9 + from . import base 10 + import hidtools.hid 11 + import libevdev 12 + import logging 13 + 14 + logger = logging.getLogger("hidtools.test.keyboard") 15 + 16 + 17 + class InvalidHIDCommunication(Exception): 18 + pass 19 + 20 + 21 + class KeyboardData(object): 22 + pass 23 + 24 + 25 + class BaseKeyboard(base.UHIDTestDevice): 26 + def __init__(self, rdesc, name=None, input_info=None): 27 + assert rdesc is not None 28 + super().__init__(name, "Key", input_info=input_info, rdesc=rdesc) 29 + self.keystates = {} 30 + 31 + def _update_key_state(self, keys): 32 + """ 33 + Update the internal state of keys with the new state given. 34 + 35 + :param key: a tuple of chars for the currently pressed keys. 36 + """ 37 + # First remove the already released keys 38 + unused_keys = [k for k, v in self.keystates.items() if not v] 39 + for key in unused_keys: 40 + del self.keystates[key] 41 + 42 + # self.keystates contains now the list of currently pressed keys, 43 + # release them... 44 + for key in self.keystates.keys(): 45 + self.keystates[key] = False 46 + 47 + # ...and press those that are in parameter 48 + for key in keys: 49 + self.keystates[key] = True 50 + 51 + def _create_report_data(self): 52 + keyboard = KeyboardData() 53 + for key, value in self.keystates.items(): 54 + key = key.replace(" ", "").lower() 55 + setattr(keyboard, key, value) 56 + return keyboard 57 + 58 + def create_array_report(self, keys, reportID=None, application=None): 59 + """ 60 + Return an input report for this device. 61 + 62 + :param keys: a tuple of chars for the pressed keys. The class maintains 63 + the list of currently pressed keys, so to release a key, the caller 64 + needs to call again this function without the key in this tuple. 65 + :param reportID: the numeric report ID for this report, if needed 66 + """ 67 + self._update_key_state(keys) 68 + reportID = reportID or self.default_reportID 69 + 70 + keyboard = self._create_report_data() 71 + return self.create_report(keyboard, reportID=reportID, application=application) 72 + 73 + def event(self, keys, reportID=None, application=None): 74 + """ 75 + Send an input event on the default report ID. 76 + 77 + :param keys: a tuple of chars for the pressed keys. The class maintains 78 + the list of currently pressed keys, so to release a key, the caller 79 + needs to call again this function without the key in this tuple. 80 + """ 81 + r = self.create_array_report(keys, reportID, application) 82 + self.call_input_event(r) 83 + return [r] 84 + 85 + 86 + class PlainKeyboard(BaseKeyboard): 87 + # fmt: off 88 + report_descriptor = [ 89 + 0x05, 0x01, # Usage Page (Generic Desktop) 90 + 0x09, 0x06, # Usage (Keyboard) 91 + 0xa1, 0x01, # Collection (Application) 92 + 0x85, 0x01, # .Report ID (1) 93 + 0x05, 0x07, # .Usage Page (Keyboard) 94 + 0x19, 0xe0, # .Usage Minimum (224) 95 + 0x29, 0xe7, # .Usage Maximum (231) 96 + 0x15, 0x00, # .Logical Minimum (0) 97 + 0x25, 0x01, # .Logical Maximum (1) 98 + 0x75, 0x01, # .Report Size (1) 99 + 0x95, 0x08, # .Report Count (8) 100 + 0x81, 0x02, # .Input (Data,Var,Abs) 101 + 0x19, 0x00, # .Usage Minimum (0) 102 + 0x29, 0x97, # .Usage Maximum (151) 103 + 0x15, 0x00, # .Logical Minimum (0) 104 + 0x25, 0x01, # .Logical Maximum (1) 105 + 0x75, 0x01, # .Report Size (1) 106 + 0x95, 0x98, # .Report Count (152) 107 + 0x81, 0x02, # .Input (Data,Var,Abs) 108 + 0xc0, # End Collection 109 + ] 110 + # fmt: on 111 + 112 + def __init__(self, rdesc=report_descriptor, name=None, input_info=None): 113 + super().__init__(rdesc, name, input_info) 114 + self.default_reportID = 1 115 + 116 + 117 + class ArrayKeyboard(BaseKeyboard): 118 + # fmt: off 119 + report_descriptor = [ 120 + 0x05, 0x01, # Usage Page (Generic Desktop) 121 + 0x09, 0x06, # Usage (Keyboard) 122 + 0xa1, 0x01, # Collection (Application) 123 + 0x05, 0x07, # .Usage Page (Keyboard) 124 + 0x19, 0xe0, # .Usage Minimum (224) 125 + 0x29, 0xe7, # .Usage Maximum (231) 126 + 0x15, 0x00, # .Logical Minimum (0) 127 + 0x25, 0x01, # .Logical Maximum (1) 128 + 0x75, 0x01, # .Report Size (1) 129 + 0x95, 0x08, # .Report Count (8) 130 + 0x81, 0x02, # .Input (Data,Var,Abs) 131 + 0x95, 0x06, # .Report Count (6) 132 + 0x75, 0x08, # .Report Size (8) 133 + 0x15, 0x00, # .Logical Minimum (0) 134 + 0x26, 0xa4, 0x00, # .Logical Maximum (164) 135 + 0x05, 0x07, # .Usage Page (Keyboard) 136 + 0x19, 0x00, # .Usage Minimum (0) 137 + 0x29, 0xa4, # .Usage Maximum (164) 138 + 0x81, 0x00, # .Input (Data,Arr,Abs) 139 + 0xc0, # End Collection 140 + ] 141 + # fmt: on 142 + 143 + def __init__(self, rdesc=report_descriptor, name=None, input_info=None): 144 + super().__init__(rdesc, name, input_info) 145 + 146 + def _create_report_data(self): 147 + data = KeyboardData() 148 + array = [] 149 + 150 + hut = hidtools.hut.HUT 151 + 152 + # strip modifiers from the array 153 + for k, v in self.keystates.items(): 154 + # we ignore depressed keys 155 + if not v: 156 + continue 157 + 158 + usage = hut[0x07].from_name[k].usage 159 + if usage >= 224 and usage <= 231: 160 + # modifier 161 + setattr(data, k.lower(), 1) 162 + else: 163 + array.append(k) 164 + 165 + # if array length is bigger than 6, report ErrorRollOver 166 + if len(array) > 6: 167 + array = ["ErrorRollOver"] * 6 168 + 169 + data.keyboard = array 170 + return data 171 + 172 + 173 + class LEDKeyboard(ArrayKeyboard): 174 + # fmt: off 175 + report_descriptor = [ 176 + 0x05, 0x01, # Usage Page (Generic Desktop) 177 + 0x09, 0x06, # Usage (Keyboard) 178 + 0xa1, 0x01, # Collection (Application) 179 + 0x05, 0x07, # .Usage Page (Keyboard) 180 + 0x19, 0xe0, # .Usage Minimum (224) 181 + 0x29, 0xe7, # .Usage Maximum (231) 182 + 0x15, 0x00, # .Logical Minimum (0) 183 + 0x25, 0x01, # .Logical Maximum (1) 184 + 0x75, 0x01, # .Report Size (1) 185 + 0x95, 0x08, # .Report Count (8) 186 + 0x81, 0x02, # .Input (Data,Var,Abs) 187 + 0x95, 0x01, # .Report Count (1) 188 + 0x75, 0x08, # .Report Size (8) 189 + 0x81, 0x01, # .Input (Cnst,Arr,Abs) 190 + 0x95, 0x05, # .Report Count (5) 191 + 0x75, 0x01, # .Report Size (1) 192 + 0x05, 0x08, # .Usage Page (LEDs) 193 + 0x19, 0x01, # .Usage Minimum (1) 194 + 0x29, 0x05, # .Usage Maximum (5) 195 + 0x91, 0x02, # .Output (Data,Var,Abs) 196 + 0x95, 0x01, # .Report Count (1) 197 + 0x75, 0x03, # .Report Size (3) 198 + 0x91, 0x01, # .Output (Cnst,Arr,Abs) 199 + 0x95, 0x06, # .Report Count (6) 200 + 0x75, 0x08, # .Report Size (8) 201 + 0x15, 0x00, # .Logical Minimum (0) 202 + 0x26, 0xa4, 0x00, # .Logical Maximum (164) 203 + 0x05, 0x07, # .Usage Page (Keyboard) 204 + 0x19, 0x00, # .Usage Minimum (0) 205 + 0x29, 0xa4, # .Usage Maximum (164) 206 + 0x81, 0x00, # .Input (Data,Arr,Abs) 207 + 0xc0, # End Collection 208 + ] 209 + # fmt: on 210 + 211 + def __init__(self, rdesc=report_descriptor, name=None, input_info=None): 212 + super().__init__(rdesc, name, input_info) 213 + 214 + 215 + # Some Primax manufactured keyboards set the Usage Page after having defined 216 + # some local Usages. It relies on the fact that the specification states that 217 + # Usages are to be concatenated with Usage Pages upon finding a Main item (see 218 + # 6.2.2.8). This test covers this case. 219 + class PrimaxKeyboard(ArrayKeyboard): 220 + # fmt: off 221 + report_descriptor = [ 222 + 0x05, 0x01, # Usage Page (Generic Desktop) 223 + 0x09, 0x06, # Usage (Keyboard) 224 + 0xA1, 0x01, # Collection (Application) 225 + 0x05, 0x07, # .Usage Page (Keyboard) 226 + 0x19, 0xE0, # .Usage Minimum (224) 227 + 0x29, 0xE7, # .Usage Maximum (231) 228 + 0x15, 0x00, # .Logical Minimum (0) 229 + 0x25, 0x01, # .Logical Maximum (1) 230 + 0x75, 0x01, # .Report Size (1) 231 + 0x95, 0x08, # .Report Count (8) 232 + 0x81, 0x02, # .Input (Data,Var,Abs) 233 + 0x75, 0x08, # .Report Size (8) 234 + 0x95, 0x01, # .Report Count (1) 235 + 0x81, 0x01, # .Input (Data,Var,Abs) 236 + 0x05, 0x08, # .Usage Page (LEDs) 237 + 0x19, 0x01, # .Usage Minimum (1) 238 + 0x29, 0x03, # .Usage Maximum (3) 239 + 0x75, 0x01, # .Report Size (1) 240 + 0x95, 0x03, # .Report Count (3) 241 + 0x91, 0x02, # .Output (Data,Var,Abs) 242 + 0x95, 0x01, # .Report Count (1) 243 + 0x75, 0x05, # .Report Size (5) 244 + 0x91, 0x01, # .Output (Constant) 245 + 0x15, 0x00, # .Logical Minimum (0) 246 + 0x26, 0xFF, 0x00, # .Logical Maximum (255) 247 + 0x19, 0x00, # .Usage Minimum (0) 248 + 0x2A, 0xFF, 0x00, # .Usage Maximum (255) 249 + 0x05, 0x07, # .Usage Page (Keyboard) 250 + 0x75, 0x08, # .Report Size (8) 251 + 0x95, 0x06, # .Report Count (6) 252 + 0x81, 0x00, # .Input (Data,Arr,Abs) 253 + 0xC0, # End Collection 254 + ] 255 + # fmt: on 256 + 257 + def __init__(self, rdesc=report_descriptor, name=None, input_info=None): 258 + super().__init__(rdesc, name, input_info) 259 + 260 + 261 + class BaseTest: 262 + class TestKeyboard(base.BaseTestCase.TestUhid): 263 + def test_single_key(self): 264 + """check for key reliability.""" 265 + uhdev = self.uhdev 266 + evdev = uhdev.get_evdev() 267 + syn_event = self.syn_event 268 + 269 + r = uhdev.event(["a and A"]) 270 + expected = [syn_event] 271 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 1)) 272 + events = uhdev.next_sync_events() 273 + self.debug_reports(r, uhdev, events) 274 + self.assertInputEventsIn(expected, events) 275 + assert evdev.value[libevdev.EV_KEY.KEY_A] == 1 276 + 277 + r = uhdev.event([]) 278 + expected = [syn_event] 279 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 0)) 280 + events = uhdev.next_sync_events() 281 + self.debug_reports(r, uhdev, events) 282 + self.assertInputEventsIn(expected, events) 283 + assert evdev.value[libevdev.EV_KEY.KEY_A] == 0 284 + 285 + def test_two_keys(self): 286 + uhdev = self.uhdev 287 + evdev = uhdev.get_evdev() 288 + syn_event = self.syn_event 289 + 290 + r = uhdev.event(["a and A", "q and Q"]) 291 + expected = [syn_event] 292 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 1)) 293 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_Q, 1)) 294 + events = uhdev.next_sync_events() 295 + self.debug_reports(r, uhdev, events) 296 + self.assertInputEventsIn(expected, events) 297 + assert evdev.value[libevdev.EV_KEY.KEY_A] == 1 298 + 299 + r = uhdev.event([]) 300 + expected = [syn_event] 301 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_A, 0)) 302 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_Q, 0)) 303 + events = uhdev.next_sync_events() 304 + self.debug_reports(r, uhdev, events) 305 + self.assertInputEventsIn(expected, events) 306 + assert evdev.value[libevdev.EV_KEY.KEY_A] == 0 307 + assert evdev.value[libevdev.EV_KEY.KEY_Q] == 0 308 + 309 + r = uhdev.event(["c and C"]) 310 + expected = [syn_event] 311 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_C, 1)) 312 + events = uhdev.next_sync_events() 313 + self.debug_reports(r, uhdev, events) 314 + self.assertInputEventsIn(expected, events) 315 + assert evdev.value[libevdev.EV_KEY.KEY_C] == 1 316 + 317 + r = uhdev.event(["c and C", "Spacebar"]) 318 + expected = [syn_event] 319 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_SPACE, 1)) 320 + events = uhdev.next_sync_events() 321 + self.debug_reports(r, uhdev, events) 322 + assert libevdev.InputEvent(libevdev.EV_KEY.KEY_C) not in events 323 + self.assertInputEventsIn(expected, events) 324 + assert evdev.value[libevdev.EV_KEY.KEY_C] == 1 325 + assert evdev.value[libevdev.EV_KEY.KEY_SPACE] == 1 326 + 327 + r = uhdev.event(["Spacebar"]) 328 + expected = [syn_event] 329 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_C, 0)) 330 + events = uhdev.next_sync_events() 331 + self.debug_reports(r, uhdev, events) 332 + assert libevdev.InputEvent(libevdev.EV_KEY.KEY_SPACE) not in events 333 + self.assertInputEventsIn(expected, events) 334 + assert evdev.value[libevdev.EV_KEY.KEY_C] == 0 335 + assert evdev.value[libevdev.EV_KEY.KEY_SPACE] == 1 336 + 337 + r = uhdev.event([]) 338 + expected = [syn_event] 339 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_SPACE, 0)) 340 + events = uhdev.next_sync_events() 341 + self.debug_reports(r, uhdev, events) 342 + self.assertInputEventsIn(expected, events) 343 + assert evdev.value[libevdev.EV_KEY.KEY_SPACE] == 0 344 + 345 + def test_modifiers(self): 346 + # ctrl-alt-del would be very nice :) 347 + uhdev = self.uhdev 348 + syn_event = self.syn_event 349 + 350 + r = uhdev.event(["LeftControl", "LeftShift", "= and +"]) 351 + expected = [syn_event] 352 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_LEFTCTRL, 1)) 353 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_LEFTSHIFT, 1)) 354 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_EQUAL, 1)) 355 + events = uhdev.next_sync_events() 356 + self.debug_reports(r, uhdev, events) 357 + self.assertInputEventsIn(expected, events) 358 + 359 + 360 + class TestPlainKeyboard(BaseTest.TestKeyboard): 361 + def create_device(self): 362 + return PlainKeyboard() 363 + 364 + def test_10_keys(self): 365 + uhdev = self.uhdev 366 + syn_event = self.syn_event 367 + 368 + r = uhdev.event( 369 + [ 370 + "1 and !", 371 + "2 and @", 372 + "3 and #", 373 + "4 and $", 374 + "5 and %", 375 + "6 and ^", 376 + "7 and &", 377 + "8 and *", 378 + "9 and (", 379 + "0 and )", 380 + ] 381 + ) 382 + expected = [syn_event] 383 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_0, 1)) 384 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 1)) 385 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 1)) 386 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 1)) 387 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 1)) 388 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 1)) 389 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 1)) 390 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_7, 1)) 391 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_8, 1)) 392 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_9, 1)) 393 + events = uhdev.next_sync_events() 394 + self.debug_reports(r, uhdev, events) 395 + self.assertInputEventsIn(expected, events) 396 + 397 + r = uhdev.event([]) 398 + expected = [syn_event] 399 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_0, 0)) 400 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 0)) 401 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 0)) 402 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 0)) 403 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 0)) 404 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 0)) 405 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 0)) 406 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_7, 0)) 407 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_8, 0)) 408 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_9, 0)) 409 + events = uhdev.next_sync_events() 410 + self.debug_reports(r, uhdev, events) 411 + self.assertInputEventsIn(expected, events) 412 + 413 + 414 + class TestArrayKeyboard(BaseTest.TestKeyboard): 415 + def create_device(self): 416 + return ArrayKeyboard() 417 + 418 + def test_10_keys(self): 419 + uhdev = self.uhdev 420 + syn_event = self.syn_event 421 + 422 + r = uhdev.event( 423 + [ 424 + "1 and !", 425 + "2 and @", 426 + "3 and #", 427 + "4 and $", 428 + "5 and %", 429 + "6 and ^", 430 + ] 431 + ) 432 + expected = [syn_event] 433 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 1)) 434 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 1)) 435 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 1)) 436 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 1)) 437 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 1)) 438 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 1)) 439 + events = uhdev.next_sync_events() 440 + 441 + self.debug_reports(r, uhdev, events) 442 + self.assertInputEventsIn(expected, events) 443 + 444 + # ErrRollOver 445 + r = uhdev.event( 446 + [ 447 + "1 and !", 448 + "2 and @", 449 + "3 and #", 450 + "4 and $", 451 + "5 and %", 452 + "6 and ^", 453 + "7 and &", 454 + "8 and *", 455 + "9 and (", 456 + "0 and )", 457 + ] 458 + ) 459 + events = uhdev.next_sync_events() 460 + 461 + self.debug_reports(r, uhdev, events) 462 + 463 + assert len(events) == 0 464 + 465 + r = uhdev.event([]) 466 + expected = [syn_event] 467 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_1, 0)) 468 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_2, 0)) 469 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_3, 0)) 470 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_4, 0)) 471 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_5, 0)) 472 + expected.append(libevdev.InputEvent(libevdev.EV_KEY.KEY_6, 0)) 473 + events = uhdev.next_sync_events() 474 + self.debug_reports(r, uhdev, events) 475 + self.assertInputEventsIn(expected, events) 476 + 477 + 478 + class TestLEDKeyboard(BaseTest.TestKeyboard): 479 + def create_device(self): 480 + return LEDKeyboard() 481 + 482 + 483 + class TestPrimaxKeyboard(BaseTest.TestKeyboard): 484 + def create_device(self): 485 + return PrimaxKeyboard()