1*ffb85d5cSBenjamin Tissoires#!/bin/env python3 2*ffb85d5cSBenjamin Tissoires# SPDX-License-Identifier: GPL-2.0 3*ffb85d5cSBenjamin Tissoires# -*- coding: utf-8 -*- 4*ffb85d5cSBenjamin Tissoires# 5*ffb85d5cSBenjamin Tissoires# Copyright (c) 2017 Benjamin Tissoires <benjamin.tissoires@gmail.com> 6*ffb85d5cSBenjamin Tissoires# Copyright (c) 2017 Red Hat, Inc. 7*ffb85d5cSBenjamin Tissoires 8*ffb85d5cSBenjamin Tissoiresimport libevdev 9*ffb85d5cSBenjamin Tissoiresimport os 10*ffb85d5cSBenjamin Tissoiresimport pytest 11*ffb85d5cSBenjamin Tissoiresimport time 12*ffb85d5cSBenjamin Tissoires 13*ffb85d5cSBenjamin Tissoiresimport logging 14*ffb85d5cSBenjamin Tissoires 15*ffb85d5cSBenjamin Tissoiresfrom hidtools.device.base_device import BaseDevice, EvdevMatch, SysfsFile 16*ffb85d5cSBenjamin Tissoiresfrom pathlib import Path 17*ffb85d5cSBenjamin Tissoiresfrom typing import Final 18*ffb85d5cSBenjamin Tissoires 19*ffb85d5cSBenjamin Tissoireslogger = logging.getLogger("hidtools.test.base") 20*ffb85d5cSBenjamin Tissoires 21*ffb85d5cSBenjamin Tissoires# application to matches 22*ffb85d5cSBenjamin Tissoiresapplication_matches: Final = { 23*ffb85d5cSBenjamin Tissoires # pyright: ignore 24*ffb85d5cSBenjamin Tissoires "Accelerometer": EvdevMatch( 25*ffb85d5cSBenjamin Tissoires req_properties=[ 26*ffb85d5cSBenjamin Tissoires libevdev.INPUT_PROP_ACCELEROMETER, 27*ffb85d5cSBenjamin Tissoires ] 28*ffb85d5cSBenjamin Tissoires ), 29*ffb85d5cSBenjamin Tissoires "Game Pad": EvdevMatch( # in systemd, this is a lot more complex, but that will do 30*ffb85d5cSBenjamin Tissoires requires=[ 31*ffb85d5cSBenjamin Tissoires libevdev.EV_ABS.ABS_X, 32*ffb85d5cSBenjamin Tissoires libevdev.EV_ABS.ABS_Y, 33*ffb85d5cSBenjamin Tissoires libevdev.EV_ABS.ABS_RX, 34*ffb85d5cSBenjamin Tissoires libevdev.EV_ABS.ABS_RY, 35*ffb85d5cSBenjamin Tissoires libevdev.EV_KEY.BTN_START, 36*ffb85d5cSBenjamin Tissoires ], 37*ffb85d5cSBenjamin Tissoires excl_properties=[ 38*ffb85d5cSBenjamin Tissoires libevdev.INPUT_PROP_ACCELEROMETER, 39*ffb85d5cSBenjamin Tissoires ], 40*ffb85d5cSBenjamin Tissoires ), 41*ffb85d5cSBenjamin Tissoires "Joystick": EvdevMatch( # in systemd, this is a lot more complex, but that will do 42*ffb85d5cSBenjamin Tissoires requires=[ 43*ffb85d5cSBenjamin Tissoires libevdev.EV_ABS.ABS_RX, 44*ffb85d5cSBenjamin Tissoires libevdev.EV_ABS.ABS_RY, 45*ffb85d5cSBenjamin Tissoires libevdev.EV_KEY.BTN_START, 46*ffb85d5cSBenjamin Tissoires ], 47*ffb85d5cSBenjamin Tissoires excl_properties=[ 48*ffb85d5cSBenjamin Tissoires libevdev.INPUT_PROP_ACCELEROMETER, 49*ffb85d5cSBenjamin Tissoires ], 50*ffb85d5cSBenjamin Tissoires ), 51*ffb85d5cSBenjamin Tissoires "Key": EvdevMatch( 52*ffb85d5cSBenjamin Tissoires requires=[ 53*ffb85d5cSBenjamin Tissoires libevdev.EV_KEY.KEY_A, 54*ffb85d5cSBenjamin Tissoires ], 55*ffb85d5cSBenjamin Tissoires excl_properties=[ 56*ffb85d5cSBenjamin Tissoires libevdev.INPUT_PROP_ACCELEROMETER, 57*ffb85d5cSBenjamin Tissoires libevdev.INPUT_PROP_DIRECT, 58*ffb85d5cSBenjamin Tissoires libevdev.INPUT_PROP_POINTER, 59*ffb85d5cSBenjamin Tissoires ], 60*ffb85d5cSBenjamin Tissoires ), 61*ffb85d5cSBenjamin Tissoires "Mouse": EvdevMatch( 62*ffb85d5cSBenjamin Tissoires requires=[ 63*ffb85d5cSBenjamin Tissoires libevdev.EV_REL.REL_X, 64*ffb85d5cSBenjamin Tissoires libevdev.EV_REL.REL_Y, 65*ffb85d5cSBenjamin Tissoires libevdev.EV_KEY.BTN_LEFT, 66*ffb85d5cSBenjamin Tissoires ], 67*ffb85d5cSBenjamin Tissoires excl_properties=[ 68*ffb85d5cSBenjamin Tissoires libevdev.INPUT_PROP_ACCELEROMETER, 69*ffb85d5cSBenjamin Tissoires ], 70*ffb85d5cSBenjamin Tissoires ), 71*ffb85d5cSBenjamin Tissoires "Pad": EvdevMatch( 72*ffb85d5cSBenjamin Tissoires requires=[ 73*ffb85d5cSBenjamin Tissoires libevdev.EV_KEY.BTN_0, 74*ffb85d5cSBenjamin Tissoires ], 75*ffb85d5cSBenjamin Tissoires excludes=[ 76*ffb85d5cSBenjamin Tissoires libevdev.EV_KEY.BTN_TOOL_PEN, 77*ffb85d5cSBenjamin Tissoires libevdev.EV_KEY.BTN_TOUCH, 78*ffb85d5cSBenjamin Tissoires libevdev.EV_ABS.ABS_DISTANCE, 79*ffb85d5cSBenjamin Tissoires ], 80*ffb85d5cSBenjamin Tissoires excl_properties=[ 81*ffb85d5cSBenjamin Tissoires libevdev.INPUT_PROP_ACCELEROMETER, 82*ffb85d5cSBenjamin Tissoires ], 83*ffb85d5cSBenjamin Tissoires ), 84*ffb85d5cSBenjamin Tissoires "Pen": EvdevMatch( 85*ffb85d5cSBenjamin Tissoires requires=[ 86*ffb85d5cSBenjamin Tissoires libevdev.EV_KEY.BTN_STYLUS, 87*ffb85d5cSBenjamin Tissoires libevdev.EV_ABS.ABS_X, 88*ffb85d5cSBenjamin Tissoires libevdev.EV_ABS.ABS_Y, 89*ffb85d5cSBenjamin Tissoires ], 90*ffb85d5cSBenjamin Tissoires excl_properties=[ 91*ffb85d5cSBenjamin Tissoires libevdev.INPUT_PROP_ACCELEROMETER, 92*ffb85d5cSBenjamin Tissoires ], 93*ffb85d5cSBenjamin Tissoires ), 94*ffb85d5cSBenjamin Tissoires "Stylus": EvdevMatch( 95*ffb85d5cSBenjamin Tissoires requires=[ 96*ffb85d5cSBenjamin Tissoires libevdev.EV_KEY.BTN_STYLUS, 97*ffb85d5cSBenjamin Tissoires libevdev.EV_ABS.ABS_X, 98*ffb85d5cSBenjamin Tissoires libevdev.EV_ABS.ABS_Y, 99*ffb85d5cSBenjamin Tissoires ], 100*ffb85d5cSBenjamin Tissoires excl_properties=[ 101*ffb85d5cSBenjamin Tissoires libevdev.INPUT_PROP_ACCELEROMETER, 102*ffb85d5cSBenjamin Tissoires ], 103*ffb85d5cSBenjamin Tissoires ), 104*ffb85d5cSBenjamin Tissoires "Touch Pad": EvdevMatch( 105*ffb85d5cSBenjamin Tissoires requires=[ 106*ffb85d5cSBenjamin Tissoires libevdev.EV_KEY.BTN_LEFT, 107*ffb85d5cSBenjamin Tissoires libevdev.EV_ABS.ABS_X, 108*ffb85d5cSBenjamin Tissoires libevdev.EV_ABS.ABS_Y, 109*ffb85d5cSBenjamin Tissoires ], 110*ffb85d5cSBenjamin Tissoires excludes=[libevdev.EV_KEY.BTN_TOOL_PEN, libevdev.EV_KEY.BTN_STYLUS], 111*ffb85d5cSBenjamin Tissoires req_properties=[ 112*ffb85d5cSBenjamin Tissoires libevdev.INPUT_PROP_POINTER, 113*ffb85d5cSBenjamin Tissoires ], 114*ffb85d5cSBenjamin Tissoires excl_properties=[ 115*ffb85d5cSBenjamin Tissoires libevdev.INPUT_PROP_ACCELEROMETER, 116*ffb85d5cSBenjamin Tissoires ], 117*ffb85d5cSBenjamin Tissoires ), 118*ffb85d5cSBenjamin Tissoires "Touch Screen": EvdevMatch( 119*ffb85d5cSBenjamin Tissoires requires=[ 120*ffb85d5cSBenjamin Tissoires libevdev.EV_KEY.BTN_TOUCH, 121*ffb85d5cSBenjamin Tissoires libevdev.EV_ABS.ABS_X, 122*ffb85d5cSBenjamin Tissoires libevdev.EV_ABS.ABS_Y, 123*ffb85d5cSBenjamin Tissoires ], 124*ffb85d5cSBenjamin Tissoires excludes=[libevdev.EV_KEY.BTN_TOOL_PEN, libevdev.EV_KEY.BTN_STYLUS], 125*ffb85d5cSBenjamin Tissoires req_properties=[ 126*ffb85d5cSBenjamin Tissoires libevdev.INPUT_PROP_DIRECT, 127*ffb85d5cSBenjamin Tissoires ], 128*ffb85d5cSBenjamin Tissoires excl_properties=[ 129*ffb85d5cSBenjamin Tissoires libevdev.INPUT_PROP_ACCELEROMETER, 130*ffb85d5cSBenjamin Tissoires ], 131*ffb85d5cSBenjamin Tissoires ), 132*ffb85d5cSBenjamin Tissoires} 133*ffb85d5cSBenjamin Tissoires 134*ffb85d5cSBenjamin Tissoires 135*ffb85d5cSBenjamin Tissoiresclass UHIDTestDevice(BaseDevice): 136*ffb85d5cSBenjamin Tissoires def __init__(self, name, application, rdesc_str=None, rdesc=None, input_info=None): 137*ffb85d5cSBenjamin Tissoires super().__init__(name, application, rdesc_str, rdesc, input_info) 138*ffb85d5cSBenjamin Tissoires self.application_matches = application_matches 139*ffb85d5cSBenjamin Tissoires if name is None: 140*ffb85d5cSBenjamin Tissoires name = f"uhid test {self.__class__.__name__}" 141*ffb85d5cSBenjamin Tissoires if not name.startswith("uhid test "): 142*ffb85d5cSBenjamin Tissoires name = "uhid test " + self.name 143*ffb85d5cSBenjamin Tissoires self.name = name 144*ffb85d5cSBenjamin Tissoires 145*ffb85d5cSBenjamin Tissoires 146*ffb85d5cSBenjamin Tissoiresclass BaseTestCase: 147*ffb85d5cSBenjamin Tissoires class TestUhid(object): 148*ffb85d5cSBenjamin Tissoires syn_event = libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT) # type: ignore 149*ffb85d5cSBenjamin Tissoires key_event = libevdev.InputEvent(libevdev.EV_KEY) # type: ignore 150*ffb85d5cSBenjamin Tissoires abs_event = libevdev.InputEvent(libevdev.EV_ABS) # type: ignore 151*ffb85d5cSBenjamin Tissoires rel_event = libevdev.InputEvent(libevdev.EV_REL) # type: ignore 152*ffb85d5cSBenjamin Tissoires msc_event = libevdev.InputEvent(libevdev.EV_MSC.MSC_SCAN) # type: ignore 153*ffb85d5cSBenjamin Tissoires 154*ffb85d5cSBenjamin Tissoires # List of kernel modules to load before starting the test 155*ffb85d5cSBenjamin Tissoires # if any module is not available (not compiled), the test will skip. 156*ffb85d5cSBenjamin Tissoires # Each element is a tuple '(kernel driver name, kernel module)', 157*ffb85d5cSBenjamin Tissoires # for example ("playstation", "hid-playstation") 158*ffb85d5cSBenjamin Tissoires kernel_modules = [] 159*ffb85d5cSBenjamin Tissoires 160*ffb85d5cSBenjamin Tissoires def assertInputEventsIn(self, expected_events, effective_events): 161*ffb85d5cSBenjamin Tissoires effective_events = effective_events.copy() 162*ffb85d5cSBenjamin Tissoires for ev in expected_events: 163*ffb85d5cSBenjamin Tissoires assert ev in effective_events 164*ffb85d5cSBenjamin Tissoires effective_events.remove(ev) 165*ffb85d5cSBenjamin Tissoires return effective_events 166*ffb85d5cSBenjamin Tissoires 167*ffb85d5cSBenjamin Tissoires def assertInputEvents(self, expected_events, effective_events): 168*ffb85d5cSBenjamin Tissoires remaining = self.assertInputEventsIn(expected_events, effective_events) 169*ffb85d5cSBenjamin Tissoires assert remaining == [] 170*ffb85d5cSBenjamin Tissoires 171*ffb85d5cSBenjamin Tissoires @classmethod 172*ffb85d5cSBenjamin Tissoires def debug_reports(cls, reports, uhdev=None, events=None): 173*ffb85d5cSBenjamin Tissoires data = [" ".join([f"{v:02x}" for v in r]) for r in reports] 174*ffb85d5cSBenjamin Tissoires 175*ffb85d5cSBenjamin Tissoires if uhdev is not None: 176*ffb85d5cSBenjamin Tissoires human_data = [ 177*ffb85d5cSBenjamin Tissoires uhdev.parsed_rdesc.format_report(r, split_lines=True) 178*ffb85d5cSBenjamin Tissoires for r in reports 179*ffb85d5cSBenjamin Tissoires ] 180*ffb85d5cSBenjamin Tissoires try: 181*ffb85d5cSBenjamin Tissoires human_data = [ 182*ffb85d5cSBenjamin Tissoires f'\n\t {" " * h.index("/")}'.join(h.split("\n")) 183*ffb85d5cSBenjamin Tissoires for h in human_data 184*ffb85d5cSBenjamin Tissoires ] 185*ffb85d5cSBenjamin Tissoires except ValueError: 186*ffb85d5cSBenjamin Tissoires # '/' not found: not a numbered report 187*ffb85d5cSBenjamin Tissoires human_data = ["\n\t ".join(h.split("\n")) for h in human_data] 188*ffb85d5cSBenjamin Tissoires data = [f"{d}\n\t ====> {h}" for d, h in zip(data, human_data)] 189*ffb85d5cSBenjamin Tissoires 190*ffb85d5cSBenjamin Tissoires reports = data 191*ffb85d5cSBenjamin Tissoires 192*ffb85d5cSBenjamin Tissoires if len(reports) == 1: 193*ffb85d5cSBenjamin Tissoires print("sending 1 report:") 194*ffb85d5cSBenjamin Tissoires else: 195*ffb85d5cSBenjamin Tissoires print(f"sending {len(reports)} reports:") 196*ffb85d5cSBenjamin Tissoires for report in reports: 197*ffb85d5cSBenjamin Tissoires print("\t", report) 198*ffb85d5cSBenjamin Tissoires 199*ffb85d5cSBenjamin Tissoires if events is not None: 200*ffb85d5cSBenjamin Tissoires print("events received:", events) 201*ffb85d5cSBenjamin Tissoires 202*ffb85d5cSBenjamin Tissoires def create_device(self): 203*ffb85d5cSBenjamin Tissoires raise Exception("please reimplement me in subclasses") 204*ffb85d5cSBenjamin Tissoires 205*ffb85d5cSBenjamin Tissoires def _load_kernel_module(self, kernel_driver, kernel_module): 206*ffb85d5cSBenjamin Tissoires sysfs_path = Path("/sys/bus/hid/drivers") 207*ffb85d5cSBenjamin Tissoires if kernel_driver is not None: 208*ffb85d5cSBenjamin Tissoires sysfs_path /= kernel_driver 209*ffb85d5cSBenjamin Tissoires else: 210*ffb85d5cSBenjamin Tissoires # special case for when testing all available modules: 211*ffb85d5cSBenjamin Tissoires # we don't know beforehand the name of the module from modinfo 212*ffb85d5cSBenjamin Tissoires sysfs_path = Path("/sys/module") / kernel_module.replace("-", "_") 213*ffb85d5cSBenjamin Tissoires if not sysfs_path.exists(): 214*ffb85d5cSBenjamin Tissoires import subprocess 215*ffb85d5cSBenjamin Tissoires 216*ffb85d5cSBenjamin Tissoires ret = subprocess.run(["/usr/sbin/modprobe", kernel_module]) 217*ffb85d5cSBenjamin Tissoires if ret.returncode != 0: 218*ffb85d5cSBenjamin Tissoires pytest.skip( 219*ffb85d5cSBenjamin Tissoires f"module {kernel_module} could not be loaded, skipping the test" 220*ffb85d5cSBenjamin Tissoires ) 221*ffb85d5cSBenjamin Tissoires 222*ffb85d5cSBenjamin Tissoires @pytest.fixture() 223*ffb85d5cSBenjamin Tissoires def load_kernel_module(self): 224*ffb85d5cSBenjamin Tissoires for kernel_driver, kernel_module in self.kernel_modules: 225*ffb85d5cSBenjamin Tissoires self._load_kernel_module(kernel_driver, kernel_module) 226*ffb85d5cSBenjamin Tissoires yield 227*ffb85d5cSBenjamin Tissoires 228*ffb85d5cSBenjamin Tissoires @pytest.fixture() 229*ffb85d5cSBenjamin Tissoires def new_uhdev(self, load_kernel_module): 230*ffb85d5cSBenjamin Tissoires return self.create_device() 231*ffb85d5cSBenjamin Tissoires 232*ffb85d5cSBenjamin Tissoires def assertName(self, uhdev): 233*ffb85d5cSBenjamin Tissoires evdev = uhdev.get_evdev() 234*ffb85d5cSBenjamin Tissoires assert uhdev.name in evdev.name 235*ffb85d5cSBenjamin Tissoires 236*ffb85d5cSBenjamin Tissoires @pytest.fixture(autouse=True) 237*ffb85d5cSBenjamin Tissoires def context(self, new_uhdev, request): 238*ffb85d5cSBenjamin Tissoires try: 239*ffb85d5cSBenjamin Tissoires with HIDTestUdevRule.instance(): 240*ffb85d5cSBenjamin Tissoires with new_uhdev as self.uhdev: 241*ffb85d5cSBenjamin Tissoires skip_cond = request.node.get_closest_marker("skip_if_uhdev") 242*ffb85d5cSBenjamin Tissoires if skip_cond: 243*ffb85d5cSBenjamin Tissoires test, message, *rest = skip_cond.args 244*ffb85d5cSBenjamin Tissoires 245*ffb85d5cSBenjamin Tissoires if test(self.uhdev): 246*ffb85d5cSBenjamin Tissoires pytest.skip(message) 247*ffb85d5cSBenjamin Tissoires 248*ffb85d5cSBenjamin Tissoires self.uhdev.create_kernel_device() 249*ffb85d5cSBenjamin Tissoires now = time.time() 250*ffb85d5cSBenjamin Tissoires while not self.uhdev.is_ready() and time.time() - now < 5: 251*ffb85d5cSBenjamin Tissoires self.uhdev.dispatch(1) 252*ffb85d5cSBenjamin Tissoires if self.uhdev.get_evdev() is None: 253*ffb85d5cSBenjamin Tissoires logger.warning( 254*ffb85d5cSBenjamin Tissoires f"available list of input nodes: (default application is '{self.uhdev.application}')" 255*ffb85d5cSBenjamin Tissoires ) 256*ffb85d5cSBenjamin Tissoires logger.warning(self.uhdev.input_nodes) 257*ffb85d5cSBenjamin Tissoires yield 258*ffb85d5cSBenjamin Tissoires self.uhdev = None 259*ffb85d5cSBenjamin Tissoires except PermissionError: 260*ffb85d5cSBenjamin Tissoires pytest.skip("Insufficient permissions, run me as root") 261*ffb85d5cSBenjamin Tissoires 262*ffb85d5cSBenjamin Tissoires @pytest.fixture(autouse=True) 263*ffb85d5cSBenjamin Tissoires def check_taint(self): 264*ffb85d5cSBenjamin Tissoires # we are abusing SysfsFile here, it's in /proc, but meh 265*ffb85d5cSBenjamin Tissoires taint_file = SysfsFile("/proc/sys/kernel/tainted") 266*ffb85d5cSBenjamin Tissoires taint = taint_file.int_value 267*ffb85d5cSBenjamin Tissoires 268*ffb85d5cSBenjamin Tissoires yield 269*ffb85d5cSBenjamin Tissoires 270*ffb85d5cSBenjamin Tissoires assert taint_file.int_value == taint 271*ffb85d5cSBenjamin Tissoires 272*ffb85d5cSBenjamin Tissoires def test_creation(self): 273*ffb85d5cSBenjamin Tissoires """Make sure the device gets processed by the kernel and creates 274*ffb85d5cSBenjamin Tissoires the expected application input node. 275*ffb85d5cSBenjamin Tissoires 276*ffb85d5cSBenjamin Tissoires If this fail, there is something wrong in the device report 277*ffb85d5cSBenjamin Tissoires descriptors.""" 278*ffb85d5cSBenjamin Tissoires uhdev = self.uhdev 279*ffb85d5cSBenjamin Tissoires assert uhdev is not None 280*ffb85d5cSBenjamin Tissoires assert uhdev.get_evdev() is not None 281*ffb85d5cSBenjamin Tissoires self.assertName(uhdev) 282*ffb85d5cSBenjamin Tissoires assert len(uhdev.next_sync_events()) == 0 283*ffb85d5cSBenjamin Tissoires assert uhdev.get_evdev() is not None 284*ffb85d5cSBenjamin Tissoires 285*ffb85d5cSBenjamin Tissoires 286*ffb85d5cSBenjamin Tissoiresclass HIDTestUdevRule(object): 287*ffb85d5cSBenjamin Tissoires _instance = None 288*ffb85d5cSBenjamin Tissoires """ 289*ffb85d5cSBenjamin Tissoires A context-manager compatible class that sets up our udev rules file and 290*ffb85d5cSBenjamin Tissoires deletes it on context exit. 291*ffb85d5cSBenjamin Tissoires 292*ffb85d5cSBenjamin Tissoires This class is tailored to our test setup: it only sets up the udev rule 293*ffb85d5cSBenjamin Tissoires on the **second** context and it cleans it up again on the last context 294*ffb85d5cSBenjamin Tissoires removed. This matches the expected pytest setup: we enter a context for 295*ffb85d5cSBenjamin Tissoires the session once, then once for each test (the first of which will 296*ffb85d5cSBenjamin Tissoires trigger the udev rule) and once the last test exited and the session 297*ffb85d5cSBenjamin Tissoires exited, we clean up after ourselves. 298*ffb85d5cSBenjamin Tissoires """ 299*ffb85d5cSBenjamin Tissoires 300*ffb85d5cSBenjamin Tissoires def __init__(self): 301*ffb85d5cSBenjamin Tissoires self.refs = 0 302*ffb85d5cSBenjamin Tissoires self.rulesfile = None 303*ffb85d5cSBenjamin Tissoires 304*ffb85d5cSBenjamin Tissoires def __enter__(self): 305*ffb85d5cSBenjamin Tissoires self.refs += 1 306*ffb85d5cSBenjamin Tissoires if self.refs == 2 and self.rulesfile is None: 307*ffb85d5cSBenjamin Tissoires self.create_udev_rule() 308*ffb85d5cSBenjamin Tissoires self.reload_udev_rules() 309*ffb85d5cSBenjamin Tissoires 310*ffb85d5cSBenjamin Tissoires def __exit__(self, exc_type, exc_value, traceback): 311*ffb85d5cSBenjamin Tissoires self.refs -= 1 312*ffb85d5cSBenjamin Tissoires if self.refs == 0 and self.rulesfile: 313*ffb85d5cSBenjamin Tissoires os.remove(self.rulesfile.name) 314*ffb85d5cSBenjamin Tissoires self.reload_udev_rules() 315*ffb85d5cSBenjamin Tissoires 316*ffb85d5cSBenjamin Tissoires def reload_udev_rules(self): 317*ffb85d5cSBenjamin Tissoires import subprocess 318*ffb85d5cSBenjamin Tissoires 319*ffb85d5cSBenjamin Tissoires subprocess.run("udevadm control --reload-rules".split()) 320*ffb85d5cSBenjamin Tissoires subprocess.run("systemd-hwdb update".split()) 321*ffb85d5cSBenjamin Tissoires 322*ffb85d5cSBenjamin Tissoires def create_udev_rule(self): 323*ffb85d5cSBenjamin Tissoires import tempfile 324*ffb85d5cSBenjamin Tissoires 325*ffb85d5cSBenjamin Tissoires os.makedirs("/run/udev/rules.d", exist_ok=True) 326*ffb85d5cSBenjamin Tissoires with tempfile.NamedTemporaryFile( 327*ffb85d5cSBenjamin Tissoires prefix="91-uhid-test-device-REMOVEME-", 328*ffb85d5cSBenjamin Tissoires suffix=".rules", 329*ffb85d5cSBenjamin Tissoires mode="w+", 330*ffb85d5cSBenjamin Tissoires dir="/run/udev/rules.d", 331*ffb85d5cSBenjamin Tissoires delete=False, 332*ffb85d5cSBenjamin Tissoires ) as f: 333*ffb85d5cSBenjamin Tissoires f.write( 334*ffb85d5cSBenjamin Tissoires 'KERNELS=="*input*", ATTRS{name}=="*uhid test *", ENV{LIBINPUT_IGNORE_DEVICE}="1"\n' 335*ffb85d5cSBenjamin Tissoires ) 336*ffb85d5cSBenjamin Tissoires f.write( 337*ffb85d5cSBenjamin Tissoires 'KERNELS=="*input*", ATTRS{name}=="*uhid test * System Multi Axis", ENV{ID_INPUT_TOUCHSCREEN}="", ENV{ID_INPUT_SYSTEM_MULTIAXIS}="1"\n' 338*ffb85d5cSBenjamin Tissoires ) 339*ffb85d5cSBenjamin Tissoires self.rulesfile = f 340*ffb85d5cSBenjamin Tissoires 341*ffb85d5cSBenjamin Tissoires @classmethod 342*ffb85d5cSBenjamin Tissoires def instance(cls): 343*ffb85d5cSBenjamin Tissoires if not cls._instance: 344*ffb85d5cSBenjamin Tissoires cls._instance = HIDTestUdevRule() 345*ffb85d5cSBenjamin Tissoires return cls._instance 346