Commit 642f9b2d authored by Benjamin Tissoires's avatar Benjamin Tissoires
Browse files

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: default avatarPeter Hutterer <peter.hutterer@who-t.net>
Link: https://patch.msgid.link/20250709-wip-fix-ci-v1-2-b7df4c271cf8@kernel.org


Signed-off-by: default avatarBenjamin Tissoires <bentiss@kernel.org>
parent c85a8cb9
Loading
Loading
Loading
Loading
+30 −16
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
# Copyright (c) 2017 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# Copyright (c) 2017 Red Hat, Inc.

import dataclasses
import libevdev
import os
import pytest
@@ -145,6 +146,18 @@ class UHIDTestDevice(BaseDevice):
        self.name = name


@dataclasses.dataclass
class HidBpf:
    object_name: str
    has_rdesc_fixup: bool


@dataclasses.dataclass
class KernelModule:
    driver_name: str
    module_name: str


class BaseTestCase:
    class TestUhid(object):
        syn_event = libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT)  # type: ignore
@@ -155,20 +168,20 @@ class BaseTestCase:

        # List of kernel modules to load before starting the test
        # if any module is not available (not compiled), the test will skip.
        # Each element is a tuple '(kernel driver name, kernel module)',
        # for example ("playstation", "hid-playstation")
        kernel_modules: List[Tuple[str, str]] = []
        # Each element is a KernelModule object, for example
        # KernelModule("playstation", "hid-playstation")
        kernel_modules: List[KernelModule] = []

        # List of in kernel HID-BPF object files to load
        # before starting the test
        # Any existing pre-loaded HID-BPF module will be removed
        # before the ones in this list will be manually loaded.
        # Each Element is a tuple '(hid_bpf_object, rdesc_fixup_present)',
        # for example '("xppen-ArtistPro16Gen2.bpf.o", True)'
        # If 'rdesc_fixup_present' is True, the test needs to wait
        # Each Element is a HidBpf object, for example
        # 'HidBpf("xppen-ArtistPro16Gen2.bpf.o", True)'
        # If 'has_rdesc_fixup' is True, the test needs to wait
        # for one unbind and rebind before it can be sure the kernel is
        # ready
        hid_bpfs: List[Tuple[str, bool]] = []
        hid_bpfs: List[HidBpf] = []

        def assertInputEventsIn(self, expected_events, effective_events):
            effective_events = effective_events.copy()
@@ -232,25 +245,26 @@ class BaseTestCase:

        @pytest.fixture()
        def load_kernel_module(self):
            for kernel_driver, kernel_module in self.kernel_modules:
                self._load_kernel_module(kernel_driver, kernel_module)
            for k in self.kernel_modules:
                self._load_kernel_module(k.driver_name, k.module_name)
            yield

        def load_hid_bpfs(self):
            # this function will only work when run in the kernel tree
            script_dir = Path(os.path.dirname(os.path.realpath(__file__)))
            root_dir = (script_dir / "../../../../..").resolve()
            bpf_dir = root_dir / "drivers/hid/bpf/progs"

            if not bpf_dir.exists():
                pytest.skip("looks like we are not in the kernel tree, skipping")

            udev_hid_bpf = shutil.which("udev-hid-bpf")
            if not udev_hid_bpf:
                pytest.skip("udev-hid-bpf not found in $PATH, skipping")

            wait = False
            for _, rdesc_fixup in self.hid_bpfs:
                if rdesc_fixup:
                    wait = True
            wait = any(b.has_rdesc_fixup for b in self.hid_bpfs)

            for hid_bpf, _ in self.hid_bpfs:
            for hid_bpf in self.hid_bpfs:
                # We need to start `udev-hid-bpf` in the background
                # and dispatch uhid events in case the kernel needs
                # to fetch features on the device
@@ -260,13 +274,13 @@ class BaseTestCase:
                        "--verbose",
                        "add",
                        str(self.uhdev.sys_path),
                        str(bpf_dir / hid_bpf),
                        str(bpf_dir / hid_bpf.object_name),
                    ],
                )
                while process.poll() is None:
                    self.uhdev.dispatch(1)

                if process.poll() != 0:
                if process.returncode != 0:
                    pytest.fail(
                        f"Couldn't insert hid-bpf program '{hid_bpf}', marking the test as failed"
                    )
+19 −11
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import dataclasses
import fcntl
import functools
import libevdev
@@ -104,6 +105,12 @@ class PowerSupply(object):
        return self._type.str_value


@dataclasses.dataclass
class HidReadiness:
    is_ready: bool = False
    count: int = 0


class HIDIsReady(object):
    """
    Companion class that binds to a kernel mechanism
@@ -115,18 +122,18 @@ class HIDIsReady(object):
    def __init__(self: "HIDIsReady", uhid: UHIDDevice) -> None:
        self.uhid = uhid

    def is_ready(self: "HIDIsReady") -> bool:
    def is_ready(self: "HIDIsReady") -> HidReadiness:
        """
        Overwrite in subclasses: should return True or False whether
        the attached uhid device is ready or not.
        """
        return False
        return HidReadiness()


class UdevHIDIsReady(HIDIsReady):
    _pyudev_context: ClassVar[Optional[pyudev.Context]] = None
    _pyudev_monitor: ClassVar[Optional[pyudev.Monitor]] = None
    _uhid_devices: ClassVar[Dict[int, Tuple[bool, int]]] = {}
    _uhid_devices: ClassVar[Dict[int, HidReadiness]] = {}

    def __init__(self: "UdevHIDIsReady", uhid: UHIDDevice) -> None:
        super().__init__(uhid)
@@ -157,18 +164,19 @@ class UdevHIDIsReady(HIDIsReady):

            id = int(event.sys_path.strip().split(".")[-1], 16)

            device_ready, count = cls._uhid_devices.get(id, (False, 0))
            readiness = cls._uhid_devices.setdefault(id, HidReadiness())

            ready = event.action == "bind"
            if not device_ready and ready:
                count += 1
            cls._uhid_devices[id] = (ready, count)
            if not readiness.is_ready and ready:
                readiness.count += 1

            readiness.is_ready = ready

    def is_ready(self: "UdevHIDIsReady") -> Tuple[bool, int]:
    def is_ready(self: "UdevHIDIsReady") -> HidReadiness:
        try:
            return self._uhid_devices[self.uhid.hid_id]
        except KeyError:
            return (False, 0)
            return HidReadiness()


class EvdevMatch(object):
@@ -322,11 +330,11 @@ class BaseDevice(UHIDDevice):

    @property
    def kernel_is_ready(self: "BaseDevice") -> bool:
        return self._kernel_is_ready.is_ready()[0] and self.started
        return self._kernel_is_ready.is_ready().is_ready and self.started

    @property
    def kernel_ready_count(self: "BaseDevice") -> int:
        return self._kernel_is_ready.is_ready()[1]
        return self._kernel_is_ready.is_ready().count

    @property
    def input_nodes(self: "BaseDevice") -> List[EvdevDevice]:
+2 −1
Original line number Diff line number Diff line
@@ -8,13 +8,14 @@

from .test_keyboard import ArrayKeyboard, TestArrayKeyboard
from hidtools.util import BusType
from . import base

import libevdev
import logging

logger = logging.getLogger("hidtools.test.apple-keyboard")

KERNEL_MODULE = ("apple", "hid-apple")
KERNEL_MODULE = base.KernelModule("apple", "hid-apple")


class KbdData(object):
+2 −1
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ import pytest

from .base_gamepad import BaseGamepad, JoystickGamepad, AxisMapping
from hidtools.util import BusType
from .base import HidBpf

import logging

@@ -654,7 +655,7 @@ class TestAsusGamepad(BaseTest.TestGamepad):


class TestRaptorMach2Joystick(BaseTest.TestGamepad):
    hid_bpfs = [("FR-TEC__Raptor-Mach-2.bpf.o", True)]
    hid_bpfs = [HidBpf("FR-TEC__Raptor-Mach-2.bpf.o", True)]

    def create_device(self):
        return RaptorMach2Joystick(
+2 −1
Original line number Diff line number Diff line
@@ -11,10 +11,11 @@ from hidtools.util import BusType

import libevdev
import logging
from . import base

logger = logging.getLogger("hidtools.test.ite-keyboard")

KERNEL_MODULE = ("itetech", "hid_ite")
KERNEL_MODULE = base.KernelModule("itetech", "hid_ite")


class KbdData(object):
Loading