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

selftests/hid: sync the python tests to hid-tools 0.8

Instead of backporting one by one each commits, let's pull them in bulk
and refer to the hid-tools project for a detailed history.

The short summary is:
- make use of dataclass when possible, to avoid tuples
- wacom: remove unused uhdev parameter
- various small fixes not worth mentioning

Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
Link: https://patch.msgid.link/20250709-wip-fix-ci-v1-2-b7df4c271cf8@kernel.org
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>

+69 -43
+30 -16
tools/testing/selftests/hid/tests/base.py
··· 5 5 # Copyright (c) 2017 Benjamin Tissoires <benjamin.tissoires@gmail.com> 6 6 # Copyright (c) 2017 Red Hat, Inc. 7 7 8 + import dataclasses 8 9 import libevdev 9 10 import os 10 11 import pytest ··· 146 145 self.name = name 147 146 148 147 148 + @dataclasses.dataclass 149 + class HidBpf: 150 + object_name: str 151 + has_rdesc_fixup: bool 152 + 153 + 154 + @dataclasses.dataclass 155 + class KernelModule: 156 + driver_name: str 157 + module_name: str 158 + 159 + 149 160 class BaseTestCase: 150 161 class TestUhid(object): 151 162 syn_event = libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT) # type: ignore ··· 168 155 169 156 # List of kernel modules to load before starting the test 170 157 # if any module is not available (not compiled), the test will skip. 171 - # Each element is a tuple '(kernel driver name, kernel module)', 172 - # for example ("playstation", "hid-playstation") 173 - kernel_modules: List[Tuple[str, str]] = [] 158 + # Each element is a KernelModule object, for example 159 + # KernelModule("playstation", "hid-playstation") 160 + kernel_modules: List[KernelModule] = [] 174 161 175 162 # List of in kernel HID-BPF object files to load 176 163 # before starting the test 177 164 # Any existing pre-loaded HID-BPF module will be removed 178 165 # before the ones in this list will be manually loaded. 179 - # Each Element is a tuple '(hid_bpf_object, rdesc_fixup_present)', 180 - # for example '("xppen-ArtistPro16Gen2.bpf.o", True)' 181 - # If 'rdesc_fixup_present' is True, the test needs to wait 166 + # Each Element is a HidBpf object, for example 167 + # 'HidBpf("xppen-ArtistPro16Gen2.bpf.o", True)' 168 + # If 'has_rdesc_fixup' is True, the test needs to wait 182 169 # for one unbind and rebind before it can be sure the kernel is 183 170 # ready 184 - hid_bpfs: List[Tuple[str, bool]] = [] 171 + hid_bpfs: List[HidBpf] = [] 185 172 186 173 def assertInputEventsIn(self, expected_events, effective_events): 187 174 effective_events = effective_events.copy() ··· 245 232 246 233 @pytest.fixture() 247 234 def load_kernel_module(self): 248 - for kernel_driver, kernel_module in self.kernel_modules: 249 - self._load_kernel_module(kernel_driver, kernel_module) 235 + for k in self.kernel_modules: 236 + self._load_kernel_module(k.driver_name, k.module_name) 250 237 yield 251 238 252 239 def load_hid_bpfs(self): 240 + # this function will only work when run in the kernel tree 253 241 script_dir = Path(os.path.dirname(os.path.realpath(__file__))) 254 242 root_dir = (script_dir / "../../../../..").resolve() 255 243 bpf_dir = root_dir / "drivers/hid/bpf/progs" 244 + 245 + if not bpf_dir.exists(): 246 + pytest.skip("looks like we are not in the kernel tree, skipping") 256 247 257 248 udev_hid_bpf = shutil.which("udev-hid-bpf") 258 249 if not udev_hid_bpf: 259 250 pytest.skip("udev-hid-bpf not found in $PATH, skipping") 260 251 261 - wait = False 262 - for _, rdesc_fixup in self.hid_bpfs: 263 - if rdesc_fixup: 264 - wait = True 252 + wait = any(b.has_rdesc_fixup for b in self.hid_bpfs) 265 253 266 - for hid_bpf, _ in self.hid_bpfs: 254 + for hid_bpf in self.hid_bpfs: 267 255 # We need to start `udev-hid-bpf` in the background 268 256 # and dispatch uhid events in case the kernel needs 269 257 # to fetch features on the device ··· 274 260 "--verbose", 275 261 "add", 276 262 str(self.uhdev.sys_path), 277 - str(bpf_dir / hid_bpf), 263 + str(bpf_dir / hid_bpf.object_name), 278 264 ], 279 265 ) 280 266 while process.poll() is None: 281 267 self.uhdev.dispatch(1) 282 268 283 - if process.poll() != 0: 269 + if process.returncode != 0: 284 270 pytest.fail( 285 271 f"Couldn't insert hid-bpf program '{hid_bpf}', marking the test as failed" 286 272 )
+19 -11
tools/testing/selftests/hid/tests/base_device.py
··· 18 18 # You should have received a copy of the GNU General Public License 19 19 # along with this program. If not, see <http://www.gnu.org/licenses/>. 20 20 21 + import dataclasses 21 22 import fcntl 22 23 import functools 23 24 import libevdev ··· 105 104 return self._type.str_value 106 105 107 106 107 + @dataclasses.dataclass 108 + class HidReadiness: 109 + is_ready: bool = False 110 + count: int = 0 111 + 112 + 108 113 class HIDIsReady(object): 109 114 """ 110 115 Companion class that binds to a kernel mechanism ··· 122 115 def __init__(self: "HIDIsReady", uhid: UHIDDevice) -> None: 123 116 self.uhid = uhid 124 117 125 - def is_ready(self: "HIDIsReady") -> bool: 118 + def is_ready(self: "HIDIsReady") -> HidReadiness: 126 119 """ 127 120 Overwrite in subclasses: should return True or False whether 128 121 the attached uhid device is ready or not. 129 122 """ 130 - return False 123 + return HidReadiness() 131 124 132 125 133 126 class UdevHIDIsReady(HIDIsReady): 134 127 _pyudev_context: ClassVar[Optional[pyudev.Context]] = None 135 128 _pyudev_monitor: ClassVar[Optional[pyudev.Monitor]] = None 136 - _uhid_devices: ClassVar[Dict[int, Tuple[bool, int]]] = {} 129 + _uhid_devices: ClassVar[Dict[int, HidReadiness]] = {} 137 130 138 131 def __init__(self: "UdevHIDIsReady", uhid: UHIDDevice) -> None: 139 132 super().__init__(uhid) ··· 164 157 165 158 id = int(event.sys_path.strip().split(".")[-1], 16) 166 159 167 - device_ready, count = cls._uhid_devices.get(id, (False, 0)) 160 + readiness = cls._uhid_devices.setdefault(id, HidReadiness()) 168 161 169 162 ready = event.action == "bind" 170 - if not device_ready and ready: 171 - count += 1 172 - cls._uhid_devices[id] = (ready, count) 163 + if not readiness.is_ready and ready: 164 + readiness.count += 1 173 165 174 - def is_ready(self: "UdevHIDIsReady") -> Tuple[bool, int]: 166 + readiness.is_ready = ready 167 + 168 + def is_ready(self: "UdevHIDIsReady") -> HidReadiness: 175 169 try: 176 170 return self._uhid_devices[self.uhid.hid_id] 177 171 except KeyError: 178 - return (False, 0) 172 + return HidReadiness() 179 173 180 174 181 175 class EvdevMatch(object): ··· 330 322 331 323 @property 332 324 def kernel_is_ready(self: "BaseDevice") -> bool: 333 - return self._kernel_is_ready.is_ready()[0] and self.started 325 + return self._kernel_is_ready.is_ready().is_ready and self.started 334 326 335 327 @property 336 328 def kernel_ready_count(self: "BaseDevice") -> int: 337 - return self._kernel_is_ready.is_ready()[1] 329 + return self._kernel_is_ready.is_ready().count 338 330 339 331 @property 340 332 def input_nodes(self: "BaseDevice") -> List[EvdevDevice]:
+2 -1
tools/testing/selftests/hid/tests/test_apple_keyboard.py
··· 8 8 9 9 from .test_keyboard import ArrayKeyboard, TestArrayKeyboard 10 10 from hidtools.util import BusType 11 + from . import base 11 12 12 13 import libevdev 13 14 import logging 14 15 15 16 logger = logging.getLogger("hidtools.test.apple-keyboard") 16 17 17 - KERNEL_MODULE = ("apple", "hid-apple") 18 + KERNEL_MODULE = base.KernelModule("apple", "hid-apple") 18 19 19 20 20 21 class KbdData(object):
+2 -1
tools/testing/selftests/hid/tests/test_gamepad.py
··· 12 12 13 13 from .base_gamepad import BaseGamepad, JoystickGamepad, AxisMapping 14 14 from hidtools.util import BusType 15 + from .base import HidBpf 15 16 16 17 import logging 17 18 ··· 655 654 656 655 657 656 class TestRaptorMach2Joystick(BaseTest.TestGamepad): 658 - hid_bpfs = [("FR-TEC__Raptor-Mach-2.bpf.o", True)] 657 + hid_bpfs = [HidBpf("FR-TEC__Raptor-Mach-2.bpf.o", True)] 659 658 660 659 def create_device(self): 661 660 return RaptorMach2Joystick(
+2 -1
tools/testing/selftests/hid/tests/test_ite_keyboard.py
··· 11 11 12 12 import libevdev 13 13 import logging 14 + from . import base 14 15 15 16 logger = logging.getLogger("hidtools.test.ite-keyboard") 16 17 17 - KERNEL_MODULE = ("itetech", "hid_ite") 18 + KERNEL_MODULE = base.KernelModule("itetech", "hid_ite") 18 19 19 20 20 21 class KbdData(object):
+1 -1
tools/testing/selftests/hid/tests/test_multitouch.py
··· 17 17 18 18 logger = logging.getLogger("hidtools.test.multitouch") 19 19 20 - KERNEL_MODULE = ("hid-multitouch", "hid_multitouch") 20 + KERNEL_MODULE = base.KernelModule("hid-multitouch", "hid_multitouch") 21 21 22 22 23 23 def BIT(x):
+4 -3
tools/testing/selftests/hid/tests/test_sony.py
··· 7 7 # 8 8 9 9 from .base import application_matches 10 + from .base import KernelModule 10 11 from .test_gamepad import BaseTest 11 12 from hidtools.device.sony_gamepad import ( 12 13 PS3Controller, ··· 25 24 26 25 logger = logging.getLogger("hidtools.test.sony") 27 26 28 - PS3_MODULE = ("sony", "hid_sony") 29 - PS4_MODULE = ("playstation", "hid_playstation") 30 - PS5_MODULE = ("playstation", "hid_playstation") 27 + PS3_MODULE = KernelModule("sony", "hid_sony") 28 + PS4_MODULE = KernelModule("playstation", "hid_playstation") 29 + PS5_MODULE = KernelModule("playstation", "hid_playstation") 31 30 32 31 33 32 class SonyBaseTest:
+4 -3
tools/testing/selftests/hid/tests/test_tablet.py
··· 10 10 import copy 11 11 from enum import Enum 12 12 from hidtools.util import BusType 13 + from .base import HidBpf 13 14 import libevdev 14 15 import logging 15 16 import pytest ··· 1473 1472 1474 1473 1475 1474 class TestXPPen_ArtistPro16Gen2_28bd_095b(BaseTest.TestTablet): 1476 - hid_bpfs = [("XPPen__ArtistPro16Gen2.bpf.o", True)] 1475 + hid_bpfs = [HidBpf("XPPen__ArtistPro16Gen2.bpf.o", True)] 1477 1476 1478 1477 def create_device(self): 1479 1478 dev = XPPen_ArtistPro16Gen2_28bd_095b( ··· 1485 1484 1486 1485 1487 1486 class TestXPPen_Artist24_28bd_093a(BaseTest.TestTablet): 1488 - hid_bpfs = [("XPPen__Artist24.bpf.o", True)] 1487 + hid_bpfs = [HidBpf("XPPen__Artist24.bpf.o", True)] 1489 1488 1490 1489 def create_device(self): 1491 1490 return XPPen_Artist24_28bd_093a( ··· 1496 1495 1497 1496 1498 1497 class TestHuion_Kamvas_Pro_19_256c_006b(BaseTest.TestTablet): 1499 - hid_bpfs = [("Huion__Kamvas-Pro-19.bpf.o", True)] 1498 + hid_bpfs = [HidBpf("Huion__Kamvas-Pro-19.bpf.o", True)] 1500 1499 1501 1500 def create_device(self): 1502 1501 return Huion_Kamvas_Pro_19_256c_006b(
+5 -6
tools/testing/selftests/hid/tests/test_wacom_generic.py
··· 40 40 41 41 logger = logging.getLogger("hidtools.test.wacom") 42 42 43 - KERNEL_MODULE = ("wacom", "wacom") 43 + KERNEL_MODULE = base.KernelModule("wacom", "wacom") 44 44 45 45 46 46 class ProximityState(Enum): ··· 894 894 """ 895 895 return [self.make_contact(id, t) for id in range(0, n)] 896 896 897 - def assert_contact(self, uhdev, evdev, contact_ids, t=0): 897 + def assert_contact(self, evdev, contact_ids, t=0): 898 898 """ 899 899 Assert properties of a contact generated by make_contact. 900 900 """ ··· 916 916 assert evdev.slots[slot_num][libevdev.EV_ABS.ABS_MT_POSITION_X] == x 917 917 assert evdev.slots[slot_num][libevdev.EV_ABS.ABS_MT_POSITION_Y] == y 918 918 919 - def assert_contacts(self, uhdev, evdev, data, t=0): 919 + def assert_contacts(self, evdev, data, t=0): 920 920 """ 921 921 Assert properties of a list of contacts generated by make_contacts. 922 922 """ 923 923 for contact_ids in data: 924 - self.assert_contact(uhdev, evdev, contact_ids, t) 924 + self.assert_contact(evdev, contact_ids, t) 925 925 926 926 def test_contact_id_0(self): 927 927 """ ··· 998 998 assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events 999 999 1000 1000 self.assert_contacts( 1001 - uhdev, 1002 1001 evdev, 1003 1002 [ 1004 1003 self.ContactIds(contact_id=0, tracking_id=-1, slot_num=None), ··· 1031 1032 self.debug_reports(r, uhdev, events) 1032 1033 1033 1034 ids = [x[0] for x in state] 1034 - self.assert_contacts(uhdev, evdev, ids, t) 1035 + self.assert_contacts(evdev, ids, t) 1035 1036 1036 1037 t += 1 1037 1038